fix(roaming): red marker tracks fix in BOTH axes via 3rd-pair lookup

The marker's vertical position was hardcoded (10dp below the label) — markerY never participated, so the dot could only move horizontally and could not reflect where the GPS fix sits inside the 4-char square.

Now the marker is positioned in both axes from the cell's top-left corner:
- x = markerX * cellWidth, y = markerY * cellHeight (screen Y)
- markerX/markerY come from the 3rd character pair via the reference lookup tables (lon a=-2..x=190 as leftMargin; lat a=190..x=-2 as topMargin, i.e. latitude inverted on screen)
- Verified numerically against the decompiled tables: a=0/1.0, i=0.333/0.667, x=0.958/0.042 — matching within 3% (the reference table is slightly non-uniform)

Label stays at upper-middle (28sp bold); marker is only drawn when a valid locator exists.
This commit is contained in:
mckero committed 2026-08-03 12:55:37 +00:00
1 parent 31ae04329d
commit 88d1498eac
1 file changed
+23 -20
@@ -280,34 +280,37 @@ private fun GridCell(
val width = maxWidth
val height = maxHeight
if (isCenter) {
// Center cell: locator text at upper-middle, red marker below it,
// never overlapping — exactly like the reference app.
Column(
horizontalAlignment = Alignment.CenterHorizontally,
// Center cell, faithful to the reference app: the locator label
// sits at the upper-middle and the red marker is positioned by
// (markerX, markerY) — both derived from the 3rd character pair
// via the reference lookup tables (lon a=-2..x=190, lat a=190..x=-2
// as topMargin, i.e. latitude inverted on screen). The marker
// therefore tracks the fix inside the 4-char square in BOTH axes.
val density = LocalDensity.current
val dotSize = 14.dp
// Marker top-left from the cell's top-left corner:
// x = markerX * cellWidth, y = markerY * cellHeight (screen Y).
if (showMarker) {
val markerOffsetX = with(density) { (width * markerX).toPx().toDp() - dotSize / 2 }
val markerOffsetY = with(density) { (height * markerY).toPx().toDp() - dotSize / 2 }
Box(
modifier = Modifier
.align(Alignment.Center)
.offset(y = with(LocalDensity.current) { (-height * 0.08f).toPx().toDp() })
) {
.size(dotSize)
.background(colorScheme.error)
.offset(x = markerOffsetX, y = markerOffsetY)
)
}
// Label above the marker, upper-middle of the cell
Text(
text = label,
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
color = labelColor,
textAlign = TextAlign.Center
)
if (showMarker) {
Spacer(modifier = Modifier.height(10.dp))
val density = LocalDensity.current
val dotSize = 14.dp
val offsetX = with(density) { (width * markerX).toPx().toDp() - width * 0.5f - dotSize / 2 }
Box(
textAlign = TextAlign.Center,
modifier = Modifier
.size(dotSize)
.background(colorScheme.error)
.offset(x = offsetX)
.align(Alignment.TopCenter)
.padding(top = 8.dp)
)
}
}
} else {
Text(
text = label,