turf-ruby 0.8.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.txt +21 -0
- data/README.md +159 -139
- data/lib/turf/along.rb +10 -3
- data/lib/turf/angle.rb +8 -0
- data/lib/turf/area.rb +4 -1
- data/lib/turf/bbox.rb +36 -0
- data/lib/turf/bbox_clip.rb +74 -0
- data/lib/turf/bbox_polygon.rb +41 -0
- data/lib/turf/bearing.rb +5 -2
- data/lib/turf/bezier_spline.rb +8 -0
- data/lib/turf/boolean_clockwise.rb +8 -0
- data/lib/turf/boolean_concave.rb +8 -0
- data/lib/turf/boolean_contains.rb +52 -0
- data/lib/turf/boolean_crosses.rb +8 -0
- data/lib/turf/boolean_disjoint.rb +8 -0
- data/lib/turf/boolean_equal.rb +8 -0
- data/lib/turf/boolean_intersects.rb +8 -0
- data/lib/turf/boolean_overlap.rb +8 -0
- data/lib/turf/boolean_parallel.rb +8 -0
- data/lib/turf/boolean_point_in_polygon.rb +5 -3
- data/lib/turf/boolean_point_on_line.rb +8 -0
- data/lib/turf/boolean_touches.rb +8 -0
- data/lib/turf/boolean_valid.rb +8 -0
- data/lib/turf/boolean_within.rb +8 -0
- data/lib/turf/buffer.rb +8 -0
- data/lib/turf/center.rb +32 -0
- data/lib/turf/center_mean.rb +8 -0
- data/lib/turf/center_median.rb +8 -0
- data/lib/turf/center_of_mass.rb +8 -0
- data/lib/turf/centroid.rb +5 -2
- data/lib/turf/circle.rb +5 -2
- data/lib/turf/clean_coords.rb +8 -0
- data/lib/turf/clone.rb +8 -0
- data/lib/turf/clusters.rb +32 -0
- data/lib/turf/clusters_dbscan.rb +8 -0
- data/lib/turf/clusters_kmeans.rb +8 -0
- data/lib/turf/collect.rb +8 -0
- data/lib/turf/combine.rb +8 -0
- data/lib/turf/concave.rb +8 -0
- data/lib/turf/convex.rb +8 -0
- data/lib/turf/destination.rb +6 -3
- data/lib/turf/difference.rb +8 -0
- data/lib/turf/directional_mean.rb +8 -0
- data/lib/turf/dissolve.rb +8 -0
- data/lib/turf/distance.rb +5 -2
- data/lib/turf/distance_weight.rb +12 -0
- data/lib/turf/ellipse.rb +8 -0
- data/lib/turf/envelope.rb +8 -0
- data/lib/turf/explode.rb +29 -0
- data/lib/turf/flatten.rb +8 -0
- data/lib/turf/flip.rb +8 -0
- data/lib/turf/geojson_rbush.rb +8 -0
- data/lib/turf/great_circle.rb +8 -0
- data/lib/turf/helpers.rb +166 -26
- data/lib/turf/hex_grid.rb +8 -0
- data/lib/turf/interpolate.rb +8 -0
- data/lib/turf/intersect.rb +8 -0
- data/lib/turf/invariant.rb +127 -1
- data/lib/turf/isobands.rb +8 -0
- data/lib/turf/isolines.rb +8 -0
- data/lib/turf/kinks.rb +8 -0
- data/lib/turf/length.rb +6 -3
- data/lib/turf/lib/lineclip.rb +118 -0
- data/lib/turf/line_arc.rb +8 -0
- data/lib/turf/line_chunk.rb +8 -0
- data/lib/turf/line_intersect.rb +8 -0
- data/lib/turf/line_offset.rb +8 -0
- data/lib/turf/line_overlap.rb +8 -0
- data/lib/turf/line_segment.rb +8 -0
- data/lib/turf/line_slice.rb +8 -0
- data/lib/turf/line_slice_along.rb +8 -0
- data/lib/turf/line_split.rb +8 -0
- data/lib/turf/line_to_polygon.rb +8 -0
- data/lib/turf/mask.rb +8 -0
- data/lib/turf/meta.rb +445 -79
- data/lib/turf/midpoint.rb +8 -0
- data/lib/turf/moran_index.rb +8 -0
- data/lib/turf/nearest_neighbor_analysis.rb +8 -0
- data/lib/turf/nearest_point.rb +8 -0
- data/lib/turf/nearest_point_on_line.rb +8 -0
- data/lib/turf/nearest_point_to_line.rb +8 -0
- data/lib/turf/planepoint.rb +8 -0
- data/lib/turf/point_grid.rb +8 -0
- data/lib/turf/point_on_feature.rb +8 -0
- data/lib/turf/point_to_line_distance.rb +8 -0
- data/lib/turf/point_to_polygon_distance.rb +8 -0
- data/lib/turf/points_within_polygon.rb +8 -0
- data/lib/turf/polygon_smooth.rb +8 -0
- data/lib/turf/polygon_tangents.rb +8 -0
- data/lib/turf/polygon_to_line.rb +20 -0
- data/lib/turf/polygonize.rb +8 -0
- data/lib/turf/projection.rb +12 -0
- data/lib/turf/quadrat_analysis.rb +8 -0
- data/lib/turf/random.rb +20 -0
- data/lib/turf/rectangle_grid.rb +8 -0
- data/lib/turf/rewind.rb +8 -0
- data/lib/turf/rhumb_bearing.rb +8 -0
- data/lib/turf/rhumb_destination.rb +8 -0
- data/lib/turf/rhumb_distance.rb +8 -0
- data/lib/turf/sample.rb +8 -0
- data/lib/turf/sector.rb +8 -0
- data/lib/turf/shortest_path.rb +8 -0
- data/lib/turf/simplify.rb +8 -0
- data/lib/turf/square.rb +42 -0
- data/lib/turf/square_grid.rb +8 -0
- data/lib/turf/standard_deviational_ellipse.rb +8 -0
- data/lib/turf/tag.rb +8 -0
- data/lib/turf/tesselate.rb +8 -0
- data/lib/turf/tin.rb +8 -0
- data/lib/turf/transform_rotate.rb +8 -0
- data/lib/turf/transform_scale.rb +8 -0
- data/lib/turf/transform_translate.rb +8 -0
- data/lib/turf/triangle_grid.rb +8 -0
- data/lib/turf/truncate.rb +3 -0
- data/lib/turf/union.rb +8 -0
- data/lib/turf/unkink_polygon.rb +8 -0
- data/lib/turf/version.rb +2 -1
- data/lib/turf/voronoi.rb +8 -0
- data/lib/turf.rb +2 -0
- data/lib/turf_ruby.rb +107 -3
- metadata +105 -2
data/lib/turf/helpers.rb
CHANGED
@@ -3,13 +3,12 @@
|
|
3
3
|
# :nodoc:
|
4
4
|
module Turf
|
5
5
|
EARTH_RADIUS = 6_371_008.8
|
6
|
-
private_constant :EARTH_RADIUS
|
7
6
|
FACTORS = {
|
8
7
|
"centimeters" => EARTH_RADIUS * 100,
|
9
8
|
"centimetres" => EARTH_RADIUS * 100,
|
10
|
-
"degrees" =>
|
9
|
+
"degrees" => 360.0 / (2 * Math::PI),
|
11
10
|
"feet" => EARTH_RADIUS * 3.28084,
|
12
|
-
"inches" => EARTH_RADIUS * 39.
|
11
|
+
"inches" => EARTH_RADIUS * 39.37,
|
13
12
|
"kilometers" => EARTH_RADIUS / 1000,
|
14
13
|
"kilometres" => EARTH_RADIUS / 1000,
|
15
14
|
"meters" => EARTH_RADIUS,
|
@@ -19,9 +18,26 @@ module Turf
|
|
19
18
|
"millimetres" => EARTH_RADIUS * 1000,
|
20
19
|
"nauticalmiles" => EARTH_RADIUS / 1852,
|
21
20
|
"radians" => 1.0,
|
22
|
-
"yards" => EARTH_RADIUS
|
21
|
+
"yards" => EARTH_RADIUS * 1.0936
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
AREA_FACTORS = {
|
25
|
+
"acres" => 0.000247105,
|
26
|
+
"centimeters" => 10_000.0,
|
27
|
+
"centimetres" => 10_000.0,
|
28
|
+
"feet" => 10.763910417,
|
29
|
+
"hectares" => 0.0001,
|
30
|
+
"inches" => 1550.003100006,
|
31
|
+
"kilometers" => 0.000001,
|
32
|
+
"kilometres" => 0.000001,
|
33
|
+
"meters" => 1.0,
|
34
|
+
"metres" => 1.0,
|
35
|
+
"miles" => 3.86e-7,
|
36
|
+
"nauticalmiles" => 2.9155334959812285e-7,
|
37
|
+
"millimeters" => 1_000_000.0,
|
38
|
+
"millimetres" => 1_000_000.0,
|
39
|
+
"yards" => 1.195990046
|
23
40
|
}.freeze
|
24
|
-
private_constant :FACTORS
|
25
41
|
|
26
42
|
# @!group Helper
|
27
43
|
|
@@ -32,14 +48,14 @@ module Turf
|
|
32
48
|
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the Feature
|
33
49
|
# @param id [string | number] Identifier associated with the Feature
|
34
50
|
# @return [Feature] a GeoJSON Feature
|
35
|
-
def feature(geom, properties
|
51
|
+
def feature(geom, properties = nil, options = {})
|
36
52
|
feat = {
|
37
53
|
type: "Feature",
|
38
|
-
properties: properties,
|
54
|
+
properties: properties || {},
|
39
55
|
geometry: geom
|
40
56
|
}
|
41
|
-
feat[:id] = options[:id] if id
|
42
|
-
feat[:bbox] = options[:bbox] if bbox
|
57
|
+
feat[:id] = options[:id] if options[:id]
|
58
|
+
feat[:bbox] = options[:bbox] if options[:bbox]
|
43
59
|
|
44
60
|
feat
|
45
61
|
end
|
@@ -50,10 +66,10 @@ module Turf
|
|
50
66
|
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the Feature
|
51
67
|
# @param id [string | number] Identifier associated with the Feature
|
52
68
|
# @return [FeatureCollection] FeatureCollection of Features
|
53
|
-
def feature_collection(features,
|
69
|
+
def feature_collection(features, options = {})
|
54
70
|
fc = { type: "FeatureCollection" }
|
55
|
-
fc[:id] = options[:id] if id
|
56
|
-
fc[:bbox] = options[:bbox] if bbox
|
71
|
+
fc[:id] = options[:id] if options[:id]
|
72
|
+
fc[:bbox] = options[:bbox] if options[:bbox]
|
57
73
|
fc[:features] = features
|
58
74
|
|
59
75
|
fc
|
@@ -66,13 +82,13 @@ module Turf
|
|
66
82
|
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the Feature
|
67
83
|
# @param id [string|number] Identifier associated with the Feature
|
68
84
|
# @return [Feature<GeometryCollection>] a GeoJSON GeometryCollection Feature
|
69
|
-
def geometry_collection(geometries,
|
85
|
+
def geometry_collection(geometries, properties = nil, options = {})
|
70
86
|
geom = {
|
71
87
|
type: "GeometryCollection",
|
72
88
|
geometries: geometries
|
73
89
|
}
|
74
90
|
|
75
|
-
feature(geom, properties
|
91
|
+
feature(geom, properties, options)
|
76
92
|
end
|
77
93
|
|
78
94
|
# Creates a Feature based on a coordinate array. Properties can be added optionally.
|
@@ -81,13 +97,13 @@ module Turf
|
|
81
97
|
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the Feature
|
82
98
|
# @param id [string|number] Identifier associated with the Feature
|
83
99
|
# @return [Feature<MultiPoint>] a MultiPoint feature
|
84
|
-
def multi_point(coordinates, properties
|
100
|
+
def multi_point(coordinates, properties = nil, options = {})
|
85
101
|
geom = {
|
86
102
|
type: "MultiPoint",
|
87
103
|
coordinates: coordinates
|
88
104
|
}
|
89
105
|
|
90
|
-
feature(geom, properties
|
106
|
+
feature(geom, properties, options)
|
91
107
|
end
|
92
108
|
|
93
109
|
# Creates a LineString Feature from an Array of Positions.
|
@@ -97,7 +113,7 @@ module Turf
|
|
97
113
|
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the Feature
|
98
114
|
# @param id [string|number] Identifier associated with the Feature
|
99
115
|
# @return [Feature<LineString>] LineString Feature
|
100
|
-
def line_string(coordinates, properties
|
116
|
+
def line_string(coordinates, properties = nil, options = {})
|
101
117
|
if coordinates.size < 2
|
102
118
|
raise Error, "coordinates must be an array of two or more positions"
|
103
119
|
end
|
@@ -106,7 +122,7 @@ module Turf
|
|
106
122
|
type: "LineString",
|
107
123
|
coordinates: coordinates
|
108
124
|
}
|
109
|
-
feature(geom, properties
|
125
|
+
feature(geom, properties, options)
|
110
126
|
end
|
111
127
|
|
112
128
|
# Creates a Point Feature from a Position.
|
@@ -116,12 +132,12 @@ module Turf
|
|
116
132
|
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the Feature
|
117
133
|
# @param id [string | number] Identifier associated with the Feature
|
118
134
|
# @return [Feature<Point>] a Point feature
|
119
|
-
def point(coordinates, properties
|
135
|
+
def point(coordinates, properties = nil, options = {})
|
120
136
|
geom = {
|
121
137
|
type: "Point",
|
122
138
|
coordinates: coordinates
|
123
139
|
}
|
124
|
-
feature(geom, properties
|
140
|
+
feature(geom, properties, options)
|
125
141
|
end
|
126
142
|
|
127
143
|
# Creates a Polygon Feature from an Array of LinearRings.
|
@@ -131,7 +147,7 @@ module Turf
|
|
131
147
|
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the Feature
|
132
148
|
# @param id [string | number] Identifier associated with the Feature
|
133
149
|
# @return [Feature<Polygon>] Polygon feature
|
134
|
-
def polygon(coordinates, properties
|
150
|
+
def polygon(coordinates, properties = nil, options = {})
|
135
151
|
coordinates.each do |ring|
|
136
152
|
if ring.size < 4
|
137
153
|
raise(
|
@@ -150,7 +166,7 @@ module Turf
|
|
150
166
|
type: "Polygon",
|
151
167
|
coordinates: coordinates
|
152
168
|
}
|
153
|
-
feature(geom, properties
|
169
|
+
feature(geom, properties, options)
|
154
170
|
end
|
155
171
|
|
156
172
|
# Creates a Feature<MultiPolygon> based on a coordinate array. Properties can be added optionally.
|
@@ -160,12 +176,12 @@ module Turf
|
|
160
176
|
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the Feature
|
161
177
|
# @param id [string|number] Identifier associated with the Feature
|
162
178
|
# @return [Feature<MultiPolygon>] a multipolygon feature
|
163
|
-
def multi_polygon(coordinates, properties
|
179
|
+
def multi_polygon(coordinates, properties = nil, options = {})
|
164
180
|
geom = {
|
165
181
|
type: "MultiPolygon",
|
166
182
|
coordinates: coordinates
|
167
183
|
}
|
168
|
-
feature(geom, properties
|
184
|
+
feature(geom, properties, options)
|
169
185
|
end
|
170
186
|
|
171
187
|
# Creates a Feature<MultiLineString> based on a coordinate array. Properties can be added optionally.
|
@@ -175,12 +191,12 @@ module Turf
|
|
175
191
|
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the Feature
|
176
192
|
# @param id [string|number] Identifier associated with the Feature
|
177
193
|
# @return [Feature<MultiLineString>] a MultiLineString feature
|
178
|
-
def multi_line_string(coordinates, properties
|
194
|
+
def multi_line_string(coordinates, properties = nil, options = {})
|
179
195
|
geom = {
|
180
196
|
type: "MultiLineString",
|
181
197
|
coordinates: coordinates
|
182
198
|
}
|
183
|
-
feature(geom, properties
|
199
|
+
feature(geom, properties, options)
|
184
200
|
end
|
185
201
|
|
186
202
|
# @!group Unit Conversion
|
@@ -230,4 +246,128 @@ module Turf
|
|
230
246
|
|
231
247
|
distance / factor
|
232
248
|
end
|
249
|
+
|
250
|
+
# Creates a FeatureCollection from an Array of LineString coordinates.
|
251
|
+
# @see https://turfjs.org/docs/#lineStrings
|
252
|
+
# @param coordinates [Array<Array<Array<number>>>] an array of LinearRings
|
253
|
+
# @param properties [Hash] a Hash of key-value pairs to add as properties
|
254
|
+
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the FeatureCollection
|
255
|
+
# @param id [string|number] Identifier associated with the FeatureCollection
|
256
|
+
# @return [FeatureCollection<LineString>] LineString FeatureCollection
|
257
|
+
def line_strings(coordinates, properties = nil, options = {})
|
258
|
+
features = coordinates.map { |coords| line_string(coords, properties) }
|
259
|
+
feature_collection(features, options)
|
260
|
+
end
|
261
|
+
|
262
|
+
# Creates a Point FeatureCollection from an Array of Point coordinates.
|
263
|
+
# @see https://turfjs.org/docs/#points
|
264
|
+
# @param coordinates [Array<Array<number>>] an array of Points
|
265
|
+
# @param properties [Hash] a Hash of key-value pairs to add as properties
|
266
|
+
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the FeatureCollection
|
267
|
+
# @param id [string|number] Identifier associated with the FeatureCollection
|
268
|
+
# @return [FeatureCollection<Point>] Point FeatureCollection
|
269
|
+
def points(coordinates, properties = nil, options = {})
|
270
|
+
features = coordinates.map { |coords| point(coords, properties) }
|
271
|
+
feature_collection(features, options)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Creates a Polygon FeatureCollection from an Array of Polygon coordinates.
|
275
|
+
# @see https://turfjs.org/docs/#polygons
|
276
|
+
# @param coordinates [Array<Array<Array<Array<number>>>>] an array of Polygon coordinates
|
277
|
+
# @param properties [Hash] a Hash of key-value pairs to add as properties
|
278
|
+
# @param bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the FeatureCollection
|
279
|
+
# @param id [string|number] Identifier associated with the FeatureCollection
|
280
|
+
# @return [FeatureCollection<Polygon>] Polygon FeatureCollection
|
281
|
+
def polygons(coordinates, properties = nil, options = {})
|
282
|
+
features = coordinates.map { |coords| polygon(coords, properties) }
|
283
|
+
feature_collection(features, options)
|
284
|
+
end
|
285
|
+
|
286
|
+
# Checks if the input is a number.
|
287
|
+
# @see https://turfjs.org/docs/#isNumber
|
288
|
+
# @param num [Object] Number to validate
|
289
|
+
# @return [boolean] true/false
|
290
|
+
def is_number(num)
|
291
|
+
num.is_a?(Numeric)
|
292
|
+
end
|
293
|
+
|
294
|
+
# Checks if the input is an object.
|
295
|
+
# @see https://turfjs.org/docs/#isObject
|
296
|
+
# @param input [Object] variable to validate
|
297
|
+
# @return [boolean] true/false, including false for Arrays and Functions
|
298
|
+
def is_object(input)
|
299
|
+
input.is_a?(Hash) && !input.is_a?(Array)
|
300
|
+
end
|
301
|
+
|
302
|
+
# Converts any azimuth angle from the north line direction (positive clockwise)
|
303
|
+
# and returns an angle between -180 and +180 degrees (positive clockwise), 0 being the north line
|
304
|
+
# @see https://turfjs.org/docs/#azimuthToBearing
|
305
|
+
# @param angle [number] between 0 and 360 degrees
|
306
|
+
# @return [number] bearing between -180 and +180 degrees
|
307
|
+
def azimuth_to_bearing(angle)
|
308
|
+
angle = angle.remainder(360)
|
309
|
+
angle -= 360 if angle > 180
|
310
|
+
angle += 360 if angle < -180
|
311
|
+
angle
|
312
|
+
end
|
313
|
+
|
314
|
+
# Converts any bearing angle from the north line direction (positive clockwise)
|
315
|
+
# and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line
|
316
|
+
# @see https://turfjs.org/docs/#bearingToAzimuth
|
317
|
+
# @param bearing [number] angle, between -180 and +180 degrees
|
318
|
+
# @return [number] angle between 0 and 360 degrees
|
319
|
+
def bearing_to_azimuth(bearing)
|
320
|
+
angle = bearing.remainder(360)
|
321
|
+
angle += 360 if angle < 0
|
322
|
+
angle
|
323
|
+
end
|
324
|
+
|
325
|
+
# Rounds a number to a specified precision
|
326
|
+
# @see https://turfjs.org/docs/#round
|
327
|
+
# @param num [number] Number to round
|
328
|
+
# @param precision [number] Precision
|
329
|
+
# @return [number] rounded number
|
330
|
+
def round(num, precision = 0)
|
331
|
+
if !precision.is_a?(Numeric) || precision < 0
|
332
|
+
raise Error, "invalid precision"
|
333
|
+
end
|
334
|
+
|
335
|
+
num.round(precision)
|
336
|
+
end
|
337
|
+
|
338
|
+
# Converts an area from one unit to another.
|
339
|
+
# @see https://turfjs.org/docs/#convertArea
|
340
|
+
# @param area [number] Area to be converted
|
341
|
+
# @param original_unit [string] Input area unit
|
342
|
+
# @param final_unit [string] Returned area unit
|
343
|
+
# @return [number] The converted length
|
344
|
+
def convert_area(area, original_unit = nil, final_unit = nil)
|
345
|
+
original_unit ||= "meters"
|
346
|
+
final_unit ||= "kilometers"
|
347
|
+
|
348
|
+
raise Error, "area must be a positive number" unless area >= 0
|
349
|
+
|
350
|
+
start_factor = AREA_FACTORS[original_unit]
|
351
|
+
raise Error, "invalid original units" unless start_factor
|
352
|
+
|
353
|
+
final_factor = AREA_FACTORS[final_unit]
|
354
|
+
raise Error, "invalid final units" unless final_factor
|
355
|
+
|
356
|
+
(area / start_factor) * final_factor
|
357
|
+
end
|
358
|
+
|
359
|
+
# Converts a length from one unit to another.
|
360
|
+
# @see https://turfjs.org/docs/#convertLength
|
361
|
+
# @param length [number] Length to be converted
|
362
|
+
# @param original_unit [string] Input length unit
|
363
|
+
# @param final_unit [string] Returned length unit
|
364
|
+
# @return [number] The converted length
|
365
|
+
def convert_length(length, original_unit = nil, final_unit = nil)
|
366
|
+
original_unit ||= "kilometers"
|
367
|
+
final_unit ||= "kilometers"
|
368
|
+
|
369
|
+
raise Error, "length must be a positive number" unless length >= 0
|
370
|
+
|
371
|
+
radians_to_length(length_to_radians(length, original_unit), final_unit)
|
372
|
+
end
|
233
373
|
end
|
data/lib/turf/invariant.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "helpers"
|
4
|
+
|
3
5
|
# :nodoc:
|
4
6
|
module Turf
|
5
7
|
# @!group Meta
|
@@ -22,7 +24,7 @@ module Turf
|
|
22
24
|
coord = deep_symbolize_keys(coord)
|
23
25
|
|
24
26
|
is_feature = coord[:type] == "Feature"
|
25
|
-
if is_feature && coord.
|
27
|
+
if is_feature && coord.dig(:geometry, :type) == "Point"
|
26
28
|
return coord[:geometry][:coordinates]
|
27
29
|
end
|
28
30
|
|
@@ -44,4 +46,128 @@ module Turf
|
|
44
46
|
|
45
47
|
geojson
|
46
48
|
end
|
49
|
+
|
50
|
+
# Unwrap coordinates from a Feature, Geometry Object or an Array
|
51
|
+
# @see https://turfjs.org/docs/#getCoords
|
52
|
+
# @param coords [Array|Geometry|Feature] Feature, Geometry Object or an Array
|
53
|
+
# @return [Array] coordinates
|
54
|
+
def get_coords(coords)
|
55
|
+
if coords.is_a?(Array)
|
56
|
+
return coords
|
57
|
+
end
|
58
|
+
|
59
|
+
coords = deep_symbolize_keys coords
|
60
|
+
|
61
|
+
# Feature
|
62
|
+
if coords.is_a?(Hash) && coords[:type] == "Feature"
|
63
|
+
if coords[:geometry]
|
64
|
+
return coords[:geometry][:coordinates]
|
65
|
+
end
|
66
|
+
elsif coords.is_a?(Hash) && coords[:coordinates]
|
67
|
+
# Geometry
|
68
|
+
return coords[:coordinates]
|
69
|
+
end
|
70
|
+
|
71
|
+
raise Error, "coords must be GeoJSON Feature, Geometry Object or an Array"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Checks if coordinates contains a number
|
75
|
+
# @see https://turfjs.org/docs/#containsNumber
|
76
|
+
# @param coordinates [Array] GeoJSON Coordinates
|
77
|
+
# @return [Boolean] true if Array contains a number
|
78
|
+
def contains_number(coordinates)
|
79
|
+
if coordinates.length > 1 && coordinates[0].is_a?(Numeric) && coordinates[1].is_a?(Numeric)
|
80
|
+
return true
|
81
|
+
end
|
82
|
+
|
83
|
+
if coordinates[0].is_a?(Array) && !coordinates[0].empty?
|
84
|
+
return contains_number(coordinates[0])
|
85
|
+
end
|
86
|
+
|
87
|
+
raise Error, "coordinates must only contain numbers"
|
88
|
+
end
|
89
|
+
|
90
|
+
# Enforce expectations about types of GeoJSON objects for Turf.
|
91
|
+
# @see https://turfjs.org/docs/#geojsonType
|
92
|
+
# @param value [GeoJSON] any GeoJSON object
|
93
|
+
# @param type [String] expected GeoJSON type
|
94
|
+
# @param name [String] name of calling function
|
95
|
+
# @raise [Error] if value is not the expected type.
|
96
|
+
def geojson_type(value = nil, type = nil, name = nil)
|
97
|
+
if !type || !name
|
98
|
+
raise Error, "type and name required"
|
99
|
+
end
|
100
|
+
|
101
|
+
return unless !value || value[:type] != type
|
102
|
+
|
103
|
+
raise Error, "Invalid input to #{name}: must be a #{type}, given #{value[:type]}"
|
104
|
+
end
|
105
|
+
|
106
|
+
# Enforce expectations about types of Feature inputs for Turf.
|
107
|
+
# @see https://turfjs.org/docs/#featureOf
|
108
|
+
# @param feature [Feature] a feature with an expected geometry type
|
109
|
+
# @param type [String] expected GeoJSON type
|
110
|
+
# @param name [String] name of calling function
|
111
|
+
# @raise [Error] error if value is not the expected type.
|
112
|
+
def feature_of(feature = nil, type = nil, name = nil)
|
113
|
+
if !feature
|
114
|
+
raise Error, "No feature passed"
|
115
|
+
end
|
116
|
+
if !name
|
117
|
+
raise Error, ".featureOf() requires a name"
|
118
|
+
end
|
119
|
+
if !feature || feature[:type] != "Feature" || !feature[:geometry]
|
120
|
+
raise Error, "Invalid input to #{name}, Feature with geometry required"
|
121
|
+
end
|
122
|
+
return unless feature[:geometry][:type] != type
|
123
|
+
|
124
|
+
raise Error, "Invalid input to #{name}: must be a #{type}, given #{feature[:geometry][:type]}"
|
125
|
+
end
|
126
|
+
|
127
|
+
# Enforce expectations about types of FeatureCollection inputs for Turf.
|
128
|
+
# @see https://turfjs.org/docs/#collectionOf
|
129
|
+
# @param feature_collection [FeatureCollection] a FeatureCollection for which features will be judged
|
130
|
+
# @param type [String] expected GeoJSON type
|
131
|
+
# @param name [String] name of calling function
|
132
|
+
# @raise [Error] if value is not the expected type.
|
133
|
+
def collection_of(feature_collection, type, name = nil)
|
134
|
+
if !feature_collection
|
135
|
+
raise Error, "No featureCollection passed"
|
136
|
+
end
|
137
|
+
if !name
|
138
|
+
raise Error, ".collectionOf() requires a name"
|
139
|
+
end
|
140
|
+
if feature_collection[:type] != "FeatureCollection"
|
141
|
+
raise Error, "Invalid input to #{name}, FeatureCollection required"
|
142
|
+
end
|
143
|
+
|
144
|
+
feature_collection[:features].each do |feature|
|
145
|
+
if !feature || feature[:type] != "Feature" || !feature[:geometry]
|
146
|
+
raise Error, "Invalid input to #{name}, Feature with geometry required"
|
147
|
+
end
|
148
|
+
if feature[:geometry][:type] != type
|
149
|
+
raise Error, "Invalid input to #{name}: must be a #{type}, given #{feature[:geometry][:type]}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Get GeoJSON object's type, Geometry type is prioritize.
|
155
|
+
# @see https://turfjs.org/docs/#getType
|
156
|
+
# @param geojson [GeoJSON] GeoJSON object
|
157
|
+
# @param _name [String] name of the variable to display in error message (unused)
|
158
|
+
# @return [String] GeoJSON type
|
159
|
+
def get_type(geojson, _name = "geojson")
|
160
|
+
geojson = deep_symbolize_keys geojson
|
161
|
+
if geojson[:type] == "FeatureCollection"
|
162
|
+
return "FeatureCollection"
|
163
|
+
end
|
164
|
+
if geojson[:type] == "GeometryCollection"
|
165
|
+
return "GeometryCollection"
|
166
|
+
end
|
167
|
+
if geojson[:type] == "Feature" && geojson[:geometry]
|
168
|
+
return geojson[:geometry][:type]
|
169
|
+
end
|
170
|
+
|
171
|
+
geojson[:type]
|
172
|
+
end
|
47
173
|
end
|
data/lib/turf/kinks.rb
ADDED
data/lib/turf/length.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "distance"
|
4
|
+
require_relative "meta"
|
5
|
+
|
3
6
|
# :nodoc:
|
4
7
|
module Turf
|
5
8
|
# @!group Measurement
|
@@ -9,13 +12,13 @@ module Turf
|
|
9
12
|
# @param geojson [Feature<LineString|MultiLinestring>] GeoJSON to measure
|
10
13
|
# @param units [string] can be degrees, radians, miles, or kilometers (optional, default "kilometers")
|
11
14
|
# @return [number] length of GeoJSON
|
12
|
-
def length(geojson,
|
15
|
+
def length(geojson, options = {})
|
13
16
|
geojson = deep_symbolize_keys(geojson)
|
14
17
|
geojson = feature(geojson) if geojson[:geometry].nil?
|
15
|
-
segment_reduce(geojson,
|
18
|
+
segment_reduce(geojson, 0) do |previous_value, segment|
|
16
19
|
previous_value ||= 0
|
17
20
|
coords = segment.dig(:geometry, :coordinates)
|
18
|
-
previous_value + Turf.distance(coords[0], coords[1],
|
21
|
+
previous_value + Turf.distance(coords[0], coords[1], options)
|
19
22
|
end
|
20
23
|
end
|
21
24
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Turf
|
4
|
+
# :nodoc:
|
5
|
+
module Lineclip
|
6
|
+
# Cohen-Sutherland line clipping algorithm, adapted to efficiently
|
7
|
+
# handle polylines rather than just segments.
|
8
|
+
def self.lineclip(points, bbox, result = [])
|
9
|
+
len = points.length
|
10
|
+
code_a = bit_code(points[0], bbox)
|
11
|
+
part = []
|
12
|
+
a = nil
|
13
|
+
b = nil
|
14
|
+
|
15
|
+
(1...len).each do |i|
|
16
|
+
a = points[i - 1]
|
17
|
+
b = points[i]
|
18
|
+
code_b = last_code = bit_code(b, bbox)
|
19
|
+
|
20
|
+
loop do
|
21
|
+
if (code_a | code_b).zero?
|
22
|
+
# accept
|
23
|
+
part << a
|
24
|
+
|
25
|
+
if code_b != last_code
|
26
|
+
# segment went outside
|
27
|
+
part << b
|
28
|
+
|
29
|
+
if i < len - 1
|
30
|
+
# start a new line
|
31
|
+
result << part
|
32
|
+
part = []
|
33
|
+
end
|
34
|
+
elsif i == len - 1
|
35
|
+
part << b
|
36
|
+
end
|
37
|
+
break
|
38
|
+
elsif code_a.anybits?(code_b)
|
39
|
+
# trivial reject
|
40
|
+
break
|
41
|
+
elsif code_a != 0
|
42
|
+
# a outside, intersect with clip edge
|
43
|
+
a = intersect(a, b, code_a, bbox)
|
44
|
+
code_a = bit_code(a, bbox)
|
45
|
+
else
|
46
|
+
# b outside
|
47
|
+
b = intersect(a, b, code_b, bbox)
|
48
|
+
code_b = bit_code(b, bbox)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
code_a = last_code
|
53
|
+
end
|
54
|
+
|
55
|
+
result << part unless part.empty?
|
56
|
+
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
# Sutherland-Hodgeman polygon clipping algorithm
|
61
|
+
def self.polygonclip(points, bbox)
|
62
|
+
result = []
|
63
|
+
prev = nil
|
64
|
+
prev_inside = nil
|
65
|
+
|
66
|
+
# clip against each side of the clip rectangle
|
67
|
+
[8, 4, 2, 1].each do |edge|
|
68
|
+
result = []
|
69
|
+
prev = points.last
|
70
|
+
prev_inside = bit_code(prev, bbox).nobits?(edge)
|
71
|
+
|
72
|
+
points.each do |p|
|
73
|
+
inside = bit_code(p, bbox).nobits?(edge)
|
74
|
+
|
75
|
+
# if segment goes through the clip window, add an intersection
|
76
|
+
result << intersect(prev, p, edge, bbox) if inside != prev_inside
|
77
|
+
|
78
|
+
result << p if inside # add a point if it's inside
|
79
|
+
|
80
|
+
prev = p
|
81
|
+
prev_inside = inside
|
82
|
+
end
|
83
|
+
|
84
|
+
points = result
|
85
|
+
break if points.empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
result
|
89
|
+
end
|
90
|
+
|
91
|
+
# intersect a segment against one of the 4 lines that make up the bbox
|
92
|
+
def self.intersect(a, b, edge, bbox) # rubocop:disable Naming/MethodParameterName
|
93
|
+
if edge.anybits?(8)
|
94
|
+
[a[0] + (((b[0] - a[0]) * (bbox[3] - a[1]).to_f) / (b[1] - a[1])), bbox[3]]
|
95
|
+
elsif edge.anybits?(4)
|
96
|
+
[a[0] + (((b[0] - a[0]) * (bbox[1] - a[1]).to_f) / (b[1] - a[1])), bbox[1]]
|
97
|
+
elsif edge.anybits?(2)
|
98
|
+
[bbox[2], a[1] + (((b[1] - a[1]) * (bbox[2] - a[0]).to_f) / (b[0] - a[0]))]
|
99
|
+
elsif edge.anybits?(1)
|
100
|
+
[bbox[0], a[1] + (((b[1] - a[1]) * (bbox[0] - a[0]).to_f) / (b[0] - a[0]))]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# bit code reflects the point position relative to the bbox:
|
105
|
+
# left mid right
|
106
|
+
# top 1001 1000 1010
|
107
|
+
# mid 0001 0000 0010
|
108
|
+
# bottom 0101 0100 0110
|
109
|
+
def self.bit_code(p, bbox) # rubocop:disable Naming/MethodParameterName
|
110
|
+
code = 0
|
111
|
+
code |= 1 if p[0] < bbox[0] # left
|
112
|
+
code |= 2 if p[0] > bbox[2] # right
|
113
|
+
code |= 4 if p[1] < bbox[1] # bottom
|
114
|
+
code |= 8 if p[1] > bbox[3] # top
|
115
|
+
code
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|