Created
July 16, 2018 01:23
-
-
Save yllan/df33b8b1b78f714dfd7d5048a9a6189b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Cocoa | |
import MapKit | |
class MultilineCluster: NSObject, MKOverlay { | |
var coordinate: CLLocationCoordinate2D | |
var boundingMapRect: MKMapRect | |
var lines: [[MKMapPoint]] = [] | |
var strokeColor: NSColor = .black | |
init(coords: [[CLLocationCoordinate2D]]) { | |
self.lines = coords.map{$0.map(MKMapPointForCoordinate)} | |
let xs = self.lines.flatMap({$0}).map({$0.x}) | |
let ys = self.lines.flatMap({$0}).map({$0.y}) | |
guard let xmin = xs.min(), let xmax = xs.max(), | |
let ymin = ys.min(), let ymax = ys.max() | |
else { | |
self.coordinate = CLLocationCoordinate2D(latitude: 0, longitude: 0) | |
self.boundingMapRect = MKMapRect(origin: MKMapPoint(x: 0, y: 0), size: MKMapSize(width: 0, height: 0)) | |
return | |
} | |
self.boundingMapRect = MKMapRect(origin: MKMapPoint(x: xmin, y: ymin), size: MKMapSizeMake(xmax - xmin, ymax - ymin)) | |
self.coordinate = MKCoordinateForMapPoint(MKMapPoint(x: xmin + (xmax - xmin) / 2, y: ymin + (ymax - ymin) / 2)) | |
} | |
} | |
class MultilineClusterRenderer: MKOverlayPathRenderer { | |
override func createPath() { | |
guard let cluster = overlay as? MultilineCluster else { return } | |
let path = CGMutablePath() | |
for line in cluster.lines { | |
guard line.count > 1 else { continue } | |
path.move(to: point(for: line[0])) | |
for idx in 1..<line.count { | |
path.addLine(to: point(for: line[idx])) | |
} | |
} | |
self.path = path | |
} | |
} | |
extension Array { | |
func split(beginAt: [Int]) -> [ArraySlice<Element>] { | |
return (0..<beginAt.count).map { (idx) in | |
let from = beginAt[idx] | |
let to = (idx == beginAt.count - 1) ? self.count : beginAt[idx + 1] | |
return self[from..<to] | |
} | |
} | |
} | |
@NSApplicationMain | |
class AppDelegate: NSObject, NSApplicationDelegate, MKMapViewDelegate { | |
@IBOutlet weak var window: NSWindow! | |
@IBOutlet weak var mapView: MKMapView! | |
func applicationDidFinishLaunching(_ aNotification: Notification) { | |
mapView.delegate = self | |
mapView.region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 25.049972, longitude: 121.538350), span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)) | |
var linesForType: [String: [[CLLocationCoordinate2D]]] = [:] | |
let filename = "noparkin_201806011722" | |
if let shpURL = Bundle.main.url(forResource: filename, withExtension: "shp", subdirectory: "Data"), | |
let shxURL = Bundle.main.url(forResource: filename, withExtension: "shx", subdirectory: "Data"), | |
let prjURL = Bundle.main.url(forResource: filename, withExtension: "prj", subdirectory: "Data"), | |
let sr = try? ShapefileReader(url: shpURL) | |
{ | |
var count = 0 | |
NSLog("reading shapes begin") | |
for (shape, r) in sr.shapeAndRecordGenerator() { | |
guard shape.shapeType == .polyLine else { | |
print("shape is not polyline! \(shape)") | |
continue | |
} | |
let type = r[5] | |
for line in shape.points.split(beginAt: shape.parts) { | |
let coords: [CLLocationCoordinate2D] = line.map { (point) in | |
let (lat, lng) = toLatitudeLongitude(x: Double(point.x), y: Double(point.y)) | |
return CLLocationCoordinate2D(latitude: lat, longitude: lng) | |
} | |
linesForType[type, default: []].append(coords) | |
} | |
count += 1 | |
} | |
} | |
NSLog("reading shapes end") | |
DispatchQueue.main.asyncAfter(deadline: .now() + 0) { | |
for (t, coords) in linesForType { | |
let cluster = MultilineCluster(coords: coords) | |
switch t { | |
case "02": | |
cluster.strokeColor = NSColor(named: NSColor.Name("YellowColor"))! | |
case "01": | |
cluster.strokeColor = .red | |
case "11": | |
cluster.strokeColor = .blue | |
default: | |
cluster.strokeColor = .green | |
} | |
self.mapView.add(cluster) | |
} | |
} | |
} | |
func applicationWillTerminate(_ aNotification: Notification) { | |
// Insert code here to tear down your application | |
} | |
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { | |
switch overlay { | |
case let cluster as MultilineCluster: | |
let r = MultilineClusterRenderer(overlay: cluster) | |
r.strokeColor = cluster.strokeColor | |
r.lineWidth = 1 | |
return r | |
default: | |
return MKOverlayRenderer() | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment