-
Notifications
You must be signed in to change notification settings - Fork 101
Expand file tree
/
Copy pathline_string.clj
More file actions
195 lines (169 loc) · 6.59 KB
/
line_string.clj
File metadata and controls
195 lines (169 loc) · 6.59 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
(ns cmr.spatial.line-string
"This contains generic functions for operating on geodetic or cartesian line strings. Geodetic line
strings are represented by the Arc record. Cartesian line strings are represented by the LineSegment
record."
(:require
[cmr.common.dev.record-pretty-printer :as record-pretty-printer]
[cmr.common.util :as u]
[cmr.spatial.arc :as a]
[cmr.spatial.arc-line-segment-intersections :as asi]
[cmr.spatial.derived :as d]
[cmr.spatial.line-segment :as s]
[cmr.spatial.math]
[cmr.spatial.mbr :as m]
[cmr.spatial.point :as p]
[cmr.spatial.points-validation-helpers :as pv]
[cmr.spatial.validation :as v]
[primitive-math])
(:import
(cmr.spatial.arc Arc)
(cmr.spatial.line_segment LineSegment)
(cmr.spatial.mbr Mbr)
(cmr.spatial.geometry LineStringIntersections)))
(primitive-math/use-primitive-operators)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Segment helpers
;; These are sets of protocol methods on line segments and arcs
(defprotocol SegmentHelpers
(segment->mbrs [segment] "Returns a sequence of mbrs covering the segment")
(point-on-segment? [segment point] "Returns true if the segment covers the point"))
(extend-protocol SegmentHelpers
Arc
(segment->mbrs
[arc]
(a/mbrs arc))
(point-on-segment?
[arc point]
(a/point-on-arc? arc point))
LineSegment
(segment->mbrs
[ls]
[(:mbr ls)])
(point-on-segment?
[ls point]
(s/point-on-segment? ls point)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defrecord LineString
[
coordinate-system
points
;; A set of the unique points in the line string.
;; This should be used as opposed to creating a set from the points many times over which is expensive.
point-set
;; Derived fields
;; arcs or line-segments
segments
mbr])
(record-pretty-printer/enable-record-pretty-printing LineString)
(defn line-string
([points]
(line-string nil points))
([coordinate-system points]
(->LineString coordinate-system (mapv #(p/with-equality coordinate-system %) points) nil nil nil)))
(defn set-coordinate-system
"Sets the coordinate system of the line string. You must recalculate derived data after setting this."
[line coordinate-system]
(-> line
(assoc :coordinate-system coordinate-system)
(update :points #(mapv (partial p/with-equality coordinate-system) %))
;; Set calculated data to nil.
(assoc :point-set nil :segments nil :mbr nil)))
(defn ords->line-string
"Takes all arguments as coordinates for points, lon1, lat1, lon2, lat2, and creates a line."
[coordinate-system ords]
(line-string coordinate-system (p/ords->points ords)))
(defn line-string->ords [line]
(p/points->ords (:points line)))
(defmulti line-string->segments
"Creates the segments representing the connections between each point"
(fn [^LineString line]
(.coordinate_system line)))
(defmethod line-string->segments :geodetic
[^LineString line]
(or (.segments line)
(a/points->arcs (.points line))))
(defmethod line-string->segments :cartesian
[^LineString line]
(or (.segments line)
(s/points->line-segments (.points line))))
(defn union-arc-segment-mbrs
"Finds the minimum bounding rectangle of all the arcs and unions them together. This was written
to be a performance optimized way to do it."
[arcs]
(reduce (fn [mbr ^Arc arc]
(let [mbr (if mbr
(m/union mbr (.mbr1 arc))
(.mbr1 arc))]
(if-let [mbr2 (.mbr2 arc)]
(m/union mbr mbr2)
mbr)))
nil
arcs))
(defn union-line-segment-mbrs
"Finds the minimum bounding rectangle of all the line segments and unions them together."
[line-segments]
(reduce (fn [mbr ^LineSegment ls]
(if mbr
(m/union mbr (.mbr ls))
(.mbr ls)))
nil
line-segments))
(defn line-string->mbr
"Determines the mbr from the points in the line."
[^LineString line]
(or (.mbr line)
(let [segments (line-string->segments line)]
(if (= :geodetic (.coordinate_system line))
(union-arc-segment-mbrs segments)
(union-line-segment-mbrs segments)))))
(extend-protocol d/DerivedCalculator
cmr.spatial.line_string.LineString
(calculate-derived
[^LineString line]
(if (.segments line)
line
(as-> line line
(assoc line :point-set (set (.points line)))
(assoc line :segments (line-string->segments line))
(assoc line :mbr (line-string->mbr line))))))
(defn covers-point?
"Returns true if the line covers the point"
[^LineString line ^cmr.spatial.point.Point point]
;; Delegate to Java implementation
(let [java-line (cmr.spatial.shape.LineString.
(name (.coordinate_system line))
(vec (p/points->ords (.points line))))
java-point (cmr.spatial.shape.Point. (.lon point) (.lat point))]
(LineStringIntersections/coversPoint java-line java-point)))
(defn intersects-br?
"Returns true if the line intersects the br"
[^LineString line ^Mbr br]
;; Delegate to Java implementation
(let [java-line (cmr.spatial.shape.LineString.
(name (.coordinate_system line))
(vec (p/points->ords (.points line))))
java-mbr (cmr.spatial.shape.Mbr. (.west br) (.north br) (.east br) (.south br))]
(LineStringIntersections/intersectsMbr java-line java-mbr)))
(defn intersects-line-string?
"Returns true if the line string intersects the other line string"
[line1 line2]
;; Delegate to Java implementation
(let [java-line1 (cmr.spatial.shape.LineString.
(name (:coordinate-system line1))
(vec (p/points->ords (:points line1))))
java-line2 (cmr.spatial.shape.LineString.
(name (:coordinate-system line2))
(vec (p/points->ords (:points line2))))]
(LineStringIntersections/intersectsLineString java-line1 java-line2)))
(extend-protocol v/SpatialValidation
cmr.spatial.line_string.LineString
(validate
[line]
;; Certain validations can only be run if earlier validations passed. Validations are grouped
;; here so that subsequent validations won't run if earlier validations fail.
(if (= (:coordinate-system line) :geodetic)
(or (seq (pv/points-in-shape-validation line))
(seq (concat (pv/duplicate-point-validation line)
(pv/consecutive-antipodal-points-validation line))))
(or (seq (pv/points-in-shape-validation line))
(seq (pv/duplicate-point-validation line))))))