Meshtastic-Apple/MeshtasticApple/Views/Map/MapView.swift

174 lines
5.3 KiB
Swift
Raw Normal View History

//
// MapView.swift
// MeshtasticApple
//
// Created by Joshua Pirihi on 22/12/21.
//
import Foundation
import UIKit
import MapKit
import SwiftUI
import CoreData
2022-01-10 06:09:31 +13:00
#if false
2021-12-25 23:48:12 -08:00
// wrap a MKMapView into something we can use in SwiftUI
struct MapView: UIViewRepresentable {
2021-12-25 23:48:12 -08:00
var nodes: FetchedResults<NodeInfoEntity>
2021-12-25 23:48:12 -08:00
2022-01-10 06:09:31 +13:00
var mapViewDelegate = MapViewDelegate()
2021-12-25 23:48:12 -08:00
// observe changes to the key in UserDefaults
@AppStorage("meshMapType") var type: String = "hybrid"
2021-12-25 23:48:12 -08:00
func makeUIView(context: Context) -> MKMapView {
2021-12-25 23:48:12 -08:00
let map = MKMapView(frame: .zero)
2021-12-25 23:48:12 -08:00
map.userTrackingMode = .follow
2021-12-25 23:48:12 -08:00
let region = MKCoordinateRegion( center: map.centerCoordinate, latitudinalMeters: CLLocationDistance(exactly: 500)!, longitudinalMeters: CLLocationDistance(exactly: 500)!)
map.setRegion(map.regionThatFits(region), animated: false)
//self.updateMapType(map)
self.showNodePositions(to: map)
self.moveToMeshRegion(in: map)
2021-12-25 23:48:12 -08:00
map.register(PositionAnnotationView.self, forAnnotationViewWithReuseIdentifier: NSStringFromClass(PositionAnnotationView.self))
2021-12-25 23:48:12 -08:00
2022-01-10 06:09:31 +13:00
let overlay = MKTileOverlay(urlTemplate: //"http://tiles-a.data-cdn.linz.govt.nz/services;key=7fa19132d53240708c4ff436df5b9800/tiles/v4/layer=50767/EPSG:3857/{z}/{x}/{y}.png")
"http://10.147.253.250:5050/local/map/{z}/{x}/{y}.png")
overlay.canReplaceMapContent = true
self.mapViewDelegate.renderer = MKTileOverlayRenderer(tileOverlay: overlay)
map.addOverlay(overlay)
return map
}
func updateUIView(_ view: MKMapView, context: Context) {
view.delegate = mapViewDelegate // (1) This should be set in makeUIView, but it is getting reset to `nil`
view.translatesAutoresizingMaskIntoConstraints = false // (2) In the absence of this, we get constraints error on rotation; and again, it seems one should do this in makeUIView, but has to be here
2021-12-25 23:48:12 -08:00
self.updateMapType(view)
2021-12-25 23:48:12 -08:00
self.showNodePositions(to: view)
//if (self.needToMoveToMeshRegion) {
// self.moveToMeshRegion(in: view)
// self.needToMoveToMeshRegion = false
//}
}
func moveToMeshRegion(in mapView: MKMapView) {
//go through the annotations and create a bounding box that encloses them
var minLat: CLLocationDegrees = 90.0
var maxLat: CLLocationDegrees = -90.0
var minLon: CLLocationDegrees = 180.0
var maxLon: CLLocationDegrees = -180.0
for annotation in mapView.annotations {
if annotation.isKind(of: PositionAnnotation.self) {
minLat = min(minLat, annotation.coordinate.latitude)
maxLat = max(maxLat, annotation.coordinate.latitude)
minLon = min(minLon, annotation.coordinate.longitude)
maxLon = max(maxLon, annotation.coordinate.longitude)
}
}
//check if the mesh region looks sensible before we move to it. Otherwise we won't move the map (leave it at the current location)
if maxLat < minLat || (maxLat-minLat) > 5 || maxLon < minLon || (maxLon-minLon) > 5 {
return
}
let centerCoord = CLLocationCoordinate2D(latitude: (minLat+maxLat)/2, longitude: (minLon+maxLon)/2)
let span = MKCoordinateSpan(latitudeDelta: (maxLat-minLat)*1.5, longitudeDelta: (maxLon-minLon)*1.5)
let region = mapView.regionThatFits(MKCoordinateRegion(center: centerCoord, span: span))
mapView.setRegion(region, animated: true)
}
2021-12-25 23:48:12 -08:00
func updateMapType(_ map: MKMapView) {
2021-12-25 23:48:12 -08:00
switch self.type {
case "satellite":
map.mapType = .satellite
break
case "standard":
map.mapType = .standard
break
case "hybrid":
map.mapType = .hybrid
break
default:
map.mapType = .hybrid
}
}
}
private extension MapView {
func showNodePositions(to view: MKMapView) {
2021-12-25 23:48:12 -08:00
// clear any existing annotations
if !view.annotations.isEmpty {
view.removeAnnotations(view.annotations)
}
2021-12-25 23:48:12 -08:00
for node in self.nodes {
2021-12-25 23:48:12 -08:00
// try and get the last position
if (node.positions?.count ?? 0) > 0 && (node.positions!.lastObject as! PositionEntity).coordinate != nil {
let annotation = PositionAnnotation()
annotation.coordinate = (node.positions!.lastObject as! PositionEntity).coordinate!
annotation.title = node.user?.longName ?? "Unknown"
annotation.shortName = node.user?.shortName?.uppercased() ?? "???"
2021-12-25 23:48:12 -08:00
view.addAnnotation(annotation)
}
}
}
}
class MapViewDelegate: NSObject, MKMapViewDelegate {
2021-12-25 23:48:12 -08:00
2022-01-10 06:09:31 +13:00
var renderer: MKTileOverlayRenderer?
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard !annotation.isKind(of: MKUserLocation.self) else {
// Make a fast exit if the annotation is the `MKUserLocation`, as it's not an annotation view we wish to customize.
return nil
}
2021-12-25 23:48:12 -08:00
var annotationView: MKAnnotationView?
2021-12-25 23:48:12 -08:00
if let annotation = annotation as? PositionAnnotation {
annotationView = self.setupPositionAnnotationView(for: annotation, on: mapView)
}
2021-12-25 23:48:12 -08:00
return annotationView
}
2022-01-10 06:09:31 +13:00
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
return self.renderer!
}
2021-12-25 23:48:12 -08:00
private func setupPositionAnnotationView(for annotation: PositionAnnotation, on mapView: MKMapView) -> PositionAnnotationView {
let identifier = NSStringFromClass(PositionAnnotationView.self)
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? PositionAnnotationView ?? PositionAnnotationView()
2021-12-25 23:48:12 -08:00
annotationView.name = annotation.shortName ?? "???"
2021-12-25 23:48:12 -08:00
annotationView.canShowCallout = true
2021-12-25 23:48:12 -08:00
return annotationView
}
}
2022-01-10 06:09:31 +13:00
#endif