From 915426e961591e223d8d56b24f359d8c8e13cea5 Mon Sep 17 00:00:00 2001 From: Kevin Mann Date: Thu, 19 Sep 2024 22:48:54 -0600 Subject: [PATCH] feat: add support for offline downloading of tilesets --- .../rnmbx/modules/RNMBXOfflineModule.kt | 23 ++++++++++++++---- example/src/examples/Map/OfflineExample.tsx | 5 ++++ ios/RNMBX/Offline/RNMBXOfflineModule.swift | 24 +++++++++++++++---- .../offline/OfflineCreatePackOptions.ts | 3 +++ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/modules/RNMBXOfflineModule.kt b/android/src/main/java/com/rnmapbox/rnmbx/modules/RNMBXOfflineModule.kt index bbc97f895..4da193088 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/modules/RNMBXOfflineModule.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/modules/RNMBXOfflineModule.kt @@ -51,6 +51,7 @@ class TileRegionPack(var name: String, var state: TileRegionPackState = TileRegi // stored in metadata for resume functionality var styleURI: String? = null + var tilesets: List = emptyList() var bounds: Geometry? = null var zoomRange: ZoomRange? = null @@ -79,10 +80,11 @@ class TileRegionPack(var name: String, var state: TileRegionPackState = TileRegi name: String, state: TileRegionPackState = TileRegionPackState.UNKNOWN, styleURI: String, + tilesets: List, bounds: Geometry, zoomRange: ZoomRange, metadata: JSONObject - ) : this(name= name, state= state,progress= null, metadata= metadata) { + ) : this(name= name, state= state, progress= null, metadata= metadata) { val rnmeta = JSONObject() rnmeta.put("styleURI", styleURI) this.styleURI = styleURI @@ -90,6 +92,8 @@ class TileRegionPack(var name: String, var state: TileRegionPackState = TileRegi this.bounds = bounds rnmeta.put("zoomRange", JSONArray(arrayOf(zoomRange.minZoom, zoomRange.maxZoom))) this.zoomRange = zoomRange + rnmeta.put("tilesets", JSONArray(tilesets)) + this.tilesets = tilesets this.metadata.put(RNMapboxInfoMetadataKey, rnmeta); } } @@ -143,9 +147,11 @@ class RNMBXOfflineModule(private val mReactContext: ReactApplicationContext) : val boundsFC = FeatureCollection.fromJson(boundsStr) val bounds = convertPointPairToBounds(boundsFC) + val tilesets = options.getArray("tilesets")?.toArrayList()?.map { it as String } ?: emptyList() val actPack = TileRegionPack( name = id, styleURI = options.getString("styleURL")!!, + tilesets = tilesets, bounds = bounds, zoomRange = ZoomRange( minZoom = options.getInt("minZoom").toByte(), @@ -293,11 +299,20 @@ class RNMBXOfflineModule(private val mReactContext: ReactApplicationContext) : .stylePackOptions(stylePackOptions) .pixelRatio(2.0f) .build() - val tilesetDescriptor = offlineManager.createTilesetDescriptor(descriptorOptions) - + val descriptor = offlineManager.createTilesetDescriptor(descriptorOptions) + val tilesetDescriptorOptions = TilesetDescriptorOptionsForTilesets.Builder() + .tilesets(pack.tilesets) + .minZoom(zoomRange.minZoom) + .maxZoom(zoomRange.maxZoom) + .build() + val tilesetDescriptor = offlineManager.createTilesetDescriptor(tilesetDescriptorOptions) + val descriptors = arrayListOf(descriptor) + if (pack.tilesets.isNotEmpty()) { + descriptors.add(tilesetDescriptor) + } val loadOptions = TileRegionLoadOptions.Builder() .geometry(bounds) - .descriptors(arrayListOf(tilesetDescriptor)) + .descriptors(descriptors) .metadata(metadata.toMapboxValue()) .acceptExpired(true) .networkRestriction(NetworkRestriction.NONE) diff --git a/example/src/examples/Map/OfflineExample.tsx b/example/src/examples/Map/OfflineExample.tsx index e05cface8..adfd51c87 100644 --- a/example/src/examples/Map/OfflineExample.tsx +++ b/example/src/examples/Map/OfflineExample.tsx @@ -106,6 +106,11 @@ const OfflineExample = () => { const options = { name: packName, styleURL: STYLE_URL, + tilesets: [ + 'mapbox://mapbox.mapbox-streets-v8', + 'mapbox://mapbox.mapbox-terrain-dem-v1', + 'mapbox://mapbox.country-boundaries-v1', + ], // Any tilesets that should be included in the offline download bounds: [ [bounds[0], bounds[1]], [bounds[2], bounds[3]], diff --git a/ios/RNMBX/Offline/RNMBXOfflineModule.swift b/ios/RNMBX/Offline/RNMBXOfflineModule.swift index e305343a7..47ee77da8 100644 --- a/ios/RNMBX/Offline/RNMBXOfflineModule.swift +++ b/ios/RNMBX/Offline/RNMBXOfflineModule.swift @@ -60,6 +60,9 @@ class RNMBXOfflineModule: RCTEventEmitter { self.state = state if let rnMetadata = metadata[RNMapboxInfoMetadataKey] as? [String:Any] { + if let tilesets = rnMetadata["tilesets"] as? [String] { + self.tilesets = tilesets + } if let styleURI = rnMetadata["styleURI"] as? String { self.styleURI = StyleURI(rawValue: styleURI) } @@ -82,20 +85,22 @@ class RNMBXOfflineModule: RCTEventEmitter { state: State = .unknown, styleURI: StyleURI, bounds: Geometry, + tilesets: [String], zoomRange: ClosedRange, metadata: [String:Any]) { self.name = name self.progress = nil self.cancelable = nil self.state = state - self.styleURI = styleURI self.bounds = bounds self.zoomRange = zoomRange + self.tilesets = tilesets var metadata = metadata metadata[RNMapboxInfoMetadataKey] = [ "styleURI": styleURI.rawValue, + "tilesets": logged("RNMBXOfflineModule.TileRegionPack: cannot encode tilesets") { try JSONSerialization.jsonObject(with: try! JSONEncoder().encode(tilesets)) }, "bounds": logged("RNMBXOfflineModule.TileRegionPack: cannot encode bounds") { try JSONSerialization.jsonObject(with: try! JSONEncoder().encode(bounds)) }, "zoomRange": logged("RNMBXOfflineModule.TileRegionPack: cannot encode zoomRange") { try JSONSerialization.jsonObject(with: try! JSONEncoder().encode(zoomRange))} ] @@ -109,6 +114,7 @@ class RNMBXOfflineModule: RCTEventEmitter { var metadata : [String:Any] // Stored in metadata for resume functionality: + var tilesets : [String] = [] var bounds: Geometry? = nil var zoomRange: ClosedRange? = nil var styleURI: StyleURI? = nil @@ -177,9 +183,11 @@ class RNMBXOfflineModule: RCTEventEmitter { name: id, styleURI: StyleURI(rawValue: options["styleURL"] as! String)!, bounds: bounds, + tilesets: options["tilesets"] as? [String] ?? [], zoomRange: (options["minZoom"] as! NSNumber).uint8Value...(options["maxZoom"] as! NSNumber).uint8Value, metadata: metadata ) + self.tileRegionPacks[id] = actPack self.startLoading(pack: actPack) @@ -362,21 +370,29 @@ class RNMBXOfflineModule: RCTEventEmitter { let descriptorOptions = TilesetDescriptorOptions( styleURI: styleURI, zoomRange: zoomRange, - tilesets: [], // RNMBX_11_TODO + tilesets: pack.tilesets, stylePackOptions: stylePackLoadOptions ) + let tilesetDescriptor = self.offlineManager.createTilesetDescriptor(for: descriptorOptions) + let descriptors = [tilesetDescriptor] #else let descriptorOptions = TilesetDescriptorOptions( styleURI: styleURI, zoomRange: zoomRange, stylePackOptions: stylePackLoadOptions ) + let descriptor = self.offlineManager.createTilesetDescriptor(for: descriptorOptions) + let tilesetDescriptorOptions = TilesetDescriptorOptionsForTilesets(tilesets: pack.tilesets, zoomRange: zoomRange) + let tilesetDescriptor = self.offlineManager.createTilesetDescriptorForTilesetDescriptorOptions(tilesetDescriptorOptions) + var descriptors = [descriptor] + if (!pack.tilesets.isEmpty) { + descriptors.append(tilesetDescriptor) + } #endif - let tilesetDescriptor = self.offlineManager.createTilesetDescriptor(for: descriptorOptions) let loadOptions = TileRegionLoadOptions( geometry: bounds, // RNMBXFeatureUtils.geometryToGeometry(bounds), - descriptors: [tilesetDescriptor], + descriptors: descriptors, metadata: metadata, acceptExpired: true, networkRestriction: .none, diff --git a/src/modules/offline/OfflineCreatePackOptions.ts b/src/modules/offline/OfflineCreatePackOptions.ts index 65863c2e9..2c39e617c 100644 --- a/src/modules/offline/OfflineCreatePackOptions.ts +++ b/src/modules/offline/OfflineCreatePackOptions.ts @@ -5,6 +5,7 @@ export type OfflineCreatePackOptionsArgs = { name: string; styleURL: string; bounds: [GeoJSON.Position, GeoJSON.Position]; + tilesets?: string[]; minZoom?: number; maxZoom?: number; metadata?: Record; @@ -13,6 +14,7 @@ export type OfflineCreatePackOptionsArgs = { class OfflineCreatePackOptions { public readonly name: string; public readonly styleURL: string; + public readonly tilesets: string[] | undefined; public readonly bounds: string; public readonly minZoom: number | undefined; public readonly maxZoom: number | undefined; @@ -27,6 +29,7 @@ class OfflineCreatePackOptions { this.minZoom = options.minZoom; this.maxZoom = options.maxZoom; this.metadata = this._makeMetadata(options.metadata); + this.tilesets = options.tilesets; } _assert(options: OfflineCreatePackOptionsArgs) {