84 lines
2.8 KiB
Swift
84 lines
2.8 KiB
Swift
import Foundation
|
|
import AppKit
|
|
import MapKit
|
|
|
|
struct MapImageCreator {
|
|
|
|
let locations: [CLLocation]
|
|
|
|
func createMapSnapshot(
|
|
size layoutSize: CGSize,
|
|
scale: CGFloat = 2.0,
|
|
lineWidth: CGFloat = 5,
|
|
paddingFactor: Double = 1.2,
|
|
completion: @escaping ((image: NSImage, imagePoints: [CGPoint])?) -> Void
|
|
) {
|
|
guard !locations.isEmpty else {
|
|
completion(nil)
|
|
return
|
|
}
|
|
let coordinates = locations.map { $0.coordinate }
|
|
|
|
let pixelSize = CGSize(width: layoutSize.width * scale, height: layoutSize.height * scale)
|
|
|
|
let options = MKMapSnapshotter.Options()
|
|
options.size = pixelSize
|
|
options.preferredConfiguration = MKHybridMapConfiguration(elevationStyle: .flat)
|
|
|
|
let polyline = MKPolyline(coordinates: coordinates, count: coordinates.count)
|
|
let boundingMapRect = polyline.boundingMapRect
|
|
let region = MKCoordinateRegion(boundingMapRect)
|
|
|
|
let latDelta = region.span.latitudeDelta * paddingFactor
|
|
let lonDelta = region.span.longitudeDelta * paddingFactor
|
|
let paddedRegion = MKCoordinateRegion(
|
|
center: region.center,
|
|
span: MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
|
|
)
|
|
|
|
options.region = paddedRegion
|
|
|
|
let snapshotter = MKMapSnapshotter(options: options)
|
|
snapshotter.start { snapshotOrNil, error in
|
|
guard let snapshot = snapshotOrNil, error == nil else {
|
|
print("Snapshot error: \(error?.localizedDescription ?? "unknown error")")
|
|
completion(nil)
|
|
return
|
|
}
|
|
|
|
let image = NSImage(size: pixelSize)
|
|
image.lockFocus()
|
|
|
|
snapshot.image.draw(in: CGRect(origin: .zero, size: pixelSize))
|
|
|
|
let path = NSBezierPath()
|
|
path.lineJoinStyle = .round
|
|
let imagePoints = coordinates.map { snapshot.point(for: $0) }
|
|
|
|
if let first = imagePoints.first {
|
|
path.move(to: first)
|
|
for point in imagePoints.dropFirst() {
|
|
path.line(to: point)
|
|
}
|
|
|
|
NSColor.systemBlue.setStroke()
|
|
path.lineWidth = lineWidth * scale
|
|
path.stroke()
|
|
}
|
|
|
|
image.unlockFocus()
|
|
|
|
// Recalculate imagePoints since they were inside the drawing block
|
|
let widthFactor = 1 / pixelSize.width
|
|
let heightFactor = 1 / pixelSize.height
|
|
let finalImagePoints = coordinates.map { coordinate in
|
|
let point = snapshot.point(for: coordinate)
|
|
return CGPoint(x: point.x * widthFactor,
|
|
y: point.y * heightFactor)
|
|
}
|
|
|
|
completion((image, finalImagePoints))
|
|
}
|
|
}
|
|
}
|