Live Coding : Faire des recherches avec OpenStreetMap

Temps de lecture : 26 minutes environ.

Lorsqu'on génère une carte, il est parfois nécessaire de créer un formulaire permettant de faire des recherches et de filtrer les marqueurs affichés.

Dans l'exemple ci-dessous, nous allons mettre en place un formulaire contenant 2 informations :

  • La ville sur laquelle se centre la recherche
  • La distance souhaitée pour la recherche

Fonctionnement

Lors de la saisie de la ville, nous allons récupérer ses coordonnées GPS avec Nominatim, les stocker et enfin centrer la carte sur la ville.

Lors du choix de la distance, nous allons tracer un cercle sur la carte, rechercher tous les marqueurs se trouvant dans la zone en question et les afficher sur la carte.

Le code HTML et CSS

Nous allons définir le code HTML de la page, contenant la carte et les champs de formulaire.

Ce code a déjà été traité dans d'autres tutoriels, et sera le suivant

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>

<!-- Fichiers CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<!--Carte-->
<div id="map"></div>

<!--Champs de recherche-->
<p>
<label for="champ-ville">Ville : </label>
<input type="text" id="champ-ville">
</p>
<p>
<label for="champ-distance">Distance : </label>
<input type="range" min="1" max="200" id="champ-distance">
</p>
<p id="valeur-distance"></p>

<!-- Fichiers JS -->
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>
<script src="js/scripts.js"></script>
</body>
</html>

Au niveau du CSS, nous devons définir une hauteur pour la carte, le CSS sera donc

#map{
height: 600px;
}

Initialiser la carte et les variables

Il est maintenant nécessaire d'initialiser la carte et les variables dont nous aurons besoin.

Nous allons ajouter le code suivant dans "scripts.js"

// Variables globales
let ville = distance = ""

window.onload = () => {
// On initialise la carte et on la centre sur Paris
let carte = L.map('map').setView([48.852969, 2.349903], 13);

// On charge les "tuiles"
L.tileLayer('https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', {
// Il est toujours bien de laisser le lien vers la source des données
attribution: 'données © <a href="//osm.org/copyright">OpenStreetMap</a>/ODbL - rendu <a href="//openstreetmap.fr">OSM France</a>',
minZoom: 1,
maxZoom: 20,
name: 'tiles' // permettra de ne pas supprimer cette couche
}).addTo(carte);
}

Gestion des champs

Nous allons maintenant gérer les modifications que l'utilisateur peut apporter à nos champs, en saisissant une ville et une distance.

Pour ce faire, nous allons mettre en place des écouteurs d'évènements "change" avec le code suivant

// On récupère les champs de la page
let champVille = document.getElementById('champ-ville')
let champDistance = document.getElementById('champ-distance')
let valeurDistance = document.getElementById('valeur-distance')


// On écoute l'évènement "change" sur le champ ville
champVille.addEventListener("change", function(){
// Ici nous chercherons les coordonnées GPS de la ville saisie
})

champDistance.addEventListener("change", function(){
// On récupère la distance choisie
distance = this.value

// On écrit cette valeur sur la page
valeurDistance.innerText = distance + " km"

// Ici nous chercherons les agences correspondant à la localisation souhaitée
})

Création d'une fonction "Ajax"

Afin de trouver les coordonnées GPS, nous utiliserons des requêtes Ajax. Pour optimiser le code, nous allons créer une fonction qui pourra être utilisée autant de fois que nécessaire.

Cette fonction contiendra le code suivant

/**
* Cette fonction effectue un appel Ajax vers une url et retourne une promesse
* @param {string} url
*/
function ajaxGet(url){
return new Promise(function(resolve, reject){
// Nous allons gérer la promesse
let xmlhttp = new XMLHttpRequest();

xmlhttp.onreadystatechange = function(){
// Si le traitement est terminé
if(xmlhttp.readyState == 4){
// Si le traitement est un succès
if(xmlhttp.status == 200){
// On résoud la promesse et on renvoie la réponse
resolve(xmlhttp.responseText);
}else{
// On résoud la promesse et on envoie l'erreur
reject(xmlhttp);
}
}
}

// Si une erreur est survenue
xmlhttp.onerror = function(error){
// On résoud la promesse et on envoie l'erreur
reject(error);
}

// On ouvre la requête
xmlhttp.open('GET', url, true);

// On envoie la requête
xmlhttp.send(null);
})
}

Recherche des coordonnées GPS de la ville

Quand l'utilisateur aura saisi une ville, nous allons utiliser Nominatim pour trouver ses coordonnées GPS.

ATTENTION : ceci est la version "simple", vous devez mettre en place une vérification en cas de doublon de ville.

On modifiera donc l'écouteur d'évènements comme suit

champVille.addEventListener("change", function(){
// On envoie la requête ajax vers nominatim et on traite la réponse
ajaxGet(`https://nominatim.openstreetmap.org/search?q=${this.value}&format=json&addressdetails=1&limit=1&polygon_svg=1`).then(reponse => {
// On convertit la réponse en objet Javascript
let data = JSON.parse(reponse)

// On stocke la latitude et la longitude dans la variable ville
ville = [data[0].lat, data[0].lon]

// On centre la carte sur la ville
carte.panTo(ville)
})
})

A partir de ce moment, notre variable "ville" contient un tableau avec la latitude et la longitude de la ville.

Extraction des données de la base de données

Nous allons utiliser une base de données contenant les différentes agences que nous souhaitons afficher sur la carte.

Cette base de données, volontairement simple, contiendra une seule table appelée "agences". Elle contiendra les données ci-dessous (données exemple)

La requête SQL

Pour effectuer une recherche géographique, nous allons devoir utiliser une requête SQL faisant appel à des formules mathématiques qui permettront de définir des limites circulaires.

Cette requête ressemblera à ceci, en partant des valeurs exemple suivantes :

  • Latitude : 48.85296900
  • Longitude : 2.34990300
  • Distance : 50 km
SELECT id, nom, lat, lon, ( 6371 * acos( cos( radians(48.85296900) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(2.34990300) ) + sin( radians(48.85296900) ) * sin( radians( lat ) ) ) ) AS distance FROM `agences` HAVING distance < 50 ORDER BY distance

Le code PHP

Le fichier PHP, que nous appellerons "chargeAgences.php" contiendra donc

<?php
// On autorise les requêtes Ajax pour toutes les sources
header('Access-Control-Allow-Origin: *');

// On vérifie qu'on utilise la méthode GET
if($_SERVER['REQUEST_METHOD'] == 'GET'){
// Ici on utilise la méthode GET
// On se connecte à la base
require_once('connect.php');

// // On récupère les données dans la base

$sql = "SELECT id, nom, lat, lon, ( 6371 * acos( cos( radians(:lat) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians(:lon) ) + sin( radians(:lat) ) * sin( radians( lat ) ) ) ) AS distance FROM `agences` HAVING distance < :distance ORDER BY distance";

$query = $db->prepare($sql);

$query->bindValue(':lat', $_GET['lat'], PDO::PARAM_STR);
$query->bindValue(':lon', $_GET['lon'], PDO::PARAM_STR);
$query->bindValue(':distance', $_GET['distance'], PDO::PARAM_INT);
$query->execute();
$result = $query->fetchAll();

// // On envoie le code de confirmation
http_response_code(200);

// // On envoie les données en json
echo json_encode($result);

// On se déconnecte de la base
require_once('close.php');
}else{
http_response_code(405);
echo 'La méthode n\'est pas autorisée';
}

Charger les marqueurs et les afficher

Nous allons maintenant revenir dans le javascript et charger les marqueurs correspondant à la recherche de l'utilisateur.

Cette recherche fonctionnera comme suit :

  • Nous appelons le fichier PHP en lui transmettant la latitude, la longitude et la distance
  • Nous recevons la réponse
  • Nous effaçons la carte (si des marqueurs sont déjà placés)
  • Nous traçons un cercle du rayon choisi
  • Nous affichons les marqueurs
  • Nous centrons la carte

Tout ceci s'effectuera dans l'écouteur d'évènements placé sur le choix de la distance.

Le code sera le suivant

champDistance.addEventListener("change", function(){
// On récupère la distance choisie
distance = this.value

// On écrit cette valeur sur la page
valeurDistance.innerText = distance + " km"

// On vérifie si une ville a été saisie
if(ville != ""){
// On envoie les données au serveur
ajaxGet(`http://agences-osm.test/chargeAgences.php?lat=${ville[0]}&lon=${ville[1]}&distance=${distance}`).then(reponse => {
// On efface toutes les couches de la carte sauf les tuiles
carte.eachLayer(function (layer) {
if(layer.options.name != "tiles") carte.removeLayer(layer);
});

// On trace le cercle de rayon "distance"
let circle = L.circle(ville, {
color: '#4471C4',
fillColor: '#4471C4',
fillOpacity: 0.3,
radius: distance * 1000,
}).addTo(carte);

// On convertit la réponse en objet Javascript
let donnees = JSON.parse(reponse)

// On boucle sur les données (ES8)
Object.entries(donnees).forEach(agence => {
// Ici j'ai une seule agence
// On crée un marqueur pour l'agence
let marker = L.marker([agence[1].lat, agence[1].lon]).addTo(carte)
marker.bindPopup(agence[1].nom)
})

// On centre et on zoome sur le cercle
bounds = circle.getBounds();
carte.fitBounds(bounds);
})
}
})

Et voilà, la recherche est fonctionnelle

Obtenir de l'aide

Pour obtenir de l'aide, vous pouvez accéder au serveur Discord pour une entraide par chat

Live Coding : Faire des recherches avec OpenStreetMap
Article publié le - Modifié le

Catégories : CSS HTML Javascript PHP Tutoriel Live-Coding openstreetmap

Mots-clés : Tutoriel Javascript MySQL Openstreetmap PHP Live-Coding css html5 ES6 leafletjs recherches

Partager : Partager sur Facebook Partager sur Twitter Partager sur LinkedIn