-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathBooleanPointInPolygon.swift
More file actions
226 lines (190 loc) · 7.93 KB
/
BooleanPointInPolygon.swift
File metadata and controls
226 lines (190 loc) · 7.93 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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#if canImport(CoreLocation)
import CoreLocation
#endif
import Foundation
// Ported from https://github.com/Turfjs/turf/blob/master/packages/turf-boolean-point-in-polygon
extension Ring {
/// Determines if *Coordinate3D* resides inside the *Ring*. The ring can be convex or concave.
///
/// - Parameters:
/// - coordinate: The coordinate to check
/// - ignoreBoundary: *true* if the ring boundary should be ignored when determining if the coordinate is inside the ring (default *false*).
///
/// - Returns: *true* if the coordinate is inside the ring, *false* otherwise.
public func contains(
_ coordinate: Coordinate3D,
ignoreBoundary: Bool = false
) -> Bool {
let coordinate = coordinate.projected(to: projection)
var isInside = false
var coordinatesCount = coordinates.count
if coordinates.first == coordinates.last {
coordinatesCount -= 1
}
var j = coordinatesCount - 1
for i in 0 ..< coordinatesCount {
defer {
j = i
}
let xi = coordinates[i].longitude
let yi = coordinates[i].latitude
let xj = coordinates[j].longitude
let yj = coordinates[j].latitude
let onBoundary = (coordinate.latitude * (xi - xj) + yi * (xj - coordinate.longitude) + yj * (coordinate.longitude - xi) == 0.0)
&& ((xi - coordinate.longitude) * (xj - coordinate.longitude) <= 0.0)
&& ((yi - coordinate.latitude) * (yj - coordinate.latitude) <= 0.0)
if onBoundary {
return !ignoreBoundary
}
let intersect = ((yi > coordinate.latitude) != (yj > coordinate.latitude))
&& (coordinate.longitude < (xj - xi) * (coordinate.latitude - yi) / (yj - yi) + xi)
if (intersect) {
isInside = !isInside
}
}
return isInside
}
/// Determines if *Point* resides inside the *Ring*. The ring can be convex or concave.
///
/// - Parameters:
/// - point: The point to check
/// - ignoreBoundary: *true* if the ring boundary should be ignored when determining if the point is inside the ring (default *false*).
///
/// - Returns: *true* if the point is inside the ring, *false* otherwise.
public func contains(
_ point: Point,
ignoreBoundary: Bool = false
) -> Bool {
contains(point.coordinate, ignoreBoundary: ignoreBoundary)
}
}
extension Polygon {
/// Determines if *Coordinate3D* resides inside the *Polygon*. The polygon can be convex or concave.
/// The function accounts for holes.
///
/// - Parameters:
/// - coordinate: The coordinate to check
/// - ignoreBoundary: *true* if the polygon boundary should be ignored when determining if the coordinate is inside the polygon (default *false*).
///
/// - Returns: *true* if the coordinate is inside the ring, *false* otherwise.
public func contains(
_ coordinate: Coordinate3D,
ignoreBoundary: Bool = false
) -> Bool {
if let boundingBox, !boundingBox.contains(coordinate) {
return false
}
guard let outerRing = outerRing,
outerRing.contains(coordinate, ignoreBoundary: ignoreBoundary)
else { return false }
if let innerRings {
for ring in innerRings {
if ring.contains(coordinate, ignoreBoundary: ignoreBoundary) {
return false
}
}
}
return true
}
/// Determines if *Point* resides inside the *Polygon*. The polygon can be convex or concave.
/// The function accounts for holes.
///
/// - Parameters:
/// - point: The point to check
/// - ignoreBoundary: *true* if the polygon boundary should be ignored when determining if the point is inside the polygon (default *false*).
///
/// - Returns: *true* if the point is inside the ring, *false* otherwise.
public func contains(
_ point: Point,
ignoreBoundary: Bool = false
) -> Bool {
contains(point.coordinate, ignoreBoundary: ignoreBoundary)
}
}
extension MultiPolygon {
/// Determines if *Coordinate3D* resides inside the *MultiPolygon*. The polygons can be convex or concave.
/// The function accounts for holes.
///
/// - Parameters:
/// - coordinate: The coordinate to check
/// - ignoreBoundary: *true* if the polygon boundaries should be ignored when determining if the coordinate is inside of one of the polygons (default *false*).
///
/// - Returns: *true* if the coordinate is inside of one of the polygons, *false* otherwise.
public func contains(
_ coordinate: Coordinate3D,
ignoreBoundary: Bool = false
) -> Bool {
if let boundingBox, !boundingBox.contains(coordinate) {
return false
}
for polygon in polygons {
if polygon.contains(coordinate, ignoreBoundary: ignoreBoundary) {
return true
}
}
return false
}
/// Determines if *Point* resides inside the *MultiPolygon*. The polygons can be convex or concave.
/// The function accounts for holes.
///
/// - Parameters:
/// - point: The coordinate to check
/// - ignoreBoundary: *true* if the polygon boundaries should be ignored when determining if the coordinate is inside of one of the polygons (default *false*).
///
/// - Returns: *true* if the coordinate is inside of one of the polygons, *false* otherwise.
public func contains(
_ point: Point,
ignoreBoundary: Bool = false
) -> Bool {
contains(point.coordinate, ignoreBoundary: ignoreBoundary)
}
}
extension GeometryCollection {
/// Determines if *Coordinate3D* resides inside the *GeometryCollection*.
///
/// - Parameters:
/// - coordinate: The coordinate to check
/// - ignoreBoundary: *true* if the boundaries should be ignored when determining if the coordinate is inside of one of the geometries (default *false*).
///
/// - Returns: *true* if the coordinate is inside of one of the geometries, *false* otherwise.
public func contains(
_ coordinate: Coordinate3D,
ignoreBoundary: Bool = false
) -> Bool {
geometries.contains { (geometry) -> Bool in
guard let polygonGeometry = geometry as? PolygonGeometry else { return false }
return polygonGeometry.contains(coordinate, ignoreBoundary: ignoreBoundary)
}
}
}
extension Feature {
/// Determines if *Coordinate3D* resides inside the *Feature*.
///
/// - Parameters:
/// - coordinate: The coordinate to check
/// - ignoreBoundary: *true* if the boundaries should be ignored when determining if the coordinate is inside of the Feature's geometry (default *false*).
///
/// - Returns: *true* if the coordinate is inside of one of the Feature's geometry, *false* otherwise.
public func contains(
_ coordinate: Coordinate3D,
ignoreBoundary: Bool = false
) -> Bool {
guard let polygonGeometry = geometry as? PolygonGeometry else { return false }
return polygonGeometry.contains(coordinate, ignoreBoundary: ignoreBoundary)
}
}
extension FeatureCollection {
/// Determines if *Coordinate3D* resides inside the *FeatureCollection*.
///
/// - Parameters:
/// - coordinate: The coordinate to check
/// - ignoreBoundary: *true* if the boundaries should be ignored when determining if the coordinate is inside of one of the geometries (default *false*).
///
/// - Returns: *true* if the coordinate is inside of one of the geometries, *false* otherwise.
public func contains(
_ coordinate: Coordinate3D,
ignoreBoundary: Bool = false
) -> Bool {
features.contains(where: { $0.contains(coordinate, ignoreBoundary: ignoreBoundary) })
}
}