- Extract COORDINATE_SCALE to shared MapConstants.kt, removing 6 duplicate
private const declarations across MapScreen, GeoJsonConverters, InlineMap,
NodeTrackMap, TracerouteLayers, and TracerouteMap
- Move node filtering from MapScreen composition into BaseMapViewModel as
filteredNodes StateFlow (testable, avoids composition-time computation)
- Move waypoint construction from MapScreen's inline onSend callback into
MapViewModel.createAndSendWaypoint() for testability and separation
- Remove unused compassBearing property from MapViewModel (bearing is read
directly from cameraState.position.bearing in MapScreen)
- Add nodes parameter to TracerouteMap for short name resolution on hop
markers (was hardcoded to emptyMap, falling back to hex node nums)
- Add GeoJsonConvertersTest with 25 tests covering nodesToFeatureCollection,
waypointsToFeatureCollection, positionsToLineString, positionsToPointFeatures,
precisionBitsToMeters, intToHexColor, and convertIntToEmoji
- Expand BaseMapViewModelTest from 5 to 21 tests covering filter toggles,
preference persistence, mapFilterState composition, filteredNodes with
favorites/last-heard/any filters, and getNodeOrFallback
- Expand MapViewModelTest from 9 to 12 tests covering createAndSendWaypoint
with new/edit/locked/no-position scenarios
- Fix precision circle radius: use zoom-based exponential interpolation
to convert meters to pixels instead of treating meters as dp values
- Fix InlineMap precision circle: compute pixel radius from meters at
the fixed zoom-15 display level
- Fix TracerouteLayers: wrap callback in LaunchedEffect to avoid state
updates during composition; add nodes to remember keys for fresh hop
labels; use relatedNodeNums.size for accurate total count
- Fix compass bearing: use epsilon comparison (±0.5°) instead of
exact float equality to prevent flickering near north
- Localize EditWaypointDialog: replace hardcoded English strings with
stringResource() using existing waypoint_edit/waypoint_new resources
- Format coordinates to 6 decimal places in waypoint position display
- Fix Int.toFloat() precision loss in track point filter by storing
time as string in GeoJSON and using string-based equality comparison
- Rename MapStyle enum values to match actual tile styles: Satellite→Light
(Positron), Hybrid→RoadMap (Americana), with updated string resources
- Reset bearingUpdate to IGNORE when gesture cancels location tracking
- Use LocationOn icon for ALWAYS_NORTH tracking mode instead of
misleading LocationDisabled
- Remove dead isOfflineManagerAvailable() expect/actual declarations
- Replace hardcoded English strings in offline map UI with
stringResource() calls backed by core:resources entries
The MarkerClusterer, RadiusMarkerClusterer, and StaticCluster Java files
under app/src/fdroid/java/ were missed during the MapLibre migration and
still referenced the removed osmdroid dependency, causing lintFdroidDebug
to fail on CI.
Wire remaining map feature gaps identified in the parity audit:
- MapFilterDropdown: favorites, waypoints, precision circle toggles and
last-heard slider matching the old Google/OSMDroid filter UIs
- MapStyleSelector: dropdown with 5 predefined MapStyle entries
- EditWaypointDialog: create, edit, delete waypoints via long-press or
marker tap, with icon picker and lock toggle
- Cluster zoom-to-expand: tap a cluster circle to zoom +2 levels
centered on the cluster position
- Bounds fitting: NodeTrackMap and TracerouteMap compute a BoundingBox
from all positions and animate the camera to fit on first load
- Location tracking: expect/actual rememberLocationProviderOrNull()
bridges platform GPS into maplibre-compose LocationPuck with
LocationTrackingEffect for auto-pan and bearing follow
- Per-node marker colors via data-driven convertToColor() expressions
- Waypoint camera animation on deep-link selection
- Compass click resets bearing to north
Replace the dual flavor-specific map implementations (Google Maps for google,
OSMDroid for fdroid) with a single MapLibre Compose Multiplatform implementation
in feature:map/commonMain, eliminating ~8,500 lines of duplicated code.
Key changes:
- Add maplibre-compose v0.12.1 dependency (KMP: Android, Desktop, iOS)
- Create unified MapViewModel with camera persistence via MapCameraPrefs
- Create MapScreen, MaplibreMapContent, NodeTrackLayers, TracerouteLayers,
InlineMap, NodeTrackMap, TracerouteMap, NodeMapScreen in commonMain
- Create MapStyle enum with predefined OpenFreeMap tile styles
- Create GeoJsonConverters for Node/Waypoint/Position to GeoJSON
- Move TracerouteMapScreen from feature:node/androidMain to commonMain
- Wire navigation to use direct imports instead of CompositionLocal providers
- Delete 61 flavor-specific map files (google + fdroid source sets)
- Remove 8 CompositionLocal map providers from core:ui
- Remove SharedMapViewModel (replaced by new MapViewModel)
- Remove dead google-maps and osmdroid entries from version catalog
- Add MapViewModelTest with 10 test cases in commonTest
Baseline verified: spotlessCheck, detekt, assembleGoogleDebug, allTests all pass.