Reputation: 289
I'm using MapKit to draw a straight line between two points on the map, and trying to put a marker in the center. When the distances are small, it all works fine, but with bigger distances, the marker in the middle is drifting off the line. Is it possible to make it more accurate? I'm using UIViewRepresentable of MKMapView because of the PolyLine, but I guess it shouldn't matter in this case. I'm copying MapView in case someone wants to reproduce, but it should be fine IMO. I center is just an example, ideally, I would like to put a marker on any part of the line.
struct ContentView: View {
let tfs = CLLocationCoordinate2D(latitude: 28.047477135170762, longitude: -16.572272806214418)
let london = CLLocationCoordinate2D(latitude: 51.507277135170762, longitude: 0.127672806214418)
private var lineCoordinates: [CLLocationCoordinate2D]
let middle: CLLocationCoordinate2D
init() {
self.lineCoordinates = [tfs, london]
middle = CLLocationCoordinate2D(latitude: (london.latitude + tfs.latitude) / 2, longitude: (london.longitude + tfs.longitude) / 2)
}
var body: some View {
MapView(
currentPosition: middle,
lineCoordinates: lineCoordinates
)
.edgesIgnoringSafeArea(.all)
}
}
struct MapView: UIViewRepresentable {
let currentPosition: CLLocationCoordinate2D
let lineCoordinates: [CLLocationCoordinate2D]
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
mapView.region = MKCoordinateRegion(coordinates: lineCoordinates)
let polyline = MKPolyline(coordinates: lineCoordinates, count: lineCoordinates.count)
mapView.addOverlay(polyline)
let annotation = MKPointAnnotation()
annotation.coordinate = currentPosition
annotation.title = "Current position"
mapView.addAnnotation(annotation)
return mapView
}
func updateUIView(_ view: MKMapView, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let routePolyline = overlay as? MKPolyline {
let renderer = MKPolylineRenderer(polyline: routePolyline)
renderer.strokeColor = UIColor.systemBlue
renderer.lineWidth = 10
return renderer
}
return MKOverlayRenderer()
}
}
extension MKCoordinateRegion {
init(coordinates: [CLLocationCoordinate2D], spanMultiplier: CLLocationDistance = 1.3) {
var topLeftCoord = CLLocationCoordinate2D(latitude: -90, longitude: 180)
var bottomRightCoord = CLLocationCoordinate2D(latitude: 90, longitude: -180)
for coordinate in coordinates {
topLeftCoord.longitude = min(topLeftCoord.longitude, coordinate.longitude)
topLeftCoord.latitude = max(topLeftCoord.latitude, coordinate.latitude)
bottomRightCoord.longitude = max(bottomRightCoord.longitude, coordinate.longitude)
bottomRightCoord.latitude = min(bottomRightCoord.latitude, coordinate.latitude)
}
let cent = CLLocationCoordinate2D.init(latitude: topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5, longitude: topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5)
let span = MKCoordinateSpan.init(latitudeDelta: abs(topLeftCoord.latitude - bottomRightCoord.latitude) * spanMultiplier, longitudeDelta: abs(bottomRightCoord.longitude - topLeftCoord.longitude) * spanMultiplier)
self.init(center: cent, span: span)
}
}
Upvotes: 0
Views: 95
Reputation: 289
I figured it out, actually I indeed need to use MKGeodesicPolyline
, so the line would follow the earth's curvature in terms of showing the shortest path, but also the way I calculated the middle of the line was incorrect
CLLocationCoordinate2D(latitude: (london.latitude + tfs.latitude) / 2, longitude: (london.longitude + tfs.longitude) / 2)
This only works on a 2D coordinate space, and cannot be used in real life scenarios, when calculating distances or paths on the map. There are some advanced formulas to do that, but I just ended up getting the points within the coordinate by this gist. I'm getting the coordinates of the line, and selecting from those points when I want to put a marker.
Upvotes: 0