Skip to content

Commit e958f2e

Browse files
authored
Merge pull request maplibre#42 from HudHud-Maps/mapstyle-pois
More SymbolLayer properties + sourceLayerIdentifier support + zoom levels + local image support
2 parents 258a8c9 + 43b2fff commit e958f2e

File tree

5 files changed

+96
-25
lines changed

5 files changed

+96
-25
lines changed

Sources/MapLibreSwiftDSL/Style Layers/Circle.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import MapLibreSwiftMacros
99
@MLNStyleProperty<UIColor>("strokeColor", supportsInterpolation: false)
1010
public struct CircleStyleLayer: SourceBoundVectorStyleLayerDefinition {
1111
public let identifier: String
12+
public let sourceLayerIdentifier: String?
1213
public var insertionPosition: LayerInsertionPosition = .aboveOthers
1314
public var isVisible: Bool = true
1415
public var maximumZoomLevel: Float? = nil
@@ -20,11 +21,13 @@ public struct CircleStyleLayer: SourceBoundVectorStyleLayerDefinition {
2021
public init(identifier: String, source: Source) {
2122
self.identifier = identifier
2223
self.source = .source(source)
24+
sourceLayerIdentifier = nil
2325
}
2426

25-
public init(identifier: String, source: MLNSource) {
27+
public init(identifier: String, source: MLNSource, sourceLayerIdentifier: String? = nil) {
2628
self.identifier = identifier
2729
self.source = .mglSource(source)
30+
self.sourceLayerIdentifier = sourceLayerIdentifier
2831
}
2932

3033
public func makeStyleLayer(style: MLNStyle) -> StyleLayer {
@@ -69,6 +72,7 @@ private struct CircleStyleLayerInternal: StyleLayer {
6972
public func makeMLNStyleLayer() -> MLNStyleLayer {
7073
let result = MLNCircleStyleLayer(identifier: identifier, source: mglSource)
7174

75+
result.sourceLayerIdentifier = definition.sourceLayerIdentifier
7276
result.circleRadius = definition.radius
7377
result.circleColor = definition.color
7478

Sources/MapLibreSwiftDSL/Style Layers/Line.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import MapLibreSwiftMacros
1010
@MLNStyleProperty<Float>("lineWidth", supportsInterpolation: true)
1111
public struct LineStyleLayer: SourceBoundVectorStyleLayerDefinition {
1212
public let identifier: String
13+
public let sourceLayerIdentifier: String?
1314
public var insertionPosition: LayerInsertionPosition = .aboveOthers
1415
public var isVisible: Bool = true
1516
public var maximumZoomLevel: Float? = nil
@@ -21,11 +22,13 @@ public struct LineStyleLayer: SourceBoundVectorStyleLayerDefinition {
2122
public init(identifier: String, source: Source) {
2223
self.identifier = identifier
2324
self.source = .source(source)
25+
sourceLayerIdentifier = nil
2426
}
2527

26-
public init(identifier: String, source: MLNSource) {
28+
public init(identifier: String, source: MLNSource, sourceLayerIdentifier: String? = nil) {
2729
self.identifier = identifier
2830
self.source = .mglSource(source)
31+
self.sourceLayerIdentifier = sourceLayerIdentifier
2932
}
3033

3134
public func makeStyleLayer(style: MLNStyle) -> StyleLayer {

Sources/MapLibreSwiftDSL/Style Layers/Style Layer.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public protocol StyleLayerDefinition {
7575

7676
public protocol SourceBoundStyleLayerDefinition: StyleLayerDefinition {
7777
var source: StyleLayerSource { get set }
78+
79+
var sourceLayerIdentifier: String? { get }
7880
}
7981

8082
/// Based on MLNVectorStyleLayer
@@ -161,3 +163,13 @@ public extension StyleLayer {
161163
modified(self) { $0.insertionPosition = .belowOthers }
162164
}
163165
}
166+
167+
public extension StyleLayerDefinition {
168+
func minimumZoomLevel(_ value: Float) -> Self {
169+
modified(self) { $0.minimumZoomLevel = value }
170+
}
171+
172+
func maximumZoomLevel(_ value: Float) -> Self {
173+
modified(self) { $0.maximumZoomLevel = value }
174+
}
175+
}

Sources/MapLibreSwiftDSL/Style Layers/Symbol.swift

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@ import MapLibreSwiftMacros
55

66
@MLNStyleProperty<Double>("iconRotation", supportsInterpolation: true)
77
@MLNStyleProperty<UIColor>("iconColor", supportsInterpolation: true)
8+
@MLNStyleProperty<Bool>("iconAllowsOverlap", supportsInterpolation: false)
9+
810
@MLNStyleProperty<UIColor>("textColor", supportsInterpolation: true)
911
@MLNStyleProperty<Double>("textFontSize", supportsInterpolation: true)
1012
@MLNStyleProperty<String>("text", supportsInterpolation: false)
11-
@MLNStyleProperty<Bool>("iconAllowsOverlap", supportsInterpolation: false)
13+
// An enum would probably be better?
14+
@MLNStyleProperty<String>("textAnchor", supportsInterpolation: false)
15+
@MLNStyleProperty<CGVector>("textOffset", supportsInterpolation: true)
16+
@MLNStyleProperty<Double>("maximumTextWidth", supportsInterpolation: true)
17+
18+
@MLNStyleProperty<UIColor>("textHaloColor", supportsInterpolation: true)
19+
@MLNStyleProperty<Double>("textHaloWidth", supportsInterpolation: true)
20+
@MLNStyleProperty<Double>("textHaloBlur", supportsInterpolation: true)
1221

1322
public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition {
1423
public let identifier: String
24+
public let sourceLayerIdentifier: String?
1525
public var insertionPosition: LayerInsertionPosition = .aboveOthers
1626
public var isVisible: Bool = true
1727
public var maximumZoomLevel: Float? = nil
@@ -22,11 +32,13 @@ public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition {
2232

2333
public init(identifier: String, source: Source) {
2434
self.identifier = identifier
35+
sourceLayerIdentifier = nil
2536
self.source = .source(source)
2637
}
2738

28-
public init(identifier: String, source: MLNSource) {
39+
public init(identifier: String, source: MLNSource, sourceLayerIdentifier: String? = nil) {
2940
self.identifier = identifier
41+
self.sourceLayerIdentifier = sourceLayerIdentifier
3042
self.source = .mglSource(source)
3143
}
3244

@@ -40,10 +52,9 @@ public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition {
4052
return SymbolStyleLayerInternal(definition: self, mglSource: styleSource)
4153
}
4254

43-
// TODO: Other properties and their modifiers
44-
fileprivate var iconImageName: NSExpression?
55+
public var iconImageName: NSExpression?
4556

46-
private var iconImages = [UIImage]()
57+
public var iconImages = [UIImage]()
4758

4859
// MARK: - Modifiers
4960

@@ -54,18 +65,42 @@ public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition {
5465
}
5566
}
5667

57-
// FIXME: This appears to be broken upstream; waiting for a new release
58-
// public func iconImage(attribute: String, mappings: [AnyHashable: UIImage], default defaultImage: UIImage) -> Self
59-
// {
60-
// return modified(self) { it in
61-
// it.iconImageName = NSExpression(forMLNMatchingKey: NSExpression(forConstantValue: attribute),
62-
// in: Dictionary(uniqueKeysWithValues: mappings.map({ (k, v) in
63-
// (NSExpression(forConstantValue: k), NSExpression(forConstantValue: v.sha256()))
64-
// })),
65-
// default: NSExpression(forConstantValue: defaultImage.sha256()))
66-
// it.iconImages = mappings.values + [defaultImage]
67-
// }
68-
// }
68+
public func iconImage(featurePropertyNamed keyPath: String) -> Self {
69+
var copy = self
70+
copy.iconImageName = NSExpression(forKeyPath: keyPath)
71+
return copy
72+
}
73+
74+
/// Add an icon image that can be dynamic and use UIImages in your app, based on a feature property of the source.
75+
/// For example, your feature could have a property called "icon-name". This name is then resolved against the key
76+
/// in the mappings dictionary and used to find a UIImage to display on the map for that feature.
77+
/// - Parameters:
78+
/// - keyPath: The keypath to the feature property containing the icon to use, for example "icon-name".
79+
/// - mappings: A lookup dictionary containing the keys found in "keyPath" and a UIImage for each keyPath. The key
80+
/// of the mappings dictionary needs to match the value type stored at keyPath, for example `String`.
81+
/// - defaultImage: A UIImage that MapLibre should fall back to if the key in your feature is not found in the
82+
/// mappings table
83+
public func iconImage(
84+
featurePropertyNamed keyPath: String,
85+
mappings: [AnyHashable: UIImage],
86+
default defaultImage: UIImage
87+
) -> Self {
88+
modified(self) { it in
89+
let attributeExpression = NSExpression(forKeyPath: keyPath)
90+
let mappingExpressions = mappings.mapValues { image in
91+
NSExpression(forConstantValue: image.sha256())
92+
}
93+
let mappingDictionary = NSDictionary(dictionary: mappingExpressions)
94+
let defaultExpression = NSExpression(forConstantValue: defaultImage.sha256())
95+
96+
it.iconImageName = NSExpression(
97+
forMLNMatchingKey: attributeExpression,
98+
in: mappingDictionary as! [NSExpression: NSExpression],
99+
default: defaultExpression
100+
)
101+
it.iconImages = mappings.values + [defaultImage]
102+
}
103+
}
69104
}
70105

71106
private struct SymbolStyleLayerInternal: StyleLayer {
@@ -100,18 +135,34 @@ private struct SymbolStyleLayerInternal: StyleLayer {
100135

101136
public func makeMLNStyleLayer() -> MLNStyleLayer {
102137
let result = MLNSymbolStyleLayer(identifier: identifier, source: mglSource)
138+
result.sourceLayerIdentifier = definition.sourceLayerIdentifier
103139

104140
result.iconImageName = definition.iconImageName
105141
result.iconRotation = definition.iconRotation
142+
result.iconAllowsOverlap = definition.iconAllowsOverlap
106143
result.iconColor = definition.iconColor
144+
107145
result.text = definition.text
108146
result.textColor = definition.textColor
109147
result.textFontSize = definition.textFontSize
148+
result.maximumTextWidth = definition.maximumTextWidth
149+
result.textAnchor = definition.textAnchor
150+
result.textOffset = definition.textOffset
110151

111-
result.iconAllowsOverlap = definition.iconAllowsOverlap
152+
result.textHaloColor = definition.textHaloColor
153+
result.textHaloWidth = definition.textHaloWidth
154+
result.textHaloBlur = definition.textHaloBlur
112155

113156
result.predicate = definition.predicate
114157

158+
if let minimumZoomLevel = definition.minimumZoomLevel {
159+
result.minimumZoomLevel = minimumZoomLevel
160+
}
161+
162+
if let maximumZoomLevel = definition.maximumZoomLevel {
163+
result.maximumZoomLevel = maximumZoomLevel
164+
}
165+
115166
return result
116167
}
117168
}

Sources/MapLibreSwiftUI/Examples/Layers.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,18 @@ let clustered = ShapeSource(identifier: "points", options: [.clustered: true, .c
133133
.ignoresSafeArea(.all)
134134
}
135135

136-
// TODO: Fixme
136+
// This example does not work within a package? But it does work when in a real app
137137
// #Preview("Multiple Symbol Icons") {
138138
// MapView(styleURL: demoTilesURL) {
139139
// // Simple symbol layer demonstration with an icon
140140
// SymbolStyleLayer(identifier: "simple-symbols", source: pointSource)
141-
// .iconImage(attribute: "icon",
141+
// .iconImage(featurePropertyNamed: "icon",
142142
// mappings: [
143-
// "missing": UIImage(systemName: "mappin.slash")!,
144-
// "club": UIImage(systemName: "figure.dance")!
143+
// "missing": UIImage(systemName: "mappin.slash")!,
144+
// "club": UIImage(systemName: "figure.dance")!,
145145
// ],
146146
// default: UIImage(systemName: "mappin")!)
147+
// .iconColor(.red)
147148
// }
148-
// .edgesIgnoringSafeArea(.all)
149+
// .ignoresSafeArea(.all)
149150
// }

0 commit comments

Comments
 (0)