Skip to content

Commit

Permalink
Support Base and Height alignment in FillExtrudeLayer; zOffset for Fi…
Browse files Browse the repository at this point in the history
…llLayer and Polygon Annotations (#2351)
  • Loading branch information
OdNairy authored Nov 12, 2024
1 parent 1cf9515 commit b7ae0d1
Show file tree
Hide file tree
Showing 20 changed files with 512 additions and 39 deletions.
189 changes: 152 additions & 37 deletions Apps/Examples/Examples/All Examples/BuildingExtrusionsExample.swift
Original file line number Diff line number Diff line change
@@ -1,43 +1,91 @@
import UIKit
@_spi(Experimental) import MapboxMaps

final class BuildingExtrusionsExample: UIViewController, ExampleProtocol {
private var cancelables = Set<AnyCancelable>()

private lazy var lightPositionButton: UIButton = {
let button = UIButton(type: .system)
extension UIButton {
static func exampleActionButton() -> UIButton {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .systemBlue
button.tintColor = .white
button.layer.cornerRadius = 4
button.clipsToBounds = true
button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
if #available(iOS 13.0, *) {
button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
return button
}
}

final class BuildingExtrusionsExample: UIViewController, ExampleProtocol {
private var cancelables = Set<AnyCancelable>()

private lazy var lightPositionButton: UIButton = {
let button = UIButton.exampleActionButton()
if #available(iOS 13.1, *) {
button.setImage(UIImage(systemName: "flashlight.on.fill"), for: .normal)
} else {
button.setTitle("Position", for: .normal)
}
button.addTarget(self, action: #selector(lightPositionButtonTapped(_:)), for: .touchUpInside)
button.addTarget(self, action: #selector(lightPositionButtonTapped(_:)), for: .primaryActionTriggered)
return button
}()

private lazy var lightColorButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .systemBlue
button.tintColor = .white
button.layer.cornerRadius = 4
button.clipsToBounds = true
button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
let button = UIButton.exampleActionButton()
if #available(iOS 13.0, *) {
button.setImage(UIImage(systemName: "paintbrush.fill"), for: .normal)
} else {
button.setTitle("Color", for: .normal)
}
button.addTarget(self, action: #selector(lightColorButtonTapped(_:)), for: .touchUpInside)
button.addTarget(self, action: #selector(lightColorButtonTapped(_:)), for: .primaryActionTriggered)
return button
}()

private lazy var heightAlignmentButton: UIButton = {
let button = UIButton.exampleActionButton()

if #available(iOS 15.0, *) {
button.setImage(UIImage(systemName: "align.vertical.top"), for: .normal)
button.setImage(UIImage(systemName: "align.vertical.top.fill"), for: .selected)
} else {
button.setTitle("Height Alignment", for: .normal)
}
button.addTarget(self, action: #selector(heightAlignmentButtonTapped(_:)), for: .primaryActionTriggered)
return button
}()

private lazy var baseAlignmentButton: UIButton = {
let button = UIButton.exampleActionButton()

if #available(iOS 15.0, *) {
button.setImage(UIImage(systemName: "align.vertical.bottom"), for: .normal)
button.setImage(UIImage(systemName: "align.vertical.bottom.fill"), for: .selected)
} else {
button.setTitle("Height Alignment", for: .normal)
}
button.addTarget(self, action: #selector(baseAlignmentButtonTapped(_:)), for: .primaryActionTriggered)
return button
}()

private lazy var terrainSwitchButton: UIButton = {
let button = UIButton.exampleActionButton()
if #available(iOS 15.0, *) {
button.setImage(UIImage(systemName: "mountain.2"), for: .normal)
button.setImage(UIImage(systemName: "mountain.2.fill"), for: .selected)
} else {
button.setTitle("Terrain", for: .normal)
}

button.addTarget(self, action: #selector(terrainButtonTapped(_:)), for: .primaryActionTriggered)
return button
}()

lazy var buttons = [
heightAlignmentButton,
baseAlignmentButton,
lightPositionButton,
lightColorButton,
terrainSwitchButton
]

private var ambientLight: AmbientLight = {
var light = AmbientLight()
light.color = .constant(StyleColor(.blue))
Expand Down Expand Up @@ -68,21 +116,27 @@ final class BuildingExtrusionsExample: UIViewController, ExampleProtocol {
self.setupExample()
}.store(in: &cancelables)

view.addSubview(lightPositionButton)
view.addSubview(lightColorButton)

buttons.forEach(view.addSubview(_:))
terrainSwitchButton.isSelected = isTerrainEnabled

let accessoryButtonsStackView = UIStackView(arrangedSubviews: buttons)
accessoryButtonsStackView.axis = .vertical
accessoryButtonsStackView.spacing = 20
accessoryButtonsStackView.translatesAutoresizingMaskIntoConstraints = false

view.addSubview(accessoryButtonsStackView)

NSLayoutConstraint.activate([
view.trailingAnchor.constraint(equalToSystemSpacingAfter: lightPositionButton.trailingAnchor, multiplier: 1),
view.bottomAnchor.constraint(equalTo: lightPositionButton.bottomAnchor, constant: 100),
view.trailingAnchor.constraint(equalToSystemSpacingAfter: lightColorButton.trailingAnchor, multiplier: 1),
lightPositionButton.topAnchor.constraint(equalToSystemSpacingBelow: lightColorButton.bottomAnchor, multiplier: 1),
lightColorButton.widthAnchor.constraint(equalTo: lightPositionButton.widthAnchor),
lightColorButton.heightAnchor.constraint(equalTo: lightPositionButton.heightAnchor)
mapView.ornaments.attributionButton.topAnchor.constraint(equalToSystemSpacingBelow: accessoryButtonsStackView.bottomAnchor, multiplier: 1),
view.trailingAnchor
.constraint(equalToSystemSpacingAfter: accessoryButtonsStackView.trailingAnchor, multiplier: 1)
])
}

internal func setupExample() {
addBuildingExtrusions()
try! addTerrain()
try! addBuildingExtrusions()

let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 40.7135, longitude: -74.0066),
zoom: 15.5,
Expand All @@ -97,18 +151,20 @@ final class BuildingExtrusionsExample: UIViewController, ExampleProtocol {
}

// See https://docs.mapbox.com/mapbox-gl-js/example/3d-buildings/ for equivalent gl-js example
internal func addBuildingExtrusions() {
internal func addBuildingExtrusions() throws {
let wallOnlyThreshold = 20
let extrudeFilter = Exp(.eq) {
Exp(.get) { "extrude" }
"true"
}
var layer = FillExtrusionLayer(id: "3d-buildings", source: "composite")
.minZoom(15)
.sourceLayer("building")
.fillExtrusionColor(.lightGray)
.fillExtrusionOpacity(0.8)
.fillExtrusionAmbientOcclusionIntensity(0.3)
.fillExtrusionAmbientOcclusionRadius(3.0)

layer.minZoom = 15
layer.sourceLayer = "building"
layer.fillExtrusionColor = .constant(StyleColor(.lightGray))
layer.fillExtrusionOpacity = .constant(0.6)

layer.filter = Exp(.all) {
extrudeFilter
Expand Down Expand Up @@ -141,13 +197,10 @@ final class BuildingExtrusionsExample: UIViewController, ExampleProtocol {
}
)

layer.fillExtrusionAmbientOcclusionIntensity = .constant(0.3)

layer.fillExtrusionAmbientOcclusionRadius = .constant(3.0)

try! mapView.mapboxMap.addLayer(layer)
try mapView.mapboxMap.addLayer(layer)

var wallsOnlyExtrusionLayer = layer
.fillExtrusionLineWidth(2)
wallsOnlyExtrusionLayer.id = "3d-buildings-wall"
wallsOnlyExtrusionLayer.filter = Exp(.all) {
extrudeFilter
Expand All @@ -157,13 +210,75 @@ final class BuildingExtrusionsExample: UIViewController, ExampleProtocol {
}
}

wallsOnlyExtrusionLayer.fillExtrusionLineWidth = .constant(2)
try mapView.mapboxMap.addLayer(wallsOnlyExtrusionLayer)
}

try! mapView.mapboxMap.addLayer(wallsOnlyExtrusionLayer)
func addTerrain() throws {
let terrainSourceID = "mapbox-dem"

if !mapView.mapboxMap.sourceExists(withId: terrainSourceID) {
try addTerrainSource(id: terrainSourceID)
}

try mapView.mapboxMap.setTerrain(Terrain(sourceId: terrainSourceID)
.exaggeration(1.5))
}

func addTerrainSource(id: String) throws {
var demSource = RasterDemSource(id: id)
demSource.url = "mapbox://mapbox.mapbox-terrain-dem-v1"
// Setting the `tileSize` to 514 provides better performance and adds padding around the outside
// of the tiles.
demSource.tileSize = 514
demSource.maxzoom = 14.0
try mapView.mapboxMap.addSource(demSource)
}

// MARK: - Actions

var isTerrainEnabled = true

@objc private func terrainButtonTapped(_ sender: UIButton) {
if isTerrainEnabled {
mapView.mapboxMap.removeTerrain()
} else {
try! addTerrain()
}

isTerrainEnabled.toggle()
sender.isSelected = isTerrainEnabled
}

var baseAlignment: FillExtrusionBaseAlignment = .flat
var heightAlignment: FillExtrusionHeightAlignment = .flat

@objc private func baseAlignmentButtonTapped(_ sender: UIButton) {
if baseAlignment == .flat {
baseAlignment = .terrain
} else {
baseAlignment = .flat
}
sender.backgroundColor = .systemBlue
sender.isSelected = baseAlignment == .terrain

try! mapView.mapboxMap.updateLayer(withId: "3d-buildings", type: FillExtrusionLayer.self) { layer in
layer.fillExtrusionBaseAlignment = .constant(baseAlignment)
}
}

@objc private func heightAlignmentButtonTapped(_ sender: UIButton) {
if heightAlignment == .flat {
heightAlignment = .terrain
} else {
heightAlignment = .flat
}
sender.isSelected = heightAlignment == .terrain

try! mapView.mapboxMap.updateLayer(withId: "3d-buildings", type: FillExtrusionLayer.self) { layer in
layer.fillExtrusionHeightAlignment = .constant(heightAlignment)
}
}

@objc private func lightColorButtonTapped(_ sender: UIButton) {
if case .constant(let color) = ambientLight.color, color == StyleColor(.red) {
ambientLight.color = .constant(StyleColor(.blue))
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Mapbox welcomes participation and contributions from everyone.
## main

* Add two separete Geofence examples in SwiftUI - `GeofencingPlayground` and `GeofencingUserLocation`
* Add support for Base and Height alignment in FillExtrusionLayer.
* Add support for `pitchAlignment` in BackgroundLayer.
* Add support for `zOffset` in FillLayer, PolygonAnnotation[Manager] and PolygonAnnotationGroup.

## 11.8.0-rc.1 - 23 October, 2024

Expand Down
11 changes: 11 additions & 0 deletions Sources/MapboxMaps/Annotations/Generated/PolygonAnnotation.swift

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
- ``TransitionOptions-struct``
- ``ModelScaleMode``
- ``SymbolElevationReference``
- ``FillExtrusionBaseAlignment``
- ``FillExtrusionHeightAlignment``
- ``BackgroundPitchAlignment``

<!-- Next two are arguable regarding it's category -->
- ``ImageContent``
Expand Down
19 changes: 19 additions & 0 deletions Sources/MapboxMaps/Style/Generated/Layers/BackgroundLayer.swift

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b7ae0d1

Please sign in to comment.