From 074f9c967ba0ee368d8bd3ebe48d15e525dea67a Mon Sep 17 00:00:00 2001 From: Dmitry Malakhov Date: Fri, 4 Jan 2019 11:16:30 -0800 Subject: [PATCH 1/4] Undepricate fetchTerrainTexture func --- MapboxSceneKit/TerrainNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MapboxSceneKit/TerrainNode.swift b/MapboxSceneKit/TerrainNode.swift index b0075a1..e929a6a 100644 --- a/MapboxSceneKit/TerrainNode.swift +++ b/MapboxSceneKit/TerrainNode.swift @@ -251,7 +251,7 @@ open class TerrainNode: SCNNode { /// - style: Mapbox style ID for given texture. /// - progress: Handler for fetch progress change. /// - completion: Handler for complete texture update. - @available(*, deprecated, message: "DEPRECATED - Please use instead fetchTerrainAndTexture.") +// @available(*, deprecated, message: "DEPRECATED - Please use instead fetchTerrainAndTexture.") @objc public func fetchTerrainTexture(_ style: String, progress: MapboxImageAPI.TileLoadProgressCallback? = nil, completion: @escaping MapboxImageAPI.TileLoadCompletion) { fetchTerrainTexture(style, zoom: terrainZoomLevel, progress: progress, completion: completion) } From 7c1f19886e019b448620d005078adc1a786d0d77 Mon Sep 17 00:00:00 2001 From: Dmitry Malakhov Date: Fri, 4 Jan 2019 13:54:06 -0800 Subject: [PATCH 2/4] Using styleZoomLevel for fetching terrain texture --- MapboxSceneKit/TerrainNode.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MapboxSceneKit/TerrainNode.swift b/MapboxSceneKit/TerrainNode.swift index e929a6a..c9d38bd 100644 --- a/MapboxSceneKit/TerrainNode.swift +++ b/MapboxSceneKit/TerrainNode.swift @@ -251,9 +251,8 @@ open class TerrainNode: SCNNode { /// - style: Mapbox style ID for given texture. /// - progress: Handler for fetch progress change. /// - completion: Handler for complete texture update. -// @available(*, deprecated, message: "DEPRECATED - Please use instead fetchTerrainAndTexture.") @objc public func fetchTerrainTexture(_ style: String, progress: MapboxImageAPI.TileLoadProgressCallback? = nil, completion: @escaping MapboxImageAPI.TileLoadCompletion) { - fetchTerrainTexture(style, zoom: terrainZoomLevel, progress: progress, completion: completion) + fetchTerrainTexture(style, zoom: styleZoomLevel, progress: progress, completion: completion) } private func fetchTerrainTexture(_ style: String, zoom: Int, progress: MapboxImageAPI.TileLoadProgressCallback? = nil, completion: @escaping MapboxImageAPI.TileLoadCompletion) { From 99dca041e41fbb998fa1872ad8648555e8a7ca2c Mon Sep 17 00:00:00 2001 From: Dmitry Malakhov Date: Wed, 13 Mar 2019 14:45:29 -0700 Subject: [PATCH 3/4] Move all pendingFetches in the main thread --- MapboxSceneKit/MapboxImageAPI.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/MapboxSceneKit/MapboxImageAPI.swift b/MapboxSceneKit/MapboxImageAPI.swift index 75c8d39..69832f7 100644 --- a/MapboxSceneKit/MapboxImageAPI.swift +++ b/MapboxSceneKit/MapboxImageAPI.swift @@ -143,7 +143,9 @@ public final class MapboxImageAPI: NSObject { imageBuilder.addTile(x: xindex, y: yindex, image: image) } }) { - self.pendingFetches[groupID]?.append(task) + group.notify(queue: DispatchQueue.main) { + self.pendingFetches[groupID]?.append(task) + } } } } @@ -223,7 +225,9 @@ public final class MapboxImageAPI: NSObject { imageBuilder.addTile(x: xindex, y: yindex, image: image) } }) { - self.pendingFetches[groupID]?.append(task) + group.notify(queue: DispatchQueue.main) { + self.pendingFetches[groupID]?.append(task) + } } } } @@ -247,7 +251,9 @@ public final class MapboxImageAPI: NSObject { for task in tasks { httpAPI.cancelRequestWithID(task) } - pendingFetches.removeValue(forKey: groupID) + DispatchQueue.main.async { + self.pendingFetches.removeValue(forKey: groupID) + } } //MARK: - Helpers From 42e8426f7a1cd07f085bf50859b425ca3a6716ce Mon Sep 17 00:00:00 2001 From: Dmitry Malakhov Date: Thu, 8 Aug 2019 17:42:40 -0700 Subject: [PATCH 4/4] fixed crash --- MapboxSceneKit/MapboxImageAPI.swift | 102 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/MapboxSceneKit/MapboxImageAPI.swift b/MapboxSceneKit/MapboxImageAPI.swift index 69832f7..5f4f9a2 100644 --- a/MapboxSceneKit/MapboxImageAPI.swift +++ b/MapboxSceneKit/MapboxImageAPI.swift @@ -4,7 +4,7 @@ import CoreLocation import MapboxMobileEvents /** -`MapboxImageAPI` provides a convenience wrapper for fetching tiles through the Mapbox web APIs. + `MapboxImageAPI` provides a convenience wrapper for fetching tiles through the Mapbox web APIs. **/ @objc(MBImageAPI) public final class MapboxImageAPI: NSObject { @@ -12,49 +12,49 @@ public final class MapboxImageAPI: NSObject { Image format for tileset fetcher, PNG uncompressed. **/ @objc static let TileImageFormatPNG = "pngraw" - + /** Image format for tileset fetcher, JPG uncompressed. **/ @objc static let TileImageFormatJPG100 = "jpg" - + /** Image format for tileset fetcher, JPG at compression 0.9. **/ @objc static let TileImageFormatJPG90 = "jpg90" - + /** Image format for tileset fetcher, JPG at compression 0.8. **/ @objc static let TileImageFormatJPG80 = "jpg80" - + /** Image format for tileset fetcher, JPG at compression 0.7. **/ @objc static let TileImageFormatJPG70 = "jpg70" - + /** In-progress callback typealias as tiles are loaded with the expected total needed and the current process as a percent. **/ public typealias TileLoadProgressCallback = (_ progress: Float, _ total: Int) -> Void - + /** Completion typealias for when tile loading is complete and the image is ready. **/ public typealias TileLoadCompletion = (_ image: UIImage?, _ error: NSError?) -> Void - + fileprivate static let tileSize = CGSize(width: 256, height: 256) fileprivate static let styleSize = CGSize(width: 256, height: 256) fileprivate var pendingFetches = [UUID: [UUID]]() public static var tileSizeWidth: Double { get { return Double(MapboxImageAPI.tileSize.width) } } - + private static let queue = DispatchQueue(label: "com.mapbox.scenekit.processing", attributes: [.concurrent]) - + private let httpAPI: MapboxHTTPAPI private var eventsManager: MMEEventsManager = MMEEventsManager.shared() - + @objc public override init() { var mapboxAccessToken: String? = nil @@ -63,40 +63,40 @@ public final class MapboxImageAPI: NSObject { let token = dict["MGLMapboxAccessToken"] as? String { mapboxAccessToken = token } - + if let mapboxAccessToken = mapboxAccessToken { eventsManager.isMetricsEnabledInSimulator = true eventsManager.isMetricsEnabledForInUsePermissions = false eventsManager.initialize(withAccessToken: mapboxAccessToken, userAgentBase: "mapbox-scenekit-ios", hostSDKVersion: String(describing: Bundle(for: MapboxImageAPI.self).object(forInfoDictionaryKey: "CFBundleShortVersionString")!)) eventsManager.disableLocationMetrics() eventsManager.sendTurnstileEvent() - + httpAPI = MapboxHTTPAPI(accessToken: mapboxAccessToken) } else { assert(false, "`accessToken` must be set in the Info.plist as `MGLMapboxAccessToken` or the `Route` passed into the `RouteController` must have the `accessToken` property set.") httpAPI = MapboxHTTPAPI(accessToken: "") } - + super.init() } - + deinit { let tasks = pendingFetches.keys for task in tasks { cancelRequestWithID(task) } } - + //MARK: - Public API - + /** Used to fetch a stitched together set of tiles from the tileset API. The tileset API can be used to fetch images representing data Mapbox datasets, such as streets-v10, terrain-rgb, and tilesets a user has uploaded through Mapbox studio. - + See: https://www.mapbox.com/api-documentation/#retrieve-tiles - + Expected formats are one of `TileImageFormatPNG`, `TileImageFormatJPG100`, `TileImageFormatJPG90`, `TileImageFormatJPG80`, `TileImageFormatJPG70`. - + Returns a UUID representing the task managing the fetching and stitching together of the tile images. Used for cancellation if needed. **/ @objc @@ -106,21 +106,21 @@ public final class MapboxImageAPI: NSObject { northEastCorner: CLLocation, format: String, progress: TileLoadProgressCallback? = nil, completion: @escaping TileLoadCompletion) -> UUID { - + let bounding = MapboxImageAPI.tiles(zoom: zoom, southWestCorner: southWestCorner, northEastCorner: northEastCorner, tileSize: MapboxImageAPI.tileSize) let imageBuilder = ImageBuilder(xs: bounding.xs.count, ys: bounding.ys.count, tileSize: MapboxImageAPI.tileSize, insets: bounding.insets) - + let group = DispatchGroup() let groupID = UUID() - pendingFetches[groupID] = [UUID]() - + var completed: Int = 0 let total = bounding.xs.count * bounding.ys.count - + DispatchQueue.main.async { + self.pendingFetches[groupID] = [UUID]() progress?(Float(0) / Float(total), total) } - + var error: FetchError? for (xindex, x) in bounding.xs.enumerated() { for (yindex, y) in bounding.ys.enumerated() { @@ -128,8 +128,8 @@ public final class MapboxImageAPI: NSObject { if let task = httpAPI.tileset(tileset, zoomLevel: zoom, xTile: x, yTile: y, format: format, completion: { image, fetchError in MapboxImageAPI.queue.async { defer { - completed += 1 DispatchQueue.main.async { + completed += 1 progress?(Float(completed) / Float(total), total) } group.leave() @@ -139,7 +139,7 @@ public final class MapboxImageAPI: NSObject { NSLog("Couldn't get image for tile {\(zoom),\(x),\(y)}") return } - + imageBuilder.addTile(x: xindex, y: yindex, image: image) } }) { @@ -149,7 +149,7 @@ public final class MapboxImageAPI: NSObject { } } } - + group.notify(queue: MapboxImageAPI.queue) { guard error == nil else { DispatchQueue.main.async { @@ -157,7 +157,7 @@ public final class MapboxImageAPI: NSObject { } return } - + //Color data gets messed up if the user expectes a PNG back but doesn't get one if format == MapboxImageAPI.TileImageFormatPNG, let image = imageBuilder.makeImage(), let png = UIImagePNGRepresentation(image), let formattedImage = UIImage(data: png) { DispatchQueue.main.async { @@ -173,16 +173,16 @@ public final class MapboxImageAPI: NSObject { } } } - + return groupID } - + /** Used to fetch a stitched together set of tiles from the style API. The style API can be used to fetch images representing user-created styles via Mapbox Studio. Styles are referenced by `username.id`. - + See: https://www.mapbox.com/api-documentation/#static - + Returns a UUID representing the task managing the fetching and stitching together of the tile images. Used for cancellation if needed. **/ @objc @@ -191,18 +191,18 @@ public final class MapboxImageAPI: NSObject { let returnedSize = MapboxImageAPI.styleSize * CGFloat(2.0) let bounding = MapboxImageAPI.tiles(zoom: zoom, southWestCorner: southWestCorner, northEastCorner: northEastCorner, tileSize: MapboxImageAPI.styleSize) let imageBuilder = ImageBuilder(xs: bounding.xs.count, ys: bounding.ys.count, tileSize: returnedSize, insets: bounding.insets * 2) - + let group = DispatchGroup() let groupID = UUID() - pendingFetches[groupID] = [UUID]() - + var completed: Int = 0 let total = bounding.xs.count * bounding.ys.count - + DispatchQueue.main.async { + self.pendingFetches[groupID] = [UUID]() progress?(Float(0) / Float(total), total) } - + var error: FetchError? for (xindex, x) in bounding.xs.enumerated() { for (yindex, y) in bounding.ys.enumerated() { @@ -210,8 +210,8 @@ public final class MapboxImageAPI: NSObject { if let task = httpAPI.style(style, zoomLevel: zoom, xTile: x, yTile: y, tileSize: returnedSize, completion: { image, fetchError in MapboxImageAPI.queue.async { defer { - completed += 1 DispatchQueue.main.async { + completed += 1 progress?(Float(completed) / Float(total), total) } group.leave() @@ -221,7 +221,7 @@ public final class MapboxImageAPI: NSObject { NSLog("Couldn't get image for tile {\(zoom),\(x),\(y)}") return } - + imageBuilder.addTile(x: xindex, y: yindex, image: image) } }) { @@ -231,15 +231,15 @@ public final class MapboxImageAPI: NSObject { } } } - + group.notify(queue: DispatchQueue.main) { self.pendingFetches.removeValue(forKey: groupID) completion(error == nil ? imageBuilder.makeImage() : nil, error?.toNSError()) } - + return groupID } - + /** Used to cancel an in-progress tile fetch request given the UUID returned from the intiial call. **/ @@ -251,13 +251,11 @@ public final class MapboxImageAPI: NSObject { for task in tasks { httpAPI.cancelRequestWithID(task) } - DispatchQueue.main.async { - self.pendingFetches.removeValue(forKey: groupID) - } + pendingFetches.removeValue(forKey: groupID) } - + //MARK: - Helpers - + internal static func tiles(zoom: Int, southWestCorner: CLLocation, northEastCorner: CLLocation, tileSize: CGSize) -> (xs: [Int], ys: [Int], insets: UIEdgeInsets) { let minLat = southWestCorner.coordinate.latitude @@ -268,13 +266,13 @@ public final class MapboxImageAPI: NSObject { var xs = [Int]() var ys = [Int]() var insets = UIEdgeInsets.zero - + for lat in [minLat, maxLat] { for lon in [minLon, maxLon] { let tile = Math.latLng2tile(lat: lat, lon: lon, zoom: zoom, tileSize: tileSize) xs.append(tile.xTile) ys.append(tile.yTile) - + if lat == minLat { insets = UIEdgeInsets(top: insets.top, left: insets.left, bottom: tileSize.height - CGFloat(tile.yPos), right: insets.right) } @@ -289,7 +287,7 @@ public final class MapboxImageAPI: NSObject { } } } - + return ((xs.min()!...xs.max()!).map({ $0 }), (ys.min()!...ys.max()!).map({ $0 }), insets) } }