diff options
Diffstat (limited to 'platform/ios/scripts/script_resources/MapboxDemo/MapboxDemo/ViewController.swift')
-rw-r--r-- | platform/ios/scripts/script_resources/MapboxDemo/MapboxDemo/ViewController.swift | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/platform/ios/scripts/script_resources/MapboxDemo/MapboxDemo/ViewController.swift b/platform/ios/scripts/script_resources/MapboxDemo/MapboxDemo/ViewController.swift new file mode 100644 index 0000000000..34bbcb7666 --- /dev/null +++ b/platform/ios/scripts/script_resources/MapboxDemo/MapboxDemo/ViewController.swift @@ -0,0 +1,239 @@ +import UIKit +import Mapbox + +/* + A simple example that uses the Mapbox Outdoors style to view + Yosemite National Park, USA with a polygon visualization of the + park boundary that can be interacted with. + + A demo access token has been provided for convenience and it may + stop working in the future. + + You can obtain your own access token from the + [Mapbox account page](https://www.mapbox.com/studio/account/tokens/) + and add it to this application's Info.plist as the value for + MGLMapboxAccessToken + */ +class ViewController: UIViewController { + + @IBOutlet weak var mapViewContainer: UIView! + @IBOutlet weak var annotationContextView: UIView! + @IBOutlet weak var accessTokenWarningView: UIView! + + var mapView: MGLMapView! + + // Define a name for our annotation + let annotationIdentifier = "yosemite-fill-layer" + + // Define the name of the map label layer we will want our annotation to be placed under + let styleLabelLayer = "mountain-peak-label" + + // The initial center geometric coordinate for the map to display - this is Yosemite National Park, USA + let centerCoordinate = CLLocationCoordinate2DMake(37.742241, -119.576923) + + // MARK: - View controller life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + setupAnnotationContextView() + setupAndAddMapView() + setupGestureHandling() + } + + // MARK: - Map object interaction + + @objc func didTapMap(sender: UITapGestureRecognizer) { + + // Search for annotation at tap point + // If the annotation is found at the tap point then modify some of its properties + // If the annotation is not found at the tap point, reset the annotation back to the way it was when originally added + if let layer = mapView.style?.layer(withIdentifier: annotationIdentifier) as? MGLFillExtrusionStyleLayer { + let point = sender.location(in: mapView) + let features = mapView.visibleFeatures(at: point, styleLayerIdentifiers: [annotationIdentifier]) + if features.count > 0 { + layer.fillExtrusionOpacity = MGLStyleValue(rawValue: 0.9) + layer.fillExtrusionHeight = MGLStyleValue(rawValue: 5000) + let camera = MGLMapCamera(lookingAtCenter: centerCoordinate, fromDistance: 100000, pitch: 60, heading: 180) + mapView.fly(to: camera, withDuration: 8, completionHandler: nil) + annotationContextView.alpha = 1.0 + } else { + layer.fillExtrusionOpacity = MGLStyleValue(rawValue: 0.5) + layer.fillExtrusionHeight = MGLStyleValue(rawValue: 0) + let camera = MGLMapCamera(lookingAtCenter: centerCoordinate, fromDistance: 100000, pitch: 60, heading: 0) + mapView.fly(to: camera, withDuration: 1, completionHandler: nil) + annotationContextView.alpha = 0 + } + } + } + + // MARK: - Initialization and setup + + /* + Simple (but somewhat styled) stub of a view to present + contextual information about tapped objects on the map. + */ + fileprivate func setupAnnotationContextView() { + annotationContextView.alpha = 0 + let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) + blurView.frame = annotationContextView.bounds + blurView.layer.cornerRadius = 5 + blurView.clipsToBounds = true + blurView.translatesAutoresizingMaskIntoConstraints = false + annotationContextView.insertSubview(blurView, at: 0) + blurView.topAnchor.constraint(equalTo: annotationContextView.topAnchor).isActive = true + blurView.leftAnchor.constraint(equalTo: annotationContextView.leftAnchor).isActive = true + blurView.rightAnchor.constraint(equalTo: annotationContextView.rightAnchor).isActive = true + blurView.bottomAnchor.constraint(equalTo: annotationContextView.bottomAnchor).isActive = true + } + + /* + Create and add a Mapbox map view to the the view + intended to contain it. Setup with the style, initial + location in the world, and make this object the delegate + of the map. Finally, set up a camera to control how the + user will view the map initially. + */ + fileprivate func setupAndAddMapView() { + mapView = MGLMapView(frame: mapViewContainer.bounds) + mapView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + mapView.styleURL = MGLStyle.outdoorsStyleURL(withVersion: 10) + mapView.setCenter(centerCoordinate, zoomLevel: 10, animated: false) + mapView.delegate = self + mapViewContainer.addSubview(mapView) + + accessTokenWarningView.layer.cornerRadius = 5 + accessTokenWarningView.isHidden = true + + // This demo access token has been provided for convenience and it may + // stop working in the future. + // You can obtain your own access token from the + // [Mapbox account page](https://www.mapbox.com/studio/account/tokens/) + // and add it to this application's Info.plist as the value for MGLMapboxAccessToken + if MGLAccountManager.accessToken() == "pk.eyJ1IjoibWFwYm94IiwiYSI6ImNqYThuNnZ3NTA5MGMyd3F1cmF1eW1xaGEifQ.TdBTSHHPeT1pfLZ_6x_1vA" { + accessTokenWarningView.isHidden = false + } + + let camera = MGLMapCamera(lookingAtCenter: centerCoordinate, fromDistance: 100000, pitch: 60, heading: 0) + mapView.setCamera(camera, animated: false) + } + + /* + Add a gesture recognizer that will be used to handle user + interactions with the map. + */ + fileprivate func setupGestureHandling() { + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapMap)) + mapView.addGestureRecognizer(tapGestureRecognizer) + } +} + +/* + Implementation of Mapbox map view delegate methods relevant to this application + */ +extension ViewController: MGLMapViewDelegate { + + /* + When the map has finished loading the style it's a great time + to do work like adding annotations. + */ + func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) { + // This project's bundle contains a geojson file that represents the park boundary of Yosemite National Park in the USA + let url = URL(fileURLWithPath: Bundle.main.path(forResource: "yose", ofType: "geojson")!) + + // Create an instance of a structure to represent a polygon + // fill visualization that can be injeced into the map's style layers + var annotation = StyledAnnotation(identifier: annotationIdentifier, resource: url, type: .extrusion) + annotation.color = UIColor(red:1.00, green:0.95, blue:0.75, alpha:1.0) + annotation.opacity = 0.75 + annotation.transitionDuration = 0.35 + + // Inject the annotation below the label layer so some map labels + // are still visible + mapView.addStyledAnnotation(annotation, belowLayer: styleLabelLayer) + + // Alternatively, a more traditional annotation / marker API + let pointFeatureAnnotation = MGLPointFeature() + let offsetCoordinate = CLLocationCoordinate2D(latitude: centerCoordinate.latitude+0.1, longitude: centerCoordinate.longitude-0.01) + pointFeatureAnnotation.coordinate = offsetCoordinate + mapView.addAnnotation(pointFeatureAnnotation) + } + +} + +/* + An extention of the Mapbox map view class that offers a simplified + API for a small subset of the runtime styling APIs used in this application. + This could be extended. + */ +extension MGLMapView { + + func addStyledAnnotation(_ annotation: StyledAnnotation, belowLayer layerName: String) { + if let labelsLayer = self.style?.layer(withIdentifier: "mountain-peak-label") { + let source = addSource(for: annotation) + let layer = addLayer(for: annotation, source: source) + self.style?.insertLayer(layer, below: labelsLayer) + } + } + + func addStyledAnnotation(_ annotation: StyledAnnotation) { + let source = addSource(for: annotation) + let layer = addLayer(for: annotation, source: source) + self.style?.addLayer(layer) + } + + func addSource(for styledAnnotation: StyledAnnotation) -> MGLSource { + let source = MGLShapeSource(identifier: UUID().uuidString, url: styledAnnotation.resource, options: nil) + self.style?.addSource(source) + return source + } + + func addLayer(for styledAnnotation: StyledAnnotation, source: MGLSource) -> MGLStyleLayer { + switch styledAnnotation.type { + case .extrusion: + let layer = MGLFillExtrusionStyleLayer(identifier: styledAnnotation.identifier, source: source) + if let height = styledAnnotation.height { + layer.fillExtrusionHeight = MGLStyleValue<NSNumber>(rawValue: NSNumber(floatLiteral: Double(height))) + } + if let color = styledAnnotation.color { + layer.fillExtrusionColor = MGLStyleValue(rawValue: color) + } + if let opacity = styledAnnotation.opacity { + layer.fillExtrusionOpacity = MGLStyleValue<NSNumber>(rawValue: NSNumber(floatLiteral: Double(opacity))) + } + if let duration = styledAnnotation.transitionDuration { + layer.fillExtrusionHeightTransition = MGLTransitionMake(Double(duration), 0) + layer.fillExtrusionOpacityTransition = MGLTransitionMake(Double(duration), 0) + } + return layer + } + } + +} + +/* + A struct to represent a small subset of the annotations that + are possible to add to a Mapbox map. This could be extended. + */ +struct StyledAnnotation { + + enum StyledAnnotationType { + case extrusion + } + + init(identifier: String, resource: URL, type: StyledAnnotationType) { + self.identifier = identifier + self.resource = resource + self.type = type + } + + var identifier: String + var resource: URL + var type: StyledAnnotationType + + var height: Float? + var color: UIColor? + var opacity: Float? + var transitionDuration: Float? + +} |