Summary
The user’s map never zooms to their location because the GeoPoint constructor was fed raw double degrees instead of microdegree integers. OSMDroid’s GeoPoint expects latitude and longitude multiplied by 1,000,000 (microdegrees), but the code passed the raw double values from Location. This produces a garbage coordinate that pins the map to an invalid point, so the animateTo call does nothing visible. On top of that, the location listener is only registered in onResume, meaning the first fix may arrive before the listener exists or the provider has no data yet.
Root Cause
-
GeoPointexpects integers in microdegrees, not doubles. The lineGeoPoint userPoint = new GeoPoint(lat, lon);passes raw doubles. TheGeoPointconstructor truncates doubles to integers, effectively storing0for both coordinates in many cases, which maps to an invalid location near the equator in the middle of the ocean. -
Location updates are only requested in
onResume. If the map loads and a location fix arrives beforeonResumeruns, the listener is never attached and no centering happens. -
hasCenteredOncelogic prevents re-centering, so even if the first fix is wrong, the map locks in that bad position and never tries again. -
No fallback to the last known location. The code doesn’t request a cached fix from the location manager, so on cold start with no GPS signal the listener never fires at all.
Why This Happens in Real Systems
-
API surface mismatch. Android location APIs and OSMDroid APIs use different coordinate representations. Raw
Locationgives youdoublein decimal degrees;GeoPointexpectsintin microdegrees. Forgetting to convert is one of the most common silent bugs in map integrations. -
Timing issues with lifecycle. Registering listeners in
onResumebut using them inonCreate-scoped logic creates a race condition. The first location fix may arrive before the listener is wired up. -
Silent truncation. Java silently truncates
doubletointwithout rounding, so a latitude of40.7128becomes40— which is a completely different location. -
Permission flows delay data. Requesting permissions at runtime means the location manager has no updates until the user grants permission, which may happen after the first fix window.
Real-World Impact
- Map never centers on the user, making the app feel broken on first launch.
- Blue accuracy circle appears in the wrong place or not at all, eroding user trust.
- Debugging is misleading because
animateToappears to run but does nothing — the developer thinks the logic is wrong when it’s the coordinate format. - In production apps this causes crash reports when downstream code assumes valid coordinates and passes them to routing or geocoding services.
Example or Code (if necessary and relevant)
// WRONG: passing raw doubles to GeoPoint
GeoPoint userPoint = new GeoPoint(lat, lon);
// RIGHT: convert to microdegrees (int)
GeoPoint userPoint = new GeoPoint(
(int) (lat * 1e6),
(int) (lon * 1e6)
);
// Also request last known location as a fallback
Location lastKnown = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (lastKnown != null) {
GeoPoint fallbackPoint = new GeoPoint(
(int) (lastKnown.getLatitude() * 1e6),
(int) (lastKnown.getLongitude() * 1e6)
);
map.getController().animateTo(fallbackPoint);
map.getController().setZoom(17.0);
hasCenteredOnce = true;
}
How Senior Engineers Fix It
-
Always convert coordinates to the library’s expected format. For OSMDroid, multiply by
1e6and cast tointbefore creating aGeoPoint. -
Request the last known location as a fallback so the map has a valid center even before the first live fix.
-
Register the location listener immediately after permission is granted, not just in
onResume. -
Log the raw and converted coordinates during development to catch truncation early.
-
Use
setZoombeforeanimateToso the map has a target zoom level when it moves. -
Handle the case where the provider returns null — wrap the listener body in a null check.
Why Juniors Miss It
- The
GeoPointconstructor doesn’t throw an exception for bad input, so the bug is silent. - The map “sort of works” — the blue dot appears, the overlays load — so the junior assumes the zoom logic is the problem, not the coordinates.
- No mental model of coordinate systems. Juniors often don’t know that maps use microdegrees internally, so they don’t think to convert.
- Lifecycle timing is an advanced topic. Registering listeners in
onResumebut referencingonCreate-scoped variables feels “fine” until it isn’t.