diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/NativeMapViewModule.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/NativeMapViewModule.kt index 5ff073e3f..38de6c8d5 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/NativeMapViewModule.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/NativeMapViewModule.kt @@ -12,6 +12,7 @@ import com.rnmapbox.rnmbx.utils.ExpressionParser import com.rnmapbox.rnmbx.utils.ViewRefTag import com.rnmapbox.rnmbx.utils.ViewTagResolver import com.rnmapbox.rnmbx.utils.extensions.toCoordinate +import com.rnmapbox.rnmbx.utils.extensions.toScreenBox import com.rnmapbox.rnmbx.utils.extensions.toScreenCoordinate import com.rnmapbox.rnmbx.utils.extensions.toValueHashMap @@ -116,7 +117,7 @@ class NativeMapViewModule(context: ReactApplicationContext, val viewTagResolver: val layerIds = ConvertUtils.toStringList(withLayerIDs) it.queryRenderedFeaturesAtPoint( - ConvertUtils.toPointF(atPoint), + atPoint.toScreenCoordinate(), ExpressionParser.from(withFilter), if (layerIds.size == 0) null else layerIds, createCommandResponse(promise) @@ -135,7 +136,7 @@ class NativeMapViewModule(context: ReactApplicationContext, val viewTagResolver: val layerIds = ConvertUtils.toStringList(withLayerIDs) it.queryRenderedFeaturesInRect( - ConvertUtils.toRectF(withBBox), + if (withBBox.size() == 0) null else withBBox.toScreenBox(), ExpressionParser.from(withFilter), if (layerIds.size == 0) null else layerIds, createCommandResponse(promise) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt index b05cdbe74..c3b9dcd61 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt @@ -2,11 +2,8 @@ package com.rnmapbox.rnmbx.components.mapview import android.content.Context import android.graphics.BitmapFactory -import android.graphics.PointF -import android.graphics.RectF import android.os.Handler import android.os.Looper -import android.util.Log import android.view.Gravity import android.view.View import android.view.View.OnLayoutChangeListener @@ -701,24 +698,26 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie return true } } - val screenPoint = mMap?.pixelForCoordinate(point) + val screenPointPx = mMap?.pixelForCoordinate(point) val touchableSources = allTouchableSources val hits = HashMap?>() - if (screenPoint != null) { - handleTapInSources(LinkedList(touchableSources), screenPoint, hits, ArrayList(), object : HandleTap { + if (screenPointPx != null) { + handleTapInSources(LinkedList(touchableSources), screenPointPx, hits, ArrayList(), object : HandleTap { override fun run(hitTouchableSources: List?>?, hits: Map?>) { + /** Android Mapbox SDK returns screen coordinates in physical pixels, while JS expects density-independent pixels. */ + val screenPointDp = toDp(screenPointPx) if (hits.size > 0) { val source = getTouchableSourceWithHighestZIndex(hitTouchableSources as List>?) if (source != null && source.hasPressListener() && source.iD != null && source.iD in hits) { source.onPress(RNMBXSource.OnPressEvent( hits[source.iD] as List, GeoJSONUtils.toLatLng(point), - PointF(screenPoint.x.toFloat(), screenPoint.y.toFloat()) + screenPointDp )) return } } - val event = MapClickEvent(_this, LatLng(point), screenPoint) + val event = MapClickEvent(_this, LatLng(point), screenPointDp) mManager.handleEvent(event) } @@ -732,9 +731,11 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie if (pointAnnotations.getAndClearAnnotationDragged()) { return true } - val screenPoint = mMap?.pixelForCoordinate(point) - if (screenPoint != null) { - val event = MapClickEvent(_this, LatLng(point), screenPoint, EventTypes.MAP_LONG_CLICK) + val screenPointPx = mMap?.pixelForCoordinate(point) + if (screenPointPx != null) { + /** Android Mapbox SDK returns screen coordinates in physical pixels, while JS expects density-independent pixels. */ + val screenPointDp = toDp(screenPointPx) + val event = MapClickEvent(_this, LatLng(point), screenPointDp, EventTypes.MAP_LONG_CLICK) mManager.handleEvent(event) } @@ -908,48 +909,64 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie } } - private fun getDisplayDensity(): Float { - return mContext.resources.displayMetrics.density + private fun getDisplayDensity(): Double { + return mContext.resources.displayMetrics.density.toDouble() } - fun getCoordinateFromView(pixel: ScreenCoordinate, response: CommandResponse) { - val density: Float = getDisplayDensity() - val screenCoordinate = ScreenCoordinate(pixel.x * density, pixel.y * density) + /** Converts a point from density-independent pixels to physical pixels. */ + private fun toDp(pointPx: ScreenCoordinate): ScreenCoordinate { + val density = getDisplayDensity() + return ScreenCoordinate(pointPx.x / density, pointPx.y / density) + } + + /** Converts a point from physical pixels to density-independent pixels. */ + private fun toPx(pointDp: ScreenCoordinate): ScreenCoordinate { + val density = getDisplayDensity() + return ScreenCoordinate(pointDp.x * density, pointDp.y * density) + } + + /** Converts a bounding box from physical pixels to density-independent pixels. */ + private fun toPx(boxDp: ScreenBox): ScreenBox { + val density = getDisplayDensity() + return ScreenBox( + ScreenCoordinate(boxDp.min.x * density, boxDp.min.y * density), + ScreenCoordinate(boxDp.max.x * density, boxDp.max.y * density), + ) + } - val coordinate = mMap!!.coordinateForPixel(screenCoordinate) + fun getCoordinateFromView(pointDp: ScreenCoordinate, response: CommandResponse) { + /** Android Mapbox SDK expects screen coordinates expressed as physical pixels, while JS specifies them as density-independent pixels. */ + val pointPx = toPx(pointDp) + + val coordinate = mMap!!.coordinateForPixel(pointPx) response.success { it.putArray("coordinateFromView", coordinate.toReadableArray()) } } - fun getPointInView(coordinate: Point, response: CommandResponse) { - val point = mMap!!.pixelForCoordinate(coordinate) - val density = getDisplayDensity() - val pointInView = PointF((point.x / density).toFloat(), (point.y / density).toFloat()) + fun getPointInView(coordinates: Point, response: CommandResponse) { + /** Android Mapbox SDK returns screen coordinates in physical pixels, while JS expects density-independent pixels. */ + val pointDp = toDp(mMap!!.pixelForCoordinate(coordinates)) response.success { val array: WritableArray = WritableNativeArray() - array.pushDouble(pointInView.x.toDouble()) - array.pushDouble(pointInView.y.toDouble()) + array.pushDouble(pointDp.x) + array.pushDouble(pointDp.y) it.putArray("pointInView", array) } } - fun queryRenderedFeaturesAtPoint(point: PointF, filter: Expression?, layerIDs: List?, response: CommandResponse) { + fun queryRenderedFeaturesAtPoint(pointDp: ScreenCoordinate, filter: Expression?, layerIDs: List?, response: CommandResponse) { if (mMap == null) { Logger.e("queryRenderedFeaturesAtPoint", "mapbox map is null") return } - // JS sends point values in DIP (see getPointInView which divides by display density), - // but Mapbox core expects screen pixel coordinates. Convert back to px here. - val density: Float = getDisplayDensity() - val screenCoordinate = ScreenCoordinate( - (point.x * density).toDouble(), - (point.y * density).toDouble() - ) - val queryGeometry = RenderedQueryGeometry(screenCoordinate) - val layers = layerIDs?.takeUnless { it.isEmpty() } ?: null; + + /** Android Mapbox SDK expects screen coordinates expressed as physical pixels, while JS specifies them as density-independent pixels. */ + val pointPx = toPx(pointDp) + val queryGeometry = RenderedQueryGeometry(pointPx) + val layers = layerIDs?.takeUnless { it.isEmpty() } ?: null val queryOptions = RenderedQueryOptions(layers, filter) mMap.queryRenderedFeatures(queryGeometry, queryOptions) { features -> if (features.isValue) { @@ -966,14 +983,20 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie } } - fun queryRenderedFeaturesInRect(rect: RectF?, filter: Expression?, layerIDs: List?, response: CommandResponse) { + fun queryRenderedFeaturesInRect(rectDp: ScreenBox?, filter: Expression?, layerIDs: List?, response: CommandResponse) { val size = mMap.getMapOptions().size - val screenBox = if (rect == null) ScreenBox(ScreenCoordinate(0.0, 0.0), ScreenCoordinate(size?.width!!.toDouble(), size?.height!!.toDouble())) else ScreenBox( - ScreenCoordinate(rect.right.toDouble(), rect.bottom.toDouble() ), - ScreenCoordinate(rect.left.toDouble(), rect.top.toDouble()), - ) + + /** Android Mapbox SDK expects screen coordinates expressed as physical pixels, while JS specifies them as density-independent pixels. */ + val rectPx: ScreenBox = + rectDp?.let { toPx(it) } + ?: ScreenBox( + ScreenCoordinate(0.0, 0.0), + /** No conversion needed, screen size is already returned in physical pixels. */ + ScreenCoordinate(size?.width?.toDouble() ?: 0.0, size?.height?.toDouble() ?: 0.0) + ) + mMap.queryRenderedFeatures( - RenderedQueryGeometry(screenBox), + RenderedQueryGeometry(rectPx), RenderedQueryOptions(layerIDs, filter) ) { features -> if (features.isValue) { diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt index b815af660..bdc25714d 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt @@ -10,25 +10,16 @@ import com.facebook.react.uimanager.LayoutShadowNode import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.annotations.ReactProp import com.rnmapbox.rnmbx.events.constants.EventKeys -import com.facebook.react.common.MapBuilder import com.facebook.react.uimanager.ViewManagerDelegate import com.facebook.react.viewmanagers.RNMBXMapViewManagerDelegate import com.facebook.react.viewmanagers.RNMBXMapViewManagerInterface -import com.mapbox.maps.MapInitOptions import com.mapbox.maps.extension.style.layers.properties.generated.ProjectionName import com.mapbox.maps.plugin.gestures.gestures -import com.mapbox.maps.plugin.logo.logo -import com.rnmapbox.rnmbx.events.AndroidCallbackEvent import com.rnmapbox.rnmbx.events.constants.eventMapOf -import com.rnmapbox.rnmbx.utils.ConvertUtils -import com.rnmapbox.rnmbx.utils.ExpressionParser import com.rnmapbox.rnmbx.utils.Logger import com.rnmapbox.rnmbx.utils.ViewTagResolver import com.rnmapbox.rnmbx.utils.extensions.getAndLogIfNotBoolean import com.rnmapbox.rnmbx.utils.extensions.getAndLogIfNotDouble -import com.rnmapbox.rnmbx.utils.extensions.toCoordinate -import com.rnmapbox.rnmbx.utils.extensions.toRectF -import com.rnmapbox.rnmbx.utils.extensions.toScreenCoordinate import java.lang.Exception import java.util.HashMap diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXSource.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXSource.kt index a3cdf28df..3dec5bbe5 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXSource.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXSource.kt @@ -6,22 +6,18 @@ import com.mapbox.maps.extension.style.sources.addSource import com.rnmapbox.rnmbx.components.AbstractMapFeature import com.rnmapbox.rnmbx.components.mapview.RNMBXMapView import com.mapbox.maps.MapboxMap -import com.rnmapbox.rnmbx.components.styles.sources.AbstractSourceConsumer import com.facebook.react.bridge.ReadableMap -import com.rnmapbox.rnmbx.components.styles.sources.RNMBXSource -import android.graphics.PointF import android.view.View import com.facebook.react.common.MapBuilder import com.mapbox.geojson.Feature +import com.mapbox.maps.ScreenCoordinate import com.mapbox.maps.Style import com.mapbox.maps.extension.style.StyleContract import com.mapbox.maps.extension.style.sources.Source import com.rnmapbox.rnmbx.components.RemovalReason -import com.rnmapbox.rnmbx.components.styles.sources.RNMBXSource.OnPressEvent import com.rnmapbox.rnmbx.utils.LatLng import com.rnmapbox.rnmbx.utils.Logger import java.lang.ClassCastException -import java.util.ArrayList import java.util.HashMap data class FeatureInfo(val feature: AbstractMapFeature?, var added: Boolean) { @@ -221,7 +217,7 @@ abstract class RNMBXSource(context: Context?) : AbstractMapFeature( abstract fun makeSource(): T - class OnPressEvent(var features: List, var latLng: LatLng, var screenPoint: PointF) + class OnPressEvent(var features: List, var latLng: LatLng, var screenPoint: ScreenCoordinate) abstract fun onPress(event: OnPressEvent?) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/events/FeatureClickEvent.java b/android/src/main/java/com/rnmapbox/rnmbx/events/FeatureClickEvent.java index 6a53837d5..175aeb1d6 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/events/FeatureClickEvent.java +++ b/android/src/main/java/com/rnmapbox/rnmbx/events/FeatureClickEvent.java @@ -1,16 +1,15 @@ package com.rnmapbox.rnmbx.events; -import android.graphics.PointF; import android.view.View; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.mapbox.geojson.Feature; +import com.mapbox.maps.ScreenCoordinate; import com.rnmapbox.rnmbx.components.styles.sources.RNMBXSource; import com.rnmapbox.rnmbx.events.constants.EventKeys; import com.rnmapbox.rnmbx.events.constants.EventTypes; -import com.rnmapbox.rnmbx.utils.ConvertUtils; import com.rnmapbox.rnmbx.utils.GeoJSONUtils; import com.rnmapbox.rnmbx.utils.LatLng; @@ -24,9 +23,9 @@ public class FeatureClickEvent extends AbstractEvent { private String mEventKey; private List mFeatures; private LatLng mLatLng; - private PointF mPoint; + private ScreenCoordinate mPoint; - public FeatureClickEvent(View view, String eventKey, String eventType, List features, LatLng latLng, PointF point) { + public FeatureClickEvent(View view, String eventKey, String eventType, List features, LatLng latLng, ScreenCoordinate point) { super(view, eventType); mFeatures = features; mEventKey = eventKey; @@ -55,8 +54,8 @@ public WritableMap getPayload() { map.putMap("coordinates", coordinates); WritableMap point = Arguments.createMap(); - point.putDouble("x", mPoint.x); - point.putDouble("y", mPoint.y); + point.putDouble("x", mPoint.getX()); + point.putDouble("y", mPoint.getY()); map.putMap("point", point); return map; diff --git a/android/src/main/java/com/rnmapbox/rnmbx/utils/ConvertUtils.kt b/android/src/main/java/com/rnmapbox/rnmbx/utils/ConvertUtils.kt index 2fc289f65..3a2ce430a 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/utils/ConvertUtils.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/utils/ConvertUtils.kt @@ -1,7 +1,5 @@ package com.rnmapbox.rnmbx.utils -import android.graphics.PointF -import android.graphics.RectF import android.util.Log import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.NoSuchKeyException @@ -172,34 +170,6 @@ object ConvertUtils { return list } - fun toPointF(array: ReadableArray?): PointF { - val pointF = PointF() - - if (array == null) { - return pointF - } - - pointF.set(array.getDouble(0).toFloat(), array.getDouble(1).toFloat()) - return pointF - } - - // returns null if array is null - fun toRectF(array: ReadableArray?): RectF? { - val rectF = RectF() - - if (array == null || array.size() == 0) { - return null - } - - rectF.set( - array.getDouble(3).toFloat(), - array.getDouble(0).toFloat(), - array.getDouble(1).toFloat(), - array.getDouble(2).toFloat() - ) - return rectF - } - fun getDouble(key: String, map: ReadableMap, defaultValue: Double): Double { var value = defaultValue diff --git a/android/src/main/java/com/rnmapbox/rnmbx/utils/extensions/ReadableArray.kt b/android/src/main/java/com/rnmapbox/rnmbx/utils/extensions/ReadableArray.kt index 9a48ba9af..bf878c7d9 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/utils/extensions/ReadableArray.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/utils/extensions/ReadableArray.kt @@ -1,17 +1,15 @@ package com.rnmapbox.rnmbx.utils.extensions -import android.graphics.RectF import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableType import com.google.gson.JsonArray import com.google.gson.JsonElement import com.mapbox.geojson.Point +import com.mapbox.maps.ScreenBox import com.mapbox.maps.ScreenCoordinate -import com.rnmapbox.rnmbx.utils.ConvertUtils import com.rnmapbox.rnmbx.utils.Logger -import org.json.JSONArray -import java.lang.Float.max -import java.lang.Float.min +import kotlin.math.max +import kotlin.math.min fun ReadableArray.toCoordinate() : Point { if (this.size() != 2) { @@ -25,20 +23,24 @@ fun ReadableArray.toCoordinate() : Point { fun ReadableArray.toScreenCoordinate() : ScreenCoordinate { if (this.size() != 2) { - Logger.e("ReadableArray.toCoordinate","Cannot convert $this to point, 2 coordinates are required") + Logger.e("ReadableArray.toScreenCoordinate","Cannot convert $this to point, 2 coordinates are required") } return ScreenCoordinate(getDouble(0), getDouble(1)) } -fun ReadableArray.toRectF() : RectF? { - if (size() != 4) { - return null; +fun ReadableArray.toScreenBox() : ScreenBox { + if (this.size() != 4) { + Logger.e("ReadableArray.toScreenBox","Cannot convert $this to box, 4 coordinates are required") } - return RectF( - min(getDouble(3).toFloat(), getDouble(1).toFloat()), - min(getDouble(0).toFloat(), getDouble(2).toFloat()), - max(getDouble(3).toFloat(), getDouble(1).toFloat()), - max(getDouble(0).toFloat(), getDouble(2).toFloat()) + + val top = getDouble(0) + val left = getDouble(1) + val bottom = getDouble(2) + val right = getDouble(3) + + return ScreenBox( + ScreenCoordinate(min(left, right), min(top, bottom)), + ScreenCoordinate(max(left, right), max(top, bottom)) ) } diff --git a/docs/Camera.md b/docs/Camera.md index 18fcac86c..c68535dff 100644 --- a/docs/Camera.md +++ b/docs/Camera.md @@ -62,7 +62,7 @@ compatibility; the root `padding` prop should be used instead. ```tsx number ``` -The heading (orientation) of the map. +Heading (bearing, orientation) of the map, measured in degrees clockwise from true north. @@ -71,7 +71,7 @@ The heading (orientation) of the map. ```tsx number ``` -The pitch of the map. +The pitch toward the horizon measured in degrees, with 0 degrees resulting in a top-down view for a two-dimensional map. @@ -212,8 +212,8 @@ type DefaultSettings = { centerCoordinate: Position; /* The location on which the map should center. */ bounds: intersection; /* The corners of a box around which the map should bound. Contains padding props for backwards compatibility; the root `padding` prop should be used instead. */ - heading: number; /* The heading (orientation) of the map. */ - pitch: number; /* The pitch of the map. */ + heading: number; /* Heading (bearing, orientation) of the map, measured in degrees clockwise from true north. */ + pitch: number; /* The pitch toward the horizon measured in degrees, with 0 degrees resulting in a top-down view for a two-dimensional map. */ zoomLevel: number; /* The zoom level of the map. */ padding: signature; /* The viewport padding in points. */ animationDuration: number; /* The duration the map takes to animate to a new configuration. */ diff --git a/docs/MapView.md b/docs/MapView.md index d2ad2d0c8..541eeaf59 100644 --- a/docs/MapView.md +++ b/docs/MapView.md @@ -317,20 +317,20 @@ Gesture configuration allows to control the user touch interaction. ```tsx func ``` -Map press listener, gets called when a user presses the map +Map press listener, called when a user presses the map. *signature:*`(feature:GeoJSON.Feature) => void` -[Show Click](../examples/Map/ShowClick) +[Screen Coordinates](../examples/Map/ScreenCoordinates), [Show Click](../examples/Map/ShowClick) ### onLongPress ```tsx func ``` -Map long press listener, gets called when a user long presses the map +Map long press listener, called when a user long presses the map. *signature:*`(feature:GeoJSON.Feature) => void` - +[Screen Coordinates](../examples/Map/ScreenCoordinates) ### onRegionWillChange @@ -549,39 +549,42 @@ the onPress event for the taps that deselect the annotation. Default is false. ## methods ### getPointInView(coordinate) -Converts a geographic coordinate to a point in the given view’s coordinate system. +Converts a geographic coordinate to a screen coordinate relative to the map view. #### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `coordinate` | `Position` | `Yes` | A point expressed in the map view's coordinate system. | +| `coordinate` | `Position` | `Yes` | A point expressed in the map view's coordinate system `[longitude, latitude]`. | ```javascript -const pointInView = await this._map.getPointInView([-37.817070, 144.949901]); +const longitude = 144.949901; +const latitude = -37.817070; +const [x, y] = await this._map.getPointInView([longitude, latitude]); ``` -### getCoordinateFromView(point) +[Screen Coordinates](../examples/Map/ScreenCoordinates)### getCoordinateFromView(point) -Converts a point in the given view’s coordinate system to a geographic coordinate. +Converts a screen coordinate relative to the map view to a geographic coordinate. #### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `point` | `Position` | `Yes` | A point expressed in the given view’s coordinate system. | +| `point` | `Position` | `Yes` | A point expressed in screen coordinates relative to the map view `[x, y]`. | ```javascript -const coordinate = await this._map.getCoordinateFromView([100, 100]); +const x = 100; const y = 100; +const [longitude, latitude] = await this._map.getCoordinateFromView([x, y]); ``` -### getVisibleBounds() +[Screen Coordinates](../examples/Map/ScreenCoordinates)### getVisibleBounds() -The coordinate bounds (ne, sw) visible in the user’s viewport. +The coordinate bounds of the map viewport. #### arguments | Name | Type | Required | Description | @@ -591,7 +594,7 @@ The coordinate bounds (ne, sw) visible in the user’s viewport. ```javascript -const visibleBounds = await this._map.getVisibleBounds(); +const [[rightLon, topLat], [leftLon, bottomLat]] = await this._map.getVisibleBounds(); ``` @@ -602,36 +605,39 @@ Returns an array of rendered map features that intersect with a given point. #### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `coordinate` | `Position` | `Yes` | A point expressed in the map view’s coordinate system. | -| `filter` | `Array` | `No` | A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array. | -| `layerIDs` | `Array` | `No` | A array of layer id's to filter the features by | +| `coordinate` | `Position` | `Yes` | A point expressed in the map view’s coordinate system `[x, y]`; | +| `filter` | `FilterExpression \| tuple` | `No` | A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array. | +| `layerIDs` | `Array` | `No` | A array of layer IDs by which to filter the features. | ```javascript -this._map.queryRenderedFeaturesAtPoint([30, 40], ['==', 'type', 'Point'], ['id1', 'id2']) +const x = 30; const y = 40; +this._map.queryRenderedFeaturesAtPoint([x, y], ['==', 'type', 'Point'], ['id1', 'id2']) ``` -### queryRenderedFeaturesInRect(bbox[, filter][, layerIDs]) +[Screen Coordinates](../examples/Map/ScreenCoordinates)### queryRenderedFeaturesInRect(bbox[, filter][, layerIDs]) Returns an array of rendered map features that intersect with the given rectangle,
restricted to the given style layers and filtered by the given predicate. In v10,
passing an empty array will query the entire visible bounds of the map. #### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `bbox` | `BBox \| []` | `Yes` | A rectangle expressed in the map view’s coordinate system, density independent pixels and not map coordinates. This can be an empty array to query the visible map area. | -| `filter` | `Array` | `No` | A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array. | -| `layerIDs` | `Array` | `No` | A array of layer id's to filter the features by | +| `bbox` | `BBox \| []` | `Yes` | A rectangle expressed in density-independent screen coordinates relative to the map view `[top, left, bottom, right]` or `[minY, minX, maxY, maxX]` (not geographic coordinates). An empty array queries the visible map area. | +| `filter` | `FilterExpression` | `No` | An array of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array. | +| `layerIDs` | `Array` | `No` | A array of layer IDs by which to filter the features. | ```javascript -this._map.queryRenderedFeaturesInRect([30, 40, 20, 10], ['==', 'type', 'Point'], ['id1', 'id2']) +const left = 40; const top = 30; +const right = 10; const bottom = 20; +this._map.queryRenderedFeaturesInRect([top, left, bottom, right], ['==', 'type', 'Point'], ['id1', 'id2']) ``` -### querySourceFeatures(sourceId[, filter][, sourceLayerIDs]) +[Screen Coordinates](../examples/Map/ScreenCoordinates)### querySourceFeatures(sourceId[, filter][, sourceLayerIDs]) Returns an array of GeoJSON Feature objects representing features within the specified vector tile or GeoJSON source that satisfy the query parameters. @@ -639,7 +645,7 @@ Returns an array of GeoJSON Feature objects representing features within the spe | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | | `sourceId` | `string` | `Yes` | Style source identifier used to query for source features. | -| `filter` | `Array` | `No` | A filter to limit query results. | +| `filter` | `FilterExpression \| tuple` | `No` | A filter to limit query results. | | `sourceLayerIDs` | `Array` | `No` | The name of the source layers to query. For vector tile sources, this parameter is required. For GeoJSON sources, it is ignored. | @@ -661,12 +667,12 @@ Map camera will perform updates based on provided config. Deprecated use Camera# ### takeSnap([writeToDisk]) -Takes snapshot of map with current tiles and returns a URI to the image +Takes snapshot of map with current tiles and returns a Base64-encoded PNG image,
or an file-system URI to a temporary PNG file if `writeToDisk` is `true`. #### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `writeToDisk` | `Boolean` | `No` | If true will create a temp file, otherwise it is in base64 | +| `writeToDisk` | `boolean` | `No` | If `true`, creates a temporary PNG file and returns a file-system URI, otherwise returns a Base64-encoded PNG image. (Defaults to `false`) | ### getZoom() @@ -687,7 +693,7 @@ const zoom = await this._map.getZoom(); ### getCenter() -Returns the map's geographical centerpoint +Returns the map's center point expressed as geographic coordinates `[longitude, latitude]`. #### arguments | Name | Type | Required | Description | @@ -718,7 +724,7 @@ Queries the currently loaded data for elevation at a geographical location.
#### arguments | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | -| `coordinate` | `Position` | `Yes` | the coordinates to query elevation at | +| `coordinate` | `Position` | `Yes` | The geographic coordinates `[longitude, latitude]` at which to query elevation. | [Query Terrain Elevation](../examples/V10/QueryTerrainElevation)### setSourceVisibility(visible, sourceId[, sourceLayerId]) @@ -729,8 +735,8 @@ Sets the visibility of all the layers referencing the specified `sourceLayerId` | Name | Type | Required | Description | | ---- | :--: | :------: | :----------: | | `visible` | `boolean` | `Yes` | Visibility of the layers | -| `sourceId` | `string` | `Yes` | Identifier of the target source (e.g. 'composite') | -| `sourceLayerId` | `String` | `No` | Identifier of the target source-layer (e.g. 'building') | +| `sourceId` | `string` | `Yes` | Target source identifier (e.g. 'composite') | +| `sourceLayerId` | `string` | `No` | Target source-layer identifier (e.g. 'building'). If `null`, the change affects all layers in the target source. | diff --git a/docs/docs.json b/docs/docs.json index 6d6bd98b2..795e2e8d4 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -755,14 +755,14 @@ "required": false, "type": "number", "default": "none", - "description": "The heading (orientation) of the map." + "description": "Heading (bearing, orientation) of the map, measured in degrees clockwise from true north." }, { "name": "pitch", "required": false, "type": "number", "default": "none", - "description": "The pitch of the map." + "description": "The pitch toward the horizon measured in degrees, with 0 degrees resulting in a top-down view for a two-dimensional map." }, { "name": "zoomLevel", @@ -937,14 +937,14 @@ "required": false, "type": "number", "default": "none", - "description": "The heading (orientation) of the map." + "description": "Heading (bearing, orientation) of the map, measured in degrees clockwise from true north." }, { "name": "pitch", "required": false, "type": "number", "default": "none", - "description": "The pitch of the map." + "description": "The pitch toward the horizon measured in degrees, with 0 degrees resulting in a top-down view for a two-dimensional map." }, { "name": "zoomLevel", @@ -4217,14 +4217,14 @@ "methods": [ { "name": "getPointInView", - "docblock": "Converts a geographic coordinate to a point in the given view’s coordinate system.\n\n@example\nconst pointInView = await this._map.getPointInView([-37.817070, 144.949901]);\n\n@param {Array} coordinate - A point expressed in the map view's coordinate system.\n@return {Array}", + "docblock": "Converts a geographic coordinate to a screen coordinate relative to the map view.\n\n@example\nconst longitude = 144.949901;\nconst latitude = -37.817070;\nconst [x, y] = await this._map.getPointInView([longitude, latitude]);\n\n@param {Position} coordinate - A point expressed in the map view's coordinate system `[longitude, latitude]`.\n@return {Position} A point expressed in screen coordinates relative to the map view `[x, y]`.", "modifiers": [ "async" ], "params": [ { "name": "coordinate", - "description": "A point expressed in the map view's coordinate system.", + "description": "A point expressed in the map view's coordinate system `[longitude, latitude]`.", "type": { "name": "Position" }, @@ -4232,6 +4232,7 @@ } ], "returns": { + "description": "A point expressed in screen coordinates relative to the map view `[x, y]`.", "type": { "name": "Promise", "elements": [ @@ -4242,21 +4243,21 @@ "raw": "Promise" } }, - "description": "Converts a geographic coordinate to a point in the given view’s coordinate system.", + "description": "Converts a geographic coordinate to a screen coordinate relative to the map view.", "examples": [ - "\nconst pointInView = await this._map.getPointInView([-37.817070, 144.949901]);\n\n" + "\nconst longitude = 144.949901;\nconst latitude = -37.817070;\nconst [x, y] = await this._map.getPointInView([longitude, latitude]);\n\n" ] }, { "name": "getCoordinateFromView", - "docblock": "Converts a point in the given view’s coordinate system to a geographic coordinate.\n\n@example\nconst coordinate = await this._map.getCoordinateFromView([100, 100]);\n\n@param {Array} point - A point expressed in the given view’s coordinate system.\n@return {Array}", + "docblock": "Converts a screen coordinate relative to the map view to a geographic coordinate.\n\n@example\nconst x = 100; const y = 100;\nconst [longitude, latitude] = await this._map.getCoordinateFromView([x, y]);\n\n@param {Position} point - A point expressed in screen coordinates relative to the map view `[x, y]`.\n@return {Position} A point expressed in the map view's coordinate system `[longitude, latitude]`.", "modifiers": [ "async" ], "params": [ { "name": "point", - "description": "A point expressed in the given view’s coordinate system.", + "description": "A point expressed in screen coordinates relative to the map view `[x, y]`.", "type": { "name": "Position" }, @@ -4264,6 +4265,7 @@ } ], "returns": { + "description": "A point expressed in the map view's coordinate system `[longitude, latitude]`.", "type": { "name": "Promise", "elements": [ @@ -4274,19 +4276,20 @@ "raw": "Promise" } }, - "description": "Converts a point in the given view’s coordinate system to a geographic coordinate.", + "description": "Converts a screen coordinate relative to the map view to a geographic coordinate.", "examples": [ - "\nconst coordinate = await this._map.getCoordinateFromView([100, 100]);\n\n" + "\nconst x = 100; const y = 100;\nconst [longitude, latitude] = await this._map.getCoordinateFromView([x, y]);\n\n" ] }, { "name": "getVisibleBounds", - "docblock": "The coordinate bounds (ne, sw) visible in the user’s viewport.\n\n@example\nconst visibleBounds = await this._map.getVisibleBounds();\n\n@return {Array}", + "docblock": "The coordinate bounds of the map viewport.\n\n@example\nconst [[rightLon, topLat], [leftLon, bottomLat]] = await this._map.getVisibleBounds();\n\n@return {[Position, Position]} The geographic coordinate bounds of the map viewport `[[rightLon, topLat], [leftLon, bottomLat]]`.", "modifiers": [ "async" ], "params": [], "returns": { + "description": "The geographic coordinate bounds of the map viewport `[[rightLon, topLat], [leftLon, bottomLat]]`.", "type": { "name": "Promise", "elements": [ @@ -4306,21 +4309,21 @@ "raw": "Promise<[Position, Position]>" } }, - "description": "The coordinate bounds (ne, sw) visible in the user’s viewport.", + "description": "The coordinate bounds of the map viewport.", "examples": [ - "\nconst visibleBounds = await this._map.getVisibleBounds();\n\n" + "\nconst [[rightLon, topLat], [leftLon, bottomLat]] = await this._map.getVisibleBounds();\n\n" ] }, { "name": "queryRenderedFeaturesAtPoint", - "docblock": "Returns an array of rendered map features that intersect with a given point.\n\n@example\nthis._map.queryRenderedFeaturesAtPoint([30, 40], ['==', 'type', 'Point'], ['id1', 'id2'])\n\n@param {Array} coordinate - A point expressed in the map view’s coordinate system.\n@param {Array=} filter - A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array.\n@param {Array=} layerIDs - A array of layer id's to filter the features by\n@return {FeatureCollection}", + "docblock": "Returns an array of rendered map features that intersect with a given point.\n\n@example\nconst x = 30; const y = 40;\nthis._map.queryRenderedFeaturesAtPoint([x, y], ['==', 'type', 'Point'], ['id1', 'id2'])\n\n@param {Position} coordinate - A point expressed in the map view’s coordinate system `[x, y]`;\n@param {FilterExpression | []} filter - A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array.\n@param {string[]} layerIDs - A array of layer IDs by which to filter the features.\n@return {FeatureCollection} A GeoJSON feature collection containing the query results.", "modifiers": [ "async" ], "params": [ { "name": "coordinate", - "description": "A point expressed in the map view’s coordinate system.", + "description": "A point expressed in the map view’s coordinate system `[x, y]`;", "type": { "name": "Position" }, @@ -4330,13 +4333,13 @@ "name": "filter", "description": "A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array.", "type": { - "name": "Array" + "name": "FilterExpression \\| tuple" }, "optional": true }, { "name": "layerIDs", - "description": "A array of layer id's to filter the features by", + "description": "A array of layer IDs by which to filter the features.", "type": { "name": "Array" }, @@ -4344,6 +4347,7 @@ } ], "returns": { + "description": "A GeoJSON feature collection containing the query results.", "type": { "name": "Promise", "elements": [ @@ -4365,19 +4369,19 @@ }, "description": "Returns an array of rendered map features that intersect with a given point.", "examples": [ - "\nthis._map.queryRenderedFeaturesAtPoint([30, 40], ['==', 'type', 'Point'], ['id1', 'id2'])\n\n" + "\nconst x = 30; const y = 40;\nthis._map.queryRenderedFeaturesAtPoint([x, y], ['==', 'type', 'Point'], ['id1', 'id2'])\n\n" ] }, { "name": "queryRenderedFeaturesInRect", - "docblock": "Returns an array of rendered map features that intersect with the given rectangle,\nrestricted to the given style layers and filtered by the given predicate. In v10,\npassing an empty array will query the entire visible bounds of the map.\n\n@example\nthis._map.queryRenderedFeaturesInRect([30, 40, 20, 10], ['==', 'type', 'Point'], ['id1', 'id2'])\n\n@param {Array} bbox - A rectangle expressed in the map view’s coordinate system, density independent pixels and not map coordinates. This can be an empty array to query the visible map area.\n@param {Array=} filter - A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array.\n@param {Array=} layerIDs - A array of layer id's to filter the features by\n@return {FeatureCollection}", + "docblock": "Returns an array of rendered map features that intersect with the given rectangle,\nrestricted to the given style layers and filtered by the given predicate. In v10,\npassing an empty array will query the entire visible bounds of the map.\n\n@example\nconst left = 40; const top = 30;\nconst right = 10; const bottom = 20;\nthis._map.queryRenderedFeaturesInRect([top, left, bottom, right], ['==', 'type', 'Point'], ['id1', 'id2'])\n\n@param {BBox | []} bbox - A rectangle expressed in density-independent screen coordinates relative to the map view `[top, left, bottom, right]` or `[minY, minX, maxY, maxX]` (not geographic coordinates). An empty array queries the visible map area.\n@param {FilterExpression} filter - An array of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array.\n@param {string[] | null} layerIDs - A array of layer IDs by which to filter the features.\n@return {FeatureCollection} A GeoJSON feature collection containing the query results.", "modifiers": [ "async" ], "params": [ { "name": "bbox", - "description": "A rectangle expressed in the map view’s coordinate system, density independent pixels and not map coordinates. This can be an empty array to query the visible map area.", + "description": "A rectangle expressed in density-independent screen coordinates relative to the map view `[top, left, bottom, right]` or `[minY, minX, maxY, maxX]` (not geographic coordinates). An empty array queries the visible map area.", "type": { "name": "BBox \\| []" }, @@ -4385,15 +4389,15 @@ }, { "name": "filter", - "description": "A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array.", + "description": "An array of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array.", "type": { - "name": "Array" + "name": "FilterExpression" }, "optional": true }, { "name": "layerIDs", - "description": " A array of layer id's to filter the features by", + "description": " A array of layer IDs by which to filter the features.", "type": { "name": "Array" }, @@ -4401,6 +4405,7 @@ } ], "returns": { + "description": "A GeoJSON feature collection containing the query results.", "type": { "name": "Promise", "elements": [ @@ -4422,12 +4427,12 @@ }, "description": "Returns an array of rendered map features that intersect with the given rectangle,\nrestricted to the given style layers and filtered by the given predicate. In v10,\npassing an empty array will query the entire visible bounds of the map.", "examples": [ - "\nthis._map.queryRenderedFeaturesInRect([30, 40, 20, 10], ['==', 'type', 'Point'], ['id1', 'id2'])\n\n" + "\nconst left = 40; const top = 30;\nconst right = 10; const bottom = 20;\nthis._map.queryRenderedFeaturesInRect([top, left, bottom, right], ['==', 'type', 'Point'], ['id1', 'id2'])\n\n" ] }, { "name": "querySourceFeatures", - "docblock": "Returns an array of GeoJSON Feature objects representing features within the specified vector tile or GeoJSON source that satisfy the query parameters.\n\n@example\nthis._map.querySourceFeatures('your-source-id', [], ['your-source-layer'])\n\n@param {String} sourceId - Style source identifier used to query for source features.\n@param {Array=} filter - A filter to limit query results.\n@param {Array=} sourceLayerIDs - The name of the source layers to query. For vector tile sources, this parameter is required. For GeoJSON sources, it is ignored.\n@return {FeatureCollection}", + "docblock": "Returns an array of GeoJSON Feature objects representing features within the specified vector tile or GeoJSON source that satisfy the query parameters.\n\n@example\nthis._map.querySourceFeatures('your-source-id', [], ['your-source-layer'])\n\n@param {String} sourceId - Style source identifier used to query for source features.\n@param {FilterExpression | []} filter - A filter to limit query results.\n@param {string[]} sourceLayerIDs - The name of the source layers to query. For vector tile sources, this parameter is required. For GeoJSON sources, it is ignored.\n@return {FeatureCollection} A GeoJSON feature collection.", "modifiers": [ "async" ], @@ -4444,7 +4449,7 @@ "name": "filter", "description": "A filter to limit query results.", "type": { - "name": "Array" + "name": "FilterExpression \\| tuple" }, "optional": true }, @@ -4458,6 +4463,7 @@ } ], "returns": { + "description": "A GeoJSON feature collection.", "type": { "name": "Promise", "elements": [ @@ -4484,21 +4490,22 @@ }, { "name": "takeSnap", - "docblock": "Takes snapshot of map with current tiles and returns a URI to the image\n@param {Boolean} writeToDisk If true will create a temp file, otherwise it is in base64\n@return {String}", + "docblock": "Takes snapshot of map with current tiles and returns a Base64-encoded PNG image,\nor an file-system URI to a temporary PNG file if `writeToDisk` is `true`.\n\n@param {boolean} writeToDisk If `true`, creates a temporary PNG file and returns a file-system URI, otherwise returns a Base64-encoded PNG image. (Defaults to `false`)\n@return {string} A a Base64-encoded PNG image or a file-system URI to a temporary PNG file.", "modifiers": [ "async" ], "params": [ { "name": "writeToDisk", - "description": "If true will create a temp file, otherwise it is in base64", + "description": "If `true`, creates a temporary PNG file and returns a file-system URI, otherwise returns a Base64-encoded PNG image. (Defaults to `false`)", "type": { - "name": "Boolean" + "name": "boolean" }, "optional": true } ], "returns": { + "description": "A a Base64-encoded PNG image or a file-system URI to a temporary PNG file.", "type": { "name": "Promise", "elements": [ @@ -4509,17 +4516,18 @@ "raw": "Promise" } }, - "description": "Takes snapshot of map with current tiles and returns a URI to the image", + "description": "Takes snapshot of map with current tiles and returns a Base64-encoded PNG image,\nor an file-system URI to a temporary PNG file if `writeToDisk` is `true`.", "examples": [] }, { "name": "getZoom", - "docblock": "Returns the current zoom of the map view.\n\n@example\nconst zoom = await this._map.getZoom();\n\n@return {Number}", + "docblock": "Returns the current zoom of the map view.\n\n@example\nconst zoom = await this._map.getZoom();\n\n@return {number} The current zoom of the map view.", "modifiers": [ "async" ], "params": [], "returns": { + "description": "The current zoom of the map view.", "type": { "name": "Promise", "elements": [ @@ -4537,13 +4545,13 @@ }, { "name": "getCenter", - "docblock": "Returns the map's geographical centerpoint\n\n@example\nconst center = await this._map.getCenter();\n\n@return {Array} Coordinates", + "docblock": "Returns the map's center point expressed as geographic coordinates `[longitude, latitude]`.\n\n@example\nconst center = await this._map.getCenter();\n\n@return {Position} The map's center point expressed as geographic coordinates `[longitude, latitude]`.", "modifiers": [ "async" ], "params": [], "returns": { - "description": "Coordinates", + "description": "The map's center point expressed as geographic coordinates `[longitude, latitude]`.", "type": { "name": "Promise", "elements": [ @@ -4554,7 +4562,7 @@ "raw": "Promise" } }, - "description": "Returns the map's geographical centerpoint", + "description": "Returns the map's center point expressed as geographic coordinates `[longitude, latitude]`.", "examples": [ "\nconst center = await this._map.getCenter();\n\n" ] @@ -4582,14 +4590,14 @@ }, { "name": "queryTerrainElevation", - "docblock": "Queries the currently loaded data for elevation at a geographical location.\nThe elevation is returned in meters relative to mean sea-level.\nReturns null if terrain is disabled or if terrain data for the location hasn't been loaded yet.\n\n@param {Array} coordinate - the coordinates to query elevation at\n@return {Number}", + "docblock": "Queries the currently loaded data for elevation at a geographical location.\nThe elevation is returned in meters relative to mean sea-level.\nReturns null if terrain is disabled or if terrain data for the location hasn't been loaded yet.\n\n@param {Position} coordinate - The geographic coordinates `[longitude, latitude]` at which to query elevation.\n@return {number} Elevation in meters relative to mean sea-level.", "modifiers": [ "async" ], "params": [ { "name": "coordinate", - "description": "the coordinates to query elevation at", + "description": "The geographic coordinates `[longitude, latitude]` at which to query elevation.", "type": { "name": "Position" }, @@ -4597,6 +4605,7 @@ } ], "returns": { + "description": "Elevation in meters relative to mean sea-level.", "type": { "name": "Promise", "elements": [ @@ -4612,7 +4621,7 @@ }, { "name": "setSourceVisibility", - "docblock": "Sets the visibility of all the layers referencing the specified `sourceLayerId` and/or `sourceId`\n\n@example\nawait this._map.setSourceVisibility(false, 'composite', 'building')\n\n@param {boolean} visible - Visibility of the layers\n@param {String} sourceId - Identifier of the target source (e.g. 'composite')\n@param {String=} sourceLayerId - Identifier of the target source-layer (e.g. 'building')", + "docblock": "Sets the visibility of all the layers referencing the specified `sourceLayerId` and/or `sourceId`\n\n@example\nawait this._map.setSourceVisibility(false, 'composite', 'building')\n\n@param {boolean} visible - Visibility of the layers\n@param {string} sourceId - Target source identifier (e.g. 'composite')\n@param {string | null} sourceLayerId - Target source-layer identifier (e.g. 'building'). If `null`, the change affects all layers in the target source.", "modifiers": [], "params": [ { @@ -4625,7 +4634,7 @@ }, { "name": "sourceId", - "description": "Identifier of the target source (e.g. 'composite')", + "description": "Target source identifier (e.g. 'composite')", "type": { "name": "string" }, @@ -4633,9 +4642,9 @@ }, { "name": "sourceLayerId", - "description": "Identifier of the target source-layer (e.g. 'building')", + "description": "Target source-layer identifier (e.g. 'building'). If `null`, the change affects all layers in the target source.", "type": { - "name": "String" + "name": "string" }, "optional": true } @@ -5124,7 +5133,7 @@ "funcSignature": "(feature:GeoJSON.Feature) => void" }, "default": "none", - "description": "Map press listener, gets called when a user presses the map\n*signature:*`(feature:GeoJSON.Feature) => void`" + "description": "Map press listener, called when a user presses the map.\n*signature:*`(feature:GeoJSON.Feature) => void`" }, { "name": "onLongPress", @@ -5134,7 +5143,7 @@ "funcSignature": "(feature:GeoJSON.Feature) => void" }, "default": "none", - "description": "Map long press listener, gets called when a user long presses the map\n*signature:*`(feature:GeoJSON.Feature) => void`" + "description": "Map long press listener, called when a user long presses the map.\n*signature:*`(feature:GeoJSON.Feature) => void`" }, { "name": "onRegionWillChange", diff --git a/docs/examples.json b/docs/examples.json index 77b52e1e0..221007d79 100644 --- a/docs/examples.json +++ b/docs/examples.json @@ -378,6 +378,23 @@ "relPath": "Map/PointInMapView.js", "name": "PointInMapView" }, + { + "metadata": { + "title": "Screen Coordinates", + "tags": [ + "MapView#onPress", + "MapView#onLongPress", + "MapView#getCoordinateFromView", + "MapView#getPointInView", + "MapView#queryRenderedFeaturesAtPoint", + "MapView#queryRenderedFeaturesInRect" + ], + "docs": "\nTests conversion from screen to geographic coordinates and vice versa. \n" + }, + "fullPath": "example/src/examples/Map/ScreenCoordinates.tsx", + "relPath": "Map/ScreenCoordinates.tsx", + "name": "ScreenCoordinates" + }, { "metadata": { "title": "Show and hide layer", diff --git a/example/src/examples/Map/ScreenCoordinates.tsx b/example/src/examples/Map/ScreenCoordinates.tsx new file mode 100644 index 000000000..c24b3dcd8 --- /dev/null +++ b/example/src/examples/Map/ScreenCoordinates.tsx @@ -0,0 +1,585 @@ +import { useCallback, useMemo, useRef, useState } from 'react'; +import { + Platform, + ScrollView, + StyleSheet, + Text, + TouchableOpacity, + TouchableOpacityProps, + View, +} from 'react-native'; +import Mapbox, { CircleLayer, FillLayer, ShapeSource } from '@rnmapbox/maps'; +import { ExampleWithMetadata } from '../common/ExampleMetadata'; +import sheet from '../../styles/sheet'; + +const INSTRUCTIONS = ` +––– INSTRUCTIONS ––– + +Pressing any of the 4 shapes should: + 1. return the geographic and screen coordinates + 2. correctly convert screen coordinates to geographic coordinates + 3. correctly convert geographic coordinates to screen coordinates + 4. return the pressed feature (check "properties.color") + 5. return the pressed feature (check "properties.color") + +Pressing the red outline should yield these coordinates: +- top-left corner: + - screenPointY: 300 + - screenPointX: 100 +- bottom-right corner: + - screenPointY: 400 + - screenPointX: 200 + +Test all of the above at different zoom levels, rotation, and heading. +`; + +const STEPS = INSTRUCTIONS.replaceAll(/^(?!\s*\d\.).*\n?/gm, '') + .split('\n') + .map((s) => s.trim()); + +const ScreenCoordinates = () => { + const mapRef = useRef(null); + const cameraRef = useRef(null); + + const [centerLon, setCenterLon] = useState(); + const [centerLat, setCenterLat] = useState(); + const [zoom, setZoom] = useState(); + const [heading, setHeading] = useState(); + const [pitch, setPitch] = useState(); + + type ValuesRef = { + centerLon?: number; + centerLat?: number; + zoom?: number; + heading?: number; + pitch?: number; + }; + + const valuesRef = useRef({}); + + const centerText = useMemo( + () => + `Center: ${ + centerLon === undefined || centerLat === undefined + ? '-' + : `${centerLon.toFixed(2)}, ${centerLat.toFixed(2)}` + }`, + [centerLon, centerLat], + ); + + const zoomText = useMemo( + () => `Zoom: ${zoom === undefined ? '-' : zoom.toFixed(0)}`, + [zoom], + ); + + const headingText = useMemo( + () => `Heading: ${heading === undefined ? '-' : heading.toFixed(0)}`, + [heading], + ); + const pitchText = useMemo( + () => `Pitch: ${pitch === undefined ? '-' : pitch.toFixed(0)}`, + [pitch], + ); + + const [pressText, setPressText] = useState(); + + const [coordinateFromViewText, setCoordinateFromViewText] = + useState(); + + const [pointInViewText, setPointInViewText] = useState(); + + const [queryRenderdFeaturesAtPointText, setQueryRenderedFeaturesAtPointText] = + useState(); + + const [queryRenderdFeaturesInRectText, setQueryRenderedFeaturesInRectText] = + useState(); + + const debugText = useMemo(() => { + return [ + [ + pressText, + coordinateFromViewText, + pointInViewText, + queryRenderdFeaturesAtPointText, + queryRenderdFeaturesInRectText, + ] + .flatMap((v, i) => (v ? [`${STEPS[i]}\n\n${v}`] : [])) + .join('\n\n'), + INSTRUCTIONS, + ] + .flatMap((v) => (v ? [v] : [])) + .join('\n\n'); + }, [ + pressText, + coordinateFromViewText, + pointInViewText, + queryRenderdFeaturesAtPointText, + queryRenderdFeaturesInRectText, + ]); + + const queryPressFeatures = useCallback( + (payload: Mapbox.ScreenPointPayload) => { + setCoordinateFromViewText(undefined); + setPointInViewText(undefined); + setQueryRenderedFeaturesAtPointText(undefined); + setQueryRenderedFeaturesInRectText(undefined); + + const map = mapRef.current; + + if (!map) return; + + const point: GeoJSON.Position = [ + payload.screenPointX, + payload.screenPointY, + ]; + + const rectSize = 40; + + const rect: GeoJSON.BBox = [ + payload.screenPointY - rectSize / 2, // top + payload.screenPointX - rectSize / 2, // left + payload.screenPointY + rectSize / 2, // bottom + payload.screenPointX + rectSize / 2, // right + ]; + + const pointText = `[ ${point.map((v) => v.toFixed(1)).join(', ')} ]`; + const rectText = `[ ${rect.map((v) => v.toFixed(1)).join(', ')} ]`; + + const getPrettyPointText = (p: GeoJSON.Position) => + `[\n${p + .map((v, i) => ` (${i === 0 ? 'x' : 'y'}) ${v.toFixed(1)}`) + .join(',\n')}\n]`; + + const getPrettyCoordsText = (c: GeoJSON.Position) => + `[\n${c + .map((v, i) => ` (${i === 0 ? 'lon' : 'lat'}) ${v.toFixed(4)}`) + .join(',\n')}\n]`; + + mapRef.current + ?.getCoordinateFromView(point) + .then((coords) => { + setCoordinateFromViewText( + `getCoordinateFromView(${getPrettyPointText( + point, + )}) -> ${getPrettyCoordsText(coords)}`, + ); + + mapRef.current + ?.getPointInView(coords) + .then((p) => { + setPointInViewText( + `getPointInView(${getPrettyCoordsText( + coords, + )}) -> ${getPrettyPointText(p)}`, + ); + }) + .catch(() => {}); + }) + .catch(() => {}); + + const layersIds = [POINTS_CIRCLE_LAYER_ID, SQUARES_FILL_LAYER_ID]; + + mapRef.current + ?.queryRenderedFeaturesAtPoint(point, undefined, layersIds) + .then((fc) => { + setQueryRenderedFeaturesAtPointText( + `queryRenderedFeaturesAtPoint(${pointText}): ${JSON.stringify( + fc, + undefined, + 2, + )}`, + ); + }) + .catch(() => {}); + + mapRef.current + ?.queryRenderedFeaturesInRect(rect, undefined, layersIds) + .then((fc) => { + setQueryRenderedFeaturesInRectText( + `queryRenderedFeaturesInRect(${rectText}): ${JSON.stringify( + fc, + undefined, + 2, + )}`, + ); + }) + .catch(() => {}); + }, + [], + ); + + const handlePress = useCallback( + (f: GeoJSON.Feature) => { + setPressText(`onPress: ${JSON.stringify(f, undefined, 2)}`); + + queryPressFeatures(f.properties); + }, + [queryPressFeatures], + ); + + const handleLongPress = useCallback( + (f: GeoJSON.Feature) => { + setPressText(`onLongPress: ${JSON.stringify(f, undefined, 2)}`); + + queryPressFeatures(f.properties); + }, + [queryPressFeatures], + ); + + const [isBusy, setBusy] = useState(true); + + const updateValues = useCallback((state: Mapbox.MapState) => { + const centerLon = state.properties.center[0] ?? 0; + const centerLat = state.properties.center[1] ?? 0; + const zoom = state.properties.zoom; + const heading = state.properties.heading; + const pitch = state.properties.pitch; + + valuesRef.current.centerLon = centerLon; + valuesRef.current.centerLat = centerLat; + valuesRef.current.zoom = zoom; + valuesRef.current.heading = heading; + valuesRef.current.pitch = pitch; + + setCenterLon(centerLon); + setCenterLat(centerLat); + setZoom(zoom); + setHeading(heading); + setPitch(pitch); + }, []); + + const handleCameraChanged = useCallback( + (state: Mapbox.MapState) => { + updateValues(state); + }, + [updateValues], + ); + + const handleMapIdle = useCallback( + (state: Mapbox.MapState) => { + updateValues(state); + setBusy(false); + }, + [updateValues], + ); + + const setCameraStop = useCallback((stop: Mapbox.CameraStop) => { + setBusy(true); + cameraRef.current?.setCamera({ + animationMode: 'easeTo', + animationDuration: 300, + ...stop, + }); + }, []); + + const handlePressCenter = useCallback(() => { + const { centerLon, centerLat } = valuesRef.current; + + if (centerLon === undefined || centerLat === undefined) return; + + setCameraStop({ + centerCoordinate: + centerLon !== DEFAULT_CAMERA_STOP.centerCoordinate[0] || + centerLat !== DEFAULT_CAMERA_STOP.centerCoordinate[1] + ? DEFAULT_CAMERA_STOP.centerCoordinate + : INITIAL_CAMERA_STOP.centerCoordinate, + }); + }, [setCameraStop]); + + const handlePressZoom = useCallback(() => { + const { zoom } = valuesRef.current; + setCameraStop({ + zoomLevel: + zoom !== DEFAULT_CAMERA_STOP.zoomLevel + ? DEFAULT_CAMERA_STOP.zoomLevel + : INITIAL_CAMERA_STOP.zoomLevel, + }); + }, [setCameraStop]); + + const handlePressHeading = useCallback(() => { + const { heading } = valuesRef.current; + setCameraStop({ + heading: + heading !== DEFAULT_CAMERA_STOP.heading + ? DEFAULT_CAMERA_STOP.heading + : INITIAL_CAMERA_STOP.heading, + }); + }, [setCameraStop]); + + const handlePressPitch = useCallback(() => { + const { pitch } = valuesRef.current; + setCameraStop({ + pitch: + pitch !== DEFAULT_CAMERA_STOP.pitch + ? DEFAULT_CAMERA_STOP.pitch + : INITIAL_CAMERA_STOP.pitch, + }); + }, [setCameraStop]); + + return ( + + + + + + + + + + + + + + + + + + {debugText} + + + + ); +}; + +const INITIAL_CAMERA_STOP = { + centerCoordinate: [-5, 20], + zoomLevel: 2, + heading: 15, + pitch: 45, +} as const satisfies Mapbox.CameraStop; + +const DEFAULT_CAMERA_STOP = { + centerCoordinate: [0, 0], + zoomLevel: 0, + heading: 0, + pitch: 0, +} as const satisfies Mapbox.CameraStop; + +const POINT_GEO_CENTERS: readonly GeoJSON.Position[] = [ + [10, 10], + [30, 30], +]; + +const SQUARES_GEO_CENTERS: readonly GeoJSON.Position[] = [ + [-10, 10], + [-30, 30], +]; + +const OUTLINE_RECT = { + x: 100, + y: 300, + width: 100, + height: 100, +} as const; + +type CustomProperties = { + readonly color: string; +}; + +const POINTS_SOURCE: GeoJSON.FeatureCollection< + GeoJSON.Point, + CustomProperties +> = { + type: 'FeatureCollection', + features: POINT_GEO_CENTERS.map( + (coordinates, i): GeoJSON.Feature => ({ + type: 'Feature', + id: `point-lon:${(coordinates[0] ?? 0).toFixed(4)},lat:${( + coordinates[1] ?? 0 + ).toFixed(4)}`, + properties: { color: i === 0 ? 'magenta' : 'orange' }, + geometry: { type: 'Point', coordinates }, + }), + ), +}; + +const SQUARE_SIZE = 5; + +const SQUARES_SOURCE: GeoJSON.FeatureCollection< + GeoJSON.Polygon, + CustomProperties +> = { + type: 'FeatureCollection', + features: SQUARES_GEO_CENTERS.map( + (coords, i): GeoJSON.Feature => { + const lon = coords[0] ?? 0; + const lat = coords[1] ?? 0; + + const r = SQUARE_SIZE / 2; + + return { + type: 'Feature', + id: `square-lon:${lon.toFixed(4)},lat:${lat.toFixed(4)}`, + properties: { color: i === 0 ? 'blue' : 'green' }, + geometry: { + type: 'Polygon', + coordinates: [ + [ + [lon + r, lat + r], + [lon + r, lat - r], + [lon - r, lat - r], + [lon - r, lat + r], + [lon + r, lat + r], + ], + ], + }, + }; + }, + ), +}; + +const POINTS_SOURCE_ID = 'points'; +const POINTS_CIRCLE_LAYER_ID = `${POINTS_SOURCE_ID}-circle`; + +const SQUARES_SOURCE_ID = 'squares'; +const SQUARES_FILL_LAYER_ID = `${SQUARES_SOURCE_ID}-fill`; + +const mapStyles = { + pointCircle: { + circleRadius: 10, + circleOpacity: 0.5, + circleColor: ['get', 'color'], + }, + squareFill: { + fillColor: ['get', 'color'], + fillOpacity: 0.5, + }, +} as const; + +const styles = StyleSheet.create({ + map: { + height: 450, + }, + overlay: { + pointerEvents: 'none', + position: 'absolute', + left: OUTLINE_RECT.x, + top: OUTLINE_RECT.y, + width: OUTLINE_RECT.width, + height: OUTLINE_RECT.height, + borderWidth: 1, + borderColor: '#ff000040', + }, + actionsContainer: { + flexDirection: 'row', + padding: 4, + gap: 8, + backgroundColor: 'gray', + }, + actionButton: { + backgroundColor: 'white', + borderRadius: 4, + paddingVertical: 2, + paddingHorizontal: 4, + }, + actionButtonFlexDefault: { + flex: 1, + }, + actionButtonFlexWide: { + flex: 2, + }, + actionButtonText: { + fontSize: 12, + color: 'black', + }, + debugTextContainer: { + flex: 1, + backgroundColor: 'white', + }, + debugTextContentContainer: { + padding: 16, + paddingBottom: 34, + }, + debugText: { + fontFamily: Platform.select({ ios: 'Menlo', default: 'monospace' }), + fontWeight: 'bold', + fontSize: 10, + color: '#000000', + }, +}); + +type ActionButtonProps = TouchableOpacityProps & { + readonly title: string; + readonly size?: 'default' | 'wide'; +}; +const ActionButton = (props: ActionButtonProps) => { + const { title, size = 'default', style, ...rest } = props; + return ( + + {title} + + ); +}; + +const metadata: ExampleWithMetadata['metadata'] = { + title: 'Screen Coordinates', + tags: [ + 'MapView#onPress', + 'MapView#onLongPress', + 'MapView#getCoordinateFromView', + 'MapView#getPointInView', + 'MapView#queryRenderedFeaturesAtPoint', + 'MapView#queryRenderedFeaturesInRect', + ], + docs: ` +Tests conversion from screen to geographic coordinates and vice versa. +`, +}; + +ScreenCoordinates.metadata = metadata; + +export default ScreenCoordinates; diff --git a/example/src/examples/Map/index.js b/example/src/examples/Map/index.js index 36a569a17..285ee58e0 100644 --- a/example/src/examples/Map/index.js +++ b/example/src/examples/Map/index.js @@ -15,6 +15,7 @@ export { default as TwoByTwo } from './TwoByTwo'; export { default as MapAndRNNavigation } from './MapAndRNNavigation'; export { default as DynamicUrl } from './DynamicUrl'; export { default as LocalizeLabels } from './LocalizeLabels'; +export { default as ScreenCoordinates} from './ScreenCoordinates'; export const metadata = { title: 'Map', diff --git a/ios/RNMBX/RNMBXMapViewManager.swift b/ios/RNMBX/RNMBXMapViewManager.swift index 1b650bf37..fca2b7306 100644 --- a/ios/RNMBX/RNMBXMapViewManager.swift +++ b/ios/RNMBX/RNMBXMapViewManager.swift @@ -247,7 +247,7 @@ extension RNMBXMapViewManager { rejecter: @escaping RCTPromiseRejectBlock ) { view.withMapboxMap { map in - let point = CGPoint(x: CGFloat(point[0].floatValue), y: CGFloat(point[1].floatValue)) + let point = CGPoint(x: point[0].CGFloat, y: point[1].CGFloat) logged("queryRenderedFeaturesAtPoint.option", rejecter: rejecter) { let options = try RenderedQueryOptions( @@ -282,16 +282,24 @@ extension RNMBXMapViewManager { resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock ) { - let top = bbox.isEmpty ? 0.0 : CGFloat(bbox[0].floatValue) - let right = bbox.isEmpty ? 0.0 : CGFloat(bbox[1].floatValue) - let bottom = bbox.isEmpty ? 0.0 : CGFloat(bbox[2].floatValue) - let left = bbox.isEmpty ? 0.0 : CGFloat(bbox[3].floatValue) - let rect = + let top = bbox.isEmpty ? 0.0 : bbox[0].CGFloat + let left = bbox.isEmpty ? 0.0 : bbox[1].CGFloat + let bottom = bbox.isEmpty ? 0.0 : bbox[2].CGFloat + let right = bbox.isEmpty ? 0.0 : bbox[3].CGFloat + let rect: CGRect = bbox.isEmpty - ? CGRect(x: 0.0, y: 0.0, width: map.bounds.size.width, height: map.bounds.size.height) - : CGRect( - x: [left, right].min()!, y: [top, bottom].min()!, width: abs(right - left), - height: abs(bottom - top)) + ? CGRect( + x: 0.0, + y: 0.0, + width: map.bounds.size.width, + height: map.bounds.size.height + ) + : CGRect( + x: min(left, right), + y: min(top, bottom), + width: abs(right - left), + height: abs(bottom - top) + ) logged("queryRenderedFeaturesInRect.option", rejecter: rejecter) { let options = try RenderedQueryOptions( layerIds: layerIDs?.isEmpty ?? true ? nil : layerIDs, filter: filter?.asExpression()) diff --git a/ios/RNMBX/RNMBXMapViewModule.mm b/ios/RNMBX/RNMBXMapViewModule.mm index 878c4c32e..b87473031 100644 --- a/ios/RNMBX/RNMBXMapViewModule.mm +++ b/ios/RNMBX/RNMBXMapViewModule.mm @@ -64,7 +64,7 @@ - (void)withMapView:(nonnull NSNumber*)viewRef block:(void (^)(RNMBXMapView *))b NSNumber* a = [atPoint objectAtIndex:0]; NSNumber* b = [atPoint objectAtIndex:1]; - [RNMBXMapViewManager getCoordinateFromView:view atPoint:CGPointMake(a.floatValue, b.floatValue) resolver:resolve rejecter:reject]; + [RNMBXMapViewManager getCoordinateFromView:view atPoint:CGPointMake(a.doubleValue, b.doubleValue) resolver:resolve rejecter:reject]; } reject:reject methodName:@"getCoordinateFromView"]; } diff --git a/src/Mapbox.native.ts b/src/Mapbox.native.ts index ae6e4a8bc..ed6e17262 100644 --- a/src/Mapbox.native.ts +++ b/src/Mapbox.native.ts @@ -9,7 +9,11 @@ export { type CameraStop, } from './components/Camera'; export { Atmosphere } from './components/Atmosphere'; -export { default as MapView, type MapState } from './components/MapView'; +export { + default as MapView, + type MapState, + type ScreenPointPayload, +} from './components/MapView'; export { default as Light } from './components/Light'; export { default as PointAnnotation } from './components/PointAnnotation'; export { default as Annotation } from './components/Annotation'; diff --git a/src/components/Camera.tsx b/src/components/Camera.tsx index 3a0467fc5..6f6ed50ee 100644 --- a/src/components/Camera.tsx +++ b/src/components/Camera.tsx @@ -141,9 +141,9 @@ export type CameraStop = { /** The corners of a box around which the map should bound. Contains padding props for backwards * compatibility; the root `padding` prop should be used instead. */ bounds?: CameraBoundsWithPadding; - /** The heading (orientation) of the map. */ + /** Heading (bearing, orientation) of the map, measured in degrees clockwise from true north. */ heading?: number; - /** The pitch of the map. */ + /** The pitch toward the horizon measured in degrees, with 0 degrees resulting in a top-down view for a two-dimensional map. */ pitch?: number; /** The zoom level of the map. */ zoomLevel?: number; diff --git a/src/components/MapView.tsx b/src/components/MapView.tsx index b458c8517..b8ad80d14 100644 --- a/src/components/MapView.tsx +++ b/src/components/MapView.tsx @@ -65,6 +65,11 @@ export type RegionPayload = { pitch: number; }; +export type ScreenPointPayload = { + readonly screenPointX: number; + readonly screenPointY: number; +}; + export type GestureSettings = { /** * Whether double tapping the map with one touch results in a zoom-in animation. @@ -312,14 +317,18 @@ type Props = ViewProps & { gestureSettings?: GestureSettings; /** - * Map press listener, gets called when a user presses the map + * Map press listener, called when a user presses the map. */ - onPress?: (feature: GeoJSON.Feature) => void; + onPress?: ( + feature: GeoJSON.Feature, + ) => void; /** - * Map long press listener, gets called when a user long presses the map + * Map long press listener, called when a user long presses the map. */ - onLongPress?: (feature: GeoJSON.Feature) => void; + onLongPress?: ( + feature: GeoJSON.Feature, + ) => void; /** * } coordinate - A point expressed in the map view's coordinate system. - * @return {Array} + * @param {Position} coordinate - A point expressed in the map view's coordinate system `[longitude, latitude]`. + * @return {Position} A point expressed in screen coordinates relative to the map view `[x, y]`. */ async getPointInView(coordinate: Position): Promise { const res = await this._runNative<{ pointInView: Position }>( @@ -688,13 +699,14 @@ class MapView extends NativeBridgeComponent( } /** - * Converts a point in the given view’s coordinate system to a geographic coordinate. + * Converts a screen coordinate relative to the map view to a geographic coordinate. * * @example - * const coordinate = await this._map.getCoordinateFromView([100, 100]); + * const x = 100; const y = 100; + * const [longitude, latitude] = await this._map.getCoordinateFromView([x, y]); * - * @param {Array} point - A point expressed in the given view’s coordinate system. - * @return {Array} + * @param {Position} point - A point expressed in screen coordinates relative to the map view `[x, y]`. + * @return {Position} A point expressed in the map view's coordinate system `[longitude, latitude]`. */ async getCoordinateFromView(point: Position): Promise { const res = await this._runNative<{ coordinateFromView: Position }>( @@ -705,12 +717,12 @@ class MapView extends NativeBridgeComponent( } /** - * The coordinate bounds (ne, sw) visible in the user’s viewport. + * The coordinate bounds of the map viewport. * * @example - * const visibleBounds = await this._map.getVisibleBounds(); + * const [[rightLon, topLat], [leftLon, bottomLat]] = await this._map.getVisibleBounds(); * - * @return {Array} + * @return {[Position, Position]} The geographic coordinate bounds of the map viewport `[[rightLon, topLat], [leftLon, bottomLat]]`. */ async getVisibleBounds(): Promise<[Position, Position]> { const res = await this._runNative<{ visibleBounds: [Position, Position] }>( @@ -723,12 +735,13 @@ class MapView extends NativeBridgeComponent( * Returns an array of rendered map features that intersect with a given point. * * @example - * this._map.queryRenderedFeaturesAtPoint([30, 40], ['==', 'type', 'Point'], ['id1', 'id2']) + * const x = 30; const y = 40; + * this._map.queryRenderedFeaturesAtPoint([x, y], ['==', 'type', 'Point'], ['id1', 'id2']) * - * @param {Array} coordinate - A point expressed in the map view’s coordinate system. - * @param {Array=} filter - A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array. - * @param {Array=} layerIDs - A array of layer id's to filter the features by - * @return {FeatureCollection} + * @param {Position} coordinate - A point expressed in the map view’s coordinate system `[x, y]`; + * @param {FilterExpression | []} filter - A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array. + * @param {string[]} layerIDs - A array of layer IDs by which to filter the features. + * @return {FeatureCollection} A GeoJSON feature collection containing the query results. */ async queryRenderedFeaturesAtPoint( @@ -758,12 +771,14 @@ class MapView extends NativeBridgeComponent( * passing an empty array will query the entire visible bounds of the map. * * @example - * this._map.queryRenderedFeaturesInRect([30, 40, 20, 10], ['==', 'type', 'Point'], ['id1', 'id2']) + * const left = 40; const top = 30; + * const right = 10; const bottom = 20; + * this._map.queryRenderedFeaturesInRect([top, left, bottom, right], ['==', 'type', 'Point'], ['id1', 'id2']) * - * @param {Array} bbox - A rectangle expressed in the map view’s coordinate system, density independent pixels and not map coordinates. This can be an empty array to query the visible map area. - * @param {Array=} filter - A set of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array. - * @param {Array=} layerIDs - A array of layer id's to filter the features by - * @return {FeatureCollection} + * @param {BBox | []} bbox - A rectangle expressed in density-independent screen coordinates relative to the map view `[top, left, bottom, right]` or `[minY, minX, maxY, maxX]` (not geographic coordinates). An empty array queries the visible map area. + * @param {FilterExpression} filter - An array of strings that correspond to the names of layers defined in the current style. Only the features contained in these layers are included in the returned array. + * @param {string[] | null} layerIDs - A array of layer IDs by which to filter the features. + * @return {FeatureCollection} A GeoJSON feature collection containing the query results. */ async queryRenderedFeaturesInRect( bbox: BBox | [], @@ -786,7 +801,7 @@ class MapView extends NativeBridgeComponent( return res.data; } else { throw new Error( - 'Must pass in a valid bounding box: [top, right, bottom, left]. An empty array [] is also acceptable in v10.', + 'Must pass in a valid bounding box: [top, left, bottom, right]. An empty array [] is also acceptable in v10.', ); } } @@ -798,9 +813,9 @@ class MapView extends NativeBridgeComponent( * this._map.querySourceFeatures('your-source-id', [], ['your-source-layer']) * * @param {String} sourceId - Style source identifier used to query for source features. - * @param {Array=} filter - A filter to limit query results. - * @param {Array=} sourceLayerIDs - The name of the source layers to query. For vector tile sources, this parameter is required. For GeoJSON sources, it is ignored. - * @return {FeatureCollection} + * @param {FilterExpression | []} filter - A filter to limit query results. + * @param {string[]} sourceLayerIDs - The name of the source layers to query. For vector tile sources, this parameter is required. For GeoJSON sources, it is ignored. + * @return {FeatureCollection} A GeoJSON feature collection. */ async querySourceFeatures( sourceId: string, @@ -844,9 +859,11 @@ class MapView extends NativeBridgeComponent( } /** - * Takes snapshot of map with current tiles and returns a URI to the image - * @param {Boolean} writeToDisk If true will create a temp file, otherwise it is in base64 - * @return {String} + * Takes snapshot of map with current tiles and returns a Base64-encoded PNG image, + * or an file-system URI to a temporary PNG file if `writeToDisk` is `true`. + * + * @param {boolean} writeToDisk If `true`, creates a temporary PNG file and returns a file-system URI, otherwise returns a Base64-encoded PNG image. (Defaults to `false`) + * @return {string} A a Base64-encoded PNG image or a file-system URI to a temporary PNG file. */ async takeSnap(writeToDisk = false): Promise { const res = await this._runNative<{ uri: string }>('takeSnap', [ @@ -861,7 +878,7 @@ class MapView extends NativeBridgeComponent( * @example * const zoom = await this._map.getZoom(); * - * @return {Number} + * @return {number} The current zoom of the map view. */ async getZoom(): Promise { @@ -870,12 +887,12 @@ class MapView extends NativeBridgeComponent( } /** - * Returns the map's geographical centerpoint + * Returns the map's center point expressed as geographic coordinates `[longitude, latitude]`. * * @example * const center = await this._map.getCenter(); * - * @return {Array} Coordinates + * @return {Position} The map's center point expressed as geographic coordinates `[longitude, latitude]`. */ async getCenter(): Promise { const res = await this._runNative<{ center: Position }>('getCenter'); @@ -905,8 +922,8 @@ class MapView extends NativeBridgeComponent( * The elevation is returned in meters relative to mean sea-level. * Returns null if terrain is disabled or if terrain data for the location hasn't been loaded yet. * - * @param {Array} coordinate - the coordinates to query elevation at - * @return {Number} + * @param {Position} coordinate - The geographic coordinates `[longitude, latitude]` at which to query elevation. + * @return {number} Elevation in meters relative to mean sea-level. */ async queryTerrainElevation(coordinate: Position): Promise { const res = await this._runNative<{ data: number }>( @@ -923,8 +940,8 @@ class MapView extends NativeBridgeComponent( * await this._map.setSourceVisibility(false, 'composite', 'building') * * @param {boolean} visible - Visibility of the layers - * @param {String} sourceId - Identifier of the target source (e.g. 'composite') - * @param {String=} sourceLayerId - Identifier of the target source-layer (e.g. 'building') + * @param {string} sourceId - Target source identifier (e.g. 'composite') + * @param {string | null} sourceLayerId - Target source-layer identifier (e.g. 'building'). If `null`, the change affects all layers in the target source. */ setSourceVisibility( visible: boolean, @@ -1036,13 +1053,21 @@ class MapView extends NativeBridgeComponent( } } - _onPress(e: NativeSyntheticEvent<{ payload: GeoJSON.Feature | string }>) { + _onPress( + e: NativeSyntheticEvent<{ + payload: GeoJSON.Feature | string; + }>, + ) { if (isFunction(this.props.onPress)) { this.props.onPress(this._decodePayload(e.nativeEvent.payload)); } } - _onLongPress(e: NativeSyntheticEvent<{ payload: GeoJSON.Feature | string }>) { + _onLongPress( + e: NativeSyntheticEvent<{ + payload: GeoJSON.Feature | string; + }>, + ) { if (isFunction(this.props.onLongPress)) { this.props.onLongPress(this._decodePayload(e.nativeEvent.payload)); }