/** * TrackerMap * Copyright (C) 2021-2022 Iván Ávalos , Henoch Ojeda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ import UIKit import WhirlyGlobe import shared typealias MarkerCallback = (Int32?) -> () class MapViewController: UIViewController { var markerCallback: MarkerCallback? = nil var mapLayer: MapLayer = MapLayer.companion.defaultLayer @IBOutlet weak var mapContainer: UIView! @IBOutlet weak var attributionText: UITextView! private var mapView: OurMaplyViewController! override func viewDidLoad() { super.viewDidLoad() // MARK: - Initialise MapViewController mapView = OurMaplyViewController(mapType: .typeFlat) mapView.delegate = self view.sendSubviewToBack(mapContainer) mapContainer.addSubview(mapView.view) mapView.view.frame = mapContainer.bounds addChild(mapView) setAttributionText(mapLayer) // MARK: - Configure MaplyViewController let tileInfo = Utils.tileInfoFrom(layer: mapLayer) mapView.setZoomLimits(minZoom: mapLayer.minZoom, maxZoom: mapLayer.maxZoom) let sampleParams = MaplySamplingParams() sampleParams.coordSys = MaplySphericalMercator(webStandard: ()) sampleParams.coverPoles = true sampleParams.edgeMatching = true sampleParams.minZoom = tileInfo.minZoom sampleParams.maxZoom = tileInfo.maxZoom sampleParams.singleLevel = true sampleParams.maxTiles = 25 if let loader = MaplyQuadImageLoader(params: sampleParams, tileInfo: tileInfo, viewC: mapView) { mapView.setLoader(loader) } mapView.runOnInit { let point = MaplyCoordinateMakeWithDegrees(-100.36, 23.191) self.mapView.setPosition(point, height: 0.4) } } func setMapLayer(_ layer: MapLayer) { setAttributionText(layer) mapView.setLayer(layer) } func display(markers: [Marker], isReport: Bool, center: Bool = false) { DispatchQueue.main.async { self.mapView.display(markers: markers, isReport: isReport, center: center) } } func focusOn(marker: Marker) { DispatchQueue.main.async { self.mapView.focusOn(marker: marker) } } func dismantle() { mapView.dismantle() } private func setAttributionText(_ layer: MapLayer) { DispatchQueue.main.async { self.attributionText.attributedText = HtmlString.htmlToAttrStr( layer.attribution, size: UIFont.smallSystemFontSize, color: UIColor.label) } attributionText.sizeToFit() attributionText.layoutIfNeeded() } @IBAction func onZoomInPressed(_ sender: UIButton) { mapView.zoomIn() } @IBAction func onZoomOutPressed(_ sender: UIButton) { mapView.zoomOut() } } extension MapViewController: MaplyViewControllerDelegate { func maplyViewController(_ viewC: MaplyViewController, didTapAt coord: MaplyCoordinate) { markerCallback?(nil) } func maplyViewController(_ viewC: MaplyViewController, didSelect selectedObj: NSObject, atLoc coord: MaplyCoordinate, onScreen screenPt: CGPoint) { if let marker = selectedObj as? MaplyScreenMarker { if let id = marker.userObject as? Int32 { markerCallback?(id) } } } } class OurMaplyViewController: MaplyViewController { private var loader: MaplyQuadImageLoader? = nil private var objects = [MaplyComponentObject]() private var geofenceObjects = [MaplyComponentObject]() func runOnInit(callback: @escaping () -> ()) { addPostInitBlock { callback() } } func setLoader(_ loader: MaplyQuadImageLoader) { self.loader = loader } func setLayer(_ layer: MapLayer) { loader?.changeTileInfo(Utils.tileInfoFrom(layer: layer)) setZoomLimits(minZoom: layer.minZoom, maxZoom: layer.maxZoom) } func focusOn(point: MaplyCoordinate, height: Float = 0.0000264, animated: Bool = true) { let z = max(height, getMinZoom()) if animated { animate(toPosition: point, height: z, time: 0.2) } else { setPosition(point, height: z) } } func focusOn(marker: Marker, animated: Bool = true) { let point = MaplyCoordinateMakeWithDegrees(Float(marker.longitude), Float(marker.latitude)) focusOn(point: point, animated: animated) } func zoomIn() { let pos = getPosition() let zoom = currentMapScale() / 2 focusOn(point: pos, height: height(forMapScale: zoom)) } func zoomOut() { let pos = getPosition() let zoom = currentMapScale() * 2 focusOn(point: pos, height: height(forMapScale: zoom)) } func clear(geofences: Bool = false) { if geofences { remove(geofenceObjects) geofenceObjects.removeAll() } else { remove(objects) objects.removeAll() } } func display(markers: [Marker], isReport: Bool, center: Bool = false) { clear() let points = markers.map { marker in MaplyCoordinateMakeWithDegrees(Float(marker.longitude), Float(marker.latitude)) } let fontSize = 11.0 let colorReport = UIColor.green let colorLabel = UIColor.secondaryLabel let colorLabelOutline = UIColor.systemBackground let vectorDesc: [AnyHashable : Any] = [ kMaplyColor: colorReport, kMaplyVecWidth: 20.0 ] let labelDesc: [AnyHashable : Any] = [ kMaplyFont: UIFont.boldSystemFont(ofSize: fontSize), kMaplyTextColor: colorLabel, kMaplyTextOutlineColor: colorLabelOutline, kMaplyTextOutlineSize: 3.0 ] /* MARK: - Draw markers for positions */ let screenMarkers = markers.enumerated().map { (i, marker) -> MaplyScreenMarker in let screenMarker = MaplyScreenMarker() screenMarker.layoutImportance = .greatestFiniteMagnitude screenMarker.loc = MaplyCoordinateMakeWithDegrees(Float(marker.longitude), Float(marker.latitude)) var type: Marker.Type_ = .default_ if isReport { // For reports, position, start and end icons must be different switch i { case markers.startIndex: type = .reportStart case markers.endIndex: type = .reportEnd default: type = .reportPosition } } else { type = marker.type } screenMarker.image = getIcon(markerType: type) var size = 50.0 if isReport { // For reports, position, start and end sizes must be different switch i { case markers.startIndex: size = 50.0 case markers.endIndex: size = 50.0 default: size = 25.0 } } screenMarker.size = CGSize(width: size, height: size) screenMarker.userObject = marker.id screenMarker.selectable = true return screenMarker } if let objs = addScreenMarkers(screenMarkers, desc: nil, mode: .any) { objects.append(objs) } /* MARK: - Add labels for markers */ if !isReport && !markers.isEmpty { let screenLabels = markers.map { marker -> MaplyScreenLabel in let label = MaplyScreenLabel() label.layoutImportance = .greatestFiniteMagnitude var text = marker.name if marker.name.count >= 20 { let end = marker.name.index(marker.name.startIndex, offsetBy: 20) text = String(marker.name[.. UIImage { return UIImage(named: MarkerTransformations .markerTypeToImageName(markerType: markerType))! } }