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/meta.rb CHANGED
@@ -1,82 +1,160 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "helpers"
4
+
3
5
  # :nodoc:
4
6
  module Turf
5
- # @!group Meta
6
-
7
- # Iterate over coordinates in any GeoJSON object, similar to Array.forEach()
8
- # @see https://turfjs.org/docs/#coordEach
9
- # @param geojson [FeatureCollection|Feature|Geometry] any GeoJSON object
10
- # @param exclude_wrap_coord [boolean] whether or not to include the final coordinate of LinearRings that wraps the
11
- # ring in its iteration
12
- # @yield [current_coord, coord_index] given any coordinate
13
- # @yieldparam current_coord [Array<number>] The current coordinate being processed.
14
- # @yieldparam coord_index [number] The current index of the coordinate being processed.
15
- def coord_each(geojson, exclude_wrap_coord: false, &block)
16
- coord_all(geojson, exclude_wrap_coord: exclude_wrap_coord, &block)
17
- end
18
-
19
- # Get all coordinates from any GeoJSON object.
20
- # @see https://turfjs.org/docs/#coordAll
21
- # @param geojson [FeatureCollection|Feature|Geometry] any GeoJSON object
22
- # @param exclude_wrap_coord [boolean] whether or not to include the final coordinate of LinearRings that wraps the
23
- # ring in its iteration
24
- # @return [Array<Array<number>>] coordinate position array
25
- def coord_all(geojson, exclude_wrap_coord: false, &block)
26
- geometry_index = -1
27
- geom_each(geojson) do |geometry, _idx, _properties|
28
- if geometry.nil?
29
- next
30
- end
31
-
32
- case geometry[:type]
33
- when "Point"
34
- geometry_index += 1
35
- block.call(geometry[:coordinates], geometry_index)
36
- when "LineString", "MultiPoint"
37
- geometry[:coordinates].each do |coords|
38
- geometry_index += 1
39
- block.call(coords, geometry_index)
40
- end
41
- when "Polygon", "MultiLineString"
42
- geometry[:coordinates].each do |line_coords|
43
- if exclude_wrap_coord
44
- line_coords = line_coords[0...-1]
45
- end
46
- line_coords.each do |coords|
47
- geometry_index += 1
48
- block.call(coords, geometry_index)
7
+ # Iterate over coordinates in any GeoJSON object, similar to Array#each.
8
+ #
9
+ # @param geojson [AllGeoJSON] any GeoJSON object
10
+ # @yield [current_coord, coord_index, feature_index, multi_feature_index, geometry_index]
11
+ # @yieldparam current_coord [Array<Number>] The current coordinate being processed.
12
+ # @yieldparam coord_index [Integer] The current index of the coordinate being processed.
13
+ # @yieldparam feature_index [Integer] The current index of the Feature being processed.
14
+ # @yieldparam multi_feature_index [Integer] The current index of the Multi-Feature being processed.
15
+ # @yieldparam geometry_index [Integer] The current index of the Geometry being processed.
16
+ # @param exclude_wrap_coord [Boolean] whether or not to include the final coordinate
17
+ # of LinearRings that wraps the ring in its iteration.
18
+ # @return [void]
19
+ # @example
20
+ # features = Turf.feature_collection([
21
+ # Turf.point([26, 37], {foo: "bar"}),
22
+ # Turf.point([36, 53], {hello: "world"})
23
+ # ])
24
+ #
25
+ # Turf.coord_each(features, exclude_wrap_coord: false) do |
26
+ # current_coord,
27
+ # coord_index,
28
+ # feature_index,
29
+ # multi_feature_index,
30
+ # geometry_index
31
+ # |
32
+ # #=current_coord
33
+ # #=coord_index
34
+ # #=feature_index
35
+ # #=multi_feature_index
36
+ # #=geometry_index
37
+ # end
38
+ def coord_each(geojson, exclude_wrap_coord: false)
39
+ return if geojson.nil?
40
+
41
+ coord_index = 0
42
+ is_geometry_collection = false
43
+ type = geojson[:type]
44
+ is_feature_collection = type == "FeatureCollection"
45
+ is_feature = type == "Feature"
46
+ stop = is_feature_collection ? geojson[:features].length : 1
47
+
48
+ (0...stop).each do |feature_index|
49
+ geometry_maybe_collection = if is_feature_collection
50
+ geojson[:features][feature_index][:geometry]
51
+ elsif is_feature
52
+ geojson[:geometry]
53
+ else
54
+ geojson
55
+ end
56
+
57
+ is_geometry_collection = if geometry_maybe_collection
58
+ geometry_maybe_collection[:type] == "GeometryCollection"
59
+ else
60
+ false
61
+ end
62
+ stop_g = is_geometry_collection ? geometry_maybe_collection[:geometries].length : 1
63
+
64
+ (0...stop_g).each do |geom_index|
65
+ multi_feature_index = 0
66
+ geometry_index = 0
67
+ geometry = if is_geometry_collection
68
+ geometry_maybe_collection[:geometries][geom_index]
69
+ else
70
+ geometry_maybe_collection
71
+ end
72
+
73
+ next if geometry.nil?
74
+
75
+ coords = geometry[:coordinates]
76
+ geom_type = geometry[:type]
77
+ wrap_shrink = exclude_wrap_coord && %w[Polygon MultiPolygon].include?(geom_type) ? 1 : 0
78
+
79
+ case geom_type
80
+ when "Point"
81
+ return false if yield(coords, coord_index, feature_index, multi_feature_index, geometry_index) == false
82
+
83
+ coord_index += 1
84
+ multi_feature_index += 1
85
+ when "LineString", "MultiPoint"
86
+ coords.each_with_index do |coord, _j|
87
+ return false if yield(coord, coord_index, feature_index, multi_feature_index, geometry_index) == false
88
+
89
+ coord_index += 1
90
+ multi_feature_index += 1 if geom_type == "MultiPoint"
49
91
  end
50
- end
51
- when "MultiPolygon"
52
- geometry[:coordinates].each do |polygon_coords|
53
- polygon_coords.each do |line_coords|
54
- if exclude_wrap_coord
55
- line_coords = line_coords[0...-1]
92
+ multi_feature_index += 1 if geom_type == "LineString"
93
+ when "Polygon", "MultiLineString"
94
+ coords.each_with_index do |coord, _j|
95
+ (0...(coord.length - wrap_shrink)).each do |k|
96
+ return false if yield(coord[k], coord_index, feature_index, multi_feature_index, geometry_index) == false
97
+
98
+ coord_index += 1
56
99
  end
57
- line_coords.each do |coords|
100
+ multi_feature_index += 1 if geom_type == "MultiLineString"
101
+ geometry_index += 1 if geom_type == "Polygon"
102
+ end
103
+ multi_feature_index += 1 if geom_type == "Polygon"
104
+ when "MultiPolygon"
105
+ coords.each_with_index do |coord, _j|
106
+ geometry_index = 0
107
+ coord.each_with_index do |inner_coord, _k|
108
+ (0...(inner_coord.length - wrap_shrink)).each do |l|
109
+ if yield(inner_coord[l], coord_index, feature_index, multi_feature_index, geometry_index) == false
110
+ return false
111
+ end
112
+
113
+ coord_index += 1
114
+ end
58
115
  geometry_index += 1
59
- block.call(coords, geometry_index)
60
116
  end
117
+ multi_feature_index += 1
61
118
  end
119
+ when "GeometryCollection"
120
+ geometry[:geometries].each do |inner_geometry|
121
+ return false if coord_each(inner_geometry, exclude_wrap_coord: exclude_wrap_coord, &Proc.new) == false
122
+ end
123
+ else
124
+ raise Error, "Unknown Geometry Type"
62
125
  end
63
- when "Feature"
64
- coord_each(geometry, exclude_wrap_coord: exclude_wrap_coord, &block)
65
- else
66
- raise Error, "Unknown Geometry Type: #{geometry[:type]}"
67
126
  end
68
127
  end
69
- geojson
70
128
  end
71
129
 
72
- # Reduce coordinates in any GeoJSON object, similar to Array.reduce()
130
+ # Get all coordinates from any GeoJSON object.
131
+ #
132
+ # @param geojson [AllGeoJSON] any GeoJSON object
133
+ # @return [Array<Array<Number>>] coordinate position array
134
+ # @example
135
+ # features = Turf.feature_collection([
136
+ # Turf.point([26, 37], {foo: 'bar'}),
137
+ # Turf.point([36, 53], {hello: 'world'})
138
+ # ])
139
+ #
140
+ # coords = Turf.coord_all(features)
141
+ # #= [[26, 37], [36, 53]]
142
+ def coord_all(geojson)
143
+ coords = []
144
+ coord_each(geojson) do |coord|
145
+ coords.push(coord)
146
+ end
147
+ coords
148
+ end
149
+
150
+ # Reduce coordinates in any GeoJSON object, similar to Array.reduce(*args)
73
151
  # @see https://turfjs.org/docs/#coordReduce
74
152
  # @param geojson [FeatureCollection|Geometry|Feature] any GeoJSON object
75
153
  # @param initial_value [*] Value to use as the first argument to the first call of the callback.
76
154
  # @param exclude_wrap_coord [Boolean] whether or not to include the final coordinate of LinearRings that wraps the
77
155
  # ring in its iteration.
78
156
  # @return [*] The value that results from the reduction.
79
- def coord_reduce(geojson, initial_value: nil, exclude_wrap_coord: false)
157
+ def coord_reduce(geojson, initial_value = nil, exclude_wrap_coord: false)
80
158
  previous_value = initial_value
81
159
 
82
160
  coord_each(
@@ -98,7 +176,7 @@ module Turf
98
176
  previous_value
99
177
  end
100
178
 
101
- # Iterate over each geometry in any GeoJSON object, similar to Array.forEach()
179
+ # Iterate over each geometry in any GeoJSON object, similar to Array.forEach(*args)
102
180
  # @see https://turfjs.org/docs/#geomReduce
103
181
  # @param geojson [FeatureCollection|Feature|Geometry] any GeoJSON object
104
182
  # @yieldparam geom [Geometry] The current Feature being processed.
@@ -139,7 +217,7 @@ module Turf
139
217
  end
140
218
  end
141
219
 
142
- # Reduce geometry in any GeoJSON object, similar to Array.reduce().
220
+ # Reduce geometry in any GeoJSON object, similar to Array.reduce(*args).
143
221
  # @see https://turfjs.org/docs/#geomReduce
144
222
  # @param geojson [FeatureCollection|Feature|Geometry] any GeoJSON object
145
223
  # @param initial_value [*] Value to use as the first argument to the first call of the callback.
@@ -150,7 +228,7 @@ module Turf
150
228
  # @yieldparam bbox [Array<number>] Bounding Box Array [west, south, east, north] associated with the Feature
151
229
  # @yieldparam id [string|number] Identifier associated with the Feature
152
230
  # @return [*] The value that results from the reduction.
153
- def geom_reduce(geojson, initial_value: nil)
231
+ def geom_reduce(geojson, initial_value = nil)
154
232
  previous_value = initial_value
155
233
 
156
234
  geom_each(
@@ -206,7 +284,7 @@ module Turf
206
284
  features.each_with_index(&block)
207
285
  end
208
286
 
209
- # Reduce features in any GeoJSON object, similar to Array.reduce().
287
+ # Reduce features in any GeoJSON object, similar to Array.reduce(*args).
210
288
  # @see https://turfjs.org/docs/#featureReduce
211
289
  # @param geojson [FeatureCollection|Feature|Geometry] any GeoJSON object
212
290
  # @param initial_value [*] Value to use as the first argument to the first call of the callback.
@@ -214,7 +292,7 @@ module Turf
214
292
  # @yieldparam feature [Feature<any>] The current Feature being processed.
215
293
  # @yieldparam feature_index [number] The current index of the Feature being processed.
216
294
  # @return [*] The value that results from the reduction.
217
- def feature_reduce(geojson, initial_value: nil)
295
+ def feature_reduce(geojson, initial_value = nil)
218
296
  previous_value = initial_value
219
297
 
220
298
  feature_each(
@@ -245,7 +323,7 @@ module Turf
245
323
  geom_each(geojson) do |geometry, feature_index, properties, bbox, id|
246
324
  if geometry.nil?
247
325
  next yield(
248
- feature(nil, properties: properties, bbox: bbox, id: id),
326
+ feature(nil, properties, bbox: bbox, id: id),
249
327
  feature_index,
250
328
  0
251
329
  )
@@ -254,7 +332,7 @@ module Turf
254
332
  case geometry[:type]
255
333
  when "Point", "LineString", "Polygon"
256
334
  yield(
257
- feature(geometry, properties: properties, bbox: bbox, id: id),
335
+ feature(geometry, properties, bbox: bbox, id: id),
258
336
  feature_index,
259
337
  0
260
338
  )
@@ -266,7 +344,7 @@ module Turf
266
344
  coordinates: coordinate
267
345
  }
268
346
  yield(
269
- feature(geom, properties: properties),
347
+ feature(geom, properties),
270
348
  feature_index,
271
349
  multi_feature_index
272
350
  )
@@ -275,7 +353,7 @@ module Turf
275
353
  end
276
354
  end
277
355
 
278
- # Reduce flattened features in any GeoJSON object, similar to Array.reduce().
356
+ # Reduce flattened features in any GeoJSON object, similar to Array.reduce(*args).
279
357
  # @see https://turfjs.org/docs/#flattenEach
280
358
  # @param geojson [FeatureCollection|Feature|Geometry] any GeoJSON object
281
359
  # @param initial_value [*] Value to use as the first argument to the first call of the callback.
@@ -284,7 +362,7 @@ module Turf
284
362
  # @yieldparam feature_index [number] The current index of the Feature being processed.
285
363
  # @yieldparam multi_feature_index [number] The current index of the Feature in the multi-Feature
286
364
  # @return [*] The value that results from the reduction.
287
- def flatten_reduce(geojson, initial_value: nil)
365
+ def flatten_reduce(geojson, initial_value = nil)
288
366
  previous_value = initial_value
289
367
 
290
368
  flatten_each(
@@ -306,35 +384,43 @@ module Turf
306
384
  previous_value
307
385
  end
308
386
 
309
- # Iterate over 2-vertex line segment in any GeoJSON object, similar to Array.forEach()
387
+ # Iterate over 2-vertex line segment in any GeoJSON object, similar to Array.forEach(*args)
310
388
  # (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
311
389
  # @see https://turfjs.org/docs/#segmentEach
312
390
  # @param geojson [FeatureCollection|Feature|Geometry] any GeoJSON object
313
391
  def segment_each(geojson)
314
- flatten_each(geojson) do |feature, feature_index|
392
+ flatten_each(geojson) do |feature, feature_index, multi_feature_index|
315
393
  # Exclude null Geometries
316
- return if feature[:geometry].nil?
394
+ next if feature[:geometry].nil?
317
395
 
318
396
  # (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
319
397
  type = feature[:geometry][:type]
320
- return if %w[Point MultiPoint].include?(type)
398
+ next if %w[Point MultiPoint].include?(type)
321
399
 
322
400
  segment_index = 0
323
401
 
324
402
  # Generate 2-vertex line segments
325
403
  previous_coords = nil
326
404
  previous_feature_index = 0
327
- coord_each(feature) do |current_coord|
328
- # Simulating a meta.coord_reduce() since `reduce` operations cannot be stopped by returning `false`
329
- if previous_coords.nil? || feature_index > previous_feature_index
405
+ previous_multi_index = 0
406
+ prev_geom_index = 0
407
+ coord_each(feature) do |current_coord, _coord_index, _feature_index_coord, multi_part_index_coord, geometry_index|
408
+ # Simulating a meta.coord_reduce(*args) since `reduce` operations cannot be stopped by returning `false`
409
+ if previous_coords.nil? ||
410
+ feature_index > previous_feature_index ||
411
+ multi_part_index_coord > previous_multi_index ||
412
+ geometry_index > prev_geom_index
413
+
330
414
  previous_coords = current_coord
331
415
  previous_feature_index = feature_index
416
+ previous_multi_index = multi_part_index_coord
417
+ prev_geom_index = geometry_index
332
418
  segment_index = 0
333
419
  next
334
420
  end
335
421
 
336
- segment = Turf.line_string([previous_coords, current_coord], properties: feature[:properties])
337
- next unless yield(segment, feature_index)
422
+ segment = Turf.line_string([previous_coords, current_coord], feature[:properties])
423
+ next unless yield(segment, feature_index, multi_feature_index, geometry_index, segment_index)
338
424
 
339
425
  segment_index += 1
340
426
  previous_coords = current_coord
@@ -342,7 +428,7 @@ module Turf
342
428
  end
343
429
  end
344
430
 
345
- def segment_reduce(geojson, initial_value: nil)
431
+ def segment_reduce(geojson, initial_value = nil)
346
432
  previous_value = initial_value
347
433
  started = false
348
434
 
@@ -365,4 +451,284 @@ module Turf
365
451
 
366
452
  previous_value
367
453
  end
454
+
455
+ # Iterate over properties in any GeoJSON object, similar to Array.forEach.
456
+ # @see https://turfjs.org/docs/#propEach
457
+ # @param geojson [FeatureCollection|Feature] any GeoJSON object
458
+ # @yieldparam current_properties [Hash] The current Properties being processed.
459
+ # @yieldparam feature_index [number] The current index of the Feature being processed.
460
+ def prop_each(geojson)
461
+ case geojson[:type]
462
+ when "FeatureCollection"
463
+ geojson[:features].each_with_index do |feature, i|
464
+ break if yield(feature[:properties], i) == false
465
+ end
466
+ when "Feature"
467
+ yield(geojson[:properties], 0)
468
+ end
469
+ end
470
+
471
+ # Reduce properties in any GeoJSON object into a single value,
472
+ # similar to how Array.reduce works. However, in this case we lazily run
473
+ # the reduction, so an array of all properties is unnecessary.
474
+ # @see https://turfjs.org/docs/#propReduce
475
+ # @param geojson [FeatureCollection|Feature|Geometry] any GeoJSON object
476
+ # @param initial_value [Object] Value to use as the first argument to the first call of the callback.
477
+ # @yieldparam previous_value [Object] The accumulated value previously returned in the last invocation
478
+ # of the callback, or initial_value, if supplied.
479
+ # @yieldparam current_properties [Hash] The current Properties being processed.
480
+ # @yieldparam feature_index [number] The current index of the Feature being processed.
481
+ # @return [Object] The value that results from the reduction.
482
+ def prop_reduce(geojson, initial_value = nil)
483
+ previous_value = initial_value
484
+
485
+ prop_each(geojson) do |current_properties, feature_index|
486
+ previous_value = if feature_index.zero? && initial_value.nil?
487
+ current_properties
488
+ else
489
+ yield(previous_value, current_properties, feature_index)
490
+ end
491
+ end
492
+
493
+ previous_value
494
+ end
495
+
496
+ # Iterate over line or ring coordinates in LineString, Polygon, MultiLineString, MultiPolygon Features or Geometries,
497
+ # similar to Array.forEach.
498
+ # @see https://turfjs.org/docs/#lineEach
499
+ # @param geojson [FeatureCollection<Lines>|Feature<Lines>|Lines|Feature<GeometryCollection>|GeometryCollection]
500
+ # any GeoJSON object
501
+ # @yieldparam current_line [Feature<LineString>] The current LineString|LinearRing being processed
502
+ # @yieldparam feature_index [number] The current index of the Feature being processed
503
+ # @yieldparam multi_feature_index [number] The current index of the Multi-Feature being processed
504
+ # @yieldparam geometry_index [number] The current index of the Geometry being processed
505
+ def line_each(geojson)
506
+ flatten_each(geojson) do |feature, feature_index, multi_feature_index|
507
+ next unless feature[:geometry]
508
+
509
+ type = feature[:geometry][:type]
510
+ coords = feature[:geometry][:coordinates]
511
+
512
+ case type
513
+ when "LineString"
514
+ yield(feature, feature_index, multi_feature_index, 0, 0)
515
+ when "Polygon"
516
+ coords.each_with_index do |ring, geometry_index|
517
+ yield(
518
+ feature({ type: "LineString", coordinates: ring }, feature[:properties]),
519
+ feature_index,
520
+ multi_feature_index,
521
+ geometry_index
522
+ )
523
+ end
524
+ end
525
+ end
526
+ end
527
+
528
+ # Reduce features in any GeoJSON object, similar to Array.reduce().
529
+ # @see https://turfjs.org/docs/#lineReduce
530
+ # @param geojson [FeatureCollection<Lines>|Feature<Lines>|Lines|Feature<GeometryCollection>|GeometryCollection]
531
+ # any GeoJSON object
532
+ # @param initial_value [Object] Value to use as the first argument to the first call of the callback.
533
+ # @yieldparam previous_value [Object] The accumulated value previously returned in the last invocation
534
+ # of the callback, or initial_value, if supplied.
535
+ # @yieldparam current_line [Feature<LineString>] The current LineString|LinearRing being processed.
536
+ # @yieldparam feature_index [number] The current index of the Feature being processed
537
+ # @yieldparam multi_feature_index [number] The current index of the Multi-Feature being processed
538
+ # @yieldparam geometry_index [number] The current index of the Geometry being processed
539
+ # @return [Object] The value that results from the reduction.
540
+ def line_reduce(geojson, initial_value = nil)
541
+ previous_value = initial_value
542
+
543
+ line_each(geojson) do |current_line, feature_index, multi_feature_index, geometry_index|
544
+ previous_value = if feature_index.zero? && initial_value.nil?
545
+ current_line
546
+ else
547
+ yield(previous_value, current_line, feature_index, multi_feature_index, geometry_index)
548
+ end
549
+ end
550
+
551
+ previous_value
552
+ end
553
+
554
+ # Finds a particular 2-vertex LineString Segment from a GeoJSON using `@turf/meta` indexes.
555
+ #
556
+ # Negative indexes are permitted.
557
+ # Point & MultiPoint will always return null.
558
+ #
559
+ # @param geojson [FeatureCollection|Feature|Geometry] Any GeoJSON Feature or Geometry
560
+ # @param options [Hash] Optional parameters
561
+ # @option options [Integer] :feature_index (0) Feature Index
562
+ # @option options [Integer] :multi_feature_index (0) Multi-Feature Index
563
+ # @option options [Integer] :geometry_index (0) Geometry Index
564
+ # @option options [Integer] :segment_index (0) Segment Index
565
+ # @option options [Hash] :properties ({}) Translate Properties to output LineString
566
+ # @option options [Array<Number>] :bbox ({}) Translate BBox to output LineString
567
+ # @option options [String, Integer] :id ({}) Translate Id to output LineString
568
+ # @return [Feature<LineString>] 2-vertex GeoJSON Feature LineString
569
+ # @example
570
+ # multi_line = Turf.multi_line_string([
571
+ # [[10, 10], [50, 30], [30, 40]],
572
+ # [[-10, -10], [-50, -30], [-30, -40]]
573
+ # ])
574
+ #
575
+ # # First Segment (defaults are 0)
576
+ # Turf.find_segment(multi_line)
577
+ # # => Feature<LineString<[[10, 10], [50, 30]]>>
578
+ #
579
+ # # First Segment of 2nd Multi Feature
580
+ # Turf.find_segment(multi_line, multi_feature_index: 1)
581
+ # # => Feature<LineString<[[-10, -10], [-50, -30]]>>
582
+ #
583
+ # # Last Segment of Last Multi Feature
584
+ # Turf.find_segment(multi_line, multi_feature_index: -1, segment_index: -1)
585
+ # # => Feature<LineString<[[-50, -30], [-30, -40]]>>
586
+ def find_segment(geojson, options = {})
587
+ options ||= {}
588
+ raise "options is invalid" unless options.is_a?(Hash)
589
+
590
+ feature_index = options.fetch(:feature_index, 0)
591
+ multi_feature_index = options.fetch(:multi_feature_index, 0)
592
+ geometry_index = options.fetch(:geometry_index, 0)
593
+ segment_index = options.fetch(:segment_index, 0)
594
+
595
+ properties = options[:properties]
596
+ geometry = nil
597
+
598
+ case geojson[:type]
599
+ when "FeatureCollection"
600
+ feature_index += geojson[:features].length if feature_index < 0
601
+ properties ||= geojson[:features][feature_index][:properties]
602
+ geometry = geojson[:features][feature_index][:geometry]
603
+ when "Feature"
604
+ properties ||= geojson[:properties]
605
+ geometry = geojson[:geometry]
606
+ when "Point", "MultiPoint"
607
+ return nil
608
+ when "LineString", "Polygon", "MultiLineString", "MultiPolygon"
609
+ geometry = geojson
610
+ else
611
+ raise "geojson is invalid"
612
+ end
613
+
614
+ return nil if geometry.nil?
615
+
616
+ coords = geometry[:coordinates]
617
+ case geometry[:type]
618
+ when "Point", "MultiPoint"
619
+ nil
620
+ when "LineString"
621
+ segment_index += coords.length - 1 if segment_index < 0
622
+ line_string([coords[segment_index], coords[segment_index + 1]], properties, options)
623
+ when "Polygon"
624
+ geometry_index += coords.length if geometry_index < 0
625
+ segment_index += coords[geometry_index].length - 1 if segment_index < 0
626
+ line_string([coords[geometry_index][segment_index], coords[geometry_index][segment_index + 1]], properties,
627
+ options)
628
+ when "MultiLineString"
629
+ multi_feature_index += coords.length if multi_feature_index < 0
630
+ segment_index += coords[multi_feature_index].length - 1 if segment_index < 0
631
+ line_string([coords[multi_feature_index][segment_index], coords[multi_feature_index][segment_index + 1]],
632
+ properties, options)
633
+ when "MultiPolygon"
634
+ multi_feature_index += coords.length if multi_feature_index < 0
635
+ geometry_index += coords[multi_feature_index].length if geometry_index < 0
636
+ segment_index += coords[multi_feature_index][geometry_index].length - 1 if segment_index < 0
637
+ line_string(
638
+ [coords[multi_feature_index][geometry_index][segment_index],
639
+ coords[multi_feature_index][geometry_index][segment_index + 1],], properties, options
640
+ )
641
+ else
642
+ raise "geojson is invalid"
643
+ end
644
+ end
645
+
646
+ # Finds a particular Point from a GeoJSON using `@turf/meta` indexes.
647
+ #
648
+ # Negative indexes are permitted.
649
+ #
650
+ # @param geojson [FeatureCollection|Feature|Geometry] Any GeoJSON Feature or Geometry
651
+ # @param options [Hash] Optional parameters
652
+ # @option options [Integer] :feature_index (0) Feature Index
653
+ # @option options [Integer] :multi_feature_index (0) Multi-Feature Index
654
+ # @option options [Integer] :geometry_index (0) Geometry Index
655
+ # @option options [Integer] :coord_index (0) Coord Index
656
+ # @option options [Hash] :properties ({}) Translate Properties to output Point
657
+ # @option options [Array<Number>] :bbox ({}) Translate BBox to output Point
658
+ # @option options [String, Integer] :id ({}) Translate Id to output Point
659
+ # @return [Feature<Point>] 2-vertex GeoJSON Feature Point
660
+ # @example
661
+ # multi_line = Turf.multi_line_string([
662
+ # [[10, 10], [50, 30], [30, 40]],
663
+ # [[-10, -10], [-50, -30], [-30, -40]]
664
+ # ])
665
+ #
666
+ # # First Segment (defaults are 0)
667
+ # Turf.find_point(multi_line)
668
+ # # => Feature<Point<[10, 10]>>
669
+ #
670
+ # # First Segment of the 2nd Multi-Feature
671
+ # Turf.find_point(multi_line, multi_feature_index: 1)
672
+ # # => Feature<Point<[-10, -10]>>
673
+ #
674
+ # # Last Segment of last Multi-Feature
675
+ # Turf.find_point(multi_line, multi_feature_index: -1, coord_index: -1)
676
+ # # => Feature<Point<[-30, -40]>>
677
+ def find_point(geojson, options = {})
678
+ options ||= {}
679
+ raise "options is invalid" unless options.is_a?(Hash)
680
+
681
+ feature_index = options[:feature_index] || 0
682
+ multi_feature_index = options[:multi_feature_index] || 0
683
+ geometry_index = options[:geometry_index] || 0
684
+ coord_index = options[:coord_index] || 0
685
+
686
+ properties = options[:properties]
687
+ geometry = nil
688
+
689
+ case geojson[:type]
690
+ when "FeatureCollection"
691
+ feature_index = geojson[:features].length + feature_index if feature_index < 0
692
+ properties ||= geojson[:features][feature_index][:properties]
693
+ geometry = geojson[:features][feature_index][:geometry]
694
+ when "Feature"
695
+ properties ||= geojson[:properties]
696
+ geometry = geojson[:geometry]
697
+ when "Point", "MultiPoint"
698
+ return nil
699
+ when "LineString", "Polygon", "MultiLineString", "MultiPolygon"
700
+ geometry = geojson
701
+ else
702
+ raise "geojson is invalid"
703
+ end
704
+
705
+ return nil if geometry.nil?
706
+
707
+ coords = geometry[:coordinates]
708
+ case geometry[:type]
709
+ when "Point"
710
+ point(coords, properties, options)
711
+ when "MultiPoint"
712
+ multi_feature_index = coords.length + multi_feature_index if multi_feature_index < 0
713
+ point(coords[multi_feature_index], properties, options)
714
+ when "LineString"
715
+ coord_index = coords.length + coord_index if coord_index < 0
716
+ point(coords[coord_index], properties, options)
717
+ when "Polygon"
718
+ geometry_index = coords.length + geometry_index if geometry_index < 0
719
+ coord_index = coords[geometry_index].length + coord_index if coord_index < 0
720
+ point(coords[geometry_index][coord_index], properties, options)
721
+ when "MultiLineString"
722
+ multi_feature_index = coords.length + multi_feature_index if multi_feature_index < 0
723
+ coord_index = coords[multi_feature_index].length + coord_index if coord_index < 0
724
+ point(coords[multi_feature_index][coord_index], properties, options)
725
+ when "MultiPolygon"
726
+ multi_feature_index = coords.length + multi_feature_index if multi_feature_index < 0
727
+ geometry_index = coords[multi_feature_index].length + geometry_index if geometry_index < 0
728
+ coord_index = coords[multi_feature_index][geometry_index].length - coord_index if coord_index < 0
729
+ point(coords[multi_feature_index][geometry_index][coord_index], properties, options)
730
+ else
731
+ raise "geojson is invalid"
732
+ end
733
+ end
368
734
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Turf
5
+ def midpoint(*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 moran_index(*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 nearest_neighbor_analysis(*args)
6
+ raise NotImplementedError
7
+ end
8
+ end