diff --git a/api/android-for-longdo.api b/api/android-for-longdo.api index 99ecfa1..b8b24f5 100644 --- a/api/android-for-longdo.api +++ b/api/android-for-longdo.api @@ -106,6 +106,8 @@ public final class com.mapconductor.longdo.LongdoMapViewController extends com.m public void setOnMarkerClickListener(kotlin.jvm.functions.Function1); public java.lang.Object compositionRasterLayers(java.util.List, kotlin.coroutines.Continuation); public java.lang.Object updateRasterLayer(com.mapconductor.core.raster.RasterLayerState, kotlin.coroutines.Continuation); + public void mountRasterLayer(com.mapconductor.core.raster.RasterLayerState); + public void unmountRasterLayer(java.lang.String); public boolean hasRasterLayer(com.mapconductor.core.raster.RasterLayerState); public boolean hasPolyline(com.mapconductor.core.polyline.PolylineState); public void setOnPolylineClickListener(kotlin.jvm.functions.Function1); @@ -149,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 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); public static final java.lang.String setRasterVisibilityJs(com.mapconductor.longdo.LongdoMapViewController, java.lang.String, boolean); @@ -169,8 +172,8 @@ public final class com.mapconductor.longdo.LongdoMapViewHolder implements com.ma public java.lang.Object getMap(); } public final class com.mapconductor.longdo.LongdoMapViewKt { - public static final void LongdoMapView(com.mapconductor.longdo.LongdoViewState, androidx.compose.ui.Modifier, com.mapconductor.core.marker.MarkerTilingOptions, com.mapconductor.core.map.CameraRestriction, kotlin.jvm.functions.Function1, kotlin.Unit>, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function3, androidx.compose.runtime.Composer, int, int, int); - public static final void LongdoMapSurface(com.mapconductor.longdo.LongdoViewState, androidx.compose.ui.Modifier, com.mapconductor.core.marker.MarkerTilingOptions, com.mapconductor.core.map.CameraRestriction, kotlin.jvm.functions.Function1, kotlin.Unit>, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function3, androidx.compose.runtime.Composer, int, int, int); + public static final void LongdoMapView(com.mapconductor.longdo.LongdoViewState, androidx.compose.ui.Modifier, com.mapconductor.core.marker.MarkerTilingOptions, com.mapconductor.core.map.CameraRestriction, kotlin.jvm.functions.Function1, kotlin.Unit>, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, com.mapconductor.core.map.MapViewStyle, kotlin.jvm.functions.Function1, kotlin.Unit>, kotlin.jvm.functions.Function3, androidx.compose.runtime.Composer, int, int, int); + public static final void LongdoMapSurface(com.mapconductor.longdo.LongdoViewState, androidx.compose.ui.Modifier, com.mapconductor.core.marker.MarkerTilingOptions, com.mapconductor.core.map.CameraRestriction, kotlin.jvm.functions.Function1, kotlin.Unit>, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1, com.mapconductor.core.map.MapViewStyle, kotlin.jvm.functions.Function1, kotlin.Unit>, kotlin.jvm.functions.Function3, androidx.compose.runtime.Composer, int, int, int); } public final class com.mapconductor.longdo.LongdoMapViewSaver extends com.mapconductor.compose.map.BaseMapViewSaver { public static final java.lang.String KEY_DESIGN_ID; diff --git a/src/main/java/com/mapconductor/longdo/LongdoMapView.kt b/src/main/java/com/mapconductor/longdo/LongdoMapView.kt index a4c4195..445220f 100644 --- a/src/main/java/com/mapconductor/longdo/LongdoMapView.kt +++ b/src/main/java/com/mapconductor/longdo/LongdoMapView.kt @@ -46,6 +46,8 @@ import com.mapconductor.core.map.MapCameraPosition import com.mapconductor.core.map.MapCapability import com.mapconductor.core.map.MapCapabilityStatus import com.mapconductor.core.map.MapServiceRegistrations +import com.mapconductor.core.map.MapViewStyle +import com.mapconductor.core.map.MapViewStyleEffect import com.mapconductor.core.map.VisibleRegion import com.mapconductor.core.marker.MarkerCapableInterface import com.mapconductor.core.marker.MarkerRenderingSupportKey @@ -88,6 +90,15 @@ fun LongdoMapView( onCameraMoveStart: OnCameraMoveHandler? = null, onCameraMove: OnCameraMoveHandler? = null, onCameraMoveEnd: OnCameraMoveHandler? = null, + /** + * How the map looks, when the app states it rather than naming a design. + * + * A vector style *is* the basemap. `com.mapconductor:vectorstyle` builds + * one; what happens underneath depends on this backend and the app does + * not have to know. + */ + style: MapViewStyle? = null, + onStyleDiagnostics: ((List) -> Unit)? = null, content: (@Composable MapViewScope.() -> Unit)? = null, ) { LongdoMapSurface( @@ -101,6 +112,8 @@ fun LongdoMapView( onCameraMoveStart = onCameraMoveStart, onCameraMove = onCameraMove, onCameraMoveEnd = onCameraMoveEnd, + style = style, + onStyleDiagnostics = onStyleDiagnostics, content = content, ) } @@ -132,6 +145,15 @@ fun LongdoMapSurface( onCameraMove: OnCameraMoveHandler? = null, onCameraMoveEnd: OnCameraMoveHandler? = null, onControllerReady: ((LongdoMapViewController) -> Unit)? = null, + /** + * How the map looks, when the app states it rather than naming a design. + * + * A vector style *is* the basemap. `com.mapconductor:vectorstyle` builds + * one; what happens underneath depends on this backend and the app does + * not have to know. + */ + style: MapViewStyle? = null, + onStyleDiagnostics: ((List) -> Unit)? = null, content: (@Composable MapViewScope.() -> Unit)? = null, ) { val context = LocalContext.current @@ -206,6 +228,16 @@ fun LongdoMapSurface( onDispose { registrations.disposeAll() } } + // 同じ理由で、スタイルの設置もここで呼ぶ(MapViewBase を通るプロバイダは + // あちらで 1 回呼んでいる)。何が起きるかはバックエンドが宣言した能力で + // 決まるので、このプロバイダ固有の分岐は無い。 + MapViewStyleEffect( + state = state, + style = style, + controller = controller, + onDiagnostics = onStyleDiagnostics, + ) + // タイル経路に倒すかはコントローラが件数を見て決める(他プロバイダと同じ規則)。 // ここで null を Default へ正規化するのも maplibre / googlemaps / here と揃えてある。 controller.markerTilingOptions = diff --git a/src/main/java/com/mapconductor/longdo/LongdoMapViewController.kt b/src/main/java/com/mapconductor/longdo/LongdoMapViewController.kt index 150438e..2161b8d 100644 --- a/src/main/java/com/mapconductor/longdo/LongdoMapViewController.kt +++ b/src/main/java/com/mapconductor/longdo/LongdoMapViewController.kt @@ -229,6 +229,11 @@ class LongdoMapViewController( dispatchCameraToOverlays() // Gesture JS issued before ready is dropped by the page; re-apply now. applyUISettings(appliedUISettings) + if (pendingMountedRasters.isNotEmpty()) { + val waiting = pendingMountedRasters.values.toList() + pendingMountedRasters.clear() + mainCoroutine.launch { waiting.forEach { applyRaster(it) } } + } } override fun setMapDesignType(value: LongdoMapDesignTypeInterface) { @@ -295,17 +300,62 @@ class LongdoMapViewController( override suspend fun compositionRasterLayers(data: List) { val newIds = data.map { it.id }.toSet() - appliedRasters.keys.filter { it !in newIds }.toList().forEach { id -> - runRasterJs(removeRasterJs(id)) - appliedRasters.remove(id) - } + appliedRasters.keys + .filter { it !in newIds && it !in mountedRasterIds } + .toList() + .forEach { id -> + runRasterJs(removeRasterJs(id)) + appliedRasters.remove(id) + } data.forEach { applyRaster(it) } } + /** + * content の外から載せたラスタの id。上の掃除から外すためにある。 + * + * `compositionRasterLayers` は「アプリが宣言した集合」で全置換するので、 + * 外から載せたものは次の再コンポーズで消える。他プロバイダでは + * [com.mapconductor.core.raster.RasterLayerController] の `upsert` が + * 同じ役割(`upsertedIds`)を持っている。 + */ + private val mountedRasterIds = mutableSetOf() + + /** [mountRasterLayer] が ready 前に受け取ったもの。 */ + private val pendingMountedRasters = mutableMapOf() + override suspend fun updateRasterLayer(state: RasterLayerState) { applyRaster(state) } + /** + * content の外から載せるラスタ(マーカータイル、ベクタースタイルのラスタ化)。 + * + * 他プロバイダは [com.mapconductor.core.raster.RasterLayerController] を + * オーバーレイコントローラとして登録してあるので基底の実装で足りるが、 + * Longdo は内部 MapLibre GL へ JS で直接足すのでそれが無い。 + * id は [mountedRasterIds] に控えて composition の掃除から外す。 + */ + override fun mountRasterLayer(state: RasterLayerState) { + mountedRasterIds.add(state.id) + // ready 前に流した JS はページに捨てられる(`runRasterJs` は握りつぶす)。 + // content 経由のラスタは composition が ready より後なので起きないが、 + // ビューの外から載せるものは地図より先に来る。退避して [onMapReady] で出す。 + if (!mapReady) { + pendingMountedRasters[state.id] = state + return + } + mainCoroutine.launch { applyRaster(state) } + } + + override fun unmountRasterLayer(id: String) { + mountedRasterIds.remove(id) + pendingMountedRasters.remove(id) + mainCoroutine.launch { + runRasterJs(removeRasterJs(id)) + appliedRasters.remove(id) + } + } + override fun hasRasterLayer(state: RasterLayerState): Boolean = appliedRasters.containsKey(state.id) // --- PolylineCapableInterface --- diff --git a/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerCamera.kt b/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerCamera.kt index ca0e5c2..2c7f3e6 100644 --- a/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerCamera.kt +++ b/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerCamera.kt @@ -91,7 +91,9 @@ internal fun LongdoMapViewController.nativeCameraFor(position: MapCameraPosition // 高度は統一ズーム(Google)基準で算出する(Longdo ネイティブズームではない)。 val altitude = zoomConverter.zoomLevelToAltitude(position.zoom, position.position.latitude, 0.0) val distanceForward = altitude * tan(tiltAbsRad) - val target = Spherical.computeOffset(position.position, distanceForward, CameraBearing.toNativeHeading(position.bearing)) + val target = + Spherical + .computeOffset(position.position, distanceForward, CameraBearing.toNativeHeading(position.bearing)) return NativeCamera(target, longdoZoom, tiltAbsDeg) } diff --git a/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerRaster.kt b/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerRaster.kt index bb55a8f..5b1cdaa 100644 --- a/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerRaster.kt +++ b/src/main/java/com/mapconductor/longdo/LongdoMapViewControllerRaster.kt @@ -101,14 +101,15 @@ internal fun LongdoMapViewController.addRasterJs( */ internal fun LongdoMapViewController.readdRastersAfterStyleSwapJs(rasters: List): String { val adds = - rasters.mapNotNull { state -> - val spec = rasterSourceSpec(state.source) ?: return@mapNotNull null - val srcId = rasterSourceId(state.id) - val layerId = rasterLayerId(state.id) - val layerSpec = rasterLayerSpec(layerId, srcId, state.opacity, state.visible) - "try{if(!m.getSource('$srcId'))m.addSource('$srcId',$spec);" + - "if(!m.getLayer('$layerId'))m.addLayer($layerSpec);}catch(e){}" - }.joinToString("") + rasters + .mapNotNull { state -> + val spec = rasterSourceSpec(state.source) ?: return@mapNotNull null + val srcId = rasterSourceId(state.id) + val layerId = rasterLayerId(state.id) + val layerSpec = rasterLayerSpec(layerId, srcId, state.opacity, state.visible) + "try{if(!m.getSource('$srcId'))m.addSource('$srcId',$spec);" + + "if(!m.getLayer('$layerId'))m.addLayer($layerSpec);}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/LongdoMapViewHolder.kt b/src/main/java/com/mapconductor/longdo/LongdoMapViewHolder.kt index aee0c40..d26ea44 100644 --- a/src/main/java/com/mapconductor/longdo/LongdoMapViewHolder.kt +++ b/src/main/java/com/mapconductor/longdo/LongdoMapViewHolder.kt @@ -2,9 +2,9 @@ package com.mapconductor.longdo import androidx.compose.ui.geometry.Offset import com.longdo.sdk3.LongdoMap +import com.mapconductor.core.ResourceProvider import com.mapconductor.core.features.GeoPoint import com.mapconductor.core.features.GeoPointInterface -import com.mapconductor.core.ResourceProvider import com.mapconductor.core.map.MapCameraPosition import com.mapconductor.core.map.MapViewHolderInterface import com.mapconductor.core.projection.WebMercatorScreenProjection