diff --git a/api/android-for-longdo.api b/api/android-for-longdo.api index b8b24f5..1678d7a 100644 --- a/api/android-for-longdo.api +++ b/api/android-for-longdo.api @@ -151,6 +151,7 @@ public final class com.mapconductor.longdo.LongdoMapViewControllerRasterKt { public static final java.lang.String rasterSourceSpec(com.mapconductor.longdo.LongdoMapViewController, com.mapconductor.core.raster.RasterLayerSource); public static final java.lang.String rasterLayerSpec(com.mapconductor.longdo.LongdoMapViewController, java.lang.String, java.lang.String, float, boolean); public static final java.lang.String addRasterJs(com.mapconductor.longdo.LongdoMapViewController, java.lang.String, java.lang.String, java.lang.String, java.lang.String); + public static final java.lang.String addRasterJs(com.mapconductor.longdo.LongdoMapViewController, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String); public static final java.lang.String readdRastersAfterStyleSwapJs(com.mapconductor.longdo.LongdoMapViewController, java.util.List); public static final java.lang.String removeRasterJs(com.mapconductor.longdo.LongdoMapViewController, java.lang.String); public static final java.lang.String setRasterOpacityJs(com.mapconductor.longdo.LongdoMapViewController, java.lang.String, float); @@ -190,6 +191,11 @@ final class com.mapconductor.longdo.LongdoMapViewSaver$Companion { public final class com.mapconductor.longdo.LongdoMapViewScope extends com.mapconductor.compose.MapViewScope { public com.mapconductor.longdo.LongdoMapViewScope(); } +public final class com.mapconductor.longdo.LongdoOverlayStacking { + public static final com.mapconductor.longdo.LongdoOverlayStacking INSTANCE; + public final java.lang.String addLayerJs(java.lang.String, com.mapconductor.core.controller.OverlayKind, int); + public final java.lang.String addMarkerTileLayerJs(java.lang.String); +} public final class com.mapconductor.longdo.LongdoOverlayState { public com.mapconductor.longdo.LongdoOverlayState(); public final androidx.compose.runtime.snapshots.SnapshotStateMap getRenderPx(); diff --git a/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerMarkerTiles.kt b/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerMarkerTiles.kt index 05e9187..b3855b8 100644 --- a/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerMarkerTiles.kt +++ b/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerMarkerTiles.kt @@ -71,10 +71,12 @@ internal fun LongdoMapViewController.applyMarkerTileRaster(state: RasterLayerSta val prev = markerTileState val layerSpec = rasterLayerSpec(layerId, srcId, state.opacity, state.visible) when { - prev == null -> runRasterJs(addRasterJs(srcId, layerId, spec, layerSpec)) + // マーカーのタイルはマーカーの帯(ポリライン等より上)。 + prev == null -> + runRasterJs(addRasterJs(srcId, layerId, spec, layerSpec, LongdoOverlayStacking.addMarkerTileLayerJs(layerSpec))) prev.source != state.source -> { runRasterJs(removeRasterJs(state.id)) - runRasterJs(addRasterJs(srcId, layerId, spec, layerSpec)) + runRasterJs(addRasterJs(srcId, layerId, spec, layerSpec, LongdoOverlayStacking.addMarkerTileLayerJs(layerSpec))) } } markerTileState = state diff --git a/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerRaster.kt b/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerRaster.kt index 5b1cdaa..5940967 100644 --- a/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerRaster.kt +++ b/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerRaster.kt @@ -1,5 +1,6 @@ package com.mapconductor.longdo +import com.mapconductor.core.controller.OverlayKind import com.mapconductor.core.raster.RasterHeaderRuleSet import com.mapconductor.core.raster.RasterLayerSource import com.mapconductor.core.raster.RasterLayerState @@ -23,10 +24,10 @@ internal fun LongdoMapViewController.applyRaster(state: RasterLayerState) { val layerSpec = rasterLayerSpec(layerId, srcId, state.opacity, state.visible) when { prev == null -> - runRasterJs(addRasterJs(srcId, layerId, spec, layerSpec)) - prev.source != state.source -> { + runRasterJs(addRasterJs(srcId, layerId, spec, layerSpec, rasterAddLayerJs(layerSpec, state))) + prev.source != state.source || prev.zIndex != state.zIndex -> { runRasterJs(removeRasterJs(state.id)) - runRasterJs(addRasterJs(srcId, layerId, spec, layerSpec)) + runRasterJs(addRasterJs(srcId, layerId, spec, layerSpec, rasterAddLayerJs(layerSpec, state))) } else -> { if (prev.opacity != state.opacity) runRasterJs(setRasterOpacityJs(layerId, state.opacity)) @@ -36,6 +37,11 @@ internal fun LongdoMapViewController.applyRaster(state: RasterLayerState) { appliedRasters[state.id] = state } +private fun rasterAddLayerJs( + layerSpec: String, + state: RasterLayerState, +): String = LongdoOverlayStacking.addLayerJs(layerSpec, OverlayKind.RasterLayer, state.zIndex) + internal fun LongdoMapViewController.runRasterJs(js: String) { runCatching { longdoMap.run(js) {} } } @@ -79,6 +85,15 @@ internal fun LongdoMapViewController.addRasterJs( layerId: String, sourceSpec: String, layerSpec: String, +): String = addRasterJs(sourceId, layerId, sourceSpec, layerSpec, LongdoOverlayStacking.addLayerJs(layerSpec, OverlayKind.RasterLayer)) + +/** [addLayer] は帯の順で層を足す JS([LongdoOverlayStacking.addLayerJs])。 */ +internal fun LongdoMapViewController.addRasterJs( + sourceId: String, + layerId: String, + sourceSpec: String, + layerSpec: String, + addLayer: String, ): String = """ (function(){var m=map.Renderer; if(!m)return; @@ -86,7 +101,7 @@ internal fun LongdoMapViewController.addRasterJs( function apply(){ try{ if(!m.getSource('$sourceId'))m.addSource('$sourceId',$sourceSpec); - if(!m.getLayer('$layerId'))m.addLayer($layerSpec); + if(!m.getLayer('$layerId'))$addLayer }catch(e){ if(tries++<30)setTimeout(apply,150); } } apply(); @@ -107,8 +122,14 @@ internal fun LongdoMapViewController.readdRastersAfterStyleSwapJs(rasters: List< val srcId = rasterSourceId(state.id) val layerId = rasterLayerId(state.id) val layerSpec = rasterLayerSpec(layerId, srcId, state.opacity, state.visible) + val addLayer = + if (state.id == markerTileState?.id) { + LongdoOverlayStacking.addMarkerTileLayerJs(layerSpec) + } else { + LongdoOverlayStacking.addLayerJs(layerSpec, OverlayKind.RasterLayer, state.zIndex) + } "try{if(!m.getSource('$srcId'))m.addSource('$srcId',$spec);" + - "if(!m.getLayer('$layerId'))m.addLayer($layerSpec);}catch(e){}" + "if(!m.getLayer('$layerId'))$addLayer}catch(e){}" }.joinToString("") return "(function(){var m=map.Renderer;if(!m)return;function re(){$adds}" + "m.once('style.load',function(){setTimeout(re,0);});setTimeout(re,1500);})()" diff --git a/src/main/java/com/mapconductor/longdo/LongdoOverlayStacking.kt b/src/main/java/com/mapconductor/longdo/LongdoOverlayStacking.kt new file mode 100644 index 0000000..20d3f90 --- /dev/null +++ b/src/main/java/com/mapconductor/longdo/LongdoOverlayStacking.kt @@ -0,0 +1,35 @@ +package com.mapconductor.longdo + +import com.mapconductor.core.controller.OverlayKind +import com.mapconductor.core.controller.OverlayStacking + +/** + * 重なり順([OverlayStacking]: ラスター < GroundImage < ポリゴン < サークル < ポリライン < マーカー)。 + * + * Longdo の地図は WebView の中の MapLibre GL JS(`map.Renderer`)で、レイヤーは + * `addLayer` した順に上へ積まれていた(追加・送り直し・スタイル差し替えの順に左右される)。 + * ここで作る JS は、足す層の帯と zIndex を `m.__mcStack` に記録し、今のスタイルの並びから + * 「鍵が自分より大きい一番下の SDK の層」を beforeId に選んで `addLayer` する。 + * ラスターだけはベースマップのラベル(SDK の層でない最初の symbol 層)の下に入れる。 + */ +internal object LongdoOverlayStacking { + private const val FN = + "function(m,s,b,z){var K=m.__mcStack||(m.__mcStack={});K[s.id]=[b,z];" + + "if(m.getLayer(s.id))return;var L=m.getStyle().layers,i,k,bi=-1;" + + "for(i=0;ib||(k[0]==b&&k[1]>z))){bi=i;break;}}" + + "if(b==0){for(i=0;i() + private fun addCircle(state: CircleState): LongdoCircleHandle? { val feature = buildGeoJson(state) ?: return null + zIndexByLayer["circle-fill-${state.id}"] = state.zIndex ?: 0 val fillArgb = state.fillColor.toArgb() val strokeArgb = state.strokeColor.toArgb() val handle = @@ -137,6 +143,9 @@ class LongdoCircleOverlayRenderer( .put("line-opacity", handle.strokeOpacity) .put("line-width", handle.strokeWidth), ).toString() + // サークルの帯。輪郭は塗りの直後。 + val fillAdd = LongdoOverlayStacking.addLayerJs(fillSpec, OverlayKind.Circle, zIndexByLayer[handle.fillLayerId] ?: 0) + val lineAdd = LongdoOverlayStacking.addLayerJs(lineSpec, OverlayKind.Circle, zIndexByLayer[handle.fillLayerId] ?: 0) return """ (function(){var m=map.Renderer; if(!m)return; var tries=0; @@ -144,8 +153,8 @@ class LongdoCircleOverlayRenderer( try{ if(!m.getSource('${handle.sourceId}')) m.addSource('${handle.sourceId}',{type:'geojson',data:${handle.featureJson}}); - if(!m.getLayer('${handle.fillLayerId}'))m.addLayer($fillSpec); - if(!m.getLayer('${handle.lineLayerId}'))m.addLayer($lineSpec); + if(!m.getLayer('${handle.fillLayerId}'))$fillAdd + if(!m.getLayer('${handle.lineLayerId}'))$lineAdd }catch(e){ if(tries++<30)setTimeout(apply,150); } } apply(); diff --git a/src/main/java/com/mapconductor/longdo/groundimage/LongdoGroundImageOverlayRenderer.kt b/src/main/java/com/mapconductor/longdo/groundimage/LongdoGroundImageOverlayRenderer.kt index 0157ef3..f1fc141 100644 --- a/src/main/java/com/mapconductor/longdo/groundimage/LongdoGroundImageOverlayRenderer.kt +++ b/src/main/java/com/mapconductor/longdo/groundimage/LongdoGroundImageOverlayRenderer.kt @@ -1,10 +1,12 @@ package com.mapconductor.longdo.groundimage import com.longdo.sdk3.LongdoMap +import com.mapconductor.core.controller.OverlayKind import com.mapconductor.core.features.GeoRectBounds import com.mapconductor.core.groundimage.GroundImageEntityInterface import com.mapconductor.core.groundimage.GroundImageOverlayRendererInterface import com.mapconductor.core.groundimage.GroundImageState +import com.mapconductor.longdo.LongdoOverlayStacking import org.json.JSONObject import java.io.ByteArrayOutputStream import android.graphics.Bitmap @@ -146,6 +148,8 @@ class LongdoGroundImageOverlayRenderer( .put("paint", JSONObject().put("raster-opacity", handle.opacity)) .put("layout", JSONObject().put("visibility", "visible")) .toString() + // GroundImage の帯(ラスターの上、ポリゴンの下)。 + val layerAdd = LongdoOverlayStacking.addLayerJs(layerSpec, OverlayKind.GroundImage) return """ (function(){var m=map.Renderer; if(!m)return; var tries=0; @@ -154,7 +158,7 @@ class LongdoGroundImageOverlayRenderer( if(!m.getSource('${handle.sourceId}')) m.addSource('${handle.sourceId}', {type:'image',url:'${handle.imageUrl}',coordinates:${handle.coordinatesJson}}); - if(!m.getLayer('${handle.layerId}'))m.addLayer($layerSpec); + if(!m.getLayer('${handle.layerId}'))$layerAdd }catch(e){ if(tries++<30)setTimeout(apply,150); } } apply(); diff --git a/src/main/java/com/mapconductor/longdo/polygon/LongdoPolygonOverlayRenderer.kt b/src/main/java/com/mapconductor/longdo/polygon/LongdoPolygonOverlayRenderer.kt index 7bf277f..1f7b81d 100644 --- a/src/main/java/com/mapconductor/longdo/polygon/LongdoPolygonOverlayRenderer.kt +++ b/src/main/java/com/mapconductor/longdo/polygon/LongdoPolygonOverlayRenderer.kt @@ -2,12 +2,14 @@ package com.mapconductor.longdo.polygon import androidx.compose.ui.graphics.toArgb import com.longdo.sdk3.LongdoMap +import com.mapconductor.core.controller.OverlayKind import com.mapconductor.core.geometry.OverlayGeoJson import com.mapconductor.core.geometry.buildUnwrappedPolygonRings import com.mapconductor.core.polygon.PolygonEntityInterface import com.mapconductor.core.polygon.PolygonOverlayRendererInterface import com.mapconductor.core.polygon.PolygonState import com.mapconductor.core.polygon.unionHoles +import com.mapconductor.longdo.LongdoOverlayStacking import org.json.JSONObject /** @@ -56,8 +58,12 @@ class LongdoPolygonOverlayRenderer( handles.forEach { runJs(addPolygonJs(it)) } } + /** 層の id → zIndex(帯の中の順)。handle は公開型なので持たせずここで覚える。 */ + private val zIndexByLayer = java.util.concurrent.ConcurrentHashMap() + private fun addPolygon(state: PolygonState): LongdoPolygonHandle? { val feature = buildGeoJson(state) ?: return null + zIndexByLayer["polygon-fill-${state.id}"] = state.zIndex val fillArgb = state.fillColor.toArgb() val strokeArgb = state.strokeColor.toArgb() val handle = @@ -140,6 +146,10 @@ class LongdoPolygonOverlayRenderer( .put("line-opacity", handle.strokeOpacity) .put("line-width", handle.strokeWidth), ).toString() + // ポリゴンの帯。帯の中は zIndex の順、輪郭は塗りの直後。 + val z = zIndexByLayer[handle.fillLayerId] ?: 0 + val fillAdd = LongdoOverlayStacking.addLayerJs(fillSpec, OverlayKind.Polygon, z) + val lineAdd = LongdoOverlayStacking.addLayerJs(lineSpec, OverlayKind.Polygon, z) return """ (function(){var m=map.Renderer; if(!m)return; var tries=0; @@ -147,8 +157,8 @@ class LongdoPolygonOverlayRenderer( try{ if(!m.getSource('${handle.sourceId}')) m.addSource('${handle.sourceId}',{type:'geojson',data:${handle.featureJson}}); - if(!m.getLayer('${handle.fillLayerId}'))m.addLayer($fillSpec); - if(!m.getLayer('${handle.lineLayerId}'))m.addLayer($lineSpec); + if(!m.getLayer('${handle.fillLayerId}'))$fillAdd + if(!m.getLayer('${handle.lineLayerId}'))$lineAdd }catch(e){ if(tries++<30)setTimeout(apply,150); } } apply(); diff --git a/src/main/java/com/mapconductor/longdo/polyline/LongdoPolylineOverlayRenderer.kt b/src/main/java/com/mapconductor/longdo/polyline/LongdoPolylineOverlayRenderer.kt index b4e08ea..bce5e88 100644 --- a/src/main/java/com/mapconductor/longdo/polyline/LongdoPolylineOverlayRenderer.kt +++ b/src/main/java/com/mapconductor/longdo/polyline/LongdoPolylineOverlayRenderer.kt @@ -2,11 +2,13 @@ package com.mapconductor.longdo.polyline import androidx.compose.ui.graphics.toArgb import com.longdo.sdk3.LongdoMap +import com.mapconductor.core.controller.OverlayKind import com.mapconductor.core.geometry.OverlayGeoJson import com.mapconductor.core.geometry.buildUnwrappedPolylinePath import com.mapconductor.core.polyline.PolylineEntityInterface import com.mapconductor.core.polyline.PolylineOverlayRendererInterface import com.mapconductor.core.polyline.PolylineState +import com.mapconductor.longdo.LongdoOverlayStacking import org.json.JSONObject /** @@ -52,8 +54,12 @@ class LongdoPolylineOverlayRenderer( handles.forEach { runJs(addLineJs(it)) } } + /** 層の id → zIndex(帯の中の順)。 */ + private val zIndexByLayer = java.util.concurrent.ConcurrentHashMap() + private fun addLine(state: PolylineState): LongdoPolylineHandle? { val feature = buildGeoJson(state) ?: return null + zIndexByLayer["polyline-layer-${state.id}"] = state.zIndex val argb = state.strokeColor.toArgb() val handle = LongdoPolylineHandle( @@ -122,6 +128,8 @@ class LongdoPolylineOverlayRenderer( .put("line-opacity", handle.opacity) .put("line-width", handle.width), ).toString() + // ポリラインの帯。 + val layerAdd = LongdoOverlayStacking.addLayerJs(layerSpec, OverlayKind.Polyline, zIndexByLayer[handle.layerId] ?: 0) return """ (function(){var m=map.Renderer; if(!m)return; var tries=0; @@ -129,7 +137,7 @@ class LongdoPolylineOverlayRenderer( try{ if(!m.getSource('${handle.sourceId}')) m.addSource('${handle.sourceId}',{type:'geojson',data:${handle.featureJson}}); - if(!m.getLayer('${handle.layerId}'))m.addLayer($layerSpec); + if(!m.getLayer('${handle.layerId}'))$layerAdd }catch(e){ if(tries++<30)setTimeout(apply,150); } } apply();