Add line style layer example to iOS docs (#2525)

This commit is contained in:
Bart Louwers
2024-06-21 17:30:42 +02:00
committed by GitHub
parent 098dca8c5a
commit 1df592c9e1
8 changed files with 542 additions and 16 deletions

View File

@@ -108,16 +108,6 @@ If you want to get direct access you need an AWS account to get permissions to u
aws sts get-caller-identity
```
When the needed resources are uploaded the filenames need to be added to the `resources` array in `platform/ios/scripts/docc.sh`.
```bash
resources=(
"AddPackageDependencies@2x.png"
# ...
# add here
)
```
### Examples
The code samples in the documentation should ideally be compiled on CI so they do not go out of date.

View File

@@ -0,0 +1,108 @@
# Using GeoJSON with a line style layer (UIKit)
Adding an ``MLNLineStyleLayer`` to the map using a GeoJSON file.
<!-- include-example(LineStyleLayerExampleUIKit) -->
```swift
class LineStyleLayerExampleUIKit: UIViewController, MLNMapViewDelegate {
var mapView: MLNMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView = MLNMapView(frame: view.bounds, styleURL: VERSATILES_COLORFUL_STYLE)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(
CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736),
zoomLevel: 11,
animated: false
)
view.addSubview(mapView)
mapView.delegate = self
}
// Wait until the map is loaded before adding to the map.
func mapView(_: MLNMapView, didFinishLoading _: MLNStyle) {
loadGeoJson()
}
func loadGeoJson() {
DispatchQueue.global().async {
// Get the path for example.geojson in the app’s bundle.
guard let jsonUrl = Bundle.main.url(forResource: "example", withExtension: "geojson") else {
preconditionFailure("Failed to load local GeoJSON file")
}
guard let jsonData = try? Data(contentsOf: jsonUrl) else {
preconditionFailure("Failed to parse GeoJSON file")
}
DispatchQueue.main.async {
self.drawPolyline(geoJson: jsonData)
}
}
}
func drawPolyline(geoJson: Data) {
// Add our GeoJSON data to the map as an MLNGeoJSONSource.
// We can then reference this data from an MLNStyleLayer.
// MLNMapView.style is optional, so you must guard against it not being set.
guard let style = mapView.style else { return }
guard let shapeFromGeoJSON = try? MLNShape(data: geoJson, encoding: String.Encoding.utf8.rawValue) else {
fatalError("Could not generate MLNShape")
}
let source = MLNShapeSource(identifier: "polyline", shape: shapeFromGeoJSON, options: nil)
style.addSource(source)
// Create new layer for the line.
let layer = MLNLineStyleLayer(identifier: "polyline", source: source)
// Set the line join and cap to a rounded end.
layer.lineJoin = NSExpression(forConstantValue: "round")
layer.lineCap = NSExpression(forConstantValue: "round")
// Set the line color to a constant blue color.
layer.lineColor = NSExpression(forConstantValue: UIColor(red: 59 / 255, green: 178 / 255, blue: 208 / 255, alpha: 1))
// Use `NSExpression` to smoothly adjust the line width from 2pt to 20pt between zoom levels 14 and 18. The `interpolationBase` parameter allows the values to interpolate along an exponential curve.
layer.lineWidth = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)",
[14: 2, 18: 20])
// We can also add a second layer that will draw a stroke around the original line.
let casingLayer = MLNLineStyleLayer(identifier: "polyline-case", source: source)
// Copy these attributes from the main line layer.
casingLayer.lineJoin = layer.lineJoin
casingLayer.lineCap = layer.lineCap
// Line gap width represents the space before the outline begins, so should match the main line’s line width exactly.
casingLayer.lineGapWidth = layer.lineWidth
// Stroke color slightly darker than the line color.
casingLayer.lineColor = NSExpression(forConstantValue: UIColor(red: 41 / 255, green: 145 / 255, blue: 171 / 255, alpha: 1))
// Use `NSExpression` to gradually increase the stroke width between zoom levels 14 and 18.
casingLayer.lineWidth = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", [14: 1, 18: 4])
// Just for fun, let’s add another copy of the line with a dash pattern.
let dashedLayer = MLNLineStyleLayer(identifier: "polyline-dash", source: source)
dashedLayer.lineJoin = layer.lineJoin
dashedLayer.lineCap = layer.lineCap
dashedLayer.lineColor = NSExpression(forConstantValue: UIColor.white)
dashedLayer.lineOpacity = NSExpression(forConstantValue: 0.5)
dashedLayer.lineWidth = layer.lineWidth
// Dash pattern in the format [dash, gap, dash, gap, ...]. You’ll want to adjust these values based on the line cap style.
dashedLayer.lineDashPattern = NSExpression(forConstantValue: [0, 1.5])
style.addLayer(layer)
style.addLayer(dashedLayer)
style.insertLayer(casingLayer, below: layer)
}
}
```
![](LineStyleLayerExample.png)
Map Data from OpenStreetMap.

View File

@@ -58,17 +58,17 @@ class PrivacyExampleViewModel: NSObject, ObservableObject {
class PrivacyExampleCoordinator: NSObject, MLNMapViewDelegate {
@ObservedObject private var mapViewModel: PrivacyExampleViewModel
private var pannedToUserLocation = false
init(mapViewModel: PrivacyExampleViewModel) {
self.mapViewModel = mapViewModel
super.init()
}
@MainActor func mapView(_: MLNMapView, didChangeLocationManagerAuthorization manager: MLNLocationManager) {
guard let accuracySetting = manager.accuracyAuthorization else {
return
}
switch accuracySetting() {
case .fullAccuracy:
mapViewModel.locationAccuracy = .fullAccuracy
@@ -93,14 +93,14 @@ class PrivacyExampleCoordinator: NSObject, MLNMapViewDelegate {
struct PrivacyExampleRepresentable: UIViewRepresentable {
@ObservedObject var mapViewModel: PrivacyExampleViewModel
func makeCoordinator() -> PrivacyExampleCoordinator {
PrivacyExampleCoordinator(mapViewModel: mapViewModel)
}
func makeUIView(context: Context) -> MLNMapView {
let mapView = MLNMapView()
mapView.delegate = context.coordinator
mapView.showsUserLocation = true
return mapView
@@ -119,7 +119,7 @@ struct PrivacyExampleRepresentable: UIViewRepresentable {
struct LocationPrivacyExampleView: View {
@StateObject private var viewModel = PrivacyExampleViewModel()
var body: some View {
VStack {
PrivacyExampleRepresentable(mapViewModel: viewModel)

View File

@@ -17,6 +17,7 @@ Powerful, free and open-source mapping toolkit with full control over data sourc
- <doc:LineOnUserTap>
- <doc:LocationPrivacyExample>
- <doc:BlockingGesturesExample>
- <doc:LineStyleLayerExampleUIKit>
## Topics

View File

@@ -27,6 +27,9 @@ ios_application(
infoplists = [":Info.plist"],
minimum_os_version = "16.0",
provisioning_profile = "xcode_profile",
resources = glob([
"Data/*",
]),
visibility = ["@rules_xcodeproj//xcodeproj:generated"],
deps = [":Sources"],
)

View File

@@ -0,0 +1,308 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Crema to Council Crest"
},
"geometry": {
"type": "LineString",
"coordinates": [
[
-122.63748,
45.52214
],
[
-122.64855,
45.52218
],
[
-122.6545,
45.52219
],
[
-122.65497,
45.52196
],
[
-122.65631,
45.52104
],
[
-122.6578,
45.51935
],
[
-122.65867,
45.51848
],
[
-122.65872,
45.51293
],
[
-122.66576,
45.51295
],
[
-122.66745,
45.51252
],
[
-122.66813,
45.51244
],
[
-122.67359,
45.51385
],
[
-122.67415,
45.51406
],
[
-122.67481,
45.51484
],
[
-122.676,
45.51532
],
[
-122.68106,
45.51668
],
[
-122.68503,
45.50934
],
[
-122.68546,
45.50858
],
[
-122.6852,
45.50783
],
[
-122.68424,
45.50714
],
[
-122.68433,
45.50585
],
[
-122.68429,
45.50521
],
[
-122.68456,
45.50445
],
[
-122.68538,
45.50371
],
[
-122.68653,
45.50311
],
[
-122.68731,
45.50292
],
[
-122.68742,
45.50253
],
[
-122.6867,
45.50239
],
[
-122.68545,
45.5026
],
[
-122.68407,
45.50294
],
[
-122.68357,
45.50271
],
[
-122.68236,
45.50055
],
[
-122.68233,
45.49994
],
[
-122.68267,
45.49955
],
[
-122.68257,
45.49919
],
[
-122.68376,
45.49842
],
[
-122.68428,
45.49821
],
[
-122.68573,
45.49798
],
[
-122.68923,
45.49805
],
[
-122.68926,
45.49857
],
[
-122.68814,
45.49911
],
[
-122.68865,
45.49921
],
[
-122.6897,
45.49905
],
[
-122.69346,
45.49917
],
[
-122.69404,
45.49902
],
[
-122.69438,
45.49796
],
[
-122.69504,
45.49697
],
[
-122.69624,
45.49661
],
[
-122.69781,
45.4955
],
[
-122.69803,
45.49517
],
[
-122.69711,
45.49508
],
[
-122.69688,
45.4948
],
[
-122.69744,
45.49368
],
[
-122.69702,
45.49311
],
[
-122.69665,
45.49294
],
[
-122.69788,
45.49212
],
[
-122.69771,
45.49264
],
[
-122.69835,
45.49332
],
[
-122.7007,
45.49334
],
[
-122.70167,
45.49358
],
[
-122.70215,
45.49401
],
[
-122.70229,
45.49439
],
[
-122.70185,
45.49566
],
[
-122.70215,
45.49635
],
[
-122.70346,
45.49674
],
[
-122.70517,
45.49758
],
[
-122.70614,
45.49736
],
[
-122.70663,
45.49736
],
[
-122.70807,
45.49767
],
[
-122.70807,
45.49798
],
[
-122.70717,
45.49798
],
[
-122.70713,
45.4984
],
[
-122.70774,
45.49893
]
]
}
}
]
}

View File

@@ -0,0 +1,113 @@
import MapLibre
import SwiftUI
import UIKit
// #-example-code(LineStyleLayerExampleUIKit)
class LineStyleLayerExampleUIKit: UIViewController, MLNMapViewDelegate {
var mapView: MLNMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView = MLNMapView(frame: view.bounds, styleURL: VERSATILES_COLORFUL_STYLE)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(
CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736),
zoomLevel: 11,
animated: false
)
view.addSubview(mapView)
mapView.delegate = self
}
// Wait until the map is loaded before adding to the map.
func mapView(_: MLNMapView, didFinishLoading _: MLNStyle) {
loadGeoJson()
}
func loadGeoJson() {
DispatchQueue.global().async {
// Get the path for example.geojson in the app’s bundle.
guard let jsonUrl = Bundle.main.url(forResource: "example", withExtension: "geojson") else {
preconditionFailure("Failed to load local GeoJSON file")
}
guard let jsonData = try? Data(contentsOf: jsonUrl) else {
preconditionFailure("Failed to parse GeoJSON file")
}
DispatchQueue.main.async {
self.drawPolyline(geoJson: jsonData)
}
}
}
func drawPolyline(geoJson: Data) {
// Add our GeoJSON data to the map as an MLNGeoJSONSource.
// We can then reference this data from an MLNStyleLayer.
// MLNMapView.style is optional, so you must guard against it not being set.
guard let style = mapView.style else { return }
guard let shapeFromGeoJSON = try? MLNShape(data: geoJson, encoding: String.Encoding.utf8.rawValue) else {
fatalError("Could not generate MLNShape")
}
let source = MLNShapeSource(identifier: "polyline", shape: shapeFromGeoJSON, options: nil)
style.addSource(source)
// Create new layer for the line.
let layer = MLNLineStyleLayer(identifier: "polyline", source: source)
// Set the line join and cap to a rounded end.
layer.lineJoin = NSExpression(forConstantValue: "round")
layer.lineCap = NSExpression(forConstantValue: "round")
// Set the line color to a constant blue color.
layer.lineColor = NSExpression(forConstantValue: UIColor(red: 59 / 255, green: 178 / 255, blue: 208 / 255, alpha: 1))
// Use `NSExpression` to smoothly adjust the line width from 2pt to 20pt between zoom levels 14 and 18. The `interpolationBase` parameter allows the values to interpolate along an exponential curve.
layer.lineWidth = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)",
[14: 2, 18: 20])
// We can also add a second layer that will draw a stroke around the original line.
let casingLayer = MLNLineStyleLayer(identifier: "polyline-case", source: source)
// Copy these attributes from the main line layer.
casingLayer.lineJoin = layer.lineJoin
casingLayer.lineCap = layer.lineCap
// Line gap width represents the space before the outline begins, so should match the main line’s line width exactly.
casingLayer.lineGapWidth = layer.lineWidth
// Stroke color slightly darker than the line color.
casingLayer.lineColor = NSExpression(forConstantValue: UIColor(red: 41 / 255, green: 145 / 255, blue: 171 / 255, alpha: 1))
// Use `NSExpression` to gradually increase the stroke width between zoom levels 14 and 18.
casingLayer.lineWidth = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", [14: 1, 18: 4])
// Just for fun, let’s add another copy of the line with a dash pattern.
let dashedLayer = MLNLineStyleLayer(identifier: "polyline-dash", source: source)
dashedLayer.lineJoin = layer.lineJoin
dashedLayer.lineCap = layer.lineCap
dashedLayer.lineColor = NSExpression(forConstantValue: UIColor.white)
dashedLayer.lineOpacity = NSExpression(forConstantValue: 0.5)
dashedLayer.lineWidth = layer.lineWidth
// Dash pattern in the format [dash, gap, dash, gap, ...]. You’ll want to adjust these values based on the line cap style.
dashedLayer.lineDashPattern = NSExpression(forConstantValue: [0, 1.5])
style.addLayer(layer)
style.addLayer(dashedLayer)
style.insertLayer(casingLayer, below: layer)
}
}
// #-end-example-code
struct LineStyleLayerExampleUIViewControllerRepresentable: UIViewControllerRepresentable {
typealias UIViewControllerType = LineStyleLayerExampleUIKit
func makeUIViewController(context _: Context) -> LineStyleLayerExampleUIKit {
LineStyleLayerExampleUIKit()
}
func updateUIViewController(_: LineStyleLayerExampleUIKit, context _: Context) {}
}

View File

@@ -17,6 +17,9 @@ struct MapLibreNavigationView: View {
NavigationLink("BlockingGesturesExample") {
BlockingGesturesExample()
}
NavigationLink("LineStyleLayerExample") {
LineStyleLayerExampleUIViewControllerRepresentable()
}
}
}
}