Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions api/android-for-maplibre.api
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,27 @@ public final class com.mapconductor.maplibre.GeoRectBoundsKt {
public static final com.mapconductor.core.features.GeoRectBounds toGeoRectBounds(org.maplibre.android.geometry.LatLngBounds);
}
public final class com.mapconductor.maplibre.MapCameraPositionKt {
public static final double MAPLIBRE_VERTICAL_FOV;
public static final double[] toMaplibrePadding(com.mapconductor.core.map.MapPaddingsInterface, float);
public static final com.mapconductor.core.map.MapPaddings toMapPaddings(double[], float);
public static final org.maplibre.android.camera.CameraPosition toCameraPosition(com.mapconductor.core.map.MapCameraPosition);
public static final org.maplibre.android.camera.CameraPosition toCameraPosition(com.mapconductor.core.map.MapCameraPosition, java.lang.Float);
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(org.maplibre.android.camera.CameraPosition);
public static final com.mapconductor.core.map.MapCameraPosition toMapCameraPosition(org.maplibre.android.camera.CameraPosition, java.lang.Double);
public static final com.mapconductor.core.map.MapCameraPosition toMapCameraPosition(org.maplibre.android.camera.CameraPosition, java.lang.Double, java.lang.Float);
}
public final class com.mapconductor.maplibre.MapLibreCameraStateSnapshot {
public com.mapconductor.maplibre.MapLibreCameraStateSnapshot(org.maplibre.android.camera.CameraPosition, java.lang.Double);
public com.mapconductor.maplibre.MapLibreCameraStateSnapshot(org.maplibre.android.camera.CameraPosition, java.lang.Double, java.lang.Float);
public com.mapconductor.maplibre.MapLibreCameraStateSnapshot(org.maplibre.android.camera.CameraPosition, java.lang.Double, java.lang.Float, int, kotlin.jvm.internal.DefaultConstructorMarker);
public final org.maplibre.android.camera.CameraPosition getCameraPosition();
public final java.lang.Double getLogicalTiltHint();
public final java.lang.Float getDensity();
public final com.mapconductor.core.map.MapCameraPosition toMapCameraPosition();
public final org.maplibre.android.camera.CameraPosition component1();
public final java.lang.Double component2();
public final com.mapconductor.maplibre.MapLibreCameraStateSnapshot copy(org.maplibre.android.camera.CameraPosition, java.lang.Double);
public final java.lang.Float component3();
public final com.mapconductor.maplibre.MapLibreCameraStateSnapshot copy(org.maplibre.android.camera.CameraPosition, java.lang.Double, java.lang.Float);
public java.lang.String toString();
public int hashCode();
public boolean equals(java.lang.Object);
Expand Down Expand Up @@ -164,6 +172,7 @@ public final class com.mapconductor.maplibre.MapLibreViewController extends com.
}
public final class com.mapconductor.maplibre.MapLibreViewControllerCameraKt {
public static final com.mapconductor.core.map.MapCameraPosition readLogicalCameraPosition(com.mapconductor.maplibre.MapLibreViewController);
public static final float density(com.mapconductor.maplibre.MapLibreViewController);
public static final com.mapconductor.core.map.MapCameraPosition getMapCameraPosition(com.mapconductor.maplibre.MapLibreViewController, com.mapconductor.core.map.MapCameraPositionInterface);
public static final void handleMoveCamera(com.mapconductor.maplibre.MapLibreViewController, com.mapconductor.core.map.MapCameraPosition);
public static final void handleAnimateCamera(com.mapconductor.maplibre.MapLibreViewController, com.mapconductor.core.map.MapCameraPosition, long);
Expand Down
59 changes: 52 additions & 7 deletions src/main/java/com/mapconductor/maplibre/MapCameraPosition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import com.mapconductor.core.features.GeoPoint
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.spherical.Spherical
import com.mapconductor.maplibre.zoom.ZoomAltitudeConverter
import org.maplibre.android.camera.CameraPosition
Expand All @@ -12,19 +14,48 @@ import kotlin.math.cos
import kotlin.math.tan

private val converter = ZoomAltitudeConverter()

/**
* MapLibre のカメラの縦方向の視野角(度)。MapLibre Native の既定値で、地図の大きさに
* よらず一定(焦点距離 = 地図の高さ × 1.5)。TB520FU の縦・横で 36.9° を実測。
*/
internal const val MAPLIBRE_VERTICAL_FOV = 36.87

/** dp のパディング → MapLibre の px のパディング(left, top, right, bottom)。 */
internal fun MapPaddingsInterface.toMaplibrePadding(density: Float): DoubleArray =
doubleArrayOf(left * density, top * density, right * density, bottom * density)

/** MapLibre の px のパディング(left, top, right, bottom)→ dp のパディング。 */
internal fun DoubleArray.toMapPaddings(density: Float): MapPaddings? {
if (size < 4 || density <= 0f) return null
return MapPaddings(
top = this[1] / density,
left = this[0] / density,
bottom = this[3] / density,
right = this[2] / density,
)
}

private const val NEGATIVE_TILT_TARGET_DISTANCE_SCALE = 1.83
private const val NEGATIVE_TILT_ZOOM_OFFSET_AT_MAX_TILT = -0.9

fun MapCameraPosition.toCameraPosition(): CameraPosition {
fun MapCameraPosition.toCameraPosition(): CameraPosition = toCameraPosition(density = null)

/**
* [density] があれば [MapCameraPosition.paddings] を MapLibre のパディングとして渡す。
* MapLibre はパディングを除いた領域の中央にカメラの中心を置き、tilt の遠近もエンジンが解く。
* MapLibre のパディングは次に変えるまで残るので、null(未指定)のときは渡さない。
*/
internal fun MapCameraPosition.toCameraPosition(density: Float?): CameraPosition {
val padding = if (density != null) paddings?.toMaplibrePadding(density) else null
if (tilt >= 0) {
return CameraPosition
.Builder()
.target(GeoPoint.from(position).toLatLng())
.zoom(ZoomAltitudeConverter.googleZoomToMaplibreZoom(zoom))
.tilt(tilt)
.bearing(CameraBearing.toNativeHeading(bearing))
// TODO:
// .padding(paddings?.toEdgeInsects())
.apply { padding?.let { padding(it) } }
.build()
} else {
// tilt < 0: MapLibre cannot represent an upward pitch directly.
Expand All @@ -47,8 +78,7 @@ fun MapCameraPosition.toCameraPosition(): CameraPosition {
.zoom(ZoomAltitudeConverter.googleZoomToMaplibreZoom(adjustedZoom))
.tilt(tiltAbsDeg)
.bearing(CameraBearing.toNativeHeading(bearing))
// TODO:
// .padding(paddings?.toEdgeInsects())
.apply { padding?.let { padding(it) } }
.build()
}
}
Expand All @@ -62,7 +92,9 @@ fun MapCameraPosition.Companion.from(cameraPosition: MapCameraPositionInterface)
zoom = cameraPosition.zoom,
bearing = cameraPosition.bearing,
tilt = cameraPosition.tilt,
paddings = cameraPosition.paddings,
visibleRegion = cameraPosition.visibleRegion,
fov = cameraPosition.fov,
)
}

Expand All @@ -71,11 +103,20 @@ fun CameraPosition.toMapCameraPosition() = toMapCameraPosition(logicalTiltHint =
internal data class MapLibreCameraStateSnapshot(
val cameraPosition: CameraPosition,
val logicalTiltHint: Double?,
val density: Float? = null,
) {
fun toMapCameraPosition(): MapCameraPosition = cameraPosition.toMapCameraPosition(logicalTiltHint)
fun toMapCameraPosition(): MapCameraPosition = cameraPosition.toMapCameraPosition(logicalTiltHint, density)
}

internal fun CameraPosition.toMapCameraPosition(logicalTiltHint: Double?): MapCameraPosition {
internal fun CameraPosition.toMapCameraPosition(logicalTiltHint: Double?): MapCameraPosition =
toMapCameraPosition(logicalTiltHint, density = null)

/** [density] があれば MapLibre のパディングを dp に直して paddings として返す。 */
internal fun CameraPosition.toMapCameraPosition(
logicalTiltHint: Double?,
density: Float?,
): MapCameraPosition {
val paddings = density?.let { d -> padding?.toMapPaddings(d) } ?: MapPaddings.Zeros
val pitch = tilt
val pitchAbsDeg = abs(pitch).coerceIn(0.0, 60.0)

Expand All @@ -85,7 +126,9 @@ internal fun CameraPosition.toMapCameraPosition(logicalTiltHint: Double?): MapCa
zoom = ZoomAltitudeConverter.maplibreZoomToGoogleZoom(zoom),
bearing = CameraBearing.bearingFromNativeHeading(bearing),
tilt = pitch,
paddings = paddings,
visibleRegion = null,
fov = MAPLIBRE_VERTICAL_FOV,
)
}

Expand All @@ -107,6 +150,8 @@ internal fun CameraPosition.toMapCameraPosition(logicalTiltHint: Double?): MapCa
zoom = originalGoogleZoom,
bearing = CameraBearing.bearingFromNativeHeading(bear),
tilt = -pitchAbsDeg,
paddings = paddings,
visibleRegion = null,
fov = MAPLIBRE_VERTICAL_FOV,
)
}
2 changes: 1 addition & 1 deletion src/main/java/com/mapconductor/maplibre/MapLibreMapView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ fun MapLibreMapView(
modifier = modifier,
viewProvider = {
val cameraPosition =
state.cameraPosition.toCameraPosition()
state.cameraPosition.toCameraPosition(context.resources.displayMetrics.density)
val mapInitOptions =
MapLibreMapOptions
.createFromAttributes(context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ internal fun MapLibreViewController.readLogicalCameraPosition(): MapCameraPositi
MapLibreCameraStateSnapshot(
cameraPosition = holder.map.cameraPosition,
logicalTiltHint = lastLogicalCameraPosition?.tilt,
density = density(),
).toMapCameraPosition()

internal fun MapLibreViewController.density(): Float = holder.mapView.resources.displayMetrics.density

internal fun MapLibreViewController.getMapCameraPosition(camera: MapCameraPositionInterface): MapCameraPosition? {
// 4 隅の逆投影は全プロバイダ共通なのでコアの buildVisibleRegion を使う。
val visibleRegion = holder.buildVisibleRegion() ?: return null
Expand All @@ -31,7 +34,7 @@ internal fun MapLibreViewController.getMapCameraPosition(camera: MapCameraPositi
internal fun MapLibreViewController.handleMoveCamera(position: MapCameraPosition) {
lastLogicalCameraPosition = position
mainCoroutine.launch {
val cameraPos = position.toCameraPosition()
val cameraPos = position.toCameraPosition(density())
val cameraUpdate =
CameraUpdateFactory
.newCameraPosition(cameraPos)
Expand All @@ -46,7 +49,7 @@ internal fun MapLibreViewController.handleAnimateCamera(
) {
lastLogicalCameraPosition = position
mainCoroutine.launch {
val cameraPos = position.toCameraPosition()
val cameraPos = position.toCameraPosition(density())
val cameraUpdate =
CameraUpdateFactory
.newCameraPosition(cameraPos)
Expand Down
Loading