Skip to content
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
35 changes: 35 additions & 0 deletions Sources/MapTilerSDK/Commands/Style/AddLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ package struct AddLayer: MTCommand {
return handleMTCircleLayer(layer)
} else if let layer = layer as? MTHeatmapLayer {
return handleMTHeatmapLayer(layer)
} else if let layer = layer as? MTFillExtrusionLayer {
return handleMTFillExtrusionLayer(layer)
}

return emptyReturnValue
Expand Down Expand Up @@ -121,6 +123,19 @@ package struct AddLayer: MTCommand {
}
return js
}

private func handleMTFillExtrusionLayer(_ layer: MTFillExtrusionLayer) -> JSString {
guard let layerString: JSString = layer.toJSON() else {
return emptyReturnValue
}

let processed = unquoteExpressions(in: layerString)
var js = "\(MTBridge.mapObject).addLayer(\(processed));"
if let filter = layer.initialFilter {
js.append("\n \(MTBridge.mapObject).setFilter('\(layer.identifier)', \(filter.toJS()));")
}
return js
}
}
/// Replaces string-encoded expressions with raw JSON arrays.
/// Ensures the style parser reads them as expressions (not strings).
Expand Down Expand Up @@ -166,6 +181,26 @@ fileprivate func unquoteExpressions(in json: String) -> String {
with: "$1$2",
options: .regularExpression
)
s = s.replacingOccurrences(
of: #"(?s)("fill-extrusion-color"\s*:\s*)"(\[.*?\])""#,
with: "$1$2",
options: .regularExpression
)
s = s.replacingOccurrences(
of: #"(?s)("fill-extrusion-height"\s*:\s*)"(\[.*?\])""#,
with: "$1$2",
options: .regularExpression
)
s = s.replacingOccurrences(
of: #"(?s)("fill-extrusion-base"\s*:\s*)"(\[.*?\])""#,
with: "$1$2",
options: .regularExpression
)
s = s.replacingOccurrences(
of: #"(?s)("fill-extrusion-opacity"\s*:\s*)"(\[.*?\])""#,
with: "$1$2",
options: .regularExpression
)
// Unescape escaped quotes inside expression arrays (e.g., \"step\" -> "step")
s = s.replacingOccurrences(of: "\\\"", with: "\"")
return s
Expand Down
34 changes: 34 additions & 0 deletions Sources/MapTilerSDK/Commands/Style/AddLayers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ package struct AddLayers: MTCommand {
jsString.append(handleMTCircleLayer(layer))
} else if let layer = layer as? MTHeatmapLayer {
jsString.append(handleMTHeatmapLayer(layer))
} else if let layer = layer as? MTFillExtrusionLayer {
jsString.append(handleMTFillExtrusionLayer(layer))
}
}

Expand Down Expand Up @@ -121,6 +123,18 @@ package struct AddLayers: MTCommand {
}
return js
}

private func handleMTFillExtrusionLayer(_ layer: MTFillExtrusionLayer) -> JSString {
guard let layerString: JSString = layer.toJSON() else {
return emptyReturnValue
}
let processed = unquoteExpressions(in: layerString)
var js = "\(MTBridge.mapObject).addLayer(\(processed));"
if let filter = layer.initialFilter {
js.append("\n \(MTBridge.mapObject).setFilter('\(layer.identifier)', \(filter.toJS()));")
}
return js
}
}

/// Replaces string-encoded expressions with raw JSON arrays.
Expand Down Expand Up @@ -167,6 +181,26 @@ fileprivate func unquoteExpressions(in json: String) -> String {
with: "$1$2",
options: .regularExpression
)
s = s.replacingOccurrences(
of: #"(?s)("fill-extrusion-color"\s*:\s*)"(\[.*?\])""#,
with: "$1$2",
options: .regularExpression
)
s = s.replacingOccurrences(
of: #"(?s)("fill-extrusion-height"\s*:\s*)"(\[.*?\])""#,
with: "$1$2",
options: .regularExpression
)
s = s.replacingOccurrences(
of: #"(?s)("fill-extrusion-base"\s*:\s*)"(\[.*?\])""#,
with: "$1$2",
options: .regularExpression
)
s = s.replacingOccurrences(
of: #"(?s)("fill-extrusion-opacity"\s*:\s*)"(\[.*?\])""#,
with: "$1$2",
options: .regularExpression
)
s = s.replacingOccurrences(of: "\\\"", with: "\"")
return s
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//
// Copyright (c) 2026, MapTiler
// All rights reserved.
// SPDX-License-Identifier: BSD 3-Clause
//
// MTFillExtrusionLayer+DSL.swift
// MapTilerSDK
//

import UIKit

// DSL
extension MTFillExtrusionLayer {
/// Adds layer to map DSL style.
/// Prefer MTStyle.addLayer(_:) on MTMapView instead.
public func addToMap(_ mapView: MTMapView) {
Task {
let layer = MTFillExtrusionLayer(
identifier: self.identifier,
sourceIdentifier: self.sourceIdentifier,
maxZoom: self.maxZoom,
minZoom: self.minZoom,
sourceLayer: self.sourceLayer,
base: self.base,
color: self.color,
height: self.height,
opacity: self.opacity,
pattern: self.pattern,
translate: self.translate,
translateAnchor: self.translateAnchor,
verticalGradient: self.verticalGradient,
visibility: self.visibility
)
try await mapView.style?.addLayer(layer)
}
}

// MARK: - Modifiers (not to be used outside DSL)

@discardableResult
public func maxZoom(_ value: Double) -> Self {
self.maxZoom = value
return self
}

@discardableResult
public func minZoom(_ value: Double) -> Self {
self.minZoom = value
return self
}

@discardableResult
public func sourceLayer(_ value: String) -> Self {
self.sourceLayer = value
return self
}

@discardableResult
public func base(_ value: MTStyleValue) -> Self {
self.base = value
return self
}

@discardableResult
public func color(_ value: MTStyleValue) -> Self {
self.color = value
return self
}

@discardableResult
public func height(_ value: MTStyleValue) -> Self {
self.height = value
return self
}

@discardableResult
public func opacity(_ value: MTStyleValue) -> Self {
self.opacity = value
return self
}

@discardableResult
public func pattern(_ value: String) -> Self {
self.pattern = value
return self
}

@discardableResult
public func translate(_ value: [Double]) -> Self {
self.translate = value
return self
}

@discardableResult
public func translateAnchor(_ value: MTFillExtrusionTranslateAnchor) -> Self {
self.translateAnchor = value
return self
}

@discardableResult
public func verticalGradient(_ value: Bool) -> Self {
self.verticalGradient = value
return self
}

@discardableResult
public func visibility(_ value: MTLayerVisibility) -> Self {
self.visibility = value
return self
}

/// Modifier. Sets the initial filter to apply upon add.
@discardableResult
public func withFilter(_ value: MTPropertyValue) -> Self {
self.initialFilter = value
return self
}
}
Loading
Loading