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.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +21 -0
  3. data/README.md +159 -139
  4. data/lib/turf/along.rb +10 -3
  5. data/lib/turf/angle.rb +8 -0
  6. data/lib/turf/area.rb +4 -1
  7. data/lib/turf/bbox.rb +36 -0
  8. data/lib/turf/bbox_clip.rb +74 -0
  9. data/lib/turf/bbox_polygon.rb +41 -0
  10. data/lib/turf/bearing.rb +5 -2
  11. data/lib/turf/bezier_spline.rb +8 -0
  12. data/lib/turf/boolean_clockwise.rb +8 -0
  13. data/lib/turf/boolean_concave.rb +8 -0
  14. data/lib/turf/boolean_contains.rb +52 -0
  15. data/lib/turf/boolean_crosses.rb +8 -0
  16. data/lib/turf/boolean_disjoint.rb +8 -0
  17. data/lib/turf/boolean_equal.rb +8 -0
  18. data/lib/turf/boolean_intersects.rb +8 -0
  19. data/lib/turf/boolean_overlap.rb +8 -0
  20. data/lib/turf/boolean_parallel.rb +8 -0
  21. data/lib/turf/boolean_point_in_polygon.rb +5 -3
  22. data/lib/turf/boolean_point_on_line.rb +8 -0
  23. data/lib/turf/boolean_touches.rb +8 -0
  24. data/lib/turf/boolean_valid.rb +8 -0
  25. data/lib/turf/boolean_within.rb +8 -0
  26. data/lib/turf/buffer.rb +8 -0
  27. data/lib/turf/center.rb +32 -0
  28. data/lib/turf/center_mean.rb +8 -0
  29. data/lib/turf/center_median.rb +8 -0
  30. data/lib/turf/center_of_mass.rb +8 -0
  31. data/lib/turf/centroid.rb +5 -2
  32. data/lib/turf/circle.rb +5 -2
  33. data/lib/turf/clean_coords.rb +8 -0
  34. data/lib/turf/clone.rb +8 -0
  35. data/lib/turf/clusters.rb +32 -0
  36. data/lib/turf/clusters_dbscan.rb +8 -0
  37. data/lib/turf/clusters_kmeans.rb +8 -0
  38. data/lib/turf/collect.rb +8 -0
  39. data/lib/turf/combine.rb +8 -0
  40. data/lib/turf/concave.rb +8 -0
  41. data/lib/turf/convex.rb +8 -0
  42. data/lib/turf/destination.rb +6 -3
  43. data/lib/turf/difference.rb +8 -0
  44. data/lib/turf/directional_mean.rb +8 -0
  45. data/lib/turf/dissolve.rb +8 -0
  46. data/lib/turf/distance.rb +5 -2
  47. data/lib/turf/distance_weight.rb +12 -0
  48. data/lib/turf/ellipse.rb +8 -0
  49. data/lib/turf/envelope.rb +8 -0
  50. data/lib/turf/explode.rb +29 -0
  51. data/lib/turf/flatten.rb +8 -0
  52. data/lib/turf/flip.rb +8 -0
  53. data/lib/turf/geojson_rbush.rb +8 -0
  54. data/lib/turf/great_circle.rb +8 -0
  55. data/lib/turf/helpers.rb +166 -26
  56. data/lib/turf/hex_grid.rb +8 -0
  57. data/lib/turf/interpolate.rb +8 -0
  58. data/lib/turf/intersect.rb +8 -0
  59. data/lib/turf/invariant.rb +127 -1
  60. data/lib/turf/isobands.rb +8 -0
  61. data/lib/turf/isolines.rb +8 -0
  62. data/lib/turf/kinks.rb +8 -0
  63. data/lib/turf/length.rb +6 -3
  64. data/lib/turf/lib/lineclip.rb +118 -0
  65. data/lib/turf/line_arc.rb +8 -0
  66. data/lib/turf/line_chunk.rb +8 -0
  67. data/lib/turf/line_intersect.rb +8 -0
  68. data/lib/turf/line_offset.rb +8 -0
  69. data/lib/turf/line_overlap.rb +8 -0
  70. data/lib/turf/line_segment.rb +8 -0
  71. data/lib/turf/line_slice.rb +8 -0
  72. data/lib/turf/line_slice_along.rb +8 -0
  73. data/lib/turf/line_split.rb +8 -0
  74. data/lib/turf/line_to_polygon.rb +8 -0
  75. data/lib/turf/mask.rb +8 -0
  76. data/lib/turf/meta.rb +445 -79
  77. data/lib/turf/midpoint.rb +8 -0
  78. data/lib/turf/moran_index.rb +8 -0
  79. data/lib/turf/nearest_neighbor_analysis.rb +8 -0
  80. data/lib/turf/nearest_point.rb +8 -0
  81. data/lib/turf/nearest_point_on_line.rb +8 -0
  82. data/lib/turf/nearest_point_to_line.rb +8 -0
  83. data/lib/turf/planepoint.rb +8 -0
  84. data/lib/turf/point_grid.rb +8 -0
  85. data/lib/turf/point_on_feature.rb +8 -0
  86. data/lib/turf/point_to_line_distance.rb +8 -0
  87. data/lib/turf/point_to_polygon_distance.rb +8 -0
  88. data/lib/turf/points_within_polygon.rb +8 -0
  89. data/lib/turf/polygon_smooth.rb +8 -0
  90. data/lib/turf/polygon_tangents.rb +8 -0
  91. data/lib/turf/polygon_to_line.rb +20 -0
  92. data/lib/turf/polygonize.rb +8 -0
  93. data/lib/turf/projection.rb +12 -0
  94. data/lib/turf/quadrat_analysis.rb +8 -0
  95. data/lib/turf/random.rb +20 -0
  96. data/lib/turf/rectangle_grid.rb +8 -0
  97. data/lib/turf/rewind.rb +8 -0
  98. data/lib/turf/rhumb_bearing.rb +8 -0
  99. data/lib/turf/rhumb_destination.rb +8 -0
  100. data/lib/turf/rhumb_distance.rb +8 -0
  101. data/lib/turf/sample.rb +8 -0
  102. data/lib/turf/sector.rb +8 -0
  103. data/lib/turf/shortest_path.rb +8 -0
  104. data/lib/turf/simplify.rb +8 -0
  105. data/lib/turf/square.rb +42 -0
  106. data/lib/turf/square_grid.rb +8 -0
  107. data/lib/turf/standard_deviational_ellipse.rb +8 -0
  108. data/lib/turf/tag.rb +8 -0
  109. data/lib/turf/tesselate.rb +8 -0
  110. data/lib/turf/tin.rb +8 -0
  111. data/lib/turf/transform_rotate.rb +8 -0
  112. data/lib/turf/transform_scale.rb +8 -0
  113. data/lib/turf/transform_translate.rb +8 -0
  114. data/lib/turf/triangle_grid.rb +8 -0
  115. data/lib/turf/truncate.rb +3 -0
  116. data/lib/turf/union.rb +8 -0
  117. data/lib/turf/unkink_polygon.rb +8 -0
  118. data/lib/turf/version.rb +2 -1
  119. data/lib/turf/voronoi.rb +8 -0
  120. data/lib/turf.rb +2 -0
  121. data/lib/turf_ruby.rb +107 -3
  122. 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" => EARTH_RADIUS / 111_325,
9
+ "degrees" => 360.0 / (2 * Math::PI),
11
10
  "feet" => EARTH_RADIUS * 3.28084,
12
- "inches" => EARTH_RADIUS * 39.370,
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 / 1.0936
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: {}, bbox: nil, id: nil)
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, bbox: nil, id: nil)
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, bbox: nil, id: nil, properties: {})
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: properties, bbox: bbox, id: id)
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: {}, bbox: nil, id: nil)
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: properties, bbox: bbox, id: id)
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: {}, bbox: nil, id: nil)
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: properties, bbox: bbox, id: id)
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: {}, id: nil, bbox: nil)
135
+ def point(coordinates, properties = nil, options = {})
120
136
  geom = {
121
137
  type: "Point",
122
138
  coordinates: coordinates
123
139
  }
124
- feature(geom, properties: properties, id: id, bbox: bbox)
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: {}, bbox: nil, id: nil)
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: properties, id: id, bbox: bbox)
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: {}, bbox: nil, id: nil)
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: properties, id: id, bbox: bbox)
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: {}, bbox: nil, id: nil)
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: properties, id: id, bbox: bbox)
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
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Turf
5
+ def hex_grid(*args)
6
+ raise NotImplementedError
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Turf
5
+ def interpolate(*args)
6
+ raise NotImplementedError
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Turf
5
+ def intersect(*args)
6
+ raise NotImplementedError
7
+ end
8
+ end
@@ -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.fetch(:geometry, {})[:type] == "Point"
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
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Turf
5
+ def isobands(*args)
6
+ raise NotImplementedError
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Turf
5
+ def isolines(*args)
6
+ raise NotImplementedError
7
+ end
8
+ end
data/lib/turf/kinks.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Turf
5
+ def kinks(*args)
6
+ raise NotImplementedError
7
+ end
8
+ end
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, units: "kilometers")
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, initial_value: 0) do |previous_value, segment|
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], units: units)
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
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Turf
5
+ def line_arc(*args)
6
+ raise NotImplementedError
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Turf
5
+ def line_chunk(*args)
6
+ raise NotImplementedError
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Turf
5
+ def line_intersect(*args)
6
+ raise NotImplementedError
7
+ end
8
+ end