From f0cf8dd030fc00dd0a507e58b963e9e5189624dd Mon Sep 17 00:00:00 2001 From: Masashi Katsumata Date: Sat, 7 Nov 2026 21:23:01 -0800 Subject: [PATCH 1/2] Interpolate the closing edge of geodesic polygons The geodesic ring was densified with createInterpolatePoints, which only interpolates between neighbouring points, so the closing edge (last vertex -> first) of a geodesic polygon was a straight chord in both fill and outline. Use android-sdk-core's densifyGeodesicRing. geodesic=false is unchanged. Requires the android-sdk-core release that adds densifyGeodesicRing. --- .../tomtom/polygon/TomTomPolygonOverlayRenderer.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/mapconductor/tomtom/polygon/TomTomPolygonOverlayRenderer.kt b/src/main/java/com/mapconductor/tomtom/polygon/TomTomPolygonOverlayRenderer.kt index 80de850..86f7d4b 100644 --- a/src/main/java/com/mapconductor/tomtom/polygon/TomTomPolygonOverlayRenderer.kt +++ b/src/main/java/com/mapconductor/tomtom/polygon/TomTomPolygonOverlayRenderer.kt @@ -3,13 +3,13 @@ package com.mapconductor.tomtom.polygon import androidx.compose.ui.graphics.toArgb import com.mapconductor.core.features.GeoPoint import com.mapconductor.core.features.GeoPointInterface +import com.mapconductor.core.geometry.densifyGeodesicRing import com.mapconductor.core.polygon.AbstractPolygonOverlayRenderer import com.mapconductor.core.polygon.PolygonEntityInterface import com.mapconductor.core.polygon.PolygonState import com.mapconductor.core.polygon.bridgeHolesIntoSingleRing import com.mapconductor.core.polygon.unionHoles import com.mapconductor.core.projection.Earth -import com.mapconductor.core.spherical.WGS84Geodesic import com.mapconductor.tomtom.TomTomActualPolygon import com.mapconductor.tomtom.TomTomMapViewHolder import com.mapconductor.tomtom.toTomTomGeoPoint @@ -43,9 +43,10 @@ class TomTomPolygonOverlayRenderer( geodesic: Boolean, ): List { // 非 geodesic は補間せず生の頂点を使う(HERE と同じ)。geodesic のみ補間する。 + // geodesic は閉じる辺(末尾 → 先頭)も大圏で補間する(塗りと輪郭が同じネイティブポリゴン)。 val geoPoints = if (geodesic) { - WGS84Geodesic.createInterpolatePoints(points, maxSegmentLength = maxSegmentLengthMeters()) + densifyGeodesicRing(points, maxSegmentLength = maxSegmentLengthMeters()) } else { points } @@ -80,7 +81,7 @@ class TomTomPolygonOverlayRenderer( ): List { val geoPoints = if (geodesic) { - WGS84Geodesic.createInterpolatePoints(points, maxSegmentLength = maxSegmentLengthMeters()) + densifyGeodesicRing(points, maxSegmentLength = maxSegmentLengthMeters()) } else { points } From aa471a30c078c8db9dc1cc922b715936dcab2b65 Mon Sep 17 00:00:00 2001 From: Masashi Katsumata Date: Sat, 7 Nov 2026 23:25:58 -0800 Subject: [PATCH 2/2] Densify non-geodesic polygon rings, closing edge included A geodesic=false polygon passed its raw vertices to TomTom, so every edge was a straight Web Mercator segment, unlike polylines with geodesic=false (linear lat/lng interpolation) and the other providers. Use android-sdk-core's densifyPolygonRing for both modes; it closes the ring before densifying, so the closing edge (last vertex -> first) matches the others (it replaces the unreleased densifyGeodesicRing). The linear case uses 200 km segments rather than the core's 10 km: raw vertices were used because a world-mask ring densified every 10 km made TomTom fail to draw it. A lat/lng line still bends smoothly at 200 km. Requires the android-sdk-core release that adds densifyPolygonRing. --- .../polygon/TomTomPolygonOverlayRenderer.kt | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/mapconductor/tomtom/polygon/TomTomPolygonOverlayRenderer.kt b/src/main/java/com/mapconductor/tomtom/polygon/TomTomPolygonOverlayRenderer.kt index 86f7d4b..c0c3152 100644 --- a/src/main/java/com/mapconductor/tomtom/polygon/TomTomPolygonOverlayRenderer.kt +++ b/src/main/java/com/mapconductor/tomtom/polygon/TomTomPolygonOverlayRenderer.kt @@ -3,7 +3,7 @@ package com.mapconductor.tomtom.polygon import androidx.compose.ui.graphics.toArgb import com.mapconductor.core.features.GeoPoint import com.mapconductor.core.features.GeoPointInterface -import com.mapconductor.core.geometry.densifyGeodesicRing +import com.mapconductor.core.geometry.densifyPolygonRing import com.mapconductor.core.polygon.AbstractPolygonOverlayRenderer import com.mapconductor.core.polygon.PolygonEntityInterface import com.mapconductor.core.polygon.PolygonState @@ -42,14 +42,7 @@ class TomTomPolygonOverlayRenderer( points: List, geodesic: Boolean, ): List { - // 非 geodesic は補間せず生の頂点を使う(HERE と同じ)。geodesic のみ補間する。 - // geodesic は閉じる辺(末尾 → 先頭)も大圏で補間する(塗りと輪郭が同じネイティブポリゴン)。 - val geoPoints = - if (geodesic) { - densifyGeodesicRing(points, maxSegmentLength = maxSegmentLengthMeters()) - } else { - points - } + val geoPoints = densify(points, geodesic) var ring = geoPoints.map { GeoPoint.from(it).toTomTomGeoPoint() } // 末尾の重複始点を除く(開いたリング)。 if (ring.size >= 2 && ring.first() == ring.last()) { @@ -79,16 +72,28 @@ class TomTomPolygonOverlayRenderer( points: List, geodesic: Boolean, ): List { - val geoPoints = - if (geodesic) { - densifyGeodesicRing(points, maxSegmentLength = maxSegmentLengthMeters()) - } else { - points - } - val ring = geoPoints.map { GeoPoint.from(it) } + val ring = densify(points, geodesic).map { GeoPoint.from(it) } return if (ring.size >= 2 && ring.first() == ring.last()) ring.dropLast(1) else ring } + /** + * 閉じる辺(末尾 → 先頭)も含めてリングを密度化する(塗りと輪郭が同じネイティブポリゴン)。 + * geodesic は大圏、geodesic=false は緯度経度の線形補間(ポリラインの false と同じ線)。 + * + * 線形補間は [MAX_GEODESIC_SEGMENT_METERS] 刻みと粗くする。世界マスク級のリングを 10 km 刻みで + * 補間すると過密になり TomTom の描画が失敗する(以前はこのため false を補間していなかった)。 + * 緯度経度の直線は 200 km 刻みでもメルカトル上の曲がりを十分に表せる。 + */ + private fun densify( + points: List, + geodesic: Boolean, + ): List = + densifyPolygonRing( + points, + geodesic, + maxSegmentLength = if (geodesic) maxSegmentLengthMeters() else MAX_GEODESIC_SEGMENT_METERS, + ) + private fun maxSegmentLengthMeters(): Double { val zoom = holder.map.cameraPosition.zoom val metersPerPixel = Earth.CIRCUMFERENCE_METERS / (256.0 * 2.0.pow(zoom))