Location Group
The node checks which of the configured areas a point lies in, for example which municipality or district. You search the areas in the editor via OpenStreetMap Nominatim and assign them to group names. At runtime the node writes the group name into an output property or lets only messages pass that lie inside an area. The check runs locally with Turf.js, without any request to a service.
- Node type
- location-group
- Package
- @htag/node-red-contrib-hypertegrity-utility
- Inputs / outputs
- 1 / 1
- Dependency
- @turf/boolean-point-in-polygon; Nominatim only in the editor
When to use this node
In a regional data platform, readings often originate from several municipalities or districts. The Location Group node assigns each data point to an area based on its coordinates, without you having to maintain polygons yourself: boundaries are loaded directly from OpenStreetMap into the node.
Typical use cases are:
- Grouping data points by municipality: Sensors of a network receive the property
locationGroupso that dashboards can filter by municipality. - Restricting data streams to an area: Only vehicles, measurements or reports inside your own city limits are processed further.
- Combining several areas into one group: Several districts or municipalities share one group name, for example for a service area.
How the node works
Define areas
In the editor you search administrative boundaries via Nominatim or click on the map. The polygons are stored with the flow configuration.
Read coordinates
At runtime the node reads latitude and longitude or a coordinate array at the configured paths, for every message or every array entry.
Test point against polygons
Turf.js checks locally whether the point lies inside one of the polygons. The first matching area wins.
Enrich or filter
Depending on the mode the group name is written into the output property or the message or array entry is dropped.
Result: Messages or array entries with a normalized group name, or only those entries that lie inside a configured area.
The node is message-driven and works entirely locally at runtime. Nominatim is queried only in the editor when searching for areas. If no areas are configured, the node forwards every message unchanged. The group name is normalized: umlauts are transcribed (ä → ae, ß → ss), spaces become _ and all characters other than letters, digits and _ are removed. Without a match the value is unknown.
Configuration
The edit dialog consists of the tabs Regions, Settings and Data Discovery. The Name field sits above the tabs.
| Field | Type | Default | Meaning |
|---|---|---|---|
| Name | Text | empty | Display name of the node in the flow. |
Regions tab
| Field | Type | Default | Meaning |
|---|---|---|---|
| Search OSM Boundaries | Search field | empty | Searches Nominatim for areas through the admin endpoint /location-group/search (up to 10 hits). Only hits with a Polygon or MultiPolygon are listed. + adds the area to the list. |
| Selected Regions | List | empty | One row per area with Group (freely editable, default: first part of the Nominatim name) and Area (full name, read-only). Several areas may share the same group name. The list, including the polygon, is stored in the node property regions. |
| Map Preview | Map | – | Leaflet map showing all stored polygons. Clicking the map queries the administrative hierarchy at that position via /location-group/find-at (zoom levels 8, 10 and 14, that is district, municipality and suburb) and offers the boundaries found for adoption. |
Settings tab
| Field | Type | Default | Meaning |
|---|---|---|---|
| Mode | Select | Enrich (Append Property) | enrich writes the group name or unknown into the output property. filter lets only messages or array entries pass that lie inside an area. |
| Also enrich passing messages | Checkbox | disabled | Filter mode only: additionally writes the group name into the output property. |
| Invert Filter (Keep Unknowns) | Checkbox | disabled | Filter mode only: keeps only messages that lie in no area. |
| Output Property | Typed input (msg) | locationGroup | Path the group name is written to. Relative to msg for single objects, relative to each entry for arrays (see Output). |
Data Discovery tab
| Field | Type | Default | Meaning |
|---|---|---|---|
| Sample JSON | Text area | empty | Sample payload (object or array). Auto-Detect Settings looks for a GeoJSON Point, an object with lat/lon or a pair of numbers and proposes the field paths; Apply to Form adopts them. The detected paths also serve as suggestions in the path fields. |
| Geo Type | Select | Point | Geometry type of the input data: Point, LineString, Polygon, MultiPoint, MultiLineString or MultiPolygon. Point geometries are evaluated. |
| Position Type | Select | Separate Lat/Lon Fields | For Geo Type Point: separate (two fields) or array (coordinate array). |
| Latitude Field | Path | empty | Path to the latitude, for example location.coordinates[1]. |
| Longitude Field | Path | empty | Path to the longitude, for example location.coordinates[0]. |
| Array Field | Path | empty | Path to the coordinate array, for example location.coordinates. |
| GeoJSON Field | Path | empty | For other Geo Types: path to the coordinates array of the geometry. |
| Coord Order | Select | Longitude, Latitude | Order in the coordinate array: lonlat (GeoJSON default) or latlon. |
| Preview Map | Map | – | Shows the position read from the sample JSON and reports invalid or missing coordinates. |
Geo Type, Position Type, Latitude/Longitude Field, Array Field, GeoJSON Field and Coord Order are stored together in the node property locationConfig.
Paths are evaluated relative to msg.payload by default. If the latitude or array field path starts with payload, the node evaluates relative to msg; if it starts with msg., relative to the message object. For arrays, paths are always relative to the individual entry.
Input
| Property | Type | Meaning |
|---|---|---|
msg.payload | object | Single data point. The configured paths are read relative to msg.payload (exceptions see note above). |
msg.payload | array of objects | List of data points. Every entry is checked individually; paths are relative to the entry. |
| Coordinate fields | number or numeric string | Latitude and longitude in decimal degrees, or an array with at least two numbers in the configured order. |
Without configured regions every message is forwarded unchanged.
Output
In Enrich mode with an array in msg.payload the output looks like this:
[
{
"id": "urn:ngsi-ld:WeatherObserved:station-01",
"location": {"type": "Point", "coordinates": [8.6, 49.5]},
"locationGroup": "Exampletown"
},
{
"id": "urn:ngsi-ld:WeatherObserved:station-02",
"location": {"type": "Point", "coordinates": [9.9, 50.8]},
"locationGroup": "unknown"
}
]
| Field | Meaning |
|---|---|
<output property> on every array entry | Normalized group name of the first matching area or unknown. Set with dot notation relative to the entry; intermediate objects are created. |
msg.<output property> for a single object | For a single object the output property is set via RED.util.setMessageProperty relative to msg. With the default locationGroup this yields msg.locationGroup; for a value inside the payload enter payload.locationGroup. |
msg.payload | In Filter mode the array contains only the entries that passed; a single message without a match is not sent at all. With Invert Filter only entries without a match remain; if Also enrich passing messages is enabled as well, they receive the value unknown. |
Example flow
The package ships with an example flow. Import it in Node-RED via Menu → Import → Examples → @htag/node-red-contrib-hypertegrity-utility → location-group. Alternatively download location-group.json and import it via Menu → Import. The file contains the complete municipality polygon from OpenStreetMap and is therefore large.
Inject and Function
An Inject node triggers a Function that produces an array with one NGSI-LD-like entity including a GeoJSON point.
Location Group
The node reads location.coordinates, tests the point against the stored municipality polygon and writes locationGroup.
Debug
A Debug node shows the enriched array in the sidebar.
Show example flow as JSON (polygon shortened)
[
{"id": "tab1", "type": "tab", "label": "location-group", "disabled": false, "info": ""},
{"id": "inject1", "type": "inject", "z": "tab1", "name": "Go!", "props": [{"p": "payload"}], "repeat": "", "once": false, "payload": "", "payloadType": "date", "x": 90, "y": 120, "wires": [["fn1"]]},
{"id": "fn1", "type": "function", "z": "tab1", "name": "Input", "func": "msg.payload = [{\"id\":\"urn:ngsi-ld:1\",\"location\":{\"type\":\"Point\",\"coordinates\":[8.6,49.5]}}];\nreturn msg;", "outputs": 1, "x": 250, "y": 120, "wires": [["lg1"]]},
{"id": "lg1", "type": "location-group", "z": "tab1", "name": "", "groupingMode": "enrich", "enrichOnFilter": false, "invertFilter": false,
"locationConfig": "{\"type\":\"Point\",\"posType\":\"array\",\"latField\":\"\",\"lonField\":\"\",\"arrayField\":\"location.coordinates\",\"polyField\":\"\",\"order\":\"lonlat\"}",
"sampleJson": "[{\"id\":\"urn:ngsi-ld:1\",\"location\":{\"type\":\"Point\",\"coordinates\":[8.6,49.5]}}]",
"outputProperty": "locationGroup",
"regions": [
{"name": "Exampletown, Example County, Baden-Württemberg, 68542, Germany", "group": "Exampletown",
"polygon": {"type": "Polygon", "coordinates": [[[8.57, 49.49], [8.63, 49.49], [8.63, 49.52], [8.57, 49.52], [8.57, 49.49]]]}}
],
"x": 460, "y": 120, "wires": [["dbg1"]]},
{"id": "dbg1", "type": "debug", "z": "tab1", "name": "Output", "active": true, "tosidebar": true, "complete": "payload", "targetType": "msg", "x": 690, "y": 120, "wires": []}
]
Status and errors
The node shows no status indicator. Notices appear as warnings in the log and the debug sidebar.
| Indicator | Meaning |
|---|---|
| Warning "location-group matches points against regions. The configured Geo Type is not supported for Point-in-Polygon." | A non-point geometry is configured as Geo Type. The entry receives unknown or is filtered out. |
| Warning "Error evaluating polygon for region '…': …" | A stored polygon could not be evaluated, for example because of invalid geometry. The remaining areas are still checked. |
| Console warning "Location Group Node: Missing @turf/boolean-point-in-polygon dependency." | The dependency is missing from the installation; there will be no matches. |
| Editor hint "Search failed. Proxy or API error." or "Search failed. Are you offline or is the proxy down?" | The Nominatim search through the Node-RED backend failed. |
Notes
- Nominatim only in the editor: Area search and map clicks query the public instance
nominatim.openstreetmap.orgthrough the Node-RED backend, with the user agentNode-RED-Location-Group-Node. One map click triggers three requests. Respect the usage policy of the public instance (at most one request per second) and avoid rapid click sequences. - External resources in the editor: The map loads Leaflet from
unpkg.comand tiles fromtile.openstreetmap.org. In networks without internet access the map preview stays empty and polygons cannot be searched. The deployed node's runtime behaviour is not affected. - Proxy: No proxy setting is provided for the backend requests to Nominatim.
- Flow size: Polygons are stored completely in the flow. Municipality or district boundaries from OpenStreetMap quickly amount to several hundred kilobytes.
- Order of areas: If areas overlap, the first one in the list wins.
- Personal data: Location data of individual persons or vehicles is personal data. The node itself stores no messages, sends nothing to third parties at runtime and only writes the group name. Still check whether assigning data to an area is permitted for the respective purpose. Search terms and click positions entered in the editor are transmitted to OpenStreetMap.
- Units: Coordinates in decimal degrees (WGS 84); GeoJSON order
[longitude, latitude].