GPS Vehicle Tracking in Mobile IoT App
A GPS tracker on a vehicle sends a packet every 10 seconds. That's 8,640 records per day per object. With 50 vehicles — 432,000 points daily. The app must display current position of each in real-time and not lag when viewing history for a week.
Getting data from IoT trackers
Hardware GPS tracker (Teltonika FMB140, Queclink GV620, Concox GT06N) sends data via TCP/UDP to server using proprietary binary protocol or MQTT. Mobile app doesn't interact with tracker directly — that's the telematics server's job. Client receives processed stream via WebSocket or REST API.
Difference between WebSocket and polling is significant here. Polling every 10 seconds for 50 objects = constant HTTP requests, handshake overhead, up to 10 second delay. WebSocket with server events: server pushes update immediately when receiving new tracker packet, 1–2 second delay, no extra requests.
Displaying on map
Each tracker — marker on map with vehicle icon, bearing, and status. Three key moments:
Bearing animation. Tracker changes direction — icon should rotate smoothly, not jump. On Android: ObjectAnimator.ofFloat(marker, "rotation", oldBearing, newBearing).setDuration(500). On iOS: CABasicAnimation(keyPath: "transform.rotation.z") on marker layer.
Smooth movement. Marker moves to new coordinate, doesn't jump. ValueAnimator with LatLngInterpolator on Android; on iOS — CABasicAnimation with CGPoint interpolation via MKAnnotationView.coordinate.
Clustering. Below zoom 12 individual markers merge. Use Supercluster port for Flutter or native GMSMarkerClusterer (Android) / CMClusterAnnotationView (iOS). Cluster shows count of vehicles inside.
Track history
Day's history — 5,000 to 15,000 points depending on recording frequency. Rendering Polyline from 10,000 points directly — lag on render. Two approaches:
Douglas-Peucker discretization on server. On history request, server simplifies track with epsilon parameter depending on zoom level: at zoom 10 ~500 points sufficient, at zoom 17 — full detail. Client requests track with zoom parameter.
LOD on scroll. Load track for selected period in chunks while scrolling time slider. Outside visible area — don't render.
Stops in track computed server-side: cluster of points with speed < 5 km/h for longer than N minutes = stop. Address — reverse geocoding via Google Maps Geocoding API or Nominatim, cached in database.
Speed and alerts
Speed limit breach, hard braking, hard acceleration — computed from raw telemetry data (speed, accelerometer if tracker supports). Alert sent via FCM/APNs push with high priority. In app — UNNotificationCategory with "Open map" action for iOS, PendingIntent with deep link for Android.
Geofence alerts: entry/exit from zone. ST_Contains check in PostGIS on every incoming packet — hundreds of thousands of checks daily with large fleet. Optimization: spatial index GIST on geometry column, R-tree on geofences in memory (GeoHashing for initial filtering).
From practice
Cement truck tracking: 35 vehicles, 15 sec recording interval, 90 day history. Problem when viewing month history: Polyline from 720,000 points hung UI for 4–5 seconds. After dynamic discretization implementation (200 points at zoom 10, 5000 at zoom 16) — smooth on Samsung A32.
Timeline
Implementing vehicle GPS tracking in mobile IoT app (real-time map + history + alerts) with ready server API: 1–2 weeks. If API needs development — depends on telematics platform integration complexity. Cost calculated individually.







