@nolag/track
Vehicle and asset GPS tracking with geofencing and location history.
Overview
Track vehicles, delivery drivers, drones, or any moving asset in real time. @nolag/track broadcasts location updates to all zone subscribers instantly. Geofence detection runs client-side using the haversine formula for circular boundaries and a ray-casting algorithm for polygon boundaries, so triggers fire without a server round-trip. Zones group assets by geographic area or fleet. Join multiple zones to observe overlapping regions.
Key Features
- Real-time GPS location broadcast to all zone subscribers
- In-memory location buffer per asset for client-side history
- Client-side geofence detection for circle (haversine) and polygon (ray-casting)
- Asset online/offline presence via lobby
- Optional metadata attached to each location point
- Automatic reconnect with zone and presence restoration
How It Works
NoLagTrack wraps the @nolag/js-sdk client and manages a lobby that tracks which assets are online. Calling joinZone(name) returns a TrackingZone that subscribes to two topics: locations for ephemeral GPS points and _geofence for ephemeral geofence configuration events. Both topics are ephemeral (no server-side retention) to handle high-frequency GPS data without storage overhead. Location history is maintained in-memory on the client. Geofence evaluation happens locally on receipt of each locationUpdate event. No additional server calls are made.
| Topic | Purpose | Replay |
|---|---|---|
locations | GPS location points with optional metadata | Ephemeral |
_geofence | Geofence add/remove events (internal, client-side evaluation) | Ephemeral |
Installation
npm install @nolag/track @nolag/js-sdkQuick Start
import { NoLagTrack } from '@nolag/track'
const tracker = new NoLagTrack('your-access-token')
await tracker.connect()
// Join a tracking zone (groups assets by geographic area or fleet)
const zone = await tracker.joinZone('fleet-london')
// Device side: report location
zone.sendLocation({ lat: 51.5074, lng: -0.1278 }, { driverId: 'drv_001', speed: 42 })
// Add a circular geofence (haversine distance check)
zone.addGeofence({
id: 'depot-central',
type: 'circle',
center: { lat: 51.5074, lng: -0.1278 },
radiusMeters: 500,
label: 'Central Depot',
})
// Add a polygon geofence (ray-casting algorithm)
zone.addGeofence({
id: 'zone-east',
type: 'polygon',
coordinates: [
{ lat: 51.52, lng: -0.05 },
{ lat: 51.50, lng: -0.03 },
{ lat: 51.48, lng: -0.06 },
{ lat: 51.50, lng: -0.09 },
],
label: 'East Zone',
})
// Controller side: listen for updates
zone.on('locationUpdate', ({ assetId, point, metadata, timestamp }) => {
console.log(`Asset ${assetId} at ${point.lat}, ${point.lng}`)
})
zone.on('geofenceTriggered', ({ assetId, geofenceId, event }) => {
console.log(`Asset ${assetId} ${event} geofence ${geofenceId}`)
})
// Get in-memory location history for a specific asset
const history = await zone.getLocationHistory('drv_001')
console.log(`${history.length} location points in buffer`)
// Clean up
tracker.on('disconnected', () => console.log('Disconnected'))API Reference
NoLagTrack
| Method | Returns | Description |
|---|---|---|
connect() | Promise<void> | Establish the WebSocket connection and join the lobby. |
disconnect() | void | Close the connection and leave the lobby. |
joinZone(name) | Promise<TrackingZone> | Subscribe to a tracking zone. Returns the zone instance. |
leaveZone(name) | Promise<void> | Unsubscribe from a zone and release its resources. |
getOnlineAssets() | Asset[] | Return the list of assets currently present in the lobby. |
NoLagTrack Events
| Event | Payload | Description |
|---|---|---|
connected | none | WebSocket connection established. |
disconnected | reason: string | Connection closed. |
reconnected | none | Connection restored after a drop; zone membership and presence are restored automatically. |
error | error: Error | A transport or protocol error occurred. |
assetOnline | asset: Asset | An asset joined the lobby. |
assetOffline | asset: Asset | An asset left the lobby. |
TrackingZone
| Method | Returns | Description |
|---|---|---|
sendLocation(point, metadata?) | void | Broadcast a GPS point { lat, lng } with optional metadata to zone subscribers. |
getLocationHistory(assetId?) | Promise<LocationPoint[]> | Fetch location points from the in-memory buffer. Omit assetId to get all assets. |
addGeofence(geofence) | void | Register a circle or polygon geofence evaluated on every incoming location update. |
removeGeofence(id) | void | Deregister a geofence by its ID. |
getGeofences() | Geofence[] | Return all currently registered geofences for this zone. |
TrackingZone Events
| Event | Payload | Description |
|---|---|---|
locationUpdate | { assetId, point, metadata?, timestamp } | A location point was received from an asset in this zone. |
assetJoined | asset: Asset | An asset joined this zone. |
assetLeft | asset: Asset | An asset left this zone. |
geofenceTriggered | { assetId, geofenceId, geofence, event: 'enter' | 'exit', point } | An asset crossed a geofence boundary. Evaluated client-side on each location update. |