diff options
-rw-r--r-- | iosApp/iosApp/Map/BaseMapView.swift | 182 | ||||
-rw-r--r-- | iosApp/iosApp/Map/MapView.swift | 4 | ||||
-rw-r--r-- | iosApp/iosApp/Map/MapWrapperView.swift | 7 | ||||
-rw-r--r-- | iosApp/iosApp/Units/UnitsViewModel.swift | 21 |
4 files changed, 201 insertions, 13 deletions
diff --git a/iosApp/iosApp/Map/BaseMapView.swift b/iosApp/iosApp/Map/BaseMapView.swift index b247c30..322ae45 100644 --- a/iosApp/iosApp/Map/BaseMapView.swift +++ b/iosApp/iosApp/Map/BaseMapView.swift @@ -21,14 +21,16 @@ import WhirlyGlobeMaplyComponent import shared struct BaseMapView: UIViewControllerRepresentable { - typealias UIViewControllerType = MaplyViewController + typealias UIViewControllerType = OurMaplyViewController @Binding var mapLayer: MapLayer + @Binding var markers: [Marker] + var markerCallback: ((Int32?) -> Void)? var link: BaseMapLink class Coordinator: NSObject, MaplyViewControllerDelegate { var parent: BaseMapView - var uiViewController: MaplyViewController? + var uiViewController: OurMaplyViewController? var loader: MaplyQuadImageLoader? = nil // Source: https://stackoverflow.com/questions/65923718 @@ -48,8 +50,24 @@ struct BaseMapView: UIViewControllerRepresentable { self.parent = uiViewController } - func maplyViewController(_ viewC: MaplyViewController, didClick annotation: MaplyAnnotation) { - print("Clicked map! :D") + 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) + } + } + } } } @@ -57,8 +75,8 @@ struct BaseMapView: UIViewControllerRepresentable { Coordinator(self) } - func makeUIViewController(context: Context) -> MaplyViewController { - let mapViewController = MaplyViewController(mapType: .typeFlat) + func makeUIViewController(context: Context) -> OurMaplyViewController { + let mapViewController = OurMaplyViewController(mapType: .typeFlat) mapViewController.delegate = context.coordinator let tileInfo = Utils.tileInfoFrom(layer: mapLayer) @@ -87,7 +105,7 @@ struct BaseMapView: UIViewControllerRepresentable { return mapViewController } - func updateUIViewController(_ uiViewController: MaplyViewController, context: Context) { + func updateUIViewController(_ uiViewController: OurMaplyViewController, context: Context) { context.coordinator.uiViewController = uiViewController context.coordinator.link = link @@ -95,6 +113,10 @@ struct BaseMapView: UIViewControllerRepresentable { context.coordinator.loader?.changeTileInfo(Utils.tileInfoFrom(layer: mapLayer)) uiViewController.setZoomLimits(minZoom: mapLayer.minZoom, maxZoom: mapLayer.maxZoom) + + // MARK: - Set markers + uiViewController.display(markers: markers, + isReport: false) } static func dismantleUIViewController(_ uiViewController: MaplyViewController, coordinator: Coordinator) { @@ -103,12 +125,15 @@ struct BaseMapView: UIViewControllerRepresentable { } } -extension MaplyViewController { +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 { @@ -120,7 +145,9 @@ extension MaplyViewController { } } - func focusOn(point: MaplyCoordinate, height: Float = 0.0000264, animated: Bool = true) { + 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) @@ -141,6 +168,136 @@ extension MaplyViewController { 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: @@ -152,11 +309,16 @@ extension MaplyViewController { ?? 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: MaplyViewController.Action? + @Published var action: OurMaplyViewController.Action? func zoomIn() { action = .zoomIn diff --git a/iosApp/iosApp/Map/MapView.swift b/iosApp/iosApp/Map/MapView.swift index c5bbc8a..a34bf63 100644 --- a/iosApp/iosApp/Map/MapView.swift +++ b/iosApp/iosApp/Map/MapView.swift @@ -22,6 +22,8 @@ struct MapView: View { @StateObject var unitsViewModel: UnitsViewModel var body: some View { - MapWrapperView(layer: $unitsViewModel.mapLayerType) + MapWrapperView(layer: $unitsViewModel.mapLayerType, + markers: $unitsViewModel.markers, + markerCallback: unitsViewModel.selectUnitWith) } } diff --git a/iosApp/iosApp/Map/MapWrapperView.swift b/iosApp/iosApp/Map/MapWrapperView.swift index 0e9eaf8..cc56937 100644 --- a/iosApp/iosApp/Map/MapWrapperView.swift +++ b/iosApp/iosApp/Map/MapWrapperView.swift @@ -22,12 +22,17 @@ import shared struct MapWrapperView: View { @Binding var layer: MapLayer + @Binding var markers: [Marker] + var markerCallback: ((Int32?) -> Void)? @ObservedObject var link: BaseMapLink = BaseMapLink() var body: some View { ZStack { // MARK: - Map - BaseMapView(mapLayer: $layer, link: link) + BaseMapView(mapLayer: $layer, + markers: $markers, + markerCallback: markerCallback, + link: link) // MARK: - Attribution VStack { diff --git a/iosApp/iosApp/Units/UnitsViewModel.swift b/iosApp/iosApp/Units/UnitsViewModel.swift index 04c0436..dc6236f 100644 --- a/iosApp/iosApp/Units/UnitsViewModel.swift +++ b/iosApp/iosApp/Units/UnitsViewModel.swift @@ -49,7 +49,12 @@ class UnitsViewModel: ObservableObject { } @Published var isEditing = false @Published var unitsDisplayMode: UnitsDisplayMode = .list - @Published var units: [UnitInformation] = [] + @Published var units: [UnitInformation] = [] { + didSet { + markers = units.compactMap(Marker.companion.fromUnit) + } + } + @Published var markers: [Marker] = [] @Published var selectedUnit: UnitInformation? = nil @Published var mapLayerType: MapLayer = .companion.defaultLayer @Published var geofences: [Int: Geofence] = [:] @@ -81,6 +86,20 @@ class UnitsViewModel: ObservableObject { self.geofences = geofences } + func selectUnitWith(position id: Int32?) { + if id == nil { + print("Deselecting unit") + selectedUnit = nil + return + } + print("Selecting unit with position id: \(id ?? 0)") + if let unit = units.first(where: { + Int32(truncating: $0.position?.id ?? 0) == id + }) { + selectedUnit = unit + } + } + func toggleDisplayMode() { switch unitsDisplayMode { case .map: |