Add line style layer example to iOS docs (#2525)
This commit is contained in:
@@ -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.
|
||||
|
||||
108
platform/ios/MapLibre.docc/LineStyleLayerExampleUIKit.md
Normal file
108
platform/ios/MapLibre.docc/LineStyleLayerExampleUIKit.md
Normal 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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
Map Data from OpenStreetMap.
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"],
|
||||
)
|
||||
|
||||
308
platform/ios/app-swift/Data/example.geojson
Normal file
308
platform/ios/app-swift/Data/example.geojson
Normal 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
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
113
platform/ios/app-swift/Sources/LineStyleLayerExampleUIKit.swift
Normal file
113
platform/ios/app-swift/Sources/LineStyleLayerExampleUIKit.swift
Normal 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) {}
|
||||
}
|
||||
@@ -17,6 +17,9 @@ struct MapLibreNavigationView: View {
|
||||
NavigationLink("BlockingGesturesExample") {
|
||||
BlockingGesturesExample()
|
||||
}
|
||||
NavigationLink("LineStyleLayerExample") {
|
||||
LineStyleLayerExampleUIViewControllerRepresentable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user