-
-
Notifications
You must be signed in to change notification settings - Fork 15
More SymbolLayer properties + sourceLayerIdentifier support + zoom levels + local image support #42
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
Changes from 3 commits
90470d7
4a69c57
05d6cf3
6a9d74a
1e41063
ec1b2eb
5b3a19d
43b2fff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
---|---|---|
|
@@ -161,3 +161,13 @@ public extension StyleLayer { | |
modified(self) { $0.insertionPosition = .belowOthers } | ||
} | ||
} | ||
|
||
public extension StyleLayerDefinition { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please review if this how you picture its usage, as I see other zoom level code in this spm already, but didn't find a way to access it from the MapView view builder. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Archdoog on our review call: "I think he is on to something fantastic" 😂 We definitely missed exposing this idiomatically in the high-level Capturing another random thought: it might make sense to split this file up since it's now got a bunch of random crap in it :) Especially for things like modifiers which might not be as obvious/discoverable. (Can do this later.) |
||
func minimumZoomLevel(_ value: Float) -> Self { | ||
modified(self) { $0.minimumZoomLevel = value } | ||
} | ||
|
||
func maximumZoomLevel(_ value: Float) -> Self { | ||
modified(self) { $0.maximumZoomLevel = value } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,13 +5,23 @@ import MapLibreSwiftMacros | |
|
||
@MLNStyleProperty<Double>("iconRotation", supportsInterpolation: true) | ||
@MLNStyleProperty<UIColor>("iconColor", supportsInterpolation: true) | ||
@MLNStyleProperty<Bool>("iconAllowsOverlap", supportsInterpolation: false) | ||
|
||
@MLNStyleProperty<UIColor>("textColor", supportsInterpolation: true) | ||
@MLNStyleProperty<Double>("textFontSize", supportsInterpolation: true) | ||
@MLNStyleProperty<String>("text", supportsInterpolation: false) | ||
@MLNStyleProperty<Bool>("iconAllowsOverlap", supportsInterpolation: false) | ||
// An enum would probably be better? | ||
@MLNStyleProperty<String>("textAnchor", supportsInterpolation: false) | ||
@MLNStyleProperty<CGVector>("textOffset", supportsInterpolation: true) | ||
@MLNStyleProperty<Double>("maximumTextWidth", supportsInterpolation: true) | ||
|
||
@MLNStyleProperty<UIColor>("textHaloColor", supportsInterpolation: true) | ||
@MLNStyleProperty<Double>("textHaloWidth", supportsInterpolation: true) | ||
@MLNStyleProperty<Double>("textHaloBlur", supportsInterpolation: true) | ||
|
||
public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition { | ||
public let identifier: String | ||
public let sourceLayerIdentifier: String? | ||
public var insertionPosition: LayerInsertionPosition = .aboveOthers | ||
public var isVisible: Bool = true | ||
public var maximumZoomLevel: Float? = nil | ||
|
@@ -22,12 +32,15 @@ public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition { | |
|
||
public init(identifier: String, source: Source) { | ||
self.identifier = identifier | ||
self.sourceLayerIdentifier = nil | ||
self.source = .source(source) | ||
} | ||
|
||
public init(identifier: String, source: MLNSource) { | ||
public init(identifier: String, source: MLNSource, sourceLayerIdentifier: String? = nil) { | ||
self.identifier = identifier | ||
self.sourceLayerIdentifier = sourceLayerIdentifier | ||
self.source = .mglSource(source) | ||
|
||
} | ||
|
||
public func makeStyleLayer(style: MLNStyle) -> StyleLayer { | ||
|
@@ -40,10 +53,9 @@ public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition { | |
return SymbolStyleLayerInternal(definition: self, mglSource: styleSource) | ||
} | ||
|
||
// TODO: Other properties and their modifiers | ||
fileprivate var iconImageName: NSExpression? | ||
public var iconImageName: NSExpression? | ||
|
||
private var iconImages = [UIImage]() | ||
public var iconImages = [UIImage]() | ||
Comment on lines
-44
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made these two public, because this allows devs to make their own extensions for the SymbolStyleLayer. For example, we created a helper extension for ourselves like this, but needed these properties exposed to do so: public extension SymbolStyleLayer {
func iconImage(mappings: [AnyHashable: UIImage], default defaultImage: UIImage) -> Self {
return modified(self) { it in
let expression1 = NSExpression(forKeyPath: "ios_category_icon_name")
let expression2 = NSExpression(forKeyPath: "ios_category_icon_color")
// Create an NSExpression that concatenates the two key paths
let attributeExpression = expression1.mgl_appending(expression2)
let mappingExpressions = mappings.mapValues { image in
NSExpression(forConstantValue: image.sha256())
}
let mappingDictionary = NSDictionary(dictionary: mappingExpressions)
let defaultExpression = NSExpression(forConstantValue: defaultImage.sha256())
// swiftlint:disable force_cast
it.iconImageName = NSExpression(
forMLNMatchingKey: attributeExpression,
in: mappingDictionary as! [NSExpression: NSExpression],
default: defaultExpression
)
// swiftlint:enable force_cast
it.iconImages = mappings.values + [defaultImage]
}
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That makes sense. Thanks for the detailed example clarifying the use case! |
||
|
||
// MARK: - Modifiers | ||
|
||
|
@@ -53,6 +65,12 @@ public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition { | |
it.iconImages = [image] | ||
} | ||
} | ||
|
||
public func iconImage(featurePropertyNamed keyPath: String) -> Self { | ||
var copy = self | ||
copy.iconImageName = NSExpression(forKeyPath: keyPath) | ||
return copy | ||
} | ||
|
||
// FIXME: This appears to be broken upstream; waiting for a new release | ||
ianthetechie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// public func iconImage(attribute: String, mappings: [AnyHashable: UIImage], default defaultImage: UIImage) -> Self | ||
|
@@ -66,6 +84,29 @@ public struct SymbolStyleLayer: SourceBoundVectorStyleLayerDefinition { | |
// it.iconImages = mappings.values + [defaultImage] | ||
// } | ||
// } | ||
|
||
/// Add an icon image that can be dynamic and use UIImages in your app, based on a feature property of the source. For example, your feature could have a property called "icon-name". This name is then resolved against the key in the mappings dictionary and used to find a UIImage to display on the map for that feature. | ||
/// - Parameters: | ||
/// - keyPath: The keypath to the feature property containing the icon to use, for example "icon-name". | ||
/// - mappings: A lookup dictionary containing the keys found in "keyPath" and a UIImage for each keyPath. The key of the mappings dictionary needs to match the value type stored at keyPath, for example `String`. | ||
/// - defaultImage: A UIImage that MapLibre should fall back to if the key in your feature is not found in the mappings table | ||
public func iconImage(featurePropertyNamed keyPath: String, mappings: [AnyHashable: UIImage], default defaultImage: UIImage) -> Self { | ||
return modified(self) { it in | ||
let attributeExpression = NSExpression(forKeyPath: keyPath) | ||
let mappingExpressions = mappings.mapValues { image in | ||
NSExpression(forConstantValue: image.sha256()) | ||
} | ||
let mappingDictionary = NSDictionary(dictionary: mappingExpressions) | ||
let defaultExpression = NSExpression(forConstantValue: defaultImage.sha256()) | ||
|
||
it.iconImageName = NSExpression( | ||
forMLNMatchingKey: attributeExpression, | ||
in: mappingDictionary as! [NSExpression: NSExpression], | ||
default: defaultExpression | ||
) | ||
it.iconImages = mappings.values + [defaultImage] | ||
} | ||
} | ||
} | ||
|
||
private struct SymbolStyleLayerInternal: StyleLayer { | ||
|
@@ -100,17 +141,33 @@ private struct SymbolStyleLayerInternal: StyleLayer { | |
|
||
public func makeMLNStyleLayer() -> MLNStyleLayer { | ||
let result = MLNSymbolStyleLayer(identifier: identifier, source: mglSource) | ||
|
||
result.sourceLayerIdentifier = definition.sourceLayerIdentifier | ||
|
||
result.iconImageName = definition.iconImageName | ||
result.iconRotation = definition.iconRotation | ||
result.iconAllowsOverlap = definition.iconAllowsOverlap | ||
result.iconColor = definition.iconColor | ||
|
||
result.text = definition.text | ||
result.textColor = definition.textColor | ||
result.textFontSize = definition.textFontSize | ||
|
||
result.iconAllowsOverlap = definition.iconAllowsOverlap | ||
|
||
result.maximumTextWidth = definition.maximumTextWidth | ||
result.textAnchor = definition.textAnchor | ||
result.textOffset = definition.textOffset | ||
|
||
result.textHaloColor = definition.textHaloColor | ||
result.textHaloWidth = definition.textHaloWidth | ||
result.textHaloBlur = definition.textHaloBlur | ||
|
||
result.predicate = definition.predicate | ||
|
||
if let minimumZoomLevel = definition.minimumZoomLevel { | ||
result.minimumZoomLevel = minimumZoomLevel | ||
} | ||
|
||
if let maximumZoomLevel = definition.maximumZoomLevel { | ||
result.maximumZoomLevel = maximumZoomLevel | ||
} | ||
|
||
return result | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.