core, android: add support for camera roll (#3011)

This commit is contained in:
Nathan Olson
2026-01-20 00:57:48 -08:00
committed by GitHub
parent a4cf4d3ffa
commit 10dbd5d68d
57 changed files with 1275 additions and 207 deletions

View File

@@ -24,16 +24,21 @@ class CameraPosition
* Constructs a CameraPosition.
*
* @param target The target location to align with the center of the screen.
* @param centerAltitude The altitude of the target location to align with the center of the
* screen, in meters above sea level.
* @param zoom Zoom level at target. See zoom(float) for details of restrictions.
* @param tilt The camera angle, in degrees, from the nadir (directly down). See tilt(float)
* for details of restrictions.
* @param bearing Direction that the camera is pointing in, in degrees clockwise from north.
* This value will be normalized to be within 0 degrees inclusive and 360 degrees
* exclusive.
* @param roll Roll of the camera, in degrees clockwise.
* @param fov Vertical field of view of the camera, in degrees.
* @param padding Padding in pixels. Specified in left, top, right, bottom order.
* @throws NullPointerException if target is null
* @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive.
*/ @Keep internal constructor(
*/
@Keep internal constructor(
/**
* The location that the camera is pointing at.
@@ -42,6 +47,13 @@ class CameraPosition
@JvmField
val target: LatLng?,
/**
* The altitude of the location that the camera is pointing at, in meters above sea level.
*/
@field:Keep
@JvmField
val centerAltitude: Double,
/**
* Zoom level near the center of the screen. See [Builder.zoom] for the definition of the camera's
* zoom level.
@@ -65,6 +77,20 @@ class CameraPosition
@JvmField
val bearing: Double,
/**
* Roll of the camera, in degrees clockwise.
*/
@field:Keep
@JvmField
val roll: Double,
/**
* Vertical field of view of the camera, in degrees.
*/
@field:Keep
@JvmField
val fov: Double,
/**
* Padding in pixels. Specified in left, top, right, bottom order.
* See [Builder.padding] for the definition of the camera's padding.
@@ -75,6 +101,22 @@ class CameraPosition
) : Parcelable {
/**
* Constructs a CameraPosition.
*
* @param target The target location to align with the center of the screen.
* @param zoom Zoom level at target. See zoom(float) for details of restrictions.
* @param tilt The camera angle, in degrees, from the nadir (directly down). See tilt(float)
* for details of restrictions.
* @param bearing Direction that the camera is pointing in, in degrees clockwise from north.
* This value will be normalized to be within 0 degrees inclusive and 360 degrees
* exclusive.
* @param padding Padding in pixels. Specified in left, top, right, bottom order.
* @throws NullPointerException if target is null
* @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive.
*/
@Keep internal constructor(target: LatLng?, zoom: Double, tilt: Double, bearing: Double, padding: DoubleArray?) : this(target, 0.0, zoom, tilt, bearing, 0.0, MapLibreConstants.DEFAULT_FOV, padding)
/**
* Constructs a CameraPosition.
*
@@ -112,7 +154,10 @@ class CameraPosition
override fun writeToParcel(out: Parcel, flags: Int) {
out.writeDouble(bearing)
out.writeParcelable(target, flags)
out.writeDouble(centerAltitude)
out.writeDouble(tilt)
out.writeDouble(roll)
out.writeDouble(fov)
out.writeDouble(zoom)
if (padding != null) {
val length = padding.size
@@ -131,7 +176,7 @@ class CameraPosition
* @return A String with CameraPosition information.
*/
override fun toString(): String {
return ("Target: " + target + ", Zoom:" + zoom + ", Bearing:" + bearing + ", Tilt:" + tilt + ", Padding:" + Arrays.toString(padding))
return ("Target: " + target + ", Center Altitude:" + centerAltitude + ", Zoom:" + zoom + ", Bearing:" + bearing + ", Tilt:" + tilt + ", Roll:" + roll + ", FOV:" + fov + ", Padding:" + Arrays.toString(padding))
}
/**
@@ -152,10 +197,16 @@ class CameraPosition
val cameraPosition = other as CameraPosition
if (target != null && target != cameraPosition.target) {
return false
} else if (centerAltitude != cameraPosition.centerAltitude) {
return false
} else if (zoom != cameraPosition.zoom) {
return false
} else if (tilt != cameraPosition.tilt) {
return false
} else if (roll != cameraPosition.roll) {
return false
} else if (fov != cameraPosition.fov) {
return false
} else if (bearing != cameraPosition.bearing) {
return false
} else if (!Arrays.equals(padding, cameraPosition.padding)) {
@@ -178,8 +229,14 @@ class CameraPosition
temp = java.lang.Double.doubleToLongBits(bearing)
result = (temp xor (temp ushr 32)).toInt()
result = 31 * result + (target?.hashCode() ?: 0)
temp = java.lang.Double.doubleToLongBits(centerAltitude)
result = 31 * result + (temp xor (temp ushr 32)).toInt()
temp = java.lang.Double.doubleToLongBits(tilt)
result = 31 * result + (temp xor (temp ushr 32)).toInt()
temp = java.lang.Double.doubleToLongBits(roll)
result = 31 * result + (temp xor (temp ushr 32)).toInt()
temp = java.lang.Double.doubleToLongBits(fov)
result = 31 * result + (temp xor (temp ushr 32)).toInt()
temp = java.lang.Double.doubleToLongBits(zoom)
result = 31 * result + (temp xor (temp ushr 32)).toInt()
result = 31 * result + Arrays.hashCode(padding)
@@ -192,7 +249,10 @@ class CameraPosition
class Builder {
private var bearing = -1.0
private var target: LatLng? = null
private var centerAltitude = -1.0
private var tilt = -1.0
private var roll = -1.0
private var fov = -1.0
private var zoom = -1.0
private var padding: DoubleArray? = null
@@ -210,7 +270,10 @@ class CameraPosition
if (previous != null) {
bearing = previous.bearing
target = previous.target
centerAltitude = previous.centerAltitude
tilt = previous.tilt
roll = previous.roll
fov = previous.fov
zoom = previous.zoom
padding = previous.padding
}
@@ -227,7 +290,10 @@ class CameraPosition
val lat = typedArray.getFloat(R.styleable.maplibre_MapView_maplibre_cameraTargetLat, 0.0f).toDouble()
val lng = typedArray.getFloat(R.styleable.maplibre_MapView_maplibre_cameraTargetLng, 0.0f).toDouble()
target = LatLng(lat, lng)
centerAltitude = typedArray.getFloat(R.styleable.maplibre_MapView_maplibre_cameraCenterAltitude, 0.0f).toDouble()
tilt = typedArray.getFloat(R.styleable.maplibre_MapView_maplibre_cameraTilt, 0.0f).toDouble()
roll = typedArray.getFloat(R.styleable.maplibre_MapView_maplibre_cameraRoll, 0.0f).toDouble()
fov = typedArray.getFloat(R.styleable.maplibre_MapView_maplibre_cameraFov, 36.86989764584402f).toDouble()
zoom = typedArray.getFloat(R.styleable.maplibre_MapView_maplibre_cameraZoom, 0.0f).toDouble()
}
}
@@ -241,7 +307,10 @@ class CameraPosition
if (update != null) {
bearing = update.bearing
target = update.target
centerAltitude = update.centerAltitude
tilt = update.tilt
roll = update.roll
fov = update.fov
zoom = update.zoom
padding = update.padding
}
@@ -287,6 +356,17 @@ class CameraPosition
return this
}
/**
* Set the altitude of the location where the camera is pointing at in meters above sea level
*
* @param centerAltitude altitude of the location where the camera is pointing at
* @return this
*/
fun centerAltitude(centerAltitude: Double): Builder {
this.centerAltitude = centerAltitude
return this
}
/**
* Set the tilt of the camera in degrees
*
@@ -302,6 +382,28 @@ class CameraPosition
return this
}
/**
* Set the roll of the camera in degrees
*
* @param roll Roll value of the camera
* @return this
*/
fun roll(roll: Double): Builder {
this.roll = roll
return this
}
/**
* Set the vertical field of view of the camera in degrees
*
* @param fov Vertical field of view of the camera
* @return this
*/
fun fov(fov: Double): Builder {
this.fov = fov
return this
}
/**
* Set the zoom of the camera
*
@@ -375,7 +477,10 @@ class CameraPosition
} else {
parcel.readParcelable(LatLng::class.java.classLoader)
}
val centerAltitude = parcel.readDouble()
val tilt = parcel.readDouble()
val roll = parcel.readDouble()
val fov = parcel.readDouble()
val zoom = parcel.readDouble()
var padding: DoubleArray? = null
val paddingSize = parcel.readInt()
@@ -385,7 +490,7 @@ class CameraPosition
padding[i] = parcel.readDouble()
}
}
return CameraPosition(target, zoom, tilt, bearing, padding)
return CameraPosition(target, centerAltitude, zoom, tilt, bearing, roll, fov, padding)
}
override fun newArray(size: Int): Array<CameraPosition?> {

View File

@@ -22,7 +22,7 @@ object CameraUpdateFactory {
*/
@JvmStatic
fun newCameraPosition(cameraPosition: CameraPosition): CameraUpdate {
return CameraPositionUpdate(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom, cameraPosition.padding)
return CameraPositionUpdate(cameraPosition.bearing, cameraPosition.target, cameraPosition.centerAltitude, cameraPosition.tilt, cameraPosition.roll, cameraPosition.fov, cameraPosition.zoom, cameraPosition.padding)
}
/**
@@ -34,7 +34,7 @@ object CameraUpdateFactory {
*/
@JvmStatic
fun newLatLng(latLng: LatLng): CameraUpdate {
return CameraPositionUpdate(-1.0, latLng, -1.0, -1.0, null)
return CameraPositionUpdate(-1.0, latLng, -1.0, -1.0, -1.0, -1.0, -1.0, null)
}
/**
@@ -133,7 +133,7 @@ object CameraUpdateFactory {
*/
@JvmStatic
fun newLatLngZoom(latLng: LatLng, zoom: Double): CameraUpdate {
return CameraPositionUpdate(-1.0, latLng, -1.0, zoom, null)
return CameraPositionUpdate(-1.0, latLng, -1.0, -1.0, -1.0, -1.0, zoom, null)
}
/**
@@ -150,7 +150,7 @@ object CameraUpdateFactory {
*/
@JvmStatic
fun newLatLngPadding(latLng: LatLng, left: Double, top: Double, right: Double, bottom: Double): CameraUpdate {
return CameraPositionUpdate(-1.0, latLng, -1.0, -1.0, doubleArrayOf(left, top, right, bottom))
return CameraPositionUpdate(-1.0, latLng, -1.0, -1.0, -1.0, -1.0, -1.0, doubleArrayOf(left, top, right, bottom))
}
/**
@@ -217,7 +217,7 @@ object CameraUpdateFactory {
*/
@JvmStatic
fun bearingTo(bearing: Double): CameraUpdate {
return CameraPositionUpdate(bearing, null, -1.0, -1.0, null)
return CameraPositionUpdate(bearing, null, -1.0, -1.0, -1.0, -1.0, -1.0, null)
}
/**
@@ -228,7 +228,7 @@ object CameraUpdateFactory {
*/
@JvmStatic
fun tiltTo(tilt: Double): CameraUpdate {
return CameraPositionUpdate(-1.0, null, tilt, -1.0, null)
return CameraPositionUpdate(-1.0, null, -1.0, tilt, -1.0, -1.0, -1.0, null)
}
/**
@@ -244,7 +244,7 @@ object CameraUpdateFactory {
*/
@JvmStatic
fun paddingTo(padding: DoubleArray?): CameraUpdate {
return CameraPositionUpdate(-1.0, null, -1.0, -1.0, padding)
return CameraPositionUpdate(-1.0, null, -1.0, -1.0, -1.0, -1.0, -1.0, padding)
}
/**
@@ -265,7 +265,7 @@ object CameraUpdateFactory {
//
// CameraUpdate types
//
class CameraPositionUpdate(val bearing: Double, val target: LatLng?, val tilt: Double, val zoom: Double, val padding: DoubleArray?) : CameraUpdate {
class CameraPositionUpdate(val bearing: Double, val target: LatLng?, val centerAltitude: Double, val tilt: Double, val roll: Double, val fov: Double, val zoom: Double, val padding: DoubleArray?) : CameraUpdate {
override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition {
if (target == null) {
@@ -289,9 +289,18 @@ object CameraUpdateFactory {
if (java.lang.Double.compare(that.tilt, tilt) != 0) {
return false
}
if (java.lang.Double.compare(that.roll, roll) != 0) {
return false
}
if (java.lang.Double.compare(that.fov, fov) != 0) {
return false
}
if (java.lang.Double.compare(that.zoom, zoom) != 0) {
return false
}
if (java.lang.Double.compare(that.centerAltitude, centerAltitude) != 0) {
return false
}
return if (if (target != null) target != that.target else that.target != null) {
false
} else {
@@ -305,8 +314,14 @@ object CameraUpdateFactory {
temp = java.lang.Double.doubleToLongBits(bearing)
result = (temp xor (temp ushr 32)).toInt()
result = 31 * result + (target?.hashCode() ?: 0)
temp = java.lang.Double.doubleToLongBits(centerAltitude)
result = 31 * result + (temp xor (temp ushr 32)).toInt()
temp = java.lang.Double.doubleToLongBits(tilt)
result = 31 * result + (temp xor (temp ushr 32)).toInt()
temp = java.lang.Double.doubleToLongBits(roll)
result = 31 * result + (temp xor (temp ushr 32)).toInt()
temp = java.lang.Double.doubleToLongBits(fov)
result = 31 * result + (temp xor (temp ushr 32)).toInt()
temp = java.lang.Double.doubleToLongBits(zoom)
result = 31 * result + (temp xor (temp ushr 32)).toInt()
result = 31 * result + Arrays.hashCode(padding)
@@ -314,7 +329,7 @@ object CameraUpdateFactory {
}
override fun toString(): String {
return ("CameraPositionUpdate{" + "bearing=" + bearing + ", target=" + target + ", tilt=" + tilt + ", zoom=" + zoom + ", padding=" + Arrays.toString(padding) + '}')
return ("CameraPositionUpdate{" + "bearing=" + bearing + ", target=" + target + ", centerAltitude=" + centerAltitude + ", tilt=" + tilt + ", roll=" + roll + ", fov=" + fov + ", zoom=" + zoom + ", padding=" + Arrays.toString(padding) + '}')
}
}

View File

@@ -168,6 +168,11 @@ public class MapLibreConstants {
*/
public static final double MINIMUM_DIRECTION = 0;
/**
* The current default vertical field of view (matches mbgl::util::DEFAULT_FOV)
*/
public static final double DEFAULT_FOV = 36.86989764584402;
/**
* The currently used minimum scale factor to clamp to when a quick zoom gesture occurs
*