-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLine.swift
More file actions
124 lines (95 loc) · 5.31 KB
/
Copy pathLine.swift
File metadata and controls
124 lines (95 loc) · 5.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//
// Line.swift
// AerialScan
//
// Created by Ramsundar Shandilya on 1/29/18.
// Copyright © 2018 Ramsundar Shandilya. All rights reserved.
//
import Foundation
import CoreLocation
struct Line {
let startPoint: CLLocationCoordinate2D
let endPoint: CLLocationCoordinate2D
var heading: Degrees {
let startPointLat = startPoint.latitude.degreesToRadians
let startPointLong = startPoint.longitude.degreesToRadians
let endPointLat = endPoint.latitude.degreesToRadians
let endPointLong = endPoint.longitude.degreesToRadians
let angleRadians = atan2(sin(endPointLong - startPointLong) * cos(endPointLat), cos(startPointLat) * sin(endPointLat) - sin(startPointLat) * cos(endPointLat) * cos(endPointLong - startPointLong))
let angleDegrees = angleRadians.radiansToDegrees
return angleDegrees > 0 ? angleDegrees : 360 + angleDegrees //warp it to positive angle
}
var length: CLLocationDistance {
return startPoint.distance(from: endPoint)
}
func nearestEndpoint(from point: CLLocationCoordinate2D) -> CLLocationCoordinate2D {
return (point.distance(from: startPoint) < point.distance(from: endPoint)) ? startPoint : endPoint
}
func farthestEndpoint(from point: CLLocationCoordinate2D) -> CLLocationCoordinate2D {
return (point.distance(from: startPoint) > point.distance(from: endPoint)) ? startPoint : endPoint
}
}
extension Line: Equatable {
static public func == (lhs: Line, rhs: Line) -> Bool {
return lhs.startPoint == rhs.startPoint && lhs.endPoint == rhs.endPoint
}
}
/// Find the intersecting point of the two lines. Returns `nil` if the lines are parallel.
func intersection(line1: Line, line2: Line) -> CLLocationCoordinate2D? {
//Using determinants
//Line1 : (x1 y1) --- (x2 y2)
//Line2 : (x3 y3) --- (x4 y4)
let denominator = (line1.startPoint.latitude - line1.endPoint.latitude) * (line2.startPoint.longitude - line2.endPoint.longitude) - (line1.startPoint.longitude - line1.endPoint.longitude) * (line2.startPoint.latitude - line2.endPoint.latitude)
guard denominator != 0 else {
return nil
}
/*
/*
| x1 y1 |
| x2 y2 |
*/
/// (x1y2 - y1x2)
let line1Determinant = (line1.startPoint.latitude * line1.endPoint.longitude) - (line1.startPoint.longitude * line1.endPoint.latitude)
/*
| x3 y3 |
| x4 y5 |
*/
/// (x3y4 - y3x4)
let line2Determinant = (line2.startPoint.latitude * line2.endPoint.longitude) - (line2.startPoint.longitude * line2.endPoint.latitude)
let intersectionLat = (line1Determinant * (line2.startPoint.latitude - line2.endPoint.latitude) - line2Determinant * (line1.startPoint.latitude - line1.endPoint.latitude)) / denominator
let intersectionLong = (line1Determinant * (line2.startPoint.longitude - line2.endPoint.longitude) - line2Determinant * (line1.startPoint.longitude - line1.endPoint.longitude)) / denominator
return CLLocationCoordinate2D(latitude: intersectionLat, longitude: intersectionLong)
*/
// let numerator1 = (line1.startPoint.longitude - line2.startPoint.longitude) * (line2.endPoint.latitude - line2.startPoint.latitude) - (line1.startPoint.latitude - line2.startPoint.latitude) * (line2.endPoint.longitude - line2.startPoint.longitude)
let numerator1 = (line1.startPoint.longitude - line2.startPoint.longitude) * (line2.endPoint.latitude - line2.startPoint.latitude) - (line1.startPoint.latitude - line2.startPoint.latitude) * (line2.endPoint.longitude - line2.startPoint.longitude)
let r = numerator1 / denominator
let numerator2 = (line1.startPoint.longitude - line2.startPoint.longitude) * (line1.endPoint.latitude - line1.startPoint.latitude) - (line1.startPoint.latitude - line2.startPoint.latitude) * (line1.endPoint.longitude - line1.startPoint.longitude)
let s = numerator2 / denominator
//Check if the intersection is within the line segments
guard r >= 0 && r <= 1 && s >= 0 && s <= 1 else {
return nil
}
let intersectionLat = line1.startPoint.latitude + r * (line1.endPoint.latitude - line1.startPoint.latitude)
let intersectionLong = line1.startPoint.longitude + r * (line1.endPoint.longitude - line1.startPoint.longitude)
return CLLocationCoordinate2D(latitude: intersectionLat, longitude: intersectionLong)
}
/// Find the nearest line from a point
func nearestLine(from point: CLLocationCoordinate2D, lines: [Line]) -> Line {
let closestLine = lines.min { (line1, line2) -> Bool in
return line1.nearestEndpoint(from: point).distance(from: point) < line2.nearestEndpoint(from: point).distance(from: point)
}
return closestLine!
}
//TODO:cleanup
func externalPoints(from points: [CLLocationCoordinate2D]) -> Line{
let meanCoord = Polygon(vertices: points).polygonBounds.midPoint
let start = farthestPoint(from: meanCoord, points: points)
let end = farthestPoint(from: start, points: points)
return Line(startPoint: start, endPoint: end)
}
func farthestPoint(from origin: CLLocationCoordinate2D, points: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D {
let farthest = points.max { (point1, point2) -> Bool in
return point1.distance(from: origin) < point2.distance(from: origin)
}
return farthest!
}