Exemple de géolocalisation d’une adresse IP sur une carte

Cet exemple présente un composant permettant de géolocaliser une adresse IP sur une carte avec l’affichage des informations liées à cette adresse. Le composant réutilise le composant MapContainer de l’exemple précédent pour la partie affichage de la carte.

Description du composant

Le composant affiche un champ de recherche pouvant contenir une adresse IP en version V4 ou V6 et une carte par défaut centrée sur la France puis sur les coordonnées de votre adresse IP si cette information a été trouvée.

Un marqueur est placé sur la carte avec une bulle ouverte récapitulant les diverses informations : adresse IP, continent, pays, région, ville, latitude, longitude, etc.

Dans le champ de recherche, il est possible de saisir ou coller une autre adresse IP et en validant, la carte est mise à jour si les informations de cette nouvelle adresse IP ont été trouvées.

Le composant utilise l’API du site GeoJS pour obtenir les informations d’une adresse IP.

Le composant est une fonction nommée IPGeolocation prenant un paramètre ayant 2 propriétés :

  1. mapProvider : un objet requis décrivant le fournisseur des fonds de cartes,
  2. ipAddress : une chaîne de caractères non requise contenant une adresse IP au format V4 ou V6.

Si la propriété ipAddress est fournie, le champ de saisie contient sa valeur et la carte est centrée sur l’adresse IP (si ses informations sont trouvées).

Code du composant

Le code du composant IPGeolocation est représenté ici.

async function IPGeolocation({mapProvider, ipAddress = ""}) {
const {div, form, input, ul, li, strong} = Room.elements();

const address = Room.createData(ipAddress);
const marker = Room.createData({});

const dataContent = data => {
const labels = ["IP", "Continent", "Pays", "Région", "Ville", "Latitude", "Longitude", "ISP", "Zone horaire", "Précision"];
const keys = ['ip', "continent_code", "country", "region", "city", "latitude", "longitude", "organization_name", "timezone", "accuracy"];
return ul({class: "ipInfos"}, labels.map((label, i) => li(strong(label), " : ", data[keys[i]])));
};

const updateMarker = data => {
if (data) {
marker.center = [
parseFloat(data.latitude) || 0,
parseFloat(data.longitude) || 0
];
marker.zoom = 12;
marker.content = dataContent(data);
}
};

const readData = async () => {
try {
const a = (address.value ? "/" + address : "").replace(/ /g, "");
const response = await fetch(`https://get.geojs.io/v1/ip/geo${a}.json`);
updateMarker(await response.json());
} catch (error) {
console.error(error);
}
};

const onSubmit = e => {
address.value = e.target.address.value;
e.target.address.select();
return false;
};

return div({onMount: () => Room.createEffect(readData)},
form({onSubmit},
input({
type: "search",
name: "address",
value: address,
required: true,
spellcheck: "false",
autocomplete: "off",
placeholder: "Adresse IP (V4 ou V6)"
})
),
await MapContainer({provider: mapProvider}, marker)
);
}

La fonction IPGeolocation() commence par créer 2 données observables avec la fonction createData() de Room :

  1. address : une chaîne de caractères avec comme valeur initiale celle de la propriété ipAddress passée en paramètre au composant,
  2. marker: un objet vide ({}) initialement et qui va contenir ensuite les données d’un marqueur.

Elle retourne ensuite un élément <div> qui va contenir le composant avec dedans :

  • un élément <form> qui contient juste un élément <input> de type search.
  • le composant MapContainer qui va contenir la carte en lui passant l’objet mapProvider reçu en paramètre et la donnée observable marker.

L’évènement submit est géré par l’élément <form> et va mettre à jour la donnée observable address avec la valeur du champ <imput>.

L’élément <div> contenant le composant, utilise l’évènement spécial mount pour créer un effet avec la fonction createEffect() de Room et la fonction readData() du composant. Donc, quand le composant est monté dans le DOM de la page, l’effet est créé et la fonction readData() est appelée une première fois.

La fonction readData() s’occupe d’appeler l’API avec l’adresse IP contenue dans la donnée observable address et quand elle reçoit les données, elle utilise la fonction updateMarker() du composant en lui passant ces données.

Si la valeur de la donnée observable address est vide, les données reçues de l’API sont celles de votre adresse IP.

La fonction updateMarker() s’occupe de mettre à jour la donnée observable marker (propriétés center, zoom et content) à partir des données reçues de l’API et c’est là que la magie / réactivité opère : ceci va impacter automatiquement le composant MapContainer qui va mettre à jour le centrage de la carte, son zoom, la position du marqueur et le contenu de l’info-bulle (généré par la fonction dataContent()).

Pour rappel, c’est pour cela qu’un effet est créé avec la fonction showMarker() dans le composant MapContainer (cf. le code du composant).

De plus, quand une nouvelle adresse IP est saisie dans le champ et après validation, la fonction readData() est à nouveau appelée car Room a détecté une dépendance de l’effet à la donnée observable address, donnée qui est modifiée dans la fonction onSubmit() du composant.

Si on regarde vite le code, on peut penser que l’effet a aussi une dépendance à la donnée observable marker, mais il n’en ait rien car Room ne détecte les dépendances que pendant l’exécution d’une fonction mais pas après, notamment avec une fonction asynchrone, ce qui est le cas ici. Donc, quand la fonction updateMarker() est appelée, la détection de dépendances par Room est déjà terminée.

Si on veut faire dépendre l’effet de la donnée observable marker, il faut indiquer cette dépendance en ajoutant la donnée comme deuxième paramètre de la fonction createEffect().

Et pour rappel, si on ne veut pas que Room détecte certaines dépendances, il faut utiliser la fonction untrack().

Utilisation du composant

Pour utiliser le composant IPGeolocation, nous supposons que nous avons un élément dans la page HTML avec un identifiant geolocation et nous réutilisons les 2 fonctions utilitaires importCSS() et openStreeMapProvider() de l’exemple avec le composant MapContainer (cf. l’utilisation du composant).

Alors le code pour ajouter le composant dans la page est simplement :

// Importation de la CSS de Leaflet via un CDN
importCSS("https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.css");
// Récupération d'un élément où mettre le composant
const geolocation = document.getElementById("geolocation");
if (geolocation) {
// Récupération du provider
const mapProvider = openStreetMapProvider();
// Ajout du composant dans l'élément
geolocation.append(await IPGeolocation({mapProvider}));
}

Ce qui donne le résultat suivant :

Normalement, la carte est centrée sur les coordonnées de votre adresse IP et vous pouvez saisir ou coller une autre adresse IP dans le champ, puis après validation, voir ses informations.

Dernière mise à jour :