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, lineColor: NSColor = .systemBlue ) -> (image: NSImage, imagePoints: [CGPoint])? { let semaphore = DispatchSemaphore(value: 0) var result: (image: NSImage, imagePoints: [CGPoint])? self.createMapSnapshot( size: layoutSize, scale: scale, lineWidth: lineWidth, paddingFactor: paddingFactor, lineColor: lineColor ) { res in result = res semaphore.signal() } semaphore.wait() return result } func createMapSnapshot( size layoutSize: CGSize, scale: CGFloat = 2.0, lineWidth: CGFloat = 5, paddingFactor: Double = 1.2, lineColor: NSColor = .systemBlue ) async -> (image: NSImage, imagePoints: [CGPoint])? { await withCheckedContinuation { c in self.createMapSnapshot( size: layoutSize, scale: scale, lineWidth: lineWidth, paddingFactor: paddingFactor, lineColor: lineColor ) { c.resume(returning: $0) } } } func createMapSnapshot( size layoutSize: CGSize, scale: CGFloat = 2.0, lineWidth: CGFloat = 5, paddingFactor: Double = 1.2, lineColor: NSColor = .systemBlue, 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) } lineColor.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)) } } }