diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSource.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSource.kt index fe7f6adff..5dcedbbca 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSource.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSource.kt @@ -34,4 +34,8 @@ class RNMBXRasterSource(context: Context?) : RNMBXTileSource(cont companion object { const val DEFAULT_TILE_SIZE = 512 } -} \ No newline at end of file + + fun setSourceBounds(value: Array) { + bounds = value + } +} diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSourceManager.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSourceManager.kt index f53c8b4ff..17807bf84 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSourceManager.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXRasterSourceManager.kt @@ -8,6 +8,8 @@ import com.facebook.react.viewmanagers.RNMBXRasterSourceManagerInterface import com.rnmapbox.rnmbx.events.constants.EventKeys import com.rnmapbox.rnmbx.events.constants.eventMapOf import javax.annotation.Nonnull +import com.facebook.react.bridge.ReadableType +import com.rnmapbox.rnmbx.utils.Logger class RNMBXRasterSourceManager(reactApplicationContext: ReactApplicationContext) : RNMBXTileSourceManager(reactApplicationContext), @@ -42,4 +44,32 @@ class RNMBXRasterSourceManager(reactApplicationContext: ReactApplicationContext) override fun setExisting(source: RNMBXRasterSource, value: Dynamic) { source.mExisting = value.asBoolean() } -} \ No newline at end of file + + @ReactProp(name = "sourceBounds") + override fun setSourceBounds(source: RNMBXRasterSource, value: Dynamic) { + if (value.type != ReadableType.Array || value.asArray().size() != 4) { + Logger.e(REACT_CLASS, "source bounds must be an array with left, bottom, top, and right values") + return + } + val bboxArray = Array(4) { i -> value.asArray().getDouble(i) } + + if(!this.validateBbox(bboxArray)){ + Logger.e(REACT_CLASS, "source bounds contain invalid bbox") + return + } + + source.setSourceBounds(bboxArray) + } + + private fun validateBbox(bbox: Array): Boolean { + if (bbox.size != 4) return false + + val (swLng, swLat, neLng, neLat) = bbox + + val isLngValid = swLng in -180.0..180.0 && neLng in -180.0..180.0 + val isLatValid = swLat in -90.0..90.0 && neLat in -90.0..90.0 + val isSouthWestOfNorthEast = swLng < neLng && swLat < neLat + + return isLngValid && isLatValid && isSouthWestOfNorthEast + } +} diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXTileSource.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXTileSource.kt index 91541bc09..84dd9f381 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXTileSource.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXTileSource.kt @@ -13,6 +13,7 @@ abstract class RNMBXTileSource(context: Context?) : RNMBXSource( var attribution: String? = null var minZoomLevel: Int? = null var maxZoomLevel: Int? = null + var bounds: Array? = null var tMS = false fun buildTileset(): TileSet { val tileUrlTemplates = @@ -35,10 +36,13 @@ abstract class RNMBXTileSource(context: Context?) : RNMBXSource( if (attribution != null) { builder.attribution(attribution) } + bounds?.let { + builder.bounds(it.toList()) + } return builder.build() } companion object { const val TILE_SPEC_VERSION = "2.1.0" } -} \ No newline at end of file +} diff --git a/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXRasterSourceManagerDelegate.java b/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXRasterSourceManagerDelegate.java index 3e6fafe73..68c26343c 100644 --- a/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXRasterSourceManagerDelegate.java +++ b/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXRasterSourceManagerDelegate.java @@ -49,6 +49,9 @@ public void setProperty(T view, String propName, @Nullable Object value) { case "attribution": mViewManager.setAttribution(view, new DynamicFromObject(value)); break; + case "sourceBounds": + mViewManager.setSourceBounds(view, new DynamicFromObject(value)); + break; default: super.setProperty(view, propName, value); } diff --git a/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXRasterSourceManagerInterface.java b/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXRasterSourceManagerInterface.java index 3361af80c..57af3c628 100644 --- a/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXRasterSourceManagerInterface.java +++ b/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXRasterSourceManagerInterface.java @@ -22,4 +22,5 @@ public interface RNMBXRasterSourceManagerInterface { void setTileSize(T view, Dynamic value); void setTms(T view, Dynamic value); void setAttribution(T view, Dynamic value); + void setSourceBounds(T view, Dynamic value); } diff --git a/docs/RasterSource.md b/docs/RasterSource.md index acf9e899d..bd4aec330 100644 --- a/docs/RasterSource.md +++ b/docs/RasterSource.md @@ -113,6 +113,17 @@ FIX ME NO DESCRIPTION +### sourceBounds + +```tsx +Array +``` +An array containing the longitude and latitude of the southwest and northeast corners of +the source's bounding box in the following order: `[sw.lng, sw.lat, ne.lng, ne.lat]`. +When this property is included in a source, no tiles outside of the given bounds are requested by Mapbox GL. + + + diff --git a/docs/docs.json b/docs/docs.json index 2d53c931f..e5da3472f 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -5052,6 +5052,13 @@ "type": "React.ReactElement \\| React.ReactElement[]", "default": "none", "description": "FIX ME NO DESCRIPTION" + }, + { + "name": "sourceBounds", + "required": false, + "type": "Array", + "default": "none", + "description": "An array containing the longitude and latitude of the southwest and northeast corners of\nthe source's bounding box in the following order: `[sw.lng, sw.lat, ne.lng, ne.lat]`.\nWhen this property is included in a source, no tiles outside of the given bounds are requested by Mapbox GL." } ], "fileNameWithExt": "RasterSource.tsx", diff --git a/example/src/examples/FillRasterLayer/RasterSource.js b/example/src/examples/FillRasterLayer/RasterSource.js index 04394bcc0..f86a7a451 100644 --- a/example/src/examples/FillRasterLayer/RasterSource.js +++ b/example/src/examples/FillRasterLayer/RasterSource.js @@ -23,6 +23,10 @@ export default function RasterSourceExample() { Source { #if RNMBX_11 @@ -28,27 +30,31 @@ public class RNMBXRasterSource : RNMBXSource { } else { result.tiles = tileUrlTemplates } - + if let tileSize = tileSize { result.tileSize = tileSize.doubleValue } - + if let minZoomLevel = minZoomLevel { result.minzoom = minZoomLevel.doubleValue } - + if let maxZoomLevel = maxZoomLevel { result.maxzoom = maxZoomLevel.doubleValue } - + if tms { result.scheme = .tms } - + if let attribution = attribution { result.attribution = attribution } - + + if let bounds = sourceBounds { + result.bounds = bounds.map { $0.doubleValue } + } + return result } diff --git a/ios/RNMBX/RNMBXRasterSourceComponentView.mm b/ios/RNMBX/RNMBXRasterSourceComponentView.mm index b1455600a..e4a3db777 100644 --- a/ios/RNMBX/RNMBXRasterSourceComponentView.mm +++ b/ios/RNMBX/RNMBXRasterSourceComponentView.mm @@ -76,7 +76,7 @@ + (ComponentDescriptorProvider)componentDescriptorProvider - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps { - const auto &newProps = static_cast(*props); + const auto &newProps = static_cast(*props); id idx = RNMBXConvertFollyDynamicToId(newProps.id); if (idx != nil) { _view.id = idx; @@ -113,7 +113,11 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & if (attribution != nil) { _view.attribution = attribution; } - + id sourceBounds = RNMBXConvertFollyDynamicToId(newProps.sourceBounds); + if (sourceBounds != nil) { + _view.sourceBounds = sourceBounds; + } + [super updateProps:props oldProps:oldProps]; } diff --git a/ios/RNMBX/RNMBXRasterSourceViewManager.m b/ios/RNMBX/RNMBXRasterSourceViewManager.m index 427c90a0a..6c95668e9 100644 --- a/ios/RNMBX/RNMBXRasterSourceViewManager.m +++ b/ios/RNMBX/RNMBXRasterSourceViewManager.m @@ -14,6 +14,6 @@ @interface RCT_EXTERN_REMAP_MODULE(RNMBXRasterSource, RNMBXRasterSourceViewManag RCT_EXPORT_VIEW_PROPERTY(tms, BOOL) RCT_EXPORT_VIEW_PROPERTY(attribution, NSString) - +RCT_EXPORT_VIEW_PROPERTY(sourceBounds, NSArray) @end diff --git a/src/components/RasterSource.tsx b/src/components/RasterSource.tsx index 161180dba..aeab76d15 100644 --- a/src/components/RasterSource.tsx +++ b/src/components/RasterSource.tsx @@ -67,6 +67,12 @@ type Props = BaseProps & { attribution?: string; children?: React.ReactElement | React.ReactElement[]; + /** + * An array containing the longitude and latitude of the southwest and northeast corners of + * the source's bounding box in the following order: `[sw.lng, sw.lat, ne.lng, ne.lat]`. + * When this property is included in a source, no tiles outside of the given bounds are requested by Mapbox GL. + */ + sourceBounds?: number[]; }; type NativeProps = Props; diff --git a/src/specs/RNMBXRasterSourceNativeComponent.ts b/src/specs/RNMBXRasterSourceNativeComponent.ts index 5b55eaad6..1bad8e7f2 100644 --- a/src/specs/RNMBXRasterSourceNativeComponent.ts +++ b/src/specs/RNMBXRasterSourceNativeComponent.ts @@ -14,6 +14,7 @@ export interface NativeProps extends ViewProps { tileSize: UnsafeMixed; tms: UnsafeMixed; attribution: UnsafeMixed; + sourceBounds: UnsafeMixed>; } export default codegenNativeComponent(