diff options
-rw-r--r-- | iosApp/iosApp.xcodeproj/project.pbxproj | 17 | ||||
-rw-r--r-- | iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 18 | ||||
-rw-r--r-- | iosApp/iosApp/Details/Reports/UnitReportsView.swift | 1 | ||||
-rw-r--r-- | iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift | 15 | ||||
-rw-r--r-- | iosApp/iosApp/Map/MapView.swift | 9 | ||||
-rw-r--r-- | iosApp/iosApp/Map/MapViewController.swift | 103 | ||||
-rw-r--r-- | iosApp/iosApp/Map/UnitMapView.swift | 1 | ||||
-rw-r--r-- | iosApp/iosApp/Units/UnitsViewModel.swift | 7 |
8 files changed, 167 insertions, 4 deletions
diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 196675f..8aefd98 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ E39ABC4627A4EBD500965D05 /* DevicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E39ABC4527A4EBD500965D05 /* DevicesView.swift */; }; E39ABC4827A4EDEC00965D05 /* DeviceRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E39ABC4727A4EDEC00965D05 /* DeviceRow.swift */; }; E3C651E727CB5426002F6F4C /* Tabler in Frameworks */ = {isa = PBXBuildFile; productRef = E3C651E627CB5426002F6F4C /* Tabler */; }; + E3C651EA27CB61EE002F6F4C /* GEOSwift in Frameworks */ = {isa = PBXBuildFile; productRef = E3C651E927CB61EE002F6F4C /* GEOSwift */; }; E3E77EE6279E6CE400150070 /* FlowCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E77EE5279E6CE400150070 /* FlowCollector.swift */; }; /* End PBXBuildFile section */ @@ -114,6 +115,7 @@ E3C651E727CB5426002F6F4C /* Tabler in Frameworks */, E36A5A8627B4BFC40070DED5 /* FirebaseMessaging in Frameworks */, E33A236A27A6898700DD647F /* SwiftUIX in Frameworks */, + E3C651EA27CB61EE002F6F4C /* GEOSwift in Frameworks */, E396281D27AF5723005D070E /* WhirlyGlobe.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -292,6 +294,7 @@ E33A236927A6898700DD647F /* SwiftUIX */, E36A5A8527B4BFC40070DED5 /* FirebaseMessaging */, E3C651E627CB5426002F6F4C /* Tabler */, + E3C651E927CB61EE002F6F4C /* GEOSwift */, ); productName = iosApp; productReference = 7555FF7B242A565900829871 /* iosApp.app */; @@ -327,6 +330,7 @@ E33A236827A6898700DD647F /* XCRemoteSwiftPackageReference "SwiftUIX" */, E36A5A8427B4BFC40070DED5 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, E3C651E527CB5425002F6F4C /* XCRemoteSwiftPackageReference "SwiftTabler" */, + E3C651E827CB61EE002F6F4C /* XCRemoteSwiftPackageReference "GEOSwift" */, ); productRefGroup = 7555FF7C242A565900829871 /* Products */; projectDirPath = ""; @@ -660,6 +664,14 @@ minimumVersion = 0.6.0; }; }; + E3C651E827CB61EE002F6F4C /* XCRemoteSwiftPackageReference "GEOSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/GEOSwift/GEOSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 9.0.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -678,6 +690,11 @@ package = E3C651E527CB5425002F6F4C /* XCRemoteSwiftPackageReference "SwiftTabler" */; productName = Tabler; }; + E3C651E927CB61EE002F6F4C /* GEOSwift */ = { + isa = XCSwiftPackageProductDependency; + package = E3C651E827CB61EE002F6F4C /* XCRemoteSwiftPackageReference "GEOSwift" */; + productName = GEOSwift; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 7555FF73242A565900829871 /* Project object */; diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3c172cd..2b63f64 100644 --- a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -29,6 +29,24 @@ } }, { + "package": "geos", + "repositoryURL": "https://github.com/GEOSwift/geos.git", + "state": { + "branch": null, + "revision": "dc3a1e798e7ebb3cee1b6b219af2b0783820ff60", + "version": "7.0.0" + } + }, + { + "package": "GEOSwift", + "repositoryURL": "https://github.com/GEOSwift/GEOSwift", + "state": { + "branch": null, + "revision": "ab813f1acbd92986e8733356b73b3f6d4ddc08e0", + "version": "9.0.0" + } + }, + { "package": "GoogleAppMeasurement", "repositoryURL": "https://github.com/google/GoogleAppMeasurement.git", "state": { diff --git a/iosApp/iosApp/Details/Reports/UnitReportsView.swift b/iosApp/iosApp/Details/Reports/UnitReportsView.swift index 910cea4..5a34ea5 100644 --- a/iosApp/iosApp/Details/Reports/UnitReportsView.swift +++ b/iosApp/iosApp/Details/Reports/UnitReportsView.swift @@ -100,6 +100,7 @@ struct UnitReportsView: View { } else { MapView(layer: $unitReportsViewModel.layer, markers: $unitReportsViewModel.markers, + geofences: $unitReportsViewModel.flatGeofences, selected: $unitReportsViewModel.selectedMarker, isReport: true) } diff --git a/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift b/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift index 5e85e89..4731a57 100644 --- a/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift +++ b/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift @@ -12,6 +12,7 @@ import shared class UnitReportsViewModel: ObservableObject { @Inject var reportController: ReportController + @Inject var geofencesController: GeofencesController @Published var deviceId: Int32? = nil @Published var reportType: ReportController.ReportType = .positions { @@ -42,16 +43,30 @@ class UnitReportsViewModel: ObservableObject { @Published var markers = [Marker]() @Published var selectedMarker: Marker? = nil + @Published var geofences: [Int: Geofence] = [:] { + didSet { + flatGeofences = Array(geofences.values) + } + } + @Published var flatGeofences: [Geofence] = [] + init(deviceId id: Int32?) { deviceId = id let collector = Collector<ReportController.Report>(callback: setReport) reportController.reportFlow.collect(collector: collector) { _, _ in } + + let geofencesCollector = Collector<[Int: Geofence]>(callback: setGeofences) + geofencesController.geofencesFlow.collect(collector: geofencesCollector) { unit, error in } } func setReport (report: ReportController.Report) { self.report = report } + private func setGeofences(geofences: [Int: Geofence]) { + self.geofences = geofences + } + func fetchReport(xlsx: Bool = false) { if let id = deviceId { reportController.fetchReport(deviceId: id, diff --git a/iosApp/iosApp/Map/MapView.swift b/iosApp/iosApp/Map/MapView.swift index 05bd9f9..84bdc05 100644 --- a/iosApp/iosApp/Map/MapView.swift +++ b/iosApp/iosApp/Map/MapView.swift @@ -25,6 +25,7 @@ struct MapView: UIViewControllerRepresentable { @Binding var layer: MapLayer @Binding var markers: [Marker] + @Binding var geofences: [Geofence] @Binding var selected: Marker? var isReport: Bool = false var markerCallback: MarkerCallback? @@ -32,6 +33,7 @@ struct MapView: UIViewControllerRepresentable { class Coordinator { var shouldCenter: Bool = true var oldMarkers: [Marker] = [] + var oldGeofences: [Geofence] = [] } func makeCoordinator() -> Coordinator { @@ -51,7 +53,6 @@ struct MapView: UIViewControllerRepresentable { // MARK: - Set markers if context.coordinator.oldMarkers != markers { - print("center = \(context.coordinator.shouldCenter)") uiViewController.display(markers: markers, isReport: isReport, center: context.coordinator.shouldCenter) @@ -59,6 +60,12 @@ struct MapView: UIViewControllerRepresentable { } context.coordinator.oldMarkers = markers + // MARK: - Set geofences + if context.coordinator.oldGeofences != geofences { + uiViewController.display(geofences: geofences) + } + context.coordinator.oldGeofences = geofences + // MARK: - Center selected marker if let selected = selected { uiViewController.focusOn(marker: selected) diff --git a/iosApp/iosApp/Map/MapViewController.swift b/iosApp/iosApp/Map/MapViewController.swift index f068bf4..3c38b89 100644 --- a/iosApp/iosApp/Map/MapViewController.swift +++ b/iosApp/iosApp/Map/MapViewController.swift @@ -18,9 +18,23 @@ import UIKit import WhirlyGlobe import shared +import GEOSwift typealias MarkerCallback = (Int32?) -> () +extension Geometry { + func getCoordinates() -> [Point] { + switch self { + case let .polygon(polygon): + return polygon.exterior.points + case let .lineString(lineString): + return lineString.points + default: + return [] + } + } +} + class MapViewController: UIViewController { var markerCallback: MarkerCallback? = nil var mapLayer: MapLayer = MapLayer.companion.defaultLayer @@ -84,6 +98,12 @@ class MapViewController: UIViewController { } } + func display(geofences: [Geofence]) { + mapView.runOnInit { + self.mapView.display(geofences: geofences) + } + } + func focusOn(marker: Marker) { mapView.runOnInit { self.mapView.focusOn(marker: marker) @@ -206,8 +226,8 @@ class OurMaplyViewController: MaplyViewController { let fontSize = 11.0 let colorReport = UIColor(red: 0.0, green: 0.5, blue: 0.0, alpha: 1.0) - let colorLabel = UIColor.secondaryLabel - let colorLabelOutline = UIColor.systemBackground + let colorLabel = UIColor.darkGray + let colorLabelOutline = UIColor.white let vectorDesc: [AnyHashable : Any] = [ kMaplyColor: colorReport, @@ -319,6 +339,85 @@ class OurMaplyViewController: MaplyViewController { } } + func display(geofences: [Geofence]) { + clear(geofences: true) + + let fontSize = 11.0 + let colorFill = UIColor(red: 0.10, green: 0.46, blue: 0.82, alpha: 1.00) + let colorLabel = UIColor(red: 0.10, green: 0.46, blue: 0.82, alpha: 1.00) + let colorLabelOutline = UIColor.white + + let vectorDesc: [AnyHashable : Any] = [ + kMaplyColor: colorFill, + kMaplyVecWidth: 12.0, + kMaplyWideVecImpl: kMaplyWideVecImplPerf + ] + + let labelDesc: [AnyHashable : Any] = [ + kMaplyFont: UIFont.boldSystemFont(ofSize: fontSize), + kMaplyTextColor: colorLabel, + kMaplyTextOutlineColor: colorLabelOutline, + kMaplyTextOutlineSize: 3.0 + ] + + let shapes = [MaplyShape]() + var vectors = [MaplyVectorObject]() + var labels = [MaplyScreenLabel]() + + geofences.forEach { geofence in + if let area = geofence.area { + print ("Draw geofence with area = \(area)") + if let geometry = try? Geometry(wkt: area) { + switch geometry { + case .polygon(_): + let geoJSON: [AnyHashable : Any] = [ + "type": "FeatureCollection", + "features": [ + [ + "type": "Feature", + "properties": [], + "geometry": [ + "type": "Polygon", + "coordinates": [ + geometry.getCoordinates().map { coordinate in + [coordinate.y, coordinate.x] + } + ] + ] + ] + ] + ] + if let vector = MaplyVectorObject(fromGeoJSONDictionary: geoJSON) { + vectors.append(vector) + } + default: + break + } + + if let centroid = try? geometry.centroid() { + let label = MaplyScreenLabel() + label.text = geofence.name + label.loc = MaplyCoordinateMakeWithDegrees(Float(centroid.y), + Float(centroid.x)) + label.layoutImportance = .infinity + + labels.append(label) + } + } + } + } + + if let objs = addShapes(shapes, desc: nil, mode: .any) { + geofenceObjects.append(objs) + } + if let objs = addWideVectors(vectors, desc: vectorDesc, mode: .any) { + geofenceObjects.append(objs) + } + if let objs = addScreenLabels(labels, desc: labelDesc, mode: .any) { + geofenceObjects.append(objs) + } + } + func setZoomLimits(minZoom: Int32, maxZoom: Int32) { setZoomLimitsMin( height(forMapScale: Float(truncating: diff --git a/iosApp/iosApp/Map/UnitMapView.swift b/iosApp/iosApp/Map/UnitMapView.swift index 533ac13..f416554 100644 --- a/iosApp/iosApp/Map/UnitMapView.swift +++ b/iosApp/iosApp/Map/UnitMapView.swift @@ -25,6 +25,7 @@ struct UnitMapView: View { ZStack { MapView(layer: $unitsViewModel.mapLayerType, markers: $unitsViewModel.markers, + geofences: $unitsViewModel.flatGeofences, selected: $unitsViewModel.selectedMarker, markerCallback: unitsViewModel.selectUnitWith) if let unit = unitsViewModel.selectedUnit { diff --git a/iosApp/iosApp/Units/UnitsViewModel.swift b/iosApp/iosApp/Units/UnitsViewModel.swift index 47adeb7..8ba6a4b 100644 --- a/iosApp/iosApp/Units/UnitsViewModel.swift +++ b/iosApp/iosApp/Units/UnitsViewModel.swift @@ -60,7 +60,12 @@ class UnitsViewModel: ObservableObject { } @Published var selectedMarker: Marker? = nil @Published var mapLayerType: MapLayer = .companion.defaultLayer - @Published var geofences: [Int: Geofence] = [:] + @Published var geofences: [Int: Geofence] = [:] { + didSet { + flatGeofences = Array(geofences.values) + } + } + @Published var flatGeofences: [Geofence] = [] init() { unitsController.fetchUnits(scope: mainScope) |