Skip to main content
Utility

Geocoder

The node resolves coordinates to addresses (reverse) or addresses to coordinates (forward) using the OpenStreetMap Nominatim service. Input and output fields are mapped freely via dot-notation paths, so the node fits into an existing payload structure. Requests are serialized, cached and throttled to respect the rate limit of the public Nominatim instance.

  • Geocoding
  • Nominatim
  • Enrichment
Node type
geocoder
Package
@htag/node-red-contrib-hypertegrity-utility
Inputs / outputs
1 / 1
Service
Nominatim (OpenStreetMap), public or self-hosted

When to use this node​

Many data sources deliver only coordinates, others only addresses. The Geocoder adds the missing half so that entities in an urban data platform can be shown on a map and searched by town or street.

Typical use cases are:

  • Labelling sensor locations: A stationary sensor reports only latitude and longitude. The node adds street, postal code and town.
  • Putting address lists on the map: Event venues, stops or facilities exist as addresses and receive a GeoJSON point for the location GeoProperty of an NGSI-LD entity.
  • Enriching array payloads: A list of objects is resolved entry by entry, for example before it is handed to the NGSI-LD Builder.

How the node works​

1

Read input

The node reads coordinates or address fields at the configured paths, either from the message or from every entry of an array.

2

Check cache

A cache key is built for every lookup. Coordinates are rounded to five decimals (about 1 m) for this. Cache hits are answered without a request.

3

Query Nominatim

On a cache miss the request enters a queue that keeps the minimum interval between two requests. On response code 429 or 503 the request is retried once after 2 s.

4

Write result

The response is normalized into a fixed field set and written into the message or the array entry according to the field mapping.

Result: The original message, enriched with address or coordinate fields and optionally a status field.

The node is message-driven: it only acts when a message arrives and never queries Nominatim on its own. In Automatic mode it first tries the coordinate mapping; only when no valid coordinates are found does it fall back to the address mapping. All requests of one node pass through a single queue, regardless of how many messages arrive at once.

Configuration​

The edit dialog is organised into the tabs General, Input, Output, Service and Test. The Name field sits above the tabs.

FieldTypeDefaultMeaning
NameTextemptyDisplay name of the node in the flow.

General tab​

FieldTypeDefaultMeaning
ModeSelectCoordinates → Address (reverse)reverse resolves coordinates to an address, forward an address to coordinates, auto tries coordinates first and then the address.
ProcessSelectSingle messagemessage: all paths are relative to msg, for example payload.lat. array: every entry of the array is resolved individually, paths are relative to the entry, for example location.lat.
Array pathTextpayloadOnly for Process = Array entries: path of the array relative to msg.
On failureSelectForward message (log warning)Behaviour on request errors: pass forwards the message and logs a warning, drop discards the message (also for lookups without a result, as long as nothing was resolved), error raises a node error that a Catch node can handle.

Input tab​

FieldTypeDefaultMeaning
Sample JSONText areaemptySample payload used to detect paths. The detected paths are offered as suggestions in all path fields; free text entry stays possible.
FormatSelectSeparate fields (lat / lon)Coordinate source for reverse: separate (two fields), array (coordinate array) or geojson (object with a coordinates array in [lon, lat] order, for example the value of an NGSI-LD GeoProperty).
Latitude fieldPathemptyPath to the latitude for format "Separate fields".
Longitude fieldPathemptyPath to the longitude for format "Separate fields".
Array fieldPathemptyPath to the coordinate array for format "Coordinate array".
Coordinate orderSelect[Longitude, Latitude]Order of the values in the coordinate array: lonlat (GeoJSON default) or latlon.
GeoJSON fieldPathemptyPath to the GeoJSON Point object for format "GeoJSON Point object".
Query typeSelectFree text (single field)Address source for forward: free (one field with the complete address) or structured (individual fields).
Address fieldPathemptyPath to the free-text field containing the complete address.
Street, House number, Postal code, City, County / district, State, CountryPathemptyPaths for the structured query. Only non-empty fields are sent. Street and house number are combined into one Nominatim parameter street.

All fields of this tab are stored together in the node property inputConfig.

Output tab​

FieldTypeDefaultMeaning
Field mappingListAddress presetOne row per result field (left) and the target path it is written to (right). Existing values are overwritten. The list is stored in outputMappings.
Address presetButton–Fills the mapping with displayName → payload.address, street → payload.street, houseNumber → payload.houseNumber, postalCode → payload.postalCode, city → payload.city and countryCode → payload.countryCode.
Coordinates presetButton–Fills the mapping with lat → payload.lat, lon → payload.lon, geoPoint → payload.location and displayName → payload.address.
Full result toPathempty (off)Optional path that receives the complete normalized result, for example payload.geocode.
Status toPathempty (off)Optional path for the status ok, notFound, noInput or error, for example payload.geocodeStatus. Useful for routing with a Switch node.
Write empty fields as nullCheckboxdisabledWrites unresolved fields (and the full result) as null instead of skipping them.

Service tab​

FieldTypeDefaultMeaning
EndpointTextsee belowBase URL of the Nominatim service. Trailing slashes are removed. Enter your own instance here.
LanguageTextdeSent as the accept-language parameter; determines the language of the address fields.
Detail level (zoom)Number 0–1818Reverse only: 18 returns house-number precision, 10 roughly town level, 8 county level.
Contact e-mailTextemptySent as the email parameter. The Nominatim usage policy expects a contact address for higher volumes.
User-AgentTextemptyHTTP header User-Agent. Empty means Node-RED-Hypertegrity-Geocoder. The usage policy requires a meaningful, application-specific user agent.
Min. interval (ms)Number1100Minimum time between two requests of this node. The default respects the one-request-per-second limit of the public instance.
Cache lifetime (s)Number86400Validity of a cache entry (24 h). 0 means valid indefinitely.
Cache entriesNumber500Maximum number of entries in the in-memory cache; the oldest entry is evicted. 0 disables the cache.
Clear cacheButton–Clears the cache of the deployed node via the admin endpoint POST /geocoder/<node id>/cache/clear.

Default values of the URL fields:

Endpoint: https://nominatim.openstreetmap.org

Test tab​

This tab stores no settings. It runs a sample lookup (coordinates or address) through the Node-RED backend, shows the result fields and the normalized JSON, and adds individual fields to the field mapping with one click. The test uses endpoint, language, detail level and contact e-mail from the Service tab.

Paste a sample JSON first

Paste a real payload on the Input tab. Afterwards all path fields offer the detected paths for selection, which avoids typos in paths.

Input​

The node evaluates the message exclusively through the configured paths. There are no control commands.

PropertyTypeMeaning
msg.payloadobjectFor Process = Single message: object containing the configured source fields. Paths are relative to msg, so they usually start with payload..
msg.<array path>array of objectsFor Process = Array entries: every element is resolved individually. Elements that are not objects receive the status noInput. If the path is not an array, the node reports the error msg.<array path> is not an array.
Coordinate fieldsnumber or numeric stringLatitude between -90 and 90, longitude between -180 and 180. Invalid values count as absent.
Address fieldsstringFree-text field or structured individual fields; empty values are ignored.

Output​

The node forwards the incoming message and writes the mapped fields into it. With the Address preset in reverse mode, msg.payload looks like this:

{
"lat": 50.0,
"lon": 8.353,
"address": "12, Sample Street, Exampletown, Example County, Hesse, 65239, Germany",
"street": "Sample Street",
"houseNumber": "12",
"postalCode": "65239",
"city": "Exampletown",
"countryCode": "DE"
}

Both directions produce the same normalized set of result fields from which you choose in the field mapping:

FieldMeaning
displayNameComplete address as text (display_name from Nominatim).
nameName of the matched OSM object, for example a POI or street name.
addressLineStreet and house number in one field.
streetStreet (from road, pedestrian, footway, path or street).
houseNumberHouse number.
postalCodePostal code.
cityTown (from city, town, village, municipality or hamlet).
suburbSuburb (from suburb, city_district, borough or neighbourhood).
countyCounty or district.
stateState or region.
countryCountry.
countryCodeCountry code in upper case, for example DE.
lat, lonCoordinates as numbers.
geoPointGeoJSON Point {"type": "Point", "coordinates": [lon, lat]}, directly usable as an NGSI-LD GeoProperty value.
boundingBoxBounding box as an array of numbers.
osmId, osmType, placeIdIdentifiers of the OSM object.
category, type, addressTypeClassification of the object by Nominatim.
addressUntouched address object of the response.
rawComplete, untouched response.

Fields without a value are skipped unless Write empty fields as null is enabled. Forward lookups use only the first result (limit=1).

Example flow​

The package ships with an example flow. Import it in Node-RED via Menu → Import → Examples → @htag/node-red-contrib-hypertegrity-utility → geocoder. Alternatively download geocoder.json and import it via Menu → Import.

Step 1

Inject and Function

An Inject node starts two Function nodes: one produces a coordinate pair, the other an address.

Step 2

Geocoder

One Geocoder in reverse mode adds address fields, a second one in forward mode adds a GeoJSON point.

Step 3

Debug

Two Debug nodes show the enriched payloads in the sidebar.

Show example flow as JSON
[
{"id": "tab1", "type": "tab", "label": "geocoder", "disabled": false, "info": ""},
{"id": "inject1", "type": "inject", "z": "tab1", "name": "", "props": [{"p": "payload"}], "repeat": "", "once": false, "payload": "", "payloadType": "date", "x": 120, "y": 140, "wires": [["fn1", "fn2"]]},
{"id": "fn1", "type": "function", "z": "tab1", "name": "input1", "func": "msg.payload = { lat: 50.000, lon: 8.353 };\nreturn msg;", "outputs": 1, "x": 270, "y": 140, "wires": [["geo1"]]},
{"id": "geo1", "type": "geocoder", "z": "tab1", "name": "", "mode": "reverse", "scope": "message", "arrayPath": "payload", "sampleJson": "{\"lat\":50,\"lon\":8.353}",
"inputConfig": "{\"posType\":\"separate\",\"order\":\"lonlat\",\"latField\":\"payload.lat\",\"lonField\":\"payload.lon\",\"queryMode\":\"free\"}",
"outputMappings": "[{\"field\":\"displayName\",\"target\":\"payload.address\"},{\"field\":\"street\",\"target\":\"payload.street\"},{\"field\":\"houseNumber\",\"target\":\"payload.houseNumber\"},{\"field\":\"postalCode\",\"target\":\"payload.postalCode\"},{\"field\":\"city\",\"target\":\"payload.city\"},{\"field\":\"countryCode\",\"target\":\"payload.countryCode\"}]",
"resultProperty": "", "statusProperty": "", "writeMissing": false, "onError": "pass",
"endpoint": "https://nominatim.openstreetmap.org", "language": "de", "email": "contact@example.org", "userAgent": "Exampletown-Nodered",
"zoom": 18, "minInterval": 1100, "cacheTtl": 86400, "cacheSize": 500, "x": 440, "y": 140, "wires": [["dbg1"]]},
{"id": "dbg1", "type": "debug", "z": "tab1", "name": "Address", "active": true, "tosidebar": true, "complete": "payload", "targetType": "msg", "x": 620, "y": 140, "wires": []},
{"id": "fn2", "type": "function", "z": "tab1", "name": "input2", "func": "msg.payload = { address: \"Sample Street, Exampletown, 65239, Germany\" };\nreturn msg;", "outputs": 1, "x": 270, "y": 200, "wires": [["geo2"]]},
{"id": "geo2", "type": "geocoder", "z": "tab1", "name": "", "mode": "forward", "scope": "message", "arrayPath": "payload", "sampleJson": "",
"inputConfig": "{\"posType\":\"separate\",\"order\":\"lonlat\",\"queryMode\":\"free\",\"queryField\":\"payload.address\"}",
"outputMappings": "[{\"field\":\"geoPoint\",\"target\":\"payload.location\"}]",
"resultProperty": "", "statusProperty": "", "writeMissing": false, "onError": "pass",
"endpoint": "https://nominatim.openstreetmap.org", "language": "de", "email": "contact@example.org", "userAgent": "Exampletown-Nodered",
"zoom": 18, "minInterval": 1100, "cacheTtl": 86400, "cacheSize": 500, "x": 440, "y": 200, "wires": [["dbg2"]]},
{"id": "dbg2", "type": "debug", "z": "tab1", "name": "Coordinates", "active": true, "tosidebar": true, "complete": "payload", "targetType": "msg", "x": 630, "y": 200, "wires": []}
]

Status and errors​

The status shows running counters: ok = resolved lookups, err = failed requests, cache = lookups answered from the cache.

IndicatorMeaning
Grey ring "ready"Node is deployed, no message processed yet.
Blue ring "5 items"An array with five entries is being processed.
Green dot "3 ok · 1 cache"All entries of the last message were resolved.
Yellow ring "2 unresolved · 3 ok"Two entries without a result (notFound) or without usable input (noInput); the message was forwarded.
Yellow ring "unresolved · …"On failure = Drop message: nothing resolved, the message was dropped.
Red ring "error · 2 ok · 1 err"At least one request failed (network, 15 s timeout, HTTP error).
Red ring "msg.payload is not an array · …"The array path does not point to an array. This error is always raised as a node error, regardless of the On failure setting.

Warnings and errors in the log:

  • Nominatim responded 429 (rate limit / unavailable) — retrying once in 2000 ms.: warning before the single retry.
  • Geocoding failed for '<coordinates or query>': <cause>: a warning for Forward message and Drop message, an error via node.error for Raise node error, which a Catch node can handle. With several failures the message reads <n> lookups failed, first: ….

"Not found" and "no input" never raise an error; they are only visible through the status field and the status indicator.

Notes​

  • Nominatim usage policy: The public instance allows at most one request per second, requires an application-specific user agent and prohibits bulk geocoding. The node honours this through the 1100 ms minimum interval, the queue, the cache, the single retry on 429/503 and the User-Agent and Contact e-mail fields. Fill in both fields with your own values. For regular or large volumes run your own Nominatim instance and enter it as the Endpoint.
  • Test lookups in the editor are executed directly. Avoid many tests in quick succession against the public instance.
  • In-memory cache only: The cache belongs to the node instance and is cleared on deploy, restart or via Clear cache. No persistent context store is involved.
  • Array processing takes time: Every entry causes one request. An array with 100 new locations takes about two minutes at a 1100 ms minimum interval.
  • Proxy: The node has no proxy setting of its own. In isolated networks a self-hosted Nominatim instance on the internal network is recommended.
  • Personal data: Addresses and precise coordinates can be personal data under the GDPR, for example home addresses or locations of individual persons. Every lookup transmits the location to the configured service, with the public endpoint to a third party outside your infrastructure. Use your own Nominatim instance for personal data, keep the detail level no higher than necessary and check that the processing is covered by its purpose.
  • Units: Coordinates are decimal degrees (WGS 84); GeoJSON uses the order [longitude, latitude].

References​