From 9910ef4330b36f0c0506130ce68dc5c81e72a87d Mon Sep 17 00:00:00 2001 From: Masashi Katsumata Date: Mon, 9 Nov 2026 09:38:06 -0800 Subject: [PATCH 1/2] Draw large polyline sets as raster tiles (PolylineTiler) The polyline controller gets a PolylineTiler, shown through the same raster layer path as the polygon tiles. Composed into the style above the polygon tiles; restacking native polylines is skipped while tiles are in use (it would tile them all again). Whatever redraws from the polyline manager skips tiled entities (no native line). --- .../tomtom/TomTomMapViewController.kt | 2 ++ .../tomtom/TomTomMapViewControllerRaster.kt | 21 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/mapconductor/tomtom/TomTomMapViewController.kt b/src/main/java/com/mapconductor/tomtom/TomTomMapViewController.kt index 50be728..2158ce9 100644 --- a/src/main/java/com/mapconductor/tomtom/TomTomMapViewController.kt +++ b/src/main/java/com/mapconductor/tomtom/TomTomMapViewController.kt @@ -284,6 +284,8 @@ class TomTomMapViewController internal constructor( } private suspend fun restackPolylines() { + // Drawn as tiles: the tile layer has its own order, and clearing would tile them all again. + if (polylineController.tiler?.tiledStates()?.isNotEmpty() == true) return val states = polylineController.polylineManager.allEntities().map { it.state } if (states.isEmpty()) return polylineController.clear() diff --git a/src/main/java/com/mapconductor/tomtom/TomTomMapViewControllerRaster.kt b/src/main/java/com/mapconductor/tomtom/TomTomMapViewControllerRaster.kt index 027e3ef..77fca4c 100644 --- a/src/main/java/com/mapconductor/tomtom/TomTomMapViewControllerRaster.kt +++ b/src/main/java/com/mapconductor/tomtom/TomTomMapViewControllerRaster.kt @@ -2,6 +2,8 @@ package com.mapconductor.tomtom import com.mapconductor.core.polygon.POLYGON_TILE_LAYER_ID import com.mapconductor.core.polygon.PolygonTiler +import com.mapconductor.core.polyline.POLYLINE_TILE_LAYER_ID +import com.mapconductor.core.polyline.PolylineTiler import com.mapconductor.core.raster.RasterLayerSource import com.mapconductor.core.raster.RasterLayerState import com.mapconductor.tomtom.marker.MarkerTileRasterLayerCallback @@ -60,6 +62,17 @@ fun TomTomMapViewController.setupMarkerTileRaster( } } } + // Many polylines too, the same way (over the polygon tiles, under the SDK's overlays). + polylineController.tiler = + PolylineTiler().apply { + onTemplate = { template, tileSize -> + if (template != null) { + upsertRasterLayer(POLYLINE_TILE_LAYER_ID, template, 1.0, tileSize, null, null) + } else { + removeRasterLayer(POLYLINE_TILE_LAYER_ID) + } + } + } } internal suspend fun TomTomMapViewController.applyPublicRasterLayer(state: RasterLayerState) { @@ -114,14 +127,18 @@ internal suspend fun TomTomMapViewController.applyComposedStyle() { // ポリゴンのタイルはアプリのラスターのすぐ上(SDK のオーバーレイの下)。ポリゴンの帯は // GroundImage の上だが、TomTom の GroundImage はネイティブの Polygon(SDK のオーバーレイ)で、 // スタイルの層をその間へは入れられない。サークル・ポリライン・マーカーの下は守れる。 + // ポリラインのタイルはポリゴンのタイルの上(同じ理由で SDK のオーバーレイの下: ネイティブの + // ポリゴン・サークルもこのタイルの上に来る)。マーカーのタイルとマーカーの下は守れる。 val (markerTiles, others) = composedRasterLayers.values.partition { it.id == TomTomMapViewController.MARKER_RASTER_ID } - val (polygonTiles, appRasters) = others.partition { it.id == POLYGON_TILE_LAYER_ID } + val (shapeTiles, appRasters) = others.partition { it.id == POLYGON_TILE_LAYER_ID || it.id == POLYLINE_TILE_LAYER_ID } + // Polygon tiles first (lower), then polyline tiles. + val shapeTilesBottomUp = shapeTiles.sortedBy { if (it.id == POLYGON_TILE_LAYER_ID) 0 else 1 } val uri = TomTomStyleComposer.composeStackedRasterStyle( apiKey = apiKey, cacheDir = cacheDir, - belowOverlays = appRasters.sortedBy { composedRasterZIndex[it.id] ?: 0 } + polygonTiles, + belowOverlays = appRasters.sortedBy { composedRasterZIndex[it.id] ?: 0 } + shapeTilesBottomUp, aboveOverlays = markerTiles, outFile = outFile, withoutBasemap = withoutBasemap, From 69318061413eaa6d30e6b6a67dfd9b421976185c Mon Sep 17 00:00:00 2001 From: Masashi Katsumata Date: Mon, 9 Nov 2026 13:57:04 -0800 Subject: [PATCH 2/2] Put the polyline tiles above the TomTom basemap's roads The polyline tiles went in front of the style's first label, like the app's rasters. TomTom's style keeps drawing road lines after its first symbol layer (67 line layers on the browsing style), so the basemap's roads covered the tile roads: on the Boston roads page only paths and the edges of the other classes showed. They are now appended after every style layer (under the marker tiles), as ios-for-tomtom does. The polygon tiles stay where they were: lifting them too put them over the native circle. Checked on the tablet (overlap check, z16, colour test): polygon tiles < circle < road tiles < marker; road taps still resolve. --- .../tomtom/TomTomMapViewControllerRaster.kt | 20 +++++++++---------- .../tomtom/raster/TomTomStyleComposer.kt | 15 ++++++++++++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/mapconductor/tomtom/TomTomMapViewControllerRaster.kt b/src/main/java/com/mapconductor/tomtom/TomTomMapViewControllerRaster.kt index 77fca4c..e586846 100644 --- a/src/main/java/com/mapconductor/tomtom/TomTomMapViewControllerRaster.kt +++ b/src/main/java/com/mapconductor/tomtom/TomTomMapViewControllerRaster.kt @@ -123,25 +123,25 @@ internal suspend fun TomTomMapViewController.applyComposedStyle() { } composedStyleToggle = composedStyleToggle xor 1 val outFile = File(cacheDir, "tomtom_composed_style_$composedStyleToggle.json") - // アプリのラスターはラスターの帯(SDK のオーバーレイの下)、マーカーのタイルはマーカーの帯(最前面)。 - // ポリゴンのタイルはアプリのラスターのすぐ上(SDK のオーバーレイの下)。ポリゴンの帯は - // GroundImage の上だが、TomTom の GroundImage はネイティブの Polygon(SDK のオーバーレイ)で、 - // スタイルの層をその間へは入れられない。サークル・ポリライン・マーカーの下は守れる。 - // ポリラインのタイルはポリゴンのタイルの上(同じ理由で SDK のオーバーレイの下: ネイティブの - // ポリゴン・サークルもこのタイルの上に来る)。マーカーのタイルとマーカーの下は守れる。 + // アプリのラスターはラスターの帯(ベースマップのラベルの下)、マーカーのタイルはマーカーの帯(最前面)。 + // ポリゴンのタイルはアプリのラスターのすぐ上(ラベルの下)。ネイティブのサークル・ポリゴン + // (SDK のオーバーレイ)はその上に来る。ポリラインのタイルはベースマップの全レイヤー(道路・ラベル)の + // 上: TomTom のスタイルは最初のラベルの後ろにも道路の line が続くので、ラベルの前では道路のタイルが + // 地図の道路に覆われて見えなかった。ネイティブのサークルより上・マーカー(とそのタイル)より下になる + // (実機で確認。ポリゴンのタイルまで上へ上げるとサークルを覆ってしまう)。 val (markerTiles, others) = composedRasterLayers.values.partition { it.id == TomTomMapViewController.MARKER_RASTER_ID } - val (shapeTiles, appRasters) = others.partition { it.id == POLYGON_TILE_LAYER_ID || it.id == POLYLINE_TILE_LAYER_ID } - // Polygon tiles first (lower), then polyline tiles. - val shapeTilesBottomUp = shapeTiles.sortedBy { if (it.id == POLYGON_TILE_LAYER_ID) 0 else 1 } + val (polylineTiles, rest) = others.partition { it.id == POLYLINE_TILE_LAYER_ID } + val (polygonTiles, appRasters) = rest.partition { it.id == POLYGON_TILE_LAYER_ID } val uri = TomTomStyleComposer.composeStackedRasterStyle( apiKey = apiKey, cacheDir = cacheDir, - belowOverlays = appRasters.sortedBy { composedRasterZIndex[it.id] ?: 0 } + shapeTilesBottomUp, + belowOverlays = appRasters.sortedBy { composedRasterZIndex[it.id] ?: 0 } + polygonTiles, aboveOverlays = markerTiles, outFile = outFile, withoutBasemap = withoutBasemap, + polylineTiles = polylineTiles, ) ?: return if (destroyed) return withContext(Dispatchers.Main) { diff --git a/src/main/java/com/mapconductor/tomtom/raster/TomTomStyleComposer.kt b/src/main/java/com/mapconductor/tomtom/raster/TomTomStyleComposer.kt index a4fd5e4..b5b265f 100644 --- a/src/main/java/com/mapconductor/tomtom/raster/TomTomStyleComposer.kt +++ b/src/main/java/com/mapconductor/tomtom/raster/TomTomStyleComposer.kt @@ -101,6 +101,9 @@ object TomTomStyleComposer { * (ラスター < GroundImage < ポリゴン < サークル < ポリライン < マーカー)でいちばん下の帯なので、 * SDK のオーバーレイ用レイヤー(polygon-shape など)とベースマップのラベルより**下**へ、 * 渡された順(下→上)に入れる。 + * - [polylineTiles]: SDK 自身のポリラインのタイル(下→上)。スタイルの全レイヤー(ベースマップの + * 道路・ラベルも含む)の上、マーカーのタイルの下へ。TomTom のスタイルは最初の symbol の後ろにも + * 道路の line レイヤーが続くので、ラベルの前に入れると道路のタイルが地図の道路に覆われて見えなかった。 * - [aboveOverlays]: マーカーのタイル。マーカーの帯なのでスタイルの末尾(最前面)へ。 * * 以前はどちらも末尾に積んでいたため、アプリのラスターがポリゴン・ポリライン・ @@ -113,6 +116,7 @@ object TomTomStyleComposer { aboveOverlays: List, outFile: File, withoutBasemap: Boolean, + polylineTiles: List = emptyList(), ): Uri? = withContext(Dispatchers.IO) { // The SDK will not take a style that has none of its own layers @@ -201,9 +205,16 @@ object TomTomStyleComposer { insertLayerAt(styleLayers, insertBelow, rasterLayer("mc-raster-layer-$index", sourceId, spec.opacity)) insertBelow++ } + // ポリラインのタイル: ベースマップの全レイヤーの上へ順に。 + polylineTiles.forEachIndexed { i, spec -> + val index = belowOverlays.size + i + val sourceId = "mc-raster-src-$index" + sources.put(sourceId, rasterSource(spec.tilesUrl, spec.tileSize, spec.minZoom, spec.maxZoom)) + styleLayers.put(rasterLayer("mc-raster-layer-$index", sourceId, spec.opacity)) + } // マーカーのタイル: 最前面(末尾)へ順に重ねる。 aboveOverlays.forEachIndexed { i, spec -> - val index = belowOverlays.size + i + val index = belowOverlays.size + polylineTiles.size + i val sourceId = "mc-raster-src-$index" sources.put(sourceId, rasterSource(spec.tilesUrl, spec.tileSize, spec.minZoom, spec.maxZoom)) styleLayers.put(rasterLayer("mc-raster-layer-$index", sourceId, spec.opacity)) @@ -218,7 +229,7 @@ object TomTomStyleComposer { Log.i( TAG, "Composed style written (${outFile.length()} bytes, ${styleLayers.length()} layers, " + - "${belowOverlays.size + aboveOverlays.size} raster)", + "${belowOverlays.size + polylineTiles.size + aboveOverlays.size} raster)", ) Uri.fromFile(outFile) }