diff options
Diffstat (limited to 'iosApp/iosApp/Map/MapView.swift')
-rw-r--r-- | iosApp/iosApp/Map/MapView.swift | 307 |
1 files changed, 16 insertions, 291 deletions
diff --git a/iosApp/iosApp/Map/MapView.swift b/iosApp/iosApp/Map/MapView.swift index 572db64..6b0fa20 100644 --- a/iosApp/iosApp/Map/MapView.swift +++ b/iosApp/iosApp/Map/MapView.swift @@ -21,310 +21,35 @@ import WhirlyGlobeMaplyComponent import shared struct MapView: UIViewControllerRepresentable { - typealias UIViewControllerType = OurMaplyViewController + typealias UIViewControllerType = MapViewController - @Binding var mapLayer: MapLayer + @Binding var layer: MapLayer @Binding var markers: [Marker] - var markerCallback: ((Int32?) -> Void)? - var link: BaseMapLink + var markerCallback: MarkerCallback? - class Coordinator: NSObject, MaplyViewControllerDelegate { - var parent: MapView - var uiViewController: OurMaplyViewController? - var loader: MaplyQuadImageLoader? = nil - - // Source: https://stackoverflow.com/questions/65923718 - var cancellable: AnyCancellable? - var link: BaseMapLink? { - didSet { - cancellable = link?.$action.sink(receiveValue: { action in - guard let action = action else { - return - } - self.uiViewController?.action(action) - }) - } - } - - init(_ uiViewController: MapView) { - self.parent = uiViewController - } - - func maplyViewController(_ viewC: MaplyViewController, - didTapAt coord: MaplyCoordinate) { - if let callback = parent.markerCallback { - callback(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 { - if let callback = parent.markerCallback { - callback(id) - } - } - } - } + class Coordinator { + var shouldCenter: Bool = true } func makeCoordinator() -> Coordinator { - Coordinator(self) + return Coordinator() } - func makeUIViewController(context: Context) -> OurMaplyViewController { - let mapViewController = OurMaplyViewController(mapType: .typeFlat) - mapViewController.delegate = context.coordinator - - let tileInfo = Utils.tileInfoFrom(layer: mapLayer) - mapViewController.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 - - let loader = MaplyQuadImageLoader(params: sampleParams, tileInfo: tileInfo, viewC: mapViewController) - loader?.baseDrawPriority = kMaplyImageLayerDrawPriorityDefault - loader?.imageFormat = .imageUShort565 - context.coordinator.loader = loader - - DispatchQueue.main.async { - let point = MaplyCoordinateMakeWithDegrees(-100.36, 23.191) - mapViewController.focusOn(point: point, height: 0.4, animated: false) - } - - return mapViewController + func makeUIViewController(context: Context) -> MapViewController { + let mapVC = MapViewController() + mapVC.markerCallback = markerCallback + mapVC.mapLayer = layer + return mapVC } - func updateUIViewController(_ uiViewController: OurMaplyViewController, context: Context) { - context.coordinator.uiViewController = uiViewController - context.coordinator.link = link - + func updateUIViewController(_ uiViewController: MapViewController, context: Context) { // MARK: - Set map layer - context.coordinator.loader?.changeTileInfo(Utils.tileInfoFrom(layer: mapLayer)) - uiViewController.setZoomLimits(minZoom: mapLayer.minZoom, - maxZoom: mapLayer.maxZoom) + uiViewController.setMapLayer(layer) // MARK: - Set markers uiViewController.display(markers: markers, - isReport: false) - } - - static func dismantleUIViewController(_ uiViewController: MaplyViewController, coordinator: Coordinator) { - coordinator.loader?.shutdown() - uiViewController.teardown() - } -} - -class OurMaplyViewController: MaplyViewController { - enum Action { - case zoomIn - case zoomOut - } - - private var objects = [MaplyComponentObject]() - private var geofenceObjects = [MaplyComponentObject]() - - func action(_ action: Action) { - DispatchQueue.main.async { - switch action { - case .zoomIn: - self.zoomIn() - case .zoomOut: - self.zoomOut() - } - } - } - - 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 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 = Color.green - let colorLabel = Color.secondary - let colorLabelOutline = Color.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[..<end]) - } - label.text = text - label.loc = MaplyCoordinateMakeWithDegrees(Float(marker.longitude), - Float(marker.latitude)) - label.offset = CGPoint(x: 0.0, y: 25.0) - - return label - } - - if let objs = addScreenLabels(screenLabels, desc: labelDesc) { - objects.append(objs) - } - } - - /* MARK: - Draw polyline for report */ - if isReport && !markers.isEmpty { - let geoJSON: [AnyHashable : Any] = [ - "type": "FeatureCollection", - "features": [ - [ - "type": "LineString", - "coordinates": points.map({ point in - [point.x, point.y] - }) - ] - ] - ] - if let vector = MaplyVectorObject(fromGeoJSONDictionary: geoJSON) { - if let objs = addVectors([vector], desc: vectorDesc, mode: .any) { - objects.append(objs) - } - } - } - - /* MARK: - Center map to bounds */ - if center && !markers.isEmpty { - let box = MaplyBoundingBoxExpandByFraction( - MaplyBoundingBoxFromCoordinates(points, UInt32(points.count)), 0.1) - let center = MaplyCoordinate(x: (box.ur.x + box.ll.x) / 2, - y: (box.ur.y + box.ll.y) / 2) - let zoom = max(findHeight(toViewBounds: box, pos: center), getMinZoom()) - setPosition(center, height: zoom) - } - } - - func setZoomLimits(minZoom: Int32, maxZoom: Int32) { - setZoomLimitsMin( - height(forMapScale: Float(truncating: - MapCalculus.companion.zoomLevelToScale(zoom: maxZoom) - ?? MapCalculus.companion.zoomLevelToScale(zoom: 21)! - )), - max: height(forMapScale: Float(truncating: - MapCalculus.companion.zoomLevelToScale(zoom: minZoom) - ?? MapCalculus.companion.zoomLevelToScale(zoom: 1)! - ))) - } - - private func getIcon(markerType: Marker.Type_) -> UIImage { - return UIImage(named: MarkerTransformations - .markerTypeToImageName(markerType: markerType))! - } -} - -// Source: https://stackoverflow.com/questions/65923718 -class BaseMapLink: ObservableObject { - @Published var action: OurMaplyViewController.Action? - - func zoomIn() { - action = .zoomIn - } - - func zoomOut() { - action = .zoomOut + isReport: false, + center: context.coordinator.shouldCenter) + context.coordinator.shouldCenter = false } } |