How it works
Geographic filtering restricts which nodes are ingested and returned in API responses. It operates at two levels:
- Ingest time — ADVERT packets carrying GPS coordinates are rejected by the ingestor if the node falls outside the configured area. The node never reaches the database.
- API responses — Nodes already in the database are filtered from the
/api/nodesresponse if they fall outside the area. This covers nodes ingested before the filter was configured.
Nodes with no GPS fix (lat=0, lon=0 or missing coordinates) always pass the filter regardless of configuration.
Configuration
Add a geo_filter block to config.json:
"geo_filter": {
"polygon": [
[51.55, 3.80],
[51.55, 5.90],
[50.65, 5.90],
[50.65, 3.80]
],
"bufferKm": 20
}
| Field | Type | Description |
|---|---|---|
polygon | [[lat, lon], ...] | Array of at least 3 coordinate pairs defining the boundary |
bufferKm | number | Extra distance (km) around the polygon edge that is also accepted. 0 = exact boundary |
Both the server and the ingestor read geo_filter from config.json. Restart both after changing this section.
To disable filtering entirely, remove the geo_filter block.
Builder workflow: Save Draft, Load Draft, Download
The GeoFilter Builder lets you draw a polygon on a map and produce the geo_filter snippet without hand-editing JSON. Three buttons drive the workflow:
- 💾 Save Draft — writes the current polygon and
bufferKmto your browser'slocalStorageunder the keygeofilter-draft. Drafts persist across page reloads and browser restarts so you can iterate on a shape over multiple sessions. - 📂 Load Draft — restores the most recently saved draft into the builder. The current polygon is replaced. If no draft exists the button is a no-op.
- ⬇ Download — exports the current polygon and
bufferKmasgeofilter-config-snippet.json— a single JSON object containing a top-levelgeo_filterblock. Open the file, copy thegeo_filterentry, and paste it into yourconfig.json.
Drafts are stored locally in your browser only — they are not uploaded anywhere. Clearing site data or switching browsers will lose the draft. Use Download to keep a portable copy.
After pasting the snippet into config.json, restart the server and ingestor for the new filter to take effect.
Coordinate ordering
Important: Coordinates are [lat, lon] — latitude first, longitude second. This is the opposite of GeoJSON, which uses [lon, lat]. Swapping them will place your polygon in the wrong location.
Multi-polygon
Only a single polygon is supported. If your deployment area consists of multiple disconnected regions, draw a single convex hull that covers all of them, or use the largest region with a generous bufferKm value.
Examples
Belgium (bounding rectangle)
"geo_filter": {
"polygon": [
[51.55, 3.80],
[51.55, 5.90],
[50.65, 5.90],
[50.65, 3.80]
],
"bufferKm": 20
}
Irregular shape
"geo_filter": {
"polygon": [
[51.10, 3.70],
[51.55, 4.20],
[51.30, 5.10],
[50.80, 5.50],
[50.50, 4.80],
[50.70, 3.90]
],
"bufferKm": 10
}
Legacy bounding box
An older bounding box format is also supported as a fallback when no polygon is present:
"geo_filter": {
"latMin": 50.65,
"latMax": 51.55,
"lonMin": 3.80,
"lonMax": 5.90
}
Prefer the polygon format — it supports irregular shapes and the bufferKm margin.
Cleaning up historical nodes
The ingestor prevents new out-of-bounds nodes from being ingested, but does not retroactively remove nodes stored before the filter was configured. Use the prune script for that:
# Dry run — shows what would be deleted without making any changes
python3 scripts/prune-nodes-outside-geo-filter.py --dry-run
# Default paths: /app/data/meshcore.db and /app/config.json
python3 scripts/prune-nodes-outside-geo-filter.py
# Custom paths
python3 scripts/prune-nodes-outside-geo-filter.py /path/to/meshcore.db \
--config /path/to/config.json
# In Docker — run inside the container
docker exec -it meshcore-analyzer \
python3 /app/scripts/prune-nodes-outside-geo-filter.py --dry-run
The script reads geo_filter.polygon and geo_filter.bufferKm from config, lists nodes that fall outside, then asks for yes confirmation before deleting. Nodes without coordinates are always kept.
This is a one-time migration tool — run it once after first configuring geo_filter to clean up pre-filter data.