mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
fix(map): grid mode centering via view.post (first-layout reset); antimeridian unwrap of visible bbox fixes Pacific-centered vanishing grid; tile meridians/fills across world repeats at low zoom
This commit is contained in:
1 parent
e75d2dc410
commit
05170541ed
2 files changed
+39
-17
No files matched your search
+34
-15
@@ -84,8 +84,19 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
val south = projection.fromPixels(canvas.width, canvas.height)
|
val south = projection.fromPixels(canvas.width, canvas.height)
|
||||||
val topLat = max(north.latitude, south.latitude).coerceIn(-90.0, 90.0)
|
val topLat = max(north.latitude, south.latitude).coerceIn(-90.0, 90.0)
|
||||||
val bottomLat = min(north.latitude, south.latitude).coerceIn(-90.0, 90.0)
|
val bottomLat = min(north.latitude, south.latitude).coerceIn(-90.0, 90.0)
|
||||||
val leftLon = min(north.longitude, south.longitude)
|
// Longitude: osmdroid normalizes to [-180, 180), so when the view
|
||||||
val rightLon = max(north.longitude, south.longitude)
|
// straddles the antimeridian (e.g. left edge 170°E, right edge 170°W)
|
||||||
|
// the raw min/max swap sides and the bounding box spans the wrong way
|
||||||
|
// (center on the Pacific → grid painted 180° off-screen). Unwrap by
|
||||||
|
// shifting one edge by +360° so left < right again.
|
||||||
|
var lonA = north.longitude
|
||||||
|
var lonB = south.longitude
|
||||||
|
if (lonA > lonB) {
|
||||||
|
val t = lonA; lonA = lonB; lonB = t
|
||||||
|
}
|
||||||
|
if (lonB - lonA > 180.0) lonA += 360.0
|
||||||
|
val leftLon = lonA
|
||||||
|
val rightLon = lonB
|
||||||
|
|
||||||
val firstRow = floor(bottomLat / cellLat).toInt()
|
val firstRow = floor(bottomLat / cellLat).toInt()
|
||||||
val lastRow = ceil(topLat / cellLat).toInt()
|
val lastRow = ceil(topLat / cellLat).toInt()
|
||||||
@@ -98,6 +109,12 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
// 2^zoom tiles across the whole 360° world). Used by the custom
|
// 2^zoom tiles across the whole 360° world). Used by the custom
|
||||||
// Mercator-X computation in projectionToX.
|
// Mercator-X computation in projectionToX.
|
||||||
val worldWidthPx = 256.0 * Math.pow(2.0, zoom)
|
val worldWidthPx = 256.0 * Math.pow(2.0, zoom)
|
||||||
|
// At low zoom the world is narrower than the viewport and osmdroid shows
|
||||||
|
// repeating copies on both sides. The visible bounding box spans more
|
||||||
|
// than 360° of longitude there; extend the column range by whole world
|
||||||
|
// turns so meridians, fills and labels tile across the repeats too.
|
||||||
|
val worldTurns = ceil(((rightLon - leftLon) / 360.0) - 1e-9).toInt().coerceAtLeast(0)
|
||||||
|
val colRepeats = if (worldTurns > 0) worldTurns else 0
|
||||||
|
|
||||||
// The station's own grid square (4-char, only meaningful at sub-square zoom)
|
// The station's own grid square (4-char, only meaningful at sub-square zoom)
|
||||||
val ownCell = ownGrid?.takeIf { zoom >= GRID_ZOOM_SUB }
|
val ownCell = ownGrid?.takeIf { zoom >= GRID_ZOOM_SUB }
|
||||||
@@ -117,7 +134,7 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
val yTop = projectionToY(projection, topLatCell)
|
val yTop = projectionToY(projection, topLatCell)
|
||||||
val yBottom = projectionToY(projection, lat)
|
val yBottom = projectionToY(projection, lat)
|
||||||
if (yTop == null || yBottom == null) continue
|
if (yTop == null || yBottom == null) continue
|
||||||
for (col in firstCol..lastCol) {
|
for (turn in -colRepeats..colRepeats) for (col in firstCol..lastCol) {
|
||||||
val lon = col * cellLon
|
val lon = col * cellLon
|
||||||
val xLeft = projectionToX(projection, lon, centerLon, worldWidthPx) ?: continue
|
val xLeft = projectionToX(projection, lon, centerLon, worldWidthPx) ?: continue
|
||||||
val xRight = projectionToX(projection, lon + cellLon, centerLon, worldWidthPx) ?: continue
|
val xRight = projectionToX(projection, lon + cellLon, centerLon, worldWidthPx) ?: continue
|
||||||
@@ -130,22 +147,24 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
} else {
|
} else {
|
||||||
for (grid in workedGrids) {
|
for (grid in workedGrids) {
|
||||||
val cell = gridCellBounds(grid) ?: continue
|
val cell = gridCellBounds(grid) ?: continue
|
||||||
if (cell.lonRight <= leftLon || cell.lonLeft >= rightLon ||
|
for (turn in -colRepeats..colRepeats) {
|
||||||
cell.latTop <= bottomLat || cell.latBottom >= topLat
|
// Shift the cell by whole world turns to cover repeats.
|
||||||
) continue
|
val dLon = turn * 360.0
|
||||||
val yTop = projectionToY(projection, cell.latTop) ?: continue
|
if (cell.lonRight + dLon <= leftLon || cell.lonLeft + dLon >= rightLon) continue
|
||||||
val yBottom = projectionToY(projection, cell.latBottom) ?: continue
|
val yTop = projectionToY(projection, cell.latTop) ?: continue
|
||||||
val xLeft = projectionToX(projection, cell.lonLeft, centerLon, worldWidthPx) ?: continue
|
val yBottom = projectionToY(projection, cell.latBottom) ?: continue
|
||||||
val xRight = projectionToX(projection, cell.lonRight, centerLon, worldWidthPx) ?: continue
|
val xLeft = projectionToX(projection, cell.lonLeft + dLon, centerLon, worldWidthPx) ?: continue
|
||||||
if (xRight < 0f || xLeft > canvas.width) continue
|
val xRight = projectionToX(projection, cell.lonRight + dLon, centerLon, worldWidthPx) ?: continue
|
||||||
if (yBottom < 0f || yTop > canvas.height) continue
|
if (xRight < 0f || xLeft > canvas.width) continue
|
||||||
canvas.drawRect(xLeft, yTop, xRight, yBottom, workedPaint)
|
if (yBottom < 0f || yTop > canvas.height) continue
|
||||||
|
canvas.drawRect(xLeft, yTop, xRight, yBottom, workedPaint)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vertical lines (meridians)
|
// Vertical lines (meridians)
|
||||||
for (col in firstCol..lastCol) {
|
for (turn in -colRepeats..colRepeats) for (col in firstCol..lastCol) {
|
||||||
val lon = col * cellLon
|
val lon = col * cellLon
|
||||||
val x = projectionToX(projection, lon, centerLon, worldWidthPx)
|
val x = projectionToX(projection, lon, centerLon, worldWidthPx)
|
||||||
if (x == null) continue
|
if (x == null) continue
|
||||||
@@ -209,7 +228,7 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
val yBottom = projectionToY(projection, lat) ?: continue
|
val yBottom = projectionToY(projection, lat) ?: continue
|
||||||
val yCenter = (yTop + yBottom) / 2f - textHalfHeight
|
val yCenter = (yTop + yBottom) / 2f - textHalfHeight
|
||||||
if (yBottom < 0f || yTop > canvas.height) continue
|
if (yBottom < 0f || yTop > canvas.height) continue
|
||||||
for (col in firstCol..lastCol) {
|
for (turn in -colRepeats..colRepeats) for (col in firstCol..lastCol) {
|
||||||
val lon = col * cellLon
|
val lon = col * cellLon
|
||||||
val xLeft = projectionToX(projection, lon, centerLon, worldWidthPx) ?: continue
|
val xLeft = projectionToX(projection, lon, centerLon, worldWidthPx) ?: continue
|
||||||
val xRight = projectionToX(projection, lon + cellLon, centerLon, worldWidthPx) ?: continue
|
val xRight = projectionToX(projection, lon + cellLon, centerLon, worldWidthPx) ?: continue
|
||||||
|
|||||||
@@ -314,7 +314,10 @@ private fun setGridMode(
|
|||||||
mapView.overlays.getOrNull(index)?.isEnabled = !gridMode
|
mapView.overlays.getOrNull(index)?.isEnabled = !gridMode
|
||||||
}
|
}
|
||||||
// Entering grid mode: center on the station's current grid square,
|
// Entering grid mode: center on the station's current grid square,
|
||||||
// keeping the current zoom level unchanged.
|
// keeping the current zoom level unchanged. post{} is essential: on the
|
||||||
|
// first composition this runs before MapView's first layout, and a
|
||||||
|
// setCenter issued pre-layout is discarded when the view lays out —
|
||||||
|
// the map then keeps its default center forever.
|
||||||
if (gridMode) {
|
if (gridMode) {
|
||||||
val pos = stationPosition ?: return
|
val pos = stationPosition ?: return
|
||||||
val lat = pos.latitude
|
val lat = pos.latitude
|
||||||
@@ -323,7 +326,7 @@ private fun setGridMode(
|
|||||||
val fieldLon = ((lon + 180.0) / 20.0).toInt().coerceIn(0, 17)
|
val fieldLon = ((lon + 180.0) / 20.0).toInt().coerceIn(0, 17)
|
||||||
val centerLat = fieldLat * 10.0 + 5.0
|
val centerLat = fieldLat * 10.0 + 5.0
|
||||||
val centerLon = fieldLon * 20.0 + 10.0
|
val centerLon = fieldLon * 20.0 + 10.0
|
||||||
mapView.controller.setCenter(GeoPoint(centerLat, centerLon))
|
mapView.post { mapView.controller.setCenter(GeoPoint(centerLat, centerLon)) }
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println(e)
|
println(e)
|
||||||
|
|||||||
Reference in new issue
Block a user