The Node-RED worldmap node turns a stream of MQTT GPS messages into moving markers on an interactive map in a few minutes. In this tutorial you will simulate three vehicles, convert their JSON positions into worldmap markers with headings and colours, flag devices that stop reporting, and draw tracks. By the end you will have a working live tracker that you can point at real GPS hardware.
How the Node-RED Worldmap Node Works
The node-red-contrib-web-worldmap package adds a worldmap node that serves a Leaflet-based map at http://<node-red-host>:1880/worldmap. Every message you send into the node describes one object:
msg.payload.nameis the unique ID. Sending a new position with the same name moves the existing marker.msg.payload.latandmsg.payload.lonare the position in decimal degrees.- Optional properties control appearance and behaviour.
| Property | Example | Effect |
|---|---|---|
icon |
car, bus, fa-truck |
Built-in vehicle icons or a Font Awesome icon |
iconColor |
green, #d33 |
Marker colour (for icons that support it) |
bearing |
87 |
Rotates directional icons to the heading |
label |
"Van 2" |
Permanent text next to the marker |
layer |
"Vans" |
Groups markers into a layer you can toggle |
ttl |
300 |
Removes the marker after this many seconds without updates |
deleted |
true |
Removes the marker immediately |
| any other key | speed: 42 |
Shown in the marker's popup |
Because any extra property ends up in the popup, you can pass sensor values, driver names or battery levels without extra configuration. Commands use the same node: a message with msg.payload.command such as { "lat": 41.01, "lon": 28.97, "zoom": 12 } pans and zooms the map.
Install the Worldmap Node
From the editor, open Manage palette → Install and search for node-red-contrib-web-worldmap. Or install it from the command line:
cd ~/.node-red
npm install node-red-contrib-web-worldmap
# then restart Node-RED
The map downloads tiles from online tile servers, so the browser that views it needs internet access. If you are new to Node-RED and MQTT, the Node-RED MQTT tutorial covers installation and broker configuration first.
Simulate GPS Devices over MQTT
Each vehicle publishes a JSON position to its own topic: connectmq/demo/a7f3k2/fleet/<vehicle>/gps. Putting the vehicle ID in the topic, not only in the payload, lets subscribers pick one vehicle or use a + wildcard for all of them. This script moves three vehicles in loops around a city centre and publishes to broker.connectmq.com, the free public MQTT broker. Replace a7f3k2 with your own random string; positions on the public broker are visible to anyone, so only use simulated data there.
# pip install "paho-mqtt>=2.0"
import json
import math
import random
import time
import uuid
import paho.mqtt.client as mqtt
PREFIX = "connectmq/demo/a7f3k2/fleet"
CENTER_LAT, CENTER_LON = 41.0082, 28.9784
vehicles = {
"van1": {"angle": 0.0, "radius": 0.020, "speed": 0.010},
"van2": {"angle": 2.0, "radius": 0.030, "speed": 0.007},
"truck1": {"angle": 4.0, "radius": 0.045, "speed": 0.005},
}
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"gps-sim-{uuid.uuid4().hex[:8]}")
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_start()
try:
while True:
for vid, v in vehicles.items():
v["angle"] += v["speed"] * random.uniform(0.8, 1.2)
lat = CENTER_LAT + v["radius"] * math.sin(v["angle"])
lon = CENTER_LON + v["radius"] * 1.3 * math.cos(v["angle"])
# Heading is tangent to the circle, in degrees clockwise from north
dlat = math.cos(v["angle"])
dlon = -1.3 * math.sin(v["angle"])
heading = (math.degrees(math.atan2(dlon, dlat)) + 360) % 360
payload = {
"lat": round(lat, 6),
"lon": round(lon, 6),
"heading": round(heading),
"speed": round(random.uniform(25, 55), 1),
"ts": int(time.time() * 1000),
}
client.publish(f"{PREFIX}/{vid}/gps", json.dumps(payload), qos=0)
time.sleep(3)
except KeyboardInterrupt:
pass
finally:
client.loop_stop()
client.disconnect()
QoS 0 is a reasonable choice for frequent positions: if one update is lost, the next arrives a few seconds later. Use QoS 1 for events you must not miss, such as geofence alarms. The guide on MQTT vehicle tracking discusses topic and payload design for fleets in more depth, and the ESP32 GPS tracker tutorial shows the device side with real hardware.
Build the Node-RED Worldmap Flow
Step 1: Subscribe to all vehicles
Add an mqtt in node with a broker config pointing at broker.connectmq.com:1883 and a unique client ID. Set the topic to connectmq/demo/a7f3k2/fleet/+/gps and output to "a parsed JSON object".
Step 2: Map MQTT payloads to markers
Add a function node between the mqtt in node and a worldmap node:
// Function node "gps to worldmap marker"
const p = msg.payload;
const lat = Number(p.lat);
const lon = Number(p.lon);
// Reject missing or impossible coordinates (and the classic 0,0 "no fix")
if (!Number.isFinite(lat) || !Number.isFinite(lon) ||
Math.abs(lat) > 90 || Math.abs(lon) > 180 || (lat === 0 && lon === 0)) {
return null;
}
// connectmq/demo/a7f3k2/fleet/van1/gps -> van1
const id = msg.topic.split("/")[4];
const isTruck = id.startsWith("truck");
const speed = Number(p.speed) || 0;
msg.payload = {
name: id,
lat: lat,
lon: lon,
bearing: Number(p.heading) || 0,
icon: isTruck ? "fa-truck" : "car",
iconColor: speed > 50 ? "orange" : "green",
layer: isTruck ? "Trucks" : "Vans",
label: id,
speed: speed + " km/h",
lastSeen: new Date(p.ts || Date.now()).toLocaleTimeString(),
ttl: 120
};
return msg;
Deploy, then open http://localhost:1880/worldmap. Three markers appear and move every three seconds. Click one to see the speed and last-seen time in the popup. If a vehicle stops publishing, its marker disappears after two minutes because of ttl.
Step 3: Centre the map on start
Add an inject node set to inject once after 1 second with a JSON payload of {"command": {"lat": 41.0082, "lon": 28.9784, "zoom": 12}} and wire it to the same worldmap node. Note that commands are applied when they arrive; a browser that connects later starts at the default view unless you also re-send the command on connection. The worldmap in node emits a connect event you can use for that.
Step 4: Draw tracks
The package also includes a worldmap tracks node. Place it between your function node and the worldmap node and set the number of points to keep, for example 50. It adds a line behind each moving marker, which makes routes and loops obvious at a glance.
Handling Stale and Offline Devices
Removing markers with ttl is simple, but often you would rather keep a marker and show that it is stale. Two patterns work well:
- Colour by age. Keep the last message per vehicle in flow context, run an inject every 30 seconds, and resend each vehicle with
iconColor: "grey"if its timestamp is older than a threshold. - Use Last Will and Testament. Each device sets a retained will message such as
offlineon.../fleet/<vehicle>/status. If its connection drops, the broker publishes the will, and a secondmqtt innode updates the marker. Retained status means a freshly started Node-RED knows straight away which vehicles are offline.
Combining both gives operators a clear picture: green moving markers are healthy, grey ones are late, and marked-offline ones have lost their connection. Routing those status messages is a good job for the Node-RED switch node.
Embedding the Map in Dashboard 2.0
The worldmap has its own page, but you can show it inside a dashboard too. In Dashboard 2.0, add a ui-template widget whose template contains an iframe pointing at /worldmap, sized to the group. This keeps the map next to gauges and alerts on one screen. The Dashboard 2.0 tutorial explains groups and layout.
Troubleshooting Checklist
- No markers. Confirm messages arrive with a debug node; check that
name,latandlonexist and are numbers, not strings. - Markers jump to the Gulf of Guinea. That is position 0,0, a typical "no GPS fix" output. The function above filters it out.
- All vehicles share one marker. Every message has the same
name. Derive it from the topic or a device ID. - Map is blank grey. The browser cannot reach the tile server; check internet access or proxy settings.
- Icons do not rotate. Only directional icons respect
bearing; switch to a vehicle icon such ascar. - Positions lag behind reality. Check device publish interval and keep-alive settings, and avoid queuing old positions with QoS 2 for live maps.
Taking GPS Tracking to Production
Location data is personal data in many contexts, so it must never travel over a public broker in production. Move devices and Node-RED to a private broker with TLS on port 8883 and per-device credentials. With ConnectMQ, the portal generates a username, password and client ID per broker, and repeated failed logins from an IP lead to it being blocked, which helps against credential guessing on field devices. Devices on cellular connections benefit from a hosted MQTT broker with a stable public endpoint, since they only ever connect outbound. If you want to compare hosting options first, read what an MQTT broker does and how brokers differ in authentication and limits.
Track Your Devices on ConnectMQ
Sign up at app.connectmq.com and start with the free Serverless plan, no credit card required. Create a broker, flash the credentials into your trackers, point the Node-RED config node at port 8883 with TLS, and your worldmap is live on a private, encrypted feed. The ConnectMQ live topic explorer is a quick way to confirm each vehicle's GPS topic is updating.
Frequently Asked Questions
What URL does the Node-RED worldmap use?
By default the map is served at http://<node-red-host>:1880/worldmap. The path can be changed in the worldmap node settings.
How do I move a marker instead of creating a new one?
Send a new message with the same name. The worldmap node treats name as the marker's ID and updates its position and properties.
How do I remove a vehicle from the map?
Send a message with the vehicle's name and deleted: true, or set ttl so markers disappear automatically when updates stop.
Can the worldmap show data other than GPS position?
Yes. Any extra properties in the payload, such as speed, battery or temperature, appear in the marker's popup, and the layer property lets you group markers by type.



