AIDrivingHelper 2: Driving Coaching with Geographic Context
Evolution of AIDrivingHelper that enriches driving events with real-world geographic context, including nearby schools, hospitals, road names, and traffic infrastructure, for location-aware safety coaching on Android.
What this is
Same context as AIDrivingHelper: this is an add-on coaching layer for apps that already record trip data. Apps like DriveKit, Zendrive, or Cambridge Mobile Telematics collect the raw events. This adds on-device AI coaching with one extra layer: geographic awareness.
Before a driving event is embedded and stored, it is enriched with the real-world context around it. What road was the driver on? Was there a school or hospital nearby? What traffic features were in the area? This context gets baked into the vector store so the coaching responses become location-aware.
What changes vs AIDrivingHelper
| Feature | AIDrivingHelper | AIDrivingHelper 2 | |---|---|---| | RAG pipeline | Yes | Yes | | Voice I/O | Yes | Yes | | Geographic enrichment | No | Yes | | Nearby schools/hospitals | No | Yes | | Road name in context | No | Yes | | Retrofit HTTP client | No | Yes (Retrofit 2 + Moshi) |
The core RAG chain is identical. The difference is what goes into the vector store before retrieval.
Architecture
Trip events (GPS coordinates per event)
↓
GeoContextEnricher
- Nominatim API → road name, neighborhood, city
- Overpass API → nearby schools, hospitals, traffic signals
↓
EnrichedDrivingEvent (road, city, nearbySchool, nearbyHospital, POI names)
↓
GeckoEmbeddingModel → SqliteVectorStore
↓
RAG chain → Gemma 3 1B IT → location-aware coaching response
Why geographic context matters
Without enrichment, the coaching response is: "You were doing 45 in a 30 zone."
With enrichment, it becomes: "You were speeding on Oak Street, which is within 200m of Lincoln Elementary School."
That is a different kind of feedback. It is more specific, more actionable, and more likely to stick.
Key Data Class
data class EnrichedDrivingEvent(
val road: String?,
val neighbourhood: String?,
val city: String?,
val poiNames: List<String>,
val nearbySchool: Boolean,
val nearbyHospital: Boolean,
// ... original event fields
)
Geo-Enrichment APIs
Nominatim (OpenStreetMap Reverse Geocoding):
- Converts GPS lat/lon to human-readable address
- Returns road name, neighbourhood, city, postcode
- Free, no auth required
Overpass API:
- Queries OpenStreetMap features within 80m radius of each event location
- Detects traffic signals, pedestrian crossings, and amenity types (school, hospital)
- Uses OverPass Query Language (OQL) with
around:radiusfilters
Both APIs are called via Retrofit 2 with Moshi JSON conversion and an OkHttp logging interceptor.
Key Screens
TripPOCDemoScreen: Displays trip metadata and the list of driving events, with a button to trigger geo enrichment on demand.
GeoContextInsightsCard: Shows each enriched event with road name and city, proximity warnings (school nearby, hospital nearby), and a list of POIs within radius.
VoiceChatWithInputScreen: Same voice chat interface as AIDrivingHelper. Ask questions about your trips, get location-aware coaching responses.
Tech Stack
Same as AIDrivingHelper, plus:
- Retrofit 2.9.0 with Moshi JSON 1.14.0
- OkHttp3 logging interceptor for API debugging
- Separate Room entities for trips and events (
TripEntity,TripEventEntity)
What I Learned
- Overpass Query Language is very powerful for radius-based POI detection. The
around:radiusfilter does the heavy lifting. The learning curve is the syntax, not the concept. - Context quality drives response quality. Richer embedding content means more specific retrieval, which means better coaching. The enrichment step directly improves the output.
- Nominatim has a 1 request/second rate limit. When enriching many events in a batch, a throttle is required or requests start getting rejected.
- Location-aware feedback lands differently. Telling a driver they were speeding near a school changes how they receive that information compared to a bare speed number.