Skip to content

Geocode with Nominatim

This guide will show you how to set up geocoding with nomatim

  1. Install the nomatim geocoder library.

    Terminal window
    bun add @maplibre/maplibre-gl-geocoder
  2. Define the Geocoder API Endpoint:

    geoapi.ts
    import {
    MaplibreGeocoderOptions
    } from '@maplibre/maplibre-gl-geocoder';
    import * as maplibre from "maplibre-gl";
    export type GeocoderControlProps = Omit<MaplibreGeocoderOptions, "maplibregl" | "marker"> & {
    marker?: boolean | Partial<maplibregl.MarkerOptions>;
    position?: maplibregl.ControlPosition;
    onLoading?: (e: any) => void;
    onResults?: (e: any) => void;
    onResult?: (e: any) => void;
    onError?: (e: any) => void;
    };
    const geocoderApi: MaplibreGeocoderApi = {
    // @ts-expect-error
    forwardGeocode: async (config) => {
    const features = [];
    try {
    const request = `https://nominatim.openstreetmap.org/search?q=${config.query}&format=geojson&polygon_geojson=1&addressdetails=1`;
    const response = await fetch(request);
    const geojson = await response.json();
    for (const feature of geojson.features) {
    const center = [
    feature.bbox[0] + (feature.bbox[2] - feature.bbox[0]) / 2,
    feature.bbox[1] + (feature.bbox[3] - feature.bbox[1]) / 2
    ];
    features.push({
    type: "Feature",
    geometry: { type: "Point", coordinates: center },
    place_name: feature.properties.display_name,
    properties: feature.properties,
    text: feature.properties.display_name,
    place_type: ["place"],
    center
    });
    }
    } catch (e) {
    console.error("Geocode error", e);
    }
    return { features };
    }
    };
  3. Create The GeocoderControl that gets the Geocoder API:

    geocoder-control.tsx
    import type {GeocoderControlProps} from './geoapi.ts'
    import * as maplibre from "maplibre-gl";
    import { onCleanup, splitProps, createSignal, type JSX, Show } from "solid-js";
    import { Marker, useMapEffect } from "solidjs-maplibre-gl";
    export function GeocoderControl(initial: GeocoderControlProps): JSX.Element {
    const [props, rest] = splitProps(initial, [
    "marker",
    "position",
    "onLoading",
    "onResults",
    "onResult",
    "onError"
    ]);
    const [markerLngLat, setMarkerLngLat] = createSignal<maplibregl.LngLatLike>();
    useMapEffect((map) => {
    const control = new MaplibreGeocoder(geocoderApi, {
    ...rest,
    marker: true,
    maplibregl: maplibre
    });
    if (props.onLoading) control.on("loading", props.onLoading);
    if (props.onResults) control.on("results", props.onResults);
    if (props.onError) control.on("error", props.onError);
    control.on("result", (e) => {
    props.onResult?.(e);
    const result = e.result;
    const coords = result.center || result.geometry?.coordinates;
    if (coords && props.marker) {
    setMarkerLngLat(coords);
    } else {
    setMarkerLngLat(undefined);
    }
    });
    map.addControl(control, props.position || "top-right");
    onCleanup(() => {
    map.removeControl(control);
    });
    });
    return (
    <Show when={markerLngLat()}>
    <Marker
    lnglat={markerLngLat()!}
    {...(typeof props.marker === "object" ? props.marker : {})}
    />
    </Show>
    )
    }
  4. Now pass in the Controller to a maplibre map.

    Geocoder.tsx
    import type Component from 'solid-js'
    import { Maplibre } from "solidjs-maplibre-gl";
    import { GeocoderControl } from "./geocoder-control.tsx"
    import "maplibre-gl/dist/maplibre-gl.css";
    import '@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css';
    const Geocoder: Component = (props) => {
    return (
    <Maplibre
    style={{
    height: "55vh",
    "min-height":'300px'
    }}
    options={{
    center: [-79.4512, 43.6568],
    zoom: 13,
    style:"https://tiles.openfreemap.org/styles/bright"
    }}
    >
    <GeocoderControl position="top-left" marker={true}/>
    </Maplibre>
    );
    };
    export default Geocoder;