Skip to content

Enable layer insertion below symbols #70

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Version 0.7.0 - 2025-02-02

### Added

- Adds support for a `belowSymbols` layer order. The `renderBelowSymbols` modifier on layers will insert the new layer below the first symbol layer in the style. This allows for rendering below labels and icons.
- BREAKING: Refactored the layer position modifiers to accept enum variants to enable better extensibility.

### Fixed

- Moved modifiers on `StyleLayer` to `StyleLayerDefinition`. The previous extension of `StyleLayer` was a mistake, since `StyleLayerDefinition` is the supertype, and none of the behavior was specific to `StyleLayer`.

## Version 0.6.0 - 2025-01-14

- Potentially BREAKING: Upgrades Mockable to 0.2.0. If you're using mockable in your project, this may require you to upgrade there as well.
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftDSL/Style Layers/Background.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import MapLibreSwiftMacros
@MLNStyleProperty<Float>("backgroundOpacity", supportsInterpolation: true)
public struct BackgroundLayer: StyleLayer {
public let identifier: String
public var insertionPosition: LayerInsertionPosition = .belowOthers
public var insertionPosition: LayerInsertionPosition = .below(.all)
public var isVisible: Bool = true
public var maximumZoomLevel: Float? = nil
public var minimumZoomLevel: Float? = nil
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftDSL/Style Layers/Circle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import MapLibreSwiftMacros
public struct CircleStyleLayer: SourceBoundVectorStyleLayerDefinition {
public let identifier: String
public let sourceLayerIdentifier: String?
public var insertionPosition: LayerInsertionPosition = .aboveOthers
public var insertionPosition: LayerInsertionPosition = .above(.all)
public var isVisible: Bool = true
public var maximumZoomLevel: Float? = nil
public var minimumZoomLevel: Float? = nil
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftDSL/Style Layers/FillStyleLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import MapLibreSwiftMacros
public struct FillStyleLayer: SourceBoundVectorStyleLayerDefinition {
public let identifier: String
public let sourceLayerIdentifier: String?
public var insertionPosition: LayerInsertionPosition = .aboveOthers
public var insertionPosition: LayerInsertionPosition = .above(.all)
public var isVisible: Bool = true
public var maximumZoomLevel: Float? = nil
public var minimumZoomLevel: Float? = nil
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftDSL/Style Layers/Line.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import MapLibreSwiftMacros
public struct LineStyleLayer: SourceBoundVectorStyleLayerDefinition {
public let identifier: String
public let sourceLayerIdentifier: String?
public var insertionPosition: LayerInsertionPosition = .aboveOthers
public var insertionPosition: LayerInsertionPosition = .above(.all)
public var isVisible: Bool = true
public var maximumZoomLevel: Float? = nil
public var minimumZoomLevel: Float? = nil
Expand Down
54 changes: 25 additions & 29 deletions Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
import InternalUtils
import MapLibre

/// A layer reference specifying which layer we should insert a new layer above.
public enum LayerReferenceAbove: Equatable {
/// A specific layer, referenced by ID.
case layer(layerId: String)
/// The group of all layers currently in the style.
case all
}

/// A layer reference specifying which layer we should insert a new layer below.
public enum LayerReferenceBelow: Equatable {
/// A specific layer, referenced by ID.
case layer(layerId: String)
/// The group of all layers currently in the style.
case all
/// The group of symbol layers currently in the style.
case symbols
}

/// Specifies a preference for where the layer should be inserted in the hierarchy.
public enum LayerInsertionPosition: Equatable {
/// The layer should be inserted above the layer with ID ``layerID``.
///
/// If no such layer exists, the layer will be added above others and an error will be logged.
case above(layerID: String)
case above(LayerReferenceAbove)
/// The layer should be inserted below the layer with ID ``layerID``.
///
/// If no such layer exists, the layer will be added above others and an error will be logged.
case below(layerID: String)
/// The layer should be inserted above other existing layers.
case aboveOthers
/// The layer should be inserted below other existing layers.
case belowOthers
case below(LayerReferenceBelow)
}

/// Internal style enum that wraps a source reference.
Expand Down Expand Up @@ -132,7 +146,7 @@ public extension StyleLayer {
}
}

public extension StyleLayer {
public extension StyleLayerDefinition {
// MARK: - Common modifiers

func visible(_ value: Bool) -> Self {
Expand All @@ -147,29 +161,11 @@ public extension StyleLayer {
modified(self) { $0.maximumZoomLevel = value }
}

func renderAbove(_ layerID: String) -> Self {
modified(self) { $0.insertionPosition = .above(layerID: layerID) }
}

func renderBelow(_ layerID: String) -> Self {
modified(self) { $0.insertionPosition = .below(layerID: layerID) }
}

func renderAboveOthers() -> Self {
modified(self) { $0.insertionPosition = .aboveOthers }
func renderAbove(_ layerReference: LayerReferenceAbove) -> Self {
modified(self) { $0.insertionPosition = .above(layerReference) }
}

func renderBelowOthers() -> Self {
modified(self) { $0.insertionPosition = .belowOthers }
}
}

public extension StyleLayerDefinition {
func minimumZoomLevel(_ value: Float) -> Self {
modified(self) { $0.minimumZoomLevel = value }
}

func maximumZoomLevel(_ value: Float) -> Self {
modified(self) { $0.maximumZoomLevel = value }
func renderBelow(_ layerReference: LayerReferenceBelow) -> Self {
modified(self) { $0.insertionPosition = .below(layerReference) }
}
}
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftDSL/Style Layers/Symbol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import MapLibreSwiftMacros
public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition {
public let identifier: String
public let sourceLayerIdentifier: String?
public var insertionPosition: LayerInsertionPosition = .aboveOthers
public var insertionPosition: LayerInsertionPosition = .above(.all)
public var isVisible: Bool = true
public var maximumZoomLevel: Float? = nil
public var minimumZoomLevel: Float? = nil
Expand Down
2 changes: 1 addition & 1 deletion Sources/MapLibreSwiftUI/Examples/Layers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ let clustered = ShapeSource(identifier: "points", options: [.clustered: true, .c
// Silly example: a background layer on top of everything to create a tint effect
BackgroundLayer(identifier: "rose-colored-glasses")
.backgroundColor(.systemPink.withAlphaComponent(0.3))
.renderAboveOthers()
.renderAbove(.all)
}
.ignoresSafeArea(.all)
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/MapLibreSwiftUI/Examples/Polyline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct PolylineMapView: View {
curveType: .exponential,
parameters: NSExpression(forConstantValue: 1.5),
stops: NSExpression(forConstantValue: [14: 6, 18: 24]))
.renderBelow(.symbols)

// Add an inner (blue) polyline
LineStyleLayer(identifier: "polyline-inner", source: polylineSource)
Expand All @@ -36,6 +37,7 @@ struct PolylineMapView: View {
curveType: .exponential,
parameters: NSExpression(forConstantValue: 1.5),
stops: NSExpression(forConstantValue: [14: 3, 18: 16]))
.renderBelow(.symbols)
}
}
}
Expand Down
20 changes: 15 additions & 5 deletions Sources/MapLibreSwiftUI/MapViewCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,11 @@
}

func addLayers(to mglStyle: MLNStyle) {
let firstSymbolLayer = mglStyle.layers.first { layer in
layer is MLNSymbolStyleLayer
}
Comment on lines +271 to +273
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Highlighting for discussion. I went for the simplest implementation here. I don't think that there should be much overhead in computing this every time, since this should be a cheap check. But it's technically not needed every time (there may not always be a style layer with the belowSymbols ordering).

If anyone is concerned about this, we could make this lazy with a bit more code.


for layerSpec in parent.userLayers {

Check warning on line 275 in Sources/MapLibreSwiftUI/MapViewCoordinator.swift

View workflow job for this annotation

GitHub Actions / platform=iOS Simulator,name=iPhone 16,OS=18.1

main actor-isolated property 'userLayers' can not be referenced from a nonisolated context; this is an error in the Swift 6 language mode
// DISCUSS: What preventions should we try to put in place against the user accidentally adding the same layer twice?
let newLayer = layerSpec.makeStyleLayer(style: mglStyle).makeMLNStyleLayer()

Expand All @@ -284,24 +288,30 @@
}

switch layerSpec.insertionPosition {
case let .above(layerID: id):
case let .above(.layer(layerId: id)):
if let layer = mglStyle.layer(withIdentifier: id) {
mglStyle.insertLayer(newLayer, above: layer)
} else {
NSLog("Failed to find layer with ID \(id). Adding layer on top.")
mglStyle.addLayer(newLayer)
}
case let .below(layerID: id):
case .above(.all):
mglStyle.addLayer(newLayer)
case let .below(.layer(layerId: id)):
if let layer = mglStyle.layer(withIdentifier: id) {
mglStyle.insertLayer(newLayer, below: layer)
} else {
NSLog("Failed to find layer with ID \(id). Adding layer on top.")
mglStyle.addLayer(newLayer)
}
case .aboveOthers:
mglStyle.addLayer(newLayer)
case .belowOthers:
case .below(.all):
mglStyle.insertLayer(newLayer, at: 0)
case .below(.symbols):
if let firstSymbolLayer {
mglStyle.insertLayer(newLayer, below: firstSymbolLayer)
} else {
mglStyle.addLayer(newLayer)
}
}
}
}
Expand Down Expand Up @@ -366,7 +376,7 @@
public func mapView(_ mapView: MLNMapView, regionDidChangeWith reason: MLNCameraChangeReason, animated _: Bool) {
// TODO: We could put this in regionIsChangingWith if we calculate significant change/debounce.
MainActor.assumeIsolated {
updateViewProxy(mapView: mapView, reason: reason)

Check warning on line 379 in Sources/MapLibreSwiftUI/MapViewCoordinator.swift

View workflow job for this annotation

GitHub Actions / platform=iOS Simulator,name=iPhone 16,OS=18.1

sending 'self' risks causing data races; this is an error in the Swift 6 language mode

guard !suppressCameraUpdatePropagation else {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ final class LayerPreviewTests: XCTestCase {
// Silly example: a background layer on top of everything to create a tint effect
BackgroundLayer(identifier: "rose-colored-glasses")
.backgroundColor(.systemPink.withAlphaComponent(0.3))
.renderAboveOthers()
.renderAbove(.all)
}
}
}
Expand Down
Loading