Un visualiseur cartographique interactif permettant d'explorer et de rechercher des villes du monde entier, avec leurs données géographiques, démographiques et administratives.
- Navigation cartographique : Carte interactive avec Leaflet affichant les villes
- Recherche par nom : Recherche instantanée par nom, nom ASCII ou noms vernaculaires
- Visualisation des données : Affichage détaillé de toutes les informations d'une ville (population, coordonnées, fuseau horaire, etc.)
- Recherche par proximité : Cliquez sur la carte pour trouver les villes les plus proches
- Résultats triés : Classement des résultats par population décroissante
Le projet utilise deux fichiers binaires :
ma_base.textindex.bin: Index textuel pour la recherche rapide par nomma_base.kdtree.bin: Arbre KD pour les recherches spatiales (k-plus-proches-voisins)
# Cloner le dépôt
git clone [url-du-depot]
cd kd-tree-citymap
# Installer les dépendances
pnpm install
# Lancer en développement
pnpm run dev
# Construire pour la production
pnpm run buildUtilisation en ligne : kd-tree-citymap.
app.ts: Point d'entrée, orchestrateur principalbinary.ts: Lecteur de fichiers binaires (petit-boutiste)kdtree.ts: Implémentation de l'arbre KD pour les recherches spatialestextindex.ts: Index textuel pour les recherches par nommaptab.ts: Gestion de la carte Leaflet et des interactionsstore.ts: Cache IndexedDB pour les fichiers binaires volumineuxtheme.ts: Gestion du thème clair/sombreui.ts: Rendu des composants d'interface utilisateurtypes.ts: Définitions des types TypeScript
- Nœuds internes/feuilles avec coordonnées (lat/lng)
- Index spatial optimisé pour les requêtes de plus proches voisins
- Index trié des noms (insensible à la casse)
- Inclut les noms ASCII et les noms vernaculaires
- TypeScript : Langage principal
- Leaflet : Bibliothèque de cartographie
- IndexedDB : Cache local pour les données volumineuses
- Vite : Bundler et serveur de développement
- Requêtes textuelles : O(log n) grâce à l'index binaire
- Requêtes spatiales : O(log n) en moyenne avec l'arbre KD
- Cache IndexedDB : réduction des temps de chargement
BASE_URL: URL de base pour les fichiers binairesKDTREE_URL: URL personnalisée pour le fichier KD-TreeTEXTINDEX_URL: URL personnalisée pour le fichier d'index textuel
?kdtree=chemin/vers/ma_base.kdtree.bin
?textindex=chemin/vers/ma_base.textindex.binLes données proviennent de Geonames et incluent :
- Populations mondiales
- Coordonnées géographiques
- Noms dans différentes langues
- Informations administratives (pays, provinces)
- Fuseaux horaires
- Altitudes
Note : Ce projet est conçu pour être utilisé avec des fichiers de données de taille importante (plusieurs dizaines de Mo). Assurez-vous d'avoir une connexion internet suffisante pour le premier chargement.
MIT
Toutes les valeurs numériques sont encodées en little-endian. Toute chaîne suit le même encodage : string := u16 len | u8[len] (UTF-8, len = nombre d'octets, pas de caractères). La troncature à l'écriture respecte les frontières de caractères UTF-8 (truncate_utf8).
Header 16 octets puis Nœud 0 variable puis Nœud 1 variable puis ... puis Nœud N-1 variable. Les nœuds ne sont pas triés par offset selon un ordre logique particulier ; chaque nœud est retrouvé par son offset absolu, référencé soit par le header (root_offset), soit par un nœud parent (left_offset/right_offset).
Offset 0 : magic u32 valeur 0x4B445452 Offset 4 : version u32 valeur 4 Offset 8 : node_count u32 nombre total de nœuds du fichier Offset 12 : root_offset u32 offset absolu du nœud racine (= 16)
Champs dans l'ordre :
- node_type : u8, toujours présent, 0 = leaf, 1 = internal
- lat : f32, toujours présent, latitude du split point
- lng : f32, toujours présent, longitude du split point
- axis : u8, toujours présent, 0 = split sur lat, 1 = split sur lng
- city_data_len : u32, toujours présent, longueur en octets du bloc CityData qui suit
- city_data : city_data_len octets, toujours présent, voir section 1.4
- left_offset : u32, présent uniquement si node_type==1, offset absolu du fils gauche ; 0 = aucun fils
- right_offset : u32, présent uniquement si node_type==1, offset absolu du fils droit ; 0 = aucun fils
Taille totale d'un nœud : 14 + city_data_len pour leaf, ou 22 + city_data_len pour internal. offset == 0 est la sentinelle "pas d'enfant" — aucun nœud réel ne peut être à l'offset 0 puisque le header occupe déjà [0, 16).
- id (geonameid) : u32
- lat : f32
- lng : f32
- population : u32
- elevation : i32
- dem : i32
- name : string, longueur max à l'écriture 255
- ascii_name : string, longueur max à l'écriture 255
- alternatenames : string, longueur max à l'écriture 65535
- feature_class : string, longueur max à l'écriture 1 (1 seul caractère)
- feature_code : string, longueur max à l'écriture 10
- country_code : string, longueur max à l'écriture 3
- cc2 : string, longueur max à l'écriture 6
- admin1_code : string, longueur max à l'écriture 20
- admin2_code : string, longueur max à l'écriture 20
- admin3_code : string, longueur max à l'écriture 20
- admin4_code : string, longueur max à l'écriture 20
- timezone : string, longueur max à l'écriture 64
- modification_date : string, longueur max à l'écriture 20
- vernacular_count : u16
- vernacular_names[] : vernacular_count × {lang: string≤15, name: string≤255}
- province_name : string, longueur max à l'écriture 255
- country_name : string, longueur max à l'écriture 255
- utc_offset_seconds : i32, i32::MIN = inconnu
- province_iso_code : string, longueur max à l'écriture 10
- province_type : string, longueur max à l'écriture 60
- province_wikidata_id : string, longueur max à l'écriture 20
- province_names_count : u16
- province_names[] : province_names_count × {lang: string≤15, name: string≤255}
Index trié pour recherche par préfixe, insensible à la casse, sur name / ascii_name / vernacular_names[].name. Chaque record référence un nœud de *.kdtree.bin par offset — les deux fichiers doivent provenir de la même exécution de kd_generator (les offsets ne sont pas portables d'une génération à l'autre).
Header 16 octets puis Offset Table (entry_count × 4) : record_offset[0], record_offset[1], ..., record_offset[entry_count-1] puis Record 0 (variable, à record_offset[0]) puis Record 1 (variable, à record_offset[1]) puis ... puis Record entry_count-1. La table d'offsets a un pas fixe (4 octets), ce qui permet une recherche binaire par index (TextIndexParser::lower_bound) même si les records eux-mêmes ont une longueur variable.
Offset 0 : magic u32 valeur 0x54455849 Offset 4 : version u32 valeur 2 Offset 8 : entry_count u32 nombre de records (après dédup) Offset 12 : reserved u32 valeur 0
entry_count × u32 — record_offset[i] = offset absolu (depuis le début du fichier) du record d'indice i, trié selon l'ordre du § 2.5.
Champs dans l'ordre :
- str_len : u16, longueur en octets du nom normalisé
- name : str_len octets, nom en minuscules, UTF-8. Diacritiques d'origine conservés pour name, déjà translittéré pour ascii_name, ou nom vernaculaire
- node_offset : u32, offset absolu du nœud correspondant dans *.kdtree.bin
- population : u32, copie de CityData.population — évite de relire le kdtree pour trier
Taille d'un record : 6 + str_len.
Les records sont triés par ordre lexicographique sur les octets UTF-8 de name, puis par node_offset en cas d'égalité. Les paires exactement identiques (name, node_offset) sont supprimées (text_index::build). Une même ville produit plusieurs records — un par nom indexé (name, ascii_name, chaque vernacular_names[i].name) — tous pointant vers le même node_offset/population. C'est le mécanisme qui permet de retrouver une ville aussi bien par son nom d'origine (avec accents) que par sa forme ASCII ou l'un de ses noms vernaculaires.