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)) } } }