diff --git a/api/android-for-mapbox.api b/api/android-for-mapbox.api index 8865c91..496c34f 100644 --- a/api/android-for-mapbox.api +++ b/api/android-for-mapbox.api @@ -20,22 +20,29 @@ public interface com.mapconductor.mapbox.IMapboxPaddingsInterface extends com.ma public abstract com.mapbox.maps.EdgeInsets toEdgeInsects(); } public final class com.mapconductor.mapbox.MapCameraPositionKt { + public static final double MAPBOX_VERTICAL_FOV; + public static final com.mapbox.maps.EdgeInsets toMapboxEdgeInsets(com.mapconductor.core.map.MapPaddingsInterface, float); + public static final com.mapconductor.core.map.MapPaddings toMapPaddings(com.mapbox.maps.EdgeInsets, float); public static final com.mapconductor.core.map.MapCameraPosition toMapCameraPosition(com.mapbox.maps.CameraChanged); public static final com.mapbox.maps.CameraOptions toCameraOptions(com.mapconductor.core.map.MapCameraPosition); + public static final com.mapbox.maps.CameraOptions toCameraOptions(com.mapconductor.core.map.MapCameraPosition, java.lang.Float); public static final com.mapbox.maps.CameraState toCameraState(com.mapconductor.core.map.MapCameraPosition); public static final com.mapconductor.core.map.MapCameraPosition from(com.mapconductor.core.map.MapCameraPosition$Companion, com.mapconductor.core.map.MapCameraPositionInterface); public static final com.mapconductor.core.map.MapCameraPosition toMapCameraPosition(com.mapbox.maps.CameraOptions); public static final com.mapconductor.core.map.MapCameraPosition toMapCameraPosition(com.mapbox.maps.CameraState); - public static final com.mapconductor.core.map.MapCameraPosition toMapCameraPosition(com.mapbox.maps.CameraState, java.lang.Double); + public static final com.mapconductor.core.map.MapCameraPosition toMapCameraPosition(com.mapbox.maps.CameraState, java.lang.Double, java.lang.Float); } public final class com.mapconductor.mapbox.MapboxCameraStateSnapshot { - public com.mapconductor.mapbox.MapboxCameraStateSnapshot(com.mapbox.maps.CameraState, java.lang.Double); + public com.mapconductor.mapbox.MapboxCameraStateSnapshot(com.mapbox.maps.CameraState, java.lang.Double, java.lang.Float); + public com.mapconductor.mapbox.MapboxCameraStateSnapshot(com.mapbox.maps.CameraState, java.lang.Double, java.lang.Float, int, kotlin.jvm.internal.DefaultConstructorMarker); public final com.mapbox.maps.CameraState getCameraState(); public final java.lang.Double getLogicalTiltHint(); + public final java.lang.Float getDensity(); public final com.mapconductor.core.map.MapCameraPosition toMapCameraPosition(); public final com.mapbox.maps.CameraState component1(); public final java.lang.Double component2(); - public final com.mapconductor.mapbox.MapboxCameraStateSnapshot copy(com.mapbox.maps.CameraState, java.lang.Double); + public final java.lang.Float component3(); + public final com.mapconductor.mapbox.MapboxCameraStateSnapshot copy(com.mapbox.maps.CameraState, java.lang.Double, java.lang.Float); public java.lang.String toString(); public int hashCode(); public boolean equals(java.lang.Object); @@ -151,6 +158,7 @@ public final class com.mapconductor.mapbox.MapboxMapViewController extends com.m public final class com.mapconductor.mapbox.MapboxMapViewControllerCameraKt { public static final com.mapconductor.core.map.MapCameraPosition getMapCameraPosition(com.mapconductor.mapbox.MapboxMapViewController); public static final com.mapconductor.core.map.MapCameraPosition readLogicalCameraPosition(com.mapconductor.mapbox.MapboxMapViewController); + public static final float density(com.mapconductor.mapbox.MapboxMapViewController); public static final java.lang.Object getMapCameraPositionAsync(com.mapconductor.mapbox.MapboxMapViewController, kotlin.coroutines.Continuation); public static final void handleMoveCamera(com.mapconductor.mapbox.MapboxMapViewController, com.mapconductor.core.map.MapCameraPosition); public static final void handleAnimateCamera(com.mapconductor.mapbox.MapboxMapViewController, com.mapconductor.core.map.MapCameraPosition, long); diff --git a/src/main/java/com/mapconductor/mapbox/MapCameraPosition.kt b/src/main/java/com/mapconductor/mapbox/MapCameraPosition.kt index 8a7e729..58cefb5 100644 --- a/src/main/java/com/mapconductor/mapbox/MapCameraPosition.kt +++ b/src/main/java/com/mapconductor/mapbox/MapCameraPosition.kt @@ -9,6 +9,7 @@ import com.mapconductor.core.features.GeoPointInterface import com.mapconductor.core.map.CameraBearing import com.mapconductor.core.map.MapCameraPosition import com.mapconductor.core.map.MapCameraPositionInterface +import com.mapconductor.core.map.MapPaddings import com.mapconductor.core.map.MapPaddingsInterface import com.mapconductor.core.map.VisibleRegion import com.mapconductor.core.spherical.Spherical @@ -19,12 +20,34 @@ import kotlin.math.tan private val converter = ZoomAltitudeConverter() +/** + * Mapbox のカメラの縦方向の視野角(度)。Mapbox Maps の既定値で、地図の大きさに + * よらず一定(焦点距離 = 地図の高さ × 1.5)。TB520FU で 36.9° を実測。 + */ +internal const val MAPBOX_VERTICAL_FOV = 36.87 + +/** dp のパディング → Mapbox の px のパディング。 */ +internal fun MapPaddingsInterface.toMapboxEdgeInsets(density: Float): EdgeInsets = + EdgeInsets(top * density, left * density, bottom * density, right * density) + +/** Mapbox の px のパディング → dp のパディング。 */ +internal fun EdgeInsets.toMapPaddings(density: Float): MapPaddings = + MapPaddings(top = top / density, left = left / density, bottom = bottom / density, right = right / density) + fun CameraChanged.toMapCameraPosition() = cameraState .toMapCameraPosition() .copy(paddings = cameraState.padding.toPaddings()) -fun MapCameraPosition.toCameraOptions(): CameraOptions { +fun MapCameraPosition.toCameraOptions(): CameraOptions = toCameraOptions(density = null) + +/** + * [density] があれば [MapCameraPosition.paddings] を Mapbox のパディングとして渡す。 + * Mapbox はパディングを除いた領域の中央にカメラの中心を置き、tilt の遠近もエンジンが解く。 + * パディングは次に変えるまで残るので、null(未指定)のときは渡さない。 + */ +internal fun MapCameraPosition.toCameraOptions(density: Float?): CameraOptions { + val padding = if (density != null) paddings?.toMapboxEdgeInsets(density) else null if (this.tilt >= 0) { return CameraOptions .Builder() @@ -32,8 +55,7 @@ fun MapCameraPosition.toCameraOptions(): CameraOptions { .zoom(ZoomAltitudeConverter.googleZoomToMapboxZoom(zoom)) .pitch(tilt) .bearing(CameraBearing.toNativeHeading(bearing)) - // TODO: - // .padding(paddings?.toEdgeInsects()) + .padding(padding) .build() } else { // tilt < 0: Mapbox cannot represent an upward pitch directly. @@ -53,8 +75,7 @@ fun MapCameraPosition.toCameraOptions(): CameraOptions { .zoom(mapboxZoom) .pitch(tiltAbsDeg) .bearing(CameraBearing.toNativeHeading(bearing)) - // TODO: - // .padding(paddings?.toEdgeInsects()) + .padding(padding) .build() } } @@ -79,6 +100,7 @@ fun MapCameraPosition.Companion.from(cameraPosition: MapCameraPositionInterface) tilt = cameraPosition.tilt, paddings = MapboxPaddings.from(cameraPosition.paddings), visibleRegion = cameraPosition.visibleRegion, + fov = cameraPosition.fov, ) } @@ -101,20 +123,24 @@ fun CameraState.toMapCameraPosition() = logicalTiltHint = null, ) -internal fun CameraState.toMapCameraPosition(logicalTiltHint: Double?) = - toMapCameraPosition( - center = center.toGeoPoint(), - mapboxZoom = zoom, - bearing = bearing, - pitch = pitch, - logicalTiltHint = logicalTiltHint, - ) +internal fun CameraState.toMapCameraPosition( + logicalTiltHint: Double?, + density: Float? = null, +) = toMapCameraPosition( + center = center.toGeoPoint(), + mapboxZoom = zoom, + bearing = bearing, + pitch = pitch, + paddings = density?.let { padding.toMapPaddings(it) }, + logicalTiltHint = logicalTiltHint, +) internal data class MapboxCameraStateSnapshot( val cameraState: CameraState, val logicalTiltHint: Double?, + val density: Float? = null, ) { - fun toMapCameraPosition(): MapCameraPosition = cameraState.toMapCameraPosition(logicalTiltHint) + fun toMapCameraPosition(): MapCameraPosition = cameraState.toMapCameraPosition(logicalTiltHint, density) } private fun toMapCameraPosition( @@ -135,6 +161,7 @@ private fun toMapCameraPosition( tilt = pitch, paddings = paddings, visibleRegion = visibleRegion, + fov = MAPBOX_VERTICAL_FOV, ) } @@ -153,5 +180,6 @@ private fun toMapCameraPosition( tilt = -pitchAbsDeg, paddings = paddings, visibleRegion = visibleRegion, + fov = MAPBOX_VERTICAL_FOV, ) } diff --git a/src/main/java/com/mapconductor/mapbox/MapboxMapView.kt b/src/main/java/com/mapconductor/mapbox/MapboxMapView.kt index 4211aea..2b54385 100644 --- a/src/main/java/com/mapconductor/mapbox/MapboxMapView.kt +++ b/src/main/java/com/mapconductor/mapbox/MapboxMapView.kt @@ -107,7 +107,7 @@ fun MapboxMapView( modifier = modifier, viewProvider = { val cameraOptions = - state.cameraPosition.toCameraOptions() + state.cameraPosition.toCameraOptions(context.resources.displayMetrics.density) val styleUri = state.mapDesignType.getValue() diff --git a/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerCamera.kt b/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerCamera.kt index fd3d37f..157b233 100644 --- a/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerCamera.kt +++ b/src/main/java/com/mapconductor/mapbox/MapboxMapViewControllerCamera.kt @@ -31,8 +31,11 @@ internal fun MapboxMapViewController.readLogicalCameraPosition(): MapCameraPosit MapboxCameraStateSnapshot( cameraState = holder.map.cameraState, logicalTiltHint = lastLogicalCameraPosition?.tilt, + density = density(), ).toMapCameraPosition() +internal fun MapboxMapViewController.density(): Float = holder.mapView.resources.displayMetrics.density + /** * Async version of getMapCameraPosition that can be called from background threads. * Uses withContext to ensure Mapbox SDK calls run on the main thread. @@ -43,7 +46,7 @@ internal suspend fun MapboxMapViewController.getMapCameraPositionAsync(): MapCam } internal fun MapboxMapViewController.handleMoveCamera(position: MapCameraPosition) { - val cameraOptions = position.toCameraOptions() + val cameraOptions = position.toCameraOptions(density()) lastLogicalCameraPosition = position mainCoroutine.launch { holder.map.setCamera(cameraOptions) @@ -54,7 +57,7 @@ internal fun MapboxMapViewController.handleAnimateCamera( position: MapCameraPosition, duration: Long, ) { - val targetCamera = position.toCameraOptions() + val targetCamera = position.toCameraOptions(density()) lastLogicalCameraPosition = position val animationOptions =