Skip to content

GeoLeaf — Data normalisation & unified POI format ​


Architecture note: data normalisation is an automatic internal process. The DataNormalizer, Normalization and DataConverter modules (src/modules/utils/data/) are not part of the public GeoLeaf API and are not exposed on the GeoLeaf.* namespace. Integrators interact with this system only through the profile configuration (dataMapping in the layers, mapping.json).


Unified POI format (reference) ​

Every data source (JSON, GeoJSON, routes) is converted automatically into this internal format before being displayed on the map.

json
{
    "id": "poi_123",
    "title": "Restaurant La Bonne Table",

    "lat": 48.8566,
    "lng": 2.3522,

    "categoryId": "restaurant",
    "subCategoryId": "gastronomique",
    "description": "Restaurant gastronomique au cœur de Paris",

    "attributes": {
        "address": "123 rue de Paris",
        "phone": "+33 1 23 45 67 89",
        "website": "https://example.com",
        "opening_hours": "9h-18h",
        "reviews": {
            "rating": 4.5,
            "count": 127,
            "summary": "Excellent restaurant",
            "recent": [{ "author": "Jean", "rating": 5, "comment": "Parfait !" }]
        }
    }
}

Key fields:

FieldTypeDescription
idstringUnique identifier (generated when absent from the source)
titlestringDisplayed name (mapped from name, nom, label, etc.)
lat / lngnumberGeographic coordinates
categoryIdstringMain category (taxonomy)
subCategoryIdstringSub-category
attributesobjectAll the business properties coming from the source

Supported sources ​

TypeSource formatAutomatic detection
jsonRaw JSON object with flat fieldsdata.lat + data.lng, or fallbacks
geojsonGeoJSON Feature (Point/LineString/Polygon)type: "Feature" + geometry
routeObject with latLng or waypointsdata.latLng or data.order
gpxGPX waypointdata.lat + data.lon + data.name (partial)

Configuration: dataMapping in the profile ​

The dataMapping entry in a layer configuration maps non-standard fields onto the GeoLeaf format, without an external mapping.json.

jsonc
{
    "layers": [
        {
            "id": "restaurants",
            "type": "poi",
            "url": "data/restaurants.json",
            "dataMapping": {
                "title": "nom", // source.nom → POI.title
                "lat": "latitude", // source.latitude → POI.lat
                "lng": "longitude", // source.longitude → POI.lng
                "categoryId": "type", // source.type → POI.categoryId
            },
        },
    ],
}

Configuration: mapping.json ​

For more complex transformations, or for transformations shared between several layers, a mapping.json file can be placed in the profile directory.

Structure ​

json
{
    "version": "1.0",
    "description": "Mapping pour API externe XYZ",
    "mapping": {
        "id": "uid",
        "title": "name",
        "description": "short_description",
        "location.lat": "coordinates.latitude",
        "location.lng": "coordinates.longitude",
        "categoryId": "type",
        "attributes.address": "full_address",
        "attributes.phone": "contact.telephone",
        "attributes.website": "links.web",
        "attributes.opening_hours": "horaires"
    }
}

Path syntax ​

  • Simple path: "title": "nom" — maps source.nom onto target.title
  • Nested path: "location.lat": "coords.latitude" — maps source.coords.latitude onto target.location.lat
  • Array: "attributes.tags": "categories[0]" — first element of the array

Placement ​

profiles/
└── mon-profil/
    ├── geoleaf.config.json
    └── mapping.json          ← mapping file

Activation in the profile ​

jsonc
{
    "layers": [
        {
            "id": "pois-externes",
            "type": "poi",
            "url": "https://api.example.com/pois",
            "normalized": false, // triggers the mapping
            "mappingFile": "mapping.json",
        },
    ],
}

Example: integrating an external API ​

Raw data returned by the API:

json
[
    {
        "restaurant_id": "rest_123",
        "restaurant_name": "Le Petit Bistrot",
        "geo": { "lat": 48.8566, "lon": 2.3522 },
        "contact_info": { "tel": "0123456789", "web": "https://petitbistrot.fr" },
        "rating": { "average": 4.5, "total_reviews": 127 }
    }
]

mapping.json:

json
{
    "mapping": {
        "id": "restaurant_id",
        "title": "restaurant_name",
        "location.lat": "geo.lat",
        "location.lng": "geo.lon",
        "attributes.phone": "contact_info.tel",
        "attributes.website": "contact_info.web",
        "attributes.reviews.rating": "rating.average",
        "attributes.reviews.count": "rating.total_reviews"
    }
}

Layer configuration in the profile:

jsonc
{
    "layers": [
        {
            "id": "restaurants",
            "type": "poi",
            "url": "https://api.example.com/restaurants",
            "normalized": false,
            "mappingFile": "mapping.json",
        },
    ],
}

GeoLeaf applies the mapping automatically at load time, with no additional code.


Default behaviour (no mapping) ​

When neither dataMapping nor mappingFile is configured:

  • JSON: GeoLeaf looks for title/name/nom/label for the title, and lat/latitude/y and lng/longitude/x for the coordinates
  • GeoJSON: standard properties (name, title, nom) are mapped automatically; coordinates are extracted from geometry.coordinates
  • POIs already matching the unified format pass through unchanged

Limitations ​

  • GPX: partial support (simple waypoints only; native .gpx import is not implemented)
  • Computed transformations: concatenations and calculations are not supported in mapping.json
  • Strict validation: a POI without an id or without coordinates after normalisation is ignored (console warning)

See also ​

Released under the MIT License.