En réalisant un module todd_weather de prévisions métgéorologiques, j'ai eu besoin de pouvoir accéder aux coordonnées GPS d'un lieu. Comment récupérer ces coordonnées GPS pour une ville connaissant son nom ?
Voici deux solutions, une en back end via PHP, une autre en front-end via JS.
Open Meteo Geocoding (PHP)
Utilisation de l'API open-meteo-geocoding. On peut interroger la base pour n'importe quelle ville au monde mais pas de précision pour les petite villes en France.
require_once __DIR__ . '/../vendor/autoload.php';
use Flibidi67\OpenMeteoGeocoding\Service\GeocodingService;
function getCoordinates($city, $code)
{
$latitude = '';
$longitude = '';
try {
$api = new GeocodingService();
$response = $api->get($city);
// 1. On vérifie que la réponse n'est pas vide et qu'elle contient bien le premier index
if (!empty($response) && isset($response[0])) {
// 2. On isole le premier résultat (la ville principale)
$firstResult = $response[0];
// 3. On extrait les coordonnées exactes
$latitude = $firstResult['latitude'];
$longitude = $firstResult['longitude'];
// 4. Réponse
return ['latitude' => $latitude, 'longitude' => $longitude];
} else {
return;
}
} catch (Exception $e) {
echo "<div style='color: white; background: red; padding: 10px;'>";
echo "<strong>Erreur :</strong> " . $e->getMessage();
echo "</div>";
}
}geo.api.gouv.fr (JS)
On utilise une API développée par l'État Français rescensant toutes le communes de France avec leurs codes postaux, leurs coordonnées,etc.
Voici le code JavaScript permettant de :
- créer une suggestion dans un champ de saisi texte basée sur des résultats d'interrogation de l'API
- remplir les champs cachés d'un formulaire par les valeurs récupérées lors du clic sur un nom de la liste.
document.addEventListener('DOMContentLoaded', function () {
const input = document.getElementById('weather_city_input');
// Empêche la soumission du formulaire si on tape sur Entrée dans le champ de recherche
input.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
}
});
const suggestionsList = document.getElementById('weather_suggestions');
const hiddenName = document.getElementById('weather_city_name');
const hiddenCode = document.getElementById('weather_postal_code');
const hiddenLat = document.getElementById('weather_lat');
const hiddenLon = document.getElementById('weather_lon');
let timeoutId;
input.addEventListener('input', function () {
clearTimeout(timeoutId);
const query = this.value.trim();
// On ne cherche qu'à partir de 2 caractères
if (query.length < 2) {
suggestionsList.style.display = 'none';
return;
}
// Debounce : on attend 300ms après la dernière frappe pour ne pas spammer l'API
timeoutId = setTimeout(() => {
// Détection si c'est un code postal (que des chiffres) ou un nom
const isPostalCode = /^\d+$/.test(query);
const param = isPostalCode ? `codePostal=${query}` : `nom=${query}`;
// Appel à l'API Geo Gouv avec une limite fixée à 5 résultats
fetch(`https://geo.api.gouv.fr/communes?${param}&fields=nom,code,codesPostaux,centre&format=json&geometry=centre&limit=5`)
.then(response => response.json())
.then(data => {
suggestionsList.innerHTML = '';
if (data.length > 0) {
data.forEach(city => {
const li = document.createElement('li');
// Format d'affichage : "Toulouse (31000)"
li.textContent = `${city.nom} (${city.codesPostaux[0]})`;
// Au clic sur une suggestion
li.addEventListener('click', () => {
// 1. On remplit l'input visible
input.value = li.textContent;
// 2. On remplit les champs cachés pour la sauvegarde en BDD
hiddenName.value = city.nom;
hiddenCode.value = city.codesPostaux[0];
// L'API gouv renvoie [longitude, latitude]
hiddenLon.value = city.centre.coordinates[0];
hiddenLat.value = city.centre.coordinates[1];
// 3. On cache la liste
suggestionsList.style.display = 'none';
console.log('Nom : ' + hiddenName.value + ' Code postal : ' + hiddenCode.value + ' Latitude : ' + hiddenLat.value + ' Longitude : ' + hiddenLon.value);
});
suggestionsList.appendChild(li);
});
suggestionsList.style.display = 'block';
} else {
suggestionsList.style.display = 'none';
}
})
.catch(err => console.error("Erreur API Geo:", err));
}, 300);
});
// Cacher la liste si on clique ailleurs sur la page
document.addEventListener('click', function (e) {
if (!e.target.closest('.todd-autocomplete')) {
suggestionsList.style.display = 'none';
}
});
});Le formulaire qui va avec ce code :
<form method="POST" action="" id="weather_form">
<label for="weather_city_input">Rechercher une ville ou un code postal :</label>
<input type="text" id="weather_city_input" class="form-control" placeholder="Ex: 31000 ou Toulouse"
autocomplete="off">
<input type="hidden" id="weather_city_name" name="city_name">
<input type="hidden" id="weather_postal_code" name="postal_code">
<input type="hidden" id="weather_lat" name="latitude">
<input type="hidden" id="weather_lon" name="longitude">
<ul id="weather_suggestions" class="suggestions-list" style="display: none;"></ul>
<button type="submit" class="btn btn-primary">Submit</button>
</form>