Skip to content

Commit

Permalink
MAPSIOS-1526: Generate data-driven properties on annotation manager (m…
Browse files Browse the repository at this point in the history
  • Loading branch information
aleksproger authored Aug 27, 2024
1 parent 33250e5 commit 849d860
Show file tree
Hide file tree
Showing 22 changed files with 2,920 additions and 749 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
Expand All @@ -27,17 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "NO"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<TestPlans>
<TestPlanReference
default = "YES"
reference = "container:../../Tests/TestPlans/Examples.xctestplan">
</TestPlanReference>
<TestPlanReference
reference = "container:../../Tests/TestPlans/Examples no unit tests.xctestplan">
</TestPlanReference>
</TestPlans>
shouldUseLaunchSchemeArgsEnv = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
Expand All @@ -47,30 +36,6 @@
ReferencedContainer = "container:Examples.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "542BE47134765F0824559C2F"
BuildableName = "ExamplesTests.xctest"
BlueprintName = "ExamplesTests"
ReferencedContainer = "container:Examples.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F3570A57FF15EEC3B4529EF0"
BuildableName = "ExamplesUITests.xctest"
BlueprintName = "ExamplesUITests"
ReferencedContainer = "container:Examples.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "MTL_HUD_ENABLED"
Expand Down Expand Up @@ -98,6 +63,37 @@
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<TestPlans>
<TestPlanReference
reference = "container:../../Tests/TestPlans/Examples.xctestplan"
default = "YES">
</TestPlanReference>
<TestPlanReference
reference = "container:../../Tests/TestPlans/Examples no unit tests.xctestplan">
</TestPlanReference>
</TestPlans>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "542BE47134765F0824559C2F"
BuildableName = "ExamplesTests.xctest"
BlueprintName = "ExamplesTests"
ReferencedContainer = "container:Examples.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F3570A57FF15EEC3B4529EF0"
BuildableName = "ExamplesUITests.xctest"
BlueprintName = "ExamplesUITests"
ReferencedContainer = "container:Examples.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand All @@ -119,8 +115,6 @@
ReferencedContainer = "container:Examples.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "MTL_HUD_ENABLED"
Expand Down Expand Up @@ -165,8 +159,6 @@
ReferencedContainer = "container:Examples.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "MTL_HUD_ENABLED"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ final class CircleAnnotationExample: UIViewController, ExampleProtocol {
let circleAnnotationManager = mapView.annotations.makeCircleAnnotationManager()

var annotations = [CircleAnnotation]()
for _ in 0...2000 {
for i in 0...2000 {
var annotation = CircleAnnotation(centerCoordinate: .random)
annotation.circleColor = StyleColor(.random)
annotation.circleStrokeColor = StyleColor(UIColor.black)
annotation.circleRadius = 12
if i % 2 == 0 {
annotation.circleColor = StyleColor(.random)
annotation.circleStrokeColor = StyleColor(UIColor.black)
annotation.circleOpacity = 0.7
}
annotation.isDraggable = true
annotation.circleStrokeWidth = 0

/// The following handlers add tap and longpress gesture handlers. The `context` parameter
/// contains the `point` of the gesture in view coordinate system and a geographical `coordinate`.
Expand Down Expand Up @@ -69,6 +70,12 @@ final class CircleAnnotationExample: UIViewController, ExampleProtocol {
}

circleAnnotationManager.annotations = annotations
circleAnnotationManager.circleColor = StyleColor(UIColor.blue)
circleAnnotationManager.circleStrokeColor = StyleColor(UIColor.white)
circleAnnotationManager.circleStrokeWidth = 4
circleAnnotationManager.circleRadius = 12
circleAnnotationManager.circleOpacity = 0.3
circleAnnotationManager.circleStrokeOpacity = 0.8
// The following line is just for testing purposes.
finish()
}
Expand Down
10 changes: 4 additions & 6 deletions Apps/Examples/Examples/SwiftUI Examples/AnnotationsExample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,11 @@ struct AnnotationsExample: View {
CircleAnnotationGroup(flight.airports, id: \.name) { airport in
CircleAnnotation(centerCoordinate: airport.coordinate, isDraggable: true)
.circleColor(StyleColor(flight.color))
.circleRadius(10)
.circleStrokeColor(.black)
.circleStrokeWidth(1)
.onTapGesture {
alert = "Airport: \(airport.name)"
}
.onTapGesture { alert = "Airport: \(airport.name)" }
}
.circleRadius(10)
.circleStrokeWidth(1)
.circleStrokeColor(.black)

PolylineAnnotation(lineCoordinates: flight.airports.map(\.coordinate))
.lineColor(.init(flight.color))
Expand Down
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,39 @@ Mapbox welcomes participation and contributions from everyone.

## main

* Expose data-driven properties on annotation managers. Now it's possible to set data-dirven properties globally on annotation manager and specify per-annotation overrides.
Previosuly user had to specify those properties on each annotation and couldn't specify them globally
```swift
CircleAnnotationGroup(circles, id: \.id) { circle in
CircleAnnotation(centerCoordinate: circle.coordinate)
.circleColor(circle.color)
.circleRadius(10)
.circleStrokeWidth(1)
.circleStrokeColor(.black)
}
```
The problem with the above approach is that most of the properties are just duplicated for each annotation, which can lead to **large memory overhead** in case of big datasets. In order to solve this issue and provide more versatile API the following approach is now possible, which is visualy identical to previous snippet, but more performant.
```swift
CircleAnnotationGroup(circles, id: \.id) { circle in
CircleAnnotation(centerCoordinate: circle.coordinate)
.circleColor(circle.color)
}
.circleRadius(10)
.circleStrokeWidth(1)
.circleStrokeColor(.black)
```

Same applies for imperative API. In this case each even annotation will have random color, but others will use the global default specified in the annotation manager.
```swift
let circleAnnotationManager = mapView.annotations.makeCircleAnnotationManager()
var annotations = [CircleAnnotation]()
for i in 0...2000 {
var annotation = CircleAnnotation(centerCoordinate: .random)
if i % 2 == 0 { annotation.circleColor = StyleColor(.random) }
annotations.append(annotation)
}
circleAnnotationManager.circleColor = .blue
```
* Improve memory reclamation behavior when using partial GeoJSON update API.

## 11.6.0 - 14 August, 2024
Expand Down
16 changes: 11 additions & 5 deletions Sources/MapboxMaps/Annotations/AnnotationManagerImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -285,14 +285,20 @@ final class AnnotationManagerImpl<AnnotationType: Annotation & AnnotationInterna

// Construct the properties dictionary from the annotations
let dataDrivenLayerPropertyKeys = Set(annotations.flatMap(\.layerProperties.keys))

/// The logic of the expression is the following
/// Firstly, it tries to get the the value for the given key from `layerProperties` in feature. Properties for the feature are set in <Type>Annotation.feature
/// Secondly, it tries to read the value from `layerProperties` dictionary from annotation manager.
/// In the end if the property is not set either on annotation or on annotation manager we just use default value from the style.gst
let dataDrivenProperties = Dictionary(
uniqueKeysWithValues: dataDrivenLayerPropertyKeys
.map { (key) -> (String, Any) in
(key, ["get", key, ["get", "layerProperties"]] as [Any])
})
uniqueKeysWithValues: dataDrivenLayerPropertyKeys.map { (key) -> (String, Any) in (key, [
"coalesce",
["get", key, ["get", "layerProperties"]],
layerProperties[key] ?? StyleManager.layerPropertyDefaultValue(for: self.layerType, property: key).value
] as [Any])})

// Merge the common layer properties
let newLayerProperties = dataDrivenProperties.merging(layerProperties, uniquingKeysWith: { $1 })
let newLayerProperties = dataDrivenProperties.merging(layerProperties, uniquingKeysWith: { dataDriven, _ in dataDriven })

// Construct the properties dictionary to reset any properties that are no longer used
let unusedPropertyKeys = previouslySetLayerPropertyKeys.subtracting(newLayerProperties.keys)
Expand Down

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

Loading

0 comments on commit 849d860

Please sign in to comment.