From 1e9c3a00b03d68a2e3b857ff865706aceffab1c9 Mon Sep 17 00:00:00 2001 From: ofernandezsantia Date: Tue, 13 Oct 2026 16:42:33 +0100 Subject: [PATCH] feat(RasterSource): add prefetchZoomDelta and tileNetworkRequestsDelay props Expose two Mapbox v11 raster source options that were not reachable from JavaScript: - prefetchZoomDelta: how many zoom levels above the current one are requested as placeholders while the current tiles load. 0 turns the prefetch off. - tileNetworkRequestsDelay: milliseconds to wait before a tile's network request goes out during a gesture or camera animation, so tiles that leave the viewport first are never requested. Both map straight to the native source builders on iOS and Android. Docs regenerated with yarn generate, and a new example scene uses both props. Co-Authored-By: Claude Fable 5.1 --- .../styles/sources/RNMBXRasterSource.kt | 23 ++++++-- .../sources/RNMBXRasterSourceManager.kt | 10 ++++ docs/RasterSource.md | 22 ++++++++ docs/docs.json | 14 +++++ docs/examples.json | 14 +++++ .../RasterSourceTileRequests.js | 52 +++++++++++++++++++ example/src/examples/FillRasterLayer/index.js | 1 + ios/RNMBX/RNMBXRasterSource.swift | 10 ++++ ios/RNMBX/RNMBXRasterSourceComponentView.mm | 8 +++ src/components/RasterSource.tsx | 16 ++++++ src/specs/RNMBXRasterSourceNativeComponent.ts | 2 + 11 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 example/src/examples/FillRasterLayer/RasterSourceTileRequests.js 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 cbb90de1fa..63df8e4914 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 @@ -5,20 +5,35 @@ import com.mapbox.maps.extension.style.sources.generated.RasterSource class RNMBXRasterSource(context: Context?) : RNMBXTileSource(context) { private var mTileSize: Int? = null + private var mPrefetchZoomDelta: Long? = null + private var mTileNetworkRequestsDelay: Double? = null override fun makeSource(): RasterSource { val id = iD!! val configurationUrl = uRL val tileSize = if (mTileSize == null) DEFAULT_TILE_SIZE else mTileSize!! - return if (configurationUrl != null) { - RasterSource.Builder(id).url(configurationUrl).tileSize(tileSize.toLong()).build() - } else RasterSource.Builder(id).tileSet(buildTileset()) - .tileSize(tileSize.toLong()).build() + val builder = if (configurationUrl != null) { + RasterSource.Builder(id).url(configurationUrl) + } else { + RasterSource.Builder(id).tileSet(buildTileset()) + } + builder.tileSize(tileSize.toLong()) + mPrefetchZoomDelta?.let { builder.prefetchZoomDelta(it) } + mTileNetworkRequestsDelay?.let { builder.tileNetworkRequestsDelay(it) } + return builder.build() } fun setTileSize(tileSize: Int) { mTileSize = tileSize } + fun setPrefetchZoomDelta(value: Long) { + mPrefetchZoomDelta = value + } + + fun setTileNetworkRequestsDelay(value: Double) { + mTileNetworkRequestsDelay = value + } + override fun hasPressListener(): Boolean { return false } 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 aa625b3c60..e6437d558e 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 @@ -35,6 +35,16 @@ class RNMBXRasterSourceManager(reactApplicationContext: ReactApplicationContext) source.setTileSize(tileSize.asInt()) } + @ReactProp(name = "prefetchZoomDelta") + override fun setPrefetchZoomDelta(source: RNMBXRasterSource, value: Dynamic) { + source.setPrefetchZoomDelta(value.asInt().toLong()) + } + + @ReactProp(name = "tileNetworkRequestsDelay") + override fun setTileNetworkRequestsDelay(source: RNMBXRasterSource, value: Dynamic) { + source.setTileNetworkRequestsDelay(value.asDouble()) + } + override fun customEvents(): Map? { return eventMapOf( EventKeys.RASTER_SOURCE_LAYER_CLICK to "onMapboxRasterSourcePress", diff --git a/docs/RasterSource.md b/docs/RasterSource.md index ae7a2f5395..e62b7aa967 100644 --- a/docs/RasterSource.md +++ b/docs/RasterSource.md @@ -85,6 +85,28 @@ Mapbox urls default to 256, all others default to 512. +### prefetchZoomDelta + +```tsx +number +``` +When a tile for the current zoom level is not loaded yet, tiles up to this many zoom levels +above it are requested and rendered in its place. 0 turns this off, so only tiles for the +current zoom level are requested. The default value for this option is 4. + +[Raster Source tile requests](../examples/FillRasterLayer/RasterSourceTileRequests) + +### tileNetworkRequestsDelay + +```tsx +number +``` +Delay in milliseconds before a tile's network request is sent while a gesture or a camera +animation is in progress. Tiles that leave the viewport before the delay elapses are never +requested. Has no effect on an idle map. The default value for this option is 0. + +[Raster Source tile requests](../examples/FillRasterLayer/RasterSourceTileRequests) + ### tms ```tsx diff --git a/docs/docs.json b/docs/docs.json index d53b39f2f5..7c455bc769 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -7609,6 +7609,20 @@ "default": "none", "description": "Size of the map tiles.\nMapbox urls default to 256, all others default to 512." }, + { + "name": "prefetchZoomDelta", + "required": false, + "type": "number", + "default": "none", + "description": "When a tile for the current zoom level is not loaded yet, tiles up to this many zoom levels\nabove it are requested and rendered in its place. 0 turns this off, so only tiles for the\ncurrent zoom level are requested. The default value for this option is 4." + }, + { + "name": "tileNetworkRequestsDelay", + "required": false, + "type": "number", + "default": "none", + "description": "Delay in milliseconds before a tile's network request is sent while a gesture or a camera\nanimation is in progress. Tiles that leave the viewport before the delay elapses are never\nrequested. Has no effect on an idle map. The default value for this option is 0." + }, { "name": "tms", "required": false, diff --git a/docs/examples.json b/docs/examples.json index a9e67ad08c..d1d1b48ff1 100644 --- a/docs/examples.json +++ b/docs/examples.json @@ -866,6 +866,20 @@ "relPath": "FillRasterLayer/RasterSource.js", "name": "RasterSource" }, + { + "metadata": { + "title": "Raster Source tile requests", + "tags": [ + "RasterSource", + "RasterSource#prefetchZoomDelta", + "RasterSource#tileNetworkRequestsDelay" + ], + "docs": "\nFetches only the tiles of the current zoom level (no parent tiles as a placeholder) and waits 200 ms\nbefore requesting tiles while the map pans, so tiles that leave the screen during a fast pan are\nnever requested. Watch the network log while panning to compare with the Raster Source example.\n" + }, + "fullPath": "example/src/examples/FillRasterLayer/RasterSourceTileRequests.js", + "relPath": "FillRasterLayer/RasterSourceTileRequests.js", + "name": "RasterSourceTileRequests" + }, { "metadata": { "title": "Watercolor Raster Tiles", diff --git a/example/src/examples/FillRasterLayer/RasterSourceTileRequests.js b/example/src/examples/FillRasterLayer/RasterSourceTileRequests.js new file mode 100644 index 0000000000..befb15c4fc --- /dev/null +++ b/example/src/examples/FillRasterLayer/RasterSourceTileRequests.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { MapView, RasterLayer, RasterSource, Camera } from '@rnmapbox/maps'; + +const styles = { + matchParent: { flex: 1 }, +}; + +export default function RasterSourceTileRequestsExample() { + return ( + + + + + + ); +} + +/* end-example-doc */ + +/** + * @typedef {import('../common/ExampleMetadata').ExampleWithMetadata} ExampleWithMetadata + * @type {ExampleWithMetadata['metadata']} + */ +const metadata = { + title: 'Raster Source tile requests', + tags: [ + 'RasterSource', + 'RasterSource#prefetchZoomDelta', + 'RasterSource#tileNetworkRequestsDelay', + ], + docs: ` +Fetches only the tiles of the current zoom level (no parent tiles as a placeholder) and waits 200 ms +before requesting tiles while the map pans, so tiles that leave the screen during a fast pan are +never requested. Watch the network log while panning to compare with the Raster Source example. +`, +}; +RasterSourceTileRequestsExample.metadata = metadata; diff --git a/example/src/examples/FillRasterLayer/index.js b/example/src/examples/FillRasterLayer/index.js index f9f81e4af9..28926e7962 100644 --- a/example/src/examples/FillRasterLayer/index.js +++ b/example/src/examples/FillRasterLayer/index.js @@ -7,6 +7,7 @@ export { default as QueryAtPoint } from './QueryAtPoint'; export { default as QuerySourceFeatures } from './QuerySourceFeatures'; export { default as QueryWithRect } from './QueryWithRect'; export { default as RasterSource } from './RasterSource'; +export { default as RasterSourceTileRequests } from './RasterSourceTileRequests'; export { default as WatercolorRasterTiles } from './WatercolorRasterTiles'; export { default as HillshadeSource } from './HillshadeSource'; diff --git a/ios/RNMBX/RNMBXRasterSource.swift b/ios/RNMBX/RNMBXRasterSource.swift index b883952410..0d2b5c1d02 100644 --- a/ios/RNMBX/RNMBXRasterSource.swift +++ b/ios/RNMBX/RNMBXRasterSource.swift @@ -11,6 +11,8 @@ public class RNMBXRasterSource : RNMBXSource { @objc public var minZoomLevel: NSNumber? @objc public var maxZoomLevel: NSNumber? @objc public var tileSize: NSNumber? + @objc public var prefetchZoomDelta: NSNumber? + @objc public var tileNetworkRequestsDelay: NSNumber? @objc public var tms: Bool = false @@ -31,6 +33,14 @@ public class RNMBXRasterSource : RNMBXSource { result.tileSize = tileSize.doubleValue } + if let prefetchZoomDelta = prefetchZoomDelta { + result.prefetchZoomDelta = prefetchZoomDelta.doubleValue + } + + if let tileNetworkRequestsDelay = tileNetworkRequestsDelay { + result.tileNetworkRequestsDelay = tileNetworkRequestsDelay.doubleValue + } + if let minZoomLevel = minZoomLevel { result.minzoom = minZoomLevel.doubleValue } diff --git a/ios/RNMBX/RNMBXRasterSourceComponentView.mm b/ios/RNMBX/RNMBXRasterSourceComponentView.mm index d007a56f69..dbfc5de905 100644 --- a/ios/RNMBX/RNMBXRasterSourceComponentView.mm +++ b/ios/RNMBX/RNMBXRasterSourceComponentView.mm @@ -104,6 +104,14 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & if (tileSize != nil) { _view.tileSize = tileSize; } + id prefetchZoomDelta = RNMBXConvertFollyDynamicToId(newProps.prefetchZoomDelta); + if (prefetchZoomDelta != nil) { + _view.prefetchZoomDelta = prefetchZoomDelta; + } + id tileNetworkRequestsDelay = RNMBXConvertFollyDynamicToId(newProps.tileNetworkRequestsDelay); + if (tileNetworkRequestsDelay != nil) { + _view.tileNetworkRequestsDelay = tileNetworkRequestsDelay; + } id tms = RNMBXConvertFollyDynamicToId(newProps.tms); if (tms != nil) { _view.tms = tms; diff --git a/src/components/RasterSource.tsx b/src/components/RasterSource.tsx index 7811f0c720..b6c41fca74 100644 --- a/src/components/RasterSource.tsx +++ b/src/components/RasterSource.tsx @@ -55,6 +55,20 @@ type Props = BaseProps & { */ tileSize?: number; + /** + * When a tile for the current zoom level is not loaded yet, tiles up to this many zoom levels + * above it are requested and rendered in its place. 0 turns this off, so only tiles for the + * current zoom level are requested. The default value for this option is 4. + */ + prefetchZoomDelta?: number; + + /** + * Delay in milliseconds before a tile's network request is sent while a gesture or a camera + * animation is in progress. Tiles that leave the viewport before the delay elapses are never + * requested. Has no effect on an idle map. The default value for this option is 0. + */ + tileNetworkRequestsDelay?: number; + /** * Influences the y direction of the tile coordinates. (tms inverts y axis) */ @@ -116,6 +130,8 @@ class RasterSource extends AbstractSource { minZoomLevel: this.props.minZoomLevel, maxZoomLevel: this.props.maxZoomLevel, tileSize: this.props.tileSize, + prefetchZoomDelta: this.props.prefetchZoomDelta, + tileNetworkRequestsDelay: this.props.tileNetworkRequestsDelay, tms: this.props.tms, attribution: this.props.attribution, }; diff --git a/src/specs/RNMBXRasterSourceNativeComponent.ts b/src/specs/RNMBXRasterSourceNativeComponent.ts index 8c22fea062..e5f4a5bf97 100644 --- a/src/specs/RNMBXRasterSourceNativeComponent.ts +++ b/src/specs/RNMBXRasterSourceNativeComponent.ts @@ -13,6 +13,8 @@ export interface NativeProps extends ViewProps { minZoomLevel: UnsafeMixed; maxZoomLevel: UnsafeMixed; tileSize: UnsafeMixed; + prefetchZoomDelta: UnsafeMixed; + tileNetworkRequestsDelay: UnsafeMixed; tms: UnsafeMixed; attribution: UnsafeMixed; sourceBounds: UnsafeMixed>;