diff --git a/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/AnnotationsOrderTestExample.swift b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/AnnotationsOrderTestExample.swift index ab080bf0d601..8b300e8d9abb 100644 --- a/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/AnnotationsOrderTestExample.swift +++ b/Apps/Examples/Examples/SwiftUI Examples/Testing Examples/AnnotationsOrderTestExample.swift @@ -1,4 +1,4 @@ -import MapboxMaps +@_spi(Experimental) import MapboxMaps import SwiftUI @available(iOS 14.0, *) @@ -33,7 +33,9 @@ struct AnnotationsOrderTestExample: View { circles: circles ) TestLayer(id: "black-layer", radius: 2, color: .black.darker, coordinate: .init(latitude: -10, longitude: 0)) + FadingCircle() } + .debugOptions(.camera) .mapStyle(mapStyle) .onLayerTapGesture("purple-layer") { _, context in tapMessage = gestureMessage("Purple layer", context: context) @@ -233,6 +235,21 @@ private struct TestLayer: MapStyleContent { } } +@available(iOS 13.0, *) +private struct FadingCircle: MapStyleContent { + var body: some MapStyleContent { + GeoJSONSource(id: "source-id") + .data(.geometry(makeFadingCircle())) + .autoMaxZoom(true) + + FillExtrusionLayer(id: "fill-layer-id", source: "source-id") + .fillExtrusionFloodLightGroundRadius(-400000) + .fillExtrusionColor(UIColor(red: 0, green: 0, blue: 0, alpha: 0)) + .fillExtrusionFloodLightColor(.blue) + .fillExtrusionFloodLightIntensity(0.5) + } +} + private func gestureMessage(_ label: String, context: MapContentGestureContext) -> String { let coordinate = String(format: "%.2f, %.2f", context.coordinate.latitude, context.coordinate.longitude) return "\(label) (\(coordinate))" @@ -312,3 +329,29 @@ private extension Polygon { ] ]) } + +private func makeFadingCircle( + _ center: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: -37.8, longitude: 145.004), + radius: Double = 600000, + tess: Int = 3600 +) -> Geometry { + let earthRadiusMeters = 6378137.0 + let degToRad = Double.pi / 180.0 + + let lon0 = center.longitude * degToRad + let lat0 = center.latitude * degToRad + + var coords = (0..: }`. public var promoteId: PromoteId? + /// When set to true, the maxZoom property is ignored and is instead calculated automatically based on the largest bounding box from the geoJSON features. This resolves rendering artifacts for features that use wide blur (e.g. fill extrusion ground flood light or circle layer) and would bring performance improvement on lower zoom levels, especially for geoJSON sources that update data frequently. However, it can lead to flickering and precision loss on zoom levels above 19. + /// Default value: false. + @_spi(Experimental) public var autoMaxZoom: Bool? + /// When loading a map, if PrefetchZoomDelta is set to any number greater than 0, the map will first request a tile at zoom level lower than zoom - delta, but so that the zoom level is multiple of delta, in an attempt to display a full map at lower resolution as quick as possible. It will get clamped at the tile source minimum zoom. /// Default value: 4. public var prefetchZoomDelta: Double? @@ -94,6 +98,7 @@ extension GeoJSONSource { case lineMetrics = "lineMetrics" case generateId = "generateId" case promoteId = "promoteId" + case autoMaxZoom = "autoMaxZoom" case prefetchZoomDelta = "prefetch-zoom-delta" case tileCacheBudget = "tile-cache-budget" } @@ -132,6 +137,7 @@ extension GeoJSONSource { try container.encodeIfPresent(lineMetrics, forKey: .lineMetrics) try container.encodeIfPresent(generateId, forKey: .generateId) try container.encodeIfPresent(promoteId, forKey: .promoteId) + try container.encodeIfPresent(autoMaxZoom, forKey: .autoMaxZoom) } } @@ -142,6 +148,13 @@ extension GeoJSONSource { with(self, setter(\.data, newValue)) } + /// When set to true, the maxZoom property is ignored and is instead calculated automatically based on the largest bounding box from the geoJSON features. This resolves rendering artifacts for features that use wide blur (e.g. fill extrusion ground flood light or circle layer) and would bring performance improvement on lower zoom levels, especially for geoJSON sources that update data frequently. However, it can lead to flickering and precision loss on zoom levels above 19. + /// Default value: false. + @_spi(Experimental) + public func autoMaxZoom(_ newValue: Bool) -> Self { + with(self, setter(\.autoMaxZoom, newValue)) + } + /// When loading a map, if PrefetchZoomDelta is set to any number greater than 0, the map will first request a tile at zoom level lower than zoom - delta, but so that the zoom level is multiple of delta, in an attempt to display a full map at lower resolution as quick as possible. It will get clamped at the tile source minimum zoom. /// Default value: 4. public func prefetchZoomDelta(_ newValue: Double) -> Self { diff --git a/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/GeoJSONSourceIntegrationTests.swift b/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/GeoJSONSourceIntegrationTests.swift index 4f33c86f8a88..2ba60086b94b 100644 --- a/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/GeoJSONSourceIntegrationTests.swift +++ b/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/GeoJSONSourceIntegrationTests.swift @@ -1,6 +1,6 @@ // This file is generated. import XCTest -@testable import MapboxMaps +@_spi(Experimental) @testable import MapboxMaps final class GeoJSONSourceIntegrationTests: MapViewIntegrationTestCase { @@ -27,6 +27,7 @@ final class GeoJSONSourceIntegrationTests: MapViewIntegrationTestCase { source.lineMetrics = Bool.testSourceValue() source.generateId = Bool.testSourceValue() source.promoteId = PromoteId.testSourceValue() + source.autoMaxZoom = Bool.testSourceValue() source.prefetchZoomDelta = Double.testSourceValue() source.tileCacheBudget = TileCacheBudgetSize.testSourceValue() diff --git a/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/ImageSourceIntegrationTests.swift b/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/ImageSourceIntegrationTests.swift index 2a1f3ad1d2e6..9a3bcd955789 100644 --- a/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/ImageSourceIntegrationTests.swift +++ b/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/ImageSourceIntegrationTests.swift @@ -1,6 +1,6 @@ // This file is generated. import XCTest -@testable import MapboxMaps +@_spi(Experimental) @testable import MapboxMaps final class ImageSourceIntegrationTests: MapViewIntegrationTestCase { diff --git a/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/RasterDemSourceIntegrationTests.swift b/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/RasterDemSourceIntegrationTests.swift index 84571cc08705..f312100e4f97 100644 --- a/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/RasterDemSourceIntegrationTests.swift +++ b/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/RasterDemSourceIntegrationTests.swift @@ -1,6 +1,6 @@ // This file is generated. import XCTest -@testable import MapboxMaps +@_spi(Experimental) @testable import MapboxMaps final class RasterDemSourceIntegrationTests: MapViewIntegrationTestCase { diff --git a/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/RasterSourceIntegrationTests.swift b/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/RasterSourceIntegrationTests.swift index 88a1a12c16b5..68d7ddbbe73f 100644 --- a/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/RasterSourceIntegrationTests.swift +++ b/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/RasterSourceIntegrationTests.swift @@ -1,6 +1,6 @@ // This file is generated. import XCTest -@testable import MapboxMaps +@_spi(Experimental) @testable import MapboxMaps final class RasterSourceIntegrationTests: MapViewIntegrationTestCase { diff --git a/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/VectorSourceIntegrationTests.swift b/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/VectorSourceIntegrationTests.swift index 33384e8a01dc..60688b3eb35f 100644 --- a/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/VectorSourceIntegrationTests.swift +++ b/Tests/MapboxMapsTests/Style/Generated/IntegrationTests/Sources/VectorSourceIntegrationTests.swift @@ -1,6 +1,6 @@ // This file is generated. import XCTest -@testable import MapboxMaps +@_spi(Experimental) @testable import MapboxMaps final class VectorSourceIntegrationTests: MapViewIntegrationTestCase { diff --git a/Tests/MapboxMapsTests/Style/Generated/Sources/GeoJSONSourceTests.swift b/Tests/MapboxMapsTests/Style/Generated/Sources/GeoJSONSourceTests.swift index 22b27e6b7808..2d8e5ab650c8 100644 --- a/Tests/MapboxMapsTests/Style/Generated/Sources/GeoJSONSourceTests.swift +++ b/Tests/MapboxMapsTests/Style/Generated/Sources/GeoJSONSourceTests.swift @@ -19,6 +19,7 @@ final class GeoJSONSourceTests: XCTestCase { source.lineMetrics = Bool.testSourceValue() source.generateId = Bool.testSourceValue() source.promoteId = PromoteId.testSourceValue() + source.autoMaxZoom = Bool.testSourceValue() source.prefetchZoomDelta = Double.testSourceValue() source.tileCacheBudget = TileCacheBudgetSize.testSourceValue() @@ -50,6 +51,7 @@ final class GeoJSONSourceTests: XCTestCase { XCTAssert(decodedSource.lineMetrics == Bool.testSourceValue()) XCTAssert(decodedSource.generateId == Bool.testSourceValue()) XCTAssert(decodedSource.promoteId == PromoteId.testSourceValue()) + XCTAssert(decodedSource.autoMaxZoom == Bool.testSourceValue()) XCTAssert(decodedSource.prefetchZoomDelta == Double.testSourceValue()) XCTAssert(decodedSource.tileCacheBudget == TileCacheBudgetSize.testSourceValue()) } catch { @@ -60,10 +62,12 @@ final class GeoJSONSourceTests: XCTestCase { func testSetPropertyValueWithFunction() { let source = GeoJSONSource(id: "test-source") .data(GeoJSONSourceData.testSourceValue()) + .autoMaxZoom(Bool.testSourceValue()) .prefetchZoomDelta(Double.testSourceValue()) .tileCacheBudget(TileCacheBudgetSize.testSourceValue()) XCTAssertEqual(source.data, GeoJSONSourceData.testSourceValue()) + XCTAssertEqual(source.autoMaxZoom, Bool.testSourceValue()) XCTAssertEqual(source.prefetchZoomDelta, Double.testSourceValue()) XCTAssertEqual(source.tileCacheBudget, TileCacheBudgetSize.testSourceValue()) }