Module: Turf

Extended by:
Turf
Included in:
Turf
Defined in:
lib/turf.rb,
lib/turf/area.rb,
lib/turf/meta.rb,
lib/turf/along.rb,
lib/turf/length.rb,
lib/turf/bearing.rb,
lib/turf/helpers.rb,
lib/turf/version.rb,
lib/turf/centroid.rb,
lib/turf/distance.rb,
lib/turf/invariant.rb,
lib/turf/destination.rb,
lib/turf/boolean_point_in_polygon.rb

Overview

:nodoc:

Defined Under Namespace

Classes: Error

Constant Summary collapse

VERSION =

Version of turf-ruby

"0.7.0"

Meta collapse

Measurement collapse

Helper collapse

Unit Conversion collapse

Booleans collapse

Instance Method Summary collapse

Instance Method Details

#along(line, distance, units: "kilometers") ⇒ Feature<Point>

Takes a LineString and returns a Point at a specified distance along the line.

Parameters:

  • line (Feature<LineString>)

    input line

  • distance (number)

    distance along the line

  • units (string) (defaults to: "kilometers")

    can be degrees, radians, miles, or kilometers (optional, default “kilometers”)

Returns:

  • (Feature<Point>)

    Point distance units along the line

See Also:



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/turf/along.rb', line 13

def along(line, distance, units: "kilometers")
  line = deep_symbolize_keys line
  travelled = 0

  geom = get_geom line
  coords = geom[:coordinates]

  coords.each_with_index do |coord, i|
    break if distance >= travelled && i == coords.length - 1

    if travelled >= distance
      overshot = distance - travelled
      return point(coord) if overshot.zero?

      direction = bearing(coord, coords[i - 1]) - 180
      interpolated = destination(coord, overshot, direction, units: units)
      return interpolated
    else
      travelled += distance(coords[i], coords[i + 1], units: units)
    end
  end

  point(coords.last)
end

#area(geojson) ⇒ number

Takes one or more features and returns their area in square meters.

Parameters:

  • geojson (GeoJSON)

    input GeoJSON feature(s)

Returns:

  • (number)

    aria in square meters

See Also:



9
10
11
12
13
# File 'lib/turf/area.rb', line 9

def area(geojson)
  geom_reduce(geojson, initial_value: 0) do |value, geom|
    value + area_calculate_area(geom)
  end
end

#bearing(from, to, final: false) ⇒ number

Takes two points and finds the geographic bearing between them, i.e. the angle measured in degrees from the north line (0 degrees)

Parameters:

  • from (Coord)

    starting Point

  • to (Coord)

    ending Point

  • final (defaults to: false)

    boolean calculates the final bearing if true

Returns:

  • (number)

    bearing in decimal degrees, between -180 and 180 degrees (positive clockwise)

See Also:



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/turf/bearing.rb', line 14

def bearing(from, to, final: false)
  return calculate_final_bearing(from, to) if final

  coordinates1 = get_coord from
  coordinates2 = get_coord to

  lon1 = degrees_to_radians(coordinates1[0])
  lon2 = degrees_to_radians(coordinates2[0])
  lat1 = degrees_to_radians(coordinates1[1])
  lat2 = degrees_to_radians(coordinates2[1])
  a = Math.sin(lon2 - lon1) * Math.cos(lat2)
  b = Math.cos(lat1) * Math.sin(lat2) -
      Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)

  radians_to_degrees(Math.atan2(a, b))
end

#boolean_point_in_polygon(point, polygon, ignore_boundary: false) ⇒ boolean

Takes a Point and a Polygon or MultiPolygon and determines if the point resides inside the polygon. The polygon can be convex or concave. The function accounts for holes. inside the polygon otherwise false.

Parameters:

  • point (Coord)

    input point

  • polygon (Feature<(Polygon | MultiPolygon)>)

    input polygon or multipolygon

  • ignore_boundary (boolean) (defaults to: false)

    True if polygon boundary should be ignored when determining if the point is

Returns:

  • (boolean)

    true if the Point is inside the Polygon; false if the Point is not inside the Polygon

See Also:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/turf/boolean_point_in_polygon.rb', line 16

def boolean_point_in_polygon(point, polygon, ignore_boundary: false)
  polygon = deep_symbolize_keys(polygon)
  pt = get_coord(point)
  geom = get_geom(polygon)
  type = geom.fetch(:type)
  bbox = polygon[:bbox]
  polys = geom.fetch(:coordinates)

  # Quick elimination if point is not inside bbox
  return false if bbox && !in_bbox(pt, bbox)

  # normalize to multipolygon
  polys = [polys] if type == "Polygon"

  inside_poly = false
  polys.each do |poly|
    # check if it is in the outer ring first
    next unless in_ring(pt, poly[0], ignore_boundary)

    in_hole = false

    # check for the point in any of the holes
    poly.slice(1, poly.size - 1).each do |hole|
      if in_ring(pt, hole, !ignore_boundary)
        in_hole = true
      end
    end
    if !in_hole
      inside_poly = true
    end
  end
  inside_poly
end

#centroid(geojson, properties: {}) ⇒ Feature<Point>

Takes one or more features and calculates the centroid using the mean of all vertices. This lessens the effect of small islands and artifacts when calculating the centroid of a set of polygons.

Parameters:

  • geojson (GeoJSON)

    GeoJSON to be centered

  • properties (Hash) (defaults to: {})

    a [Hash] that is used as the Feature’s properties

Returns:

  • (Feature<Point>)

    the centroid of the input features

See Also:



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/turf/centroid.rb', line 13

def centroid(geojson, properties: {})
  x_sum = 0
  y_sum = 0
  len = 0

  coord_each geojson, exclude_wrap_coord: true do |coord|
    x_sum += coord[0]
    y_sum += coord[1]
    len += 1
  end

  point(
    [x_sum / len, y_sum / len],
    properties: properties,
  )
end

#coord_all(geojson, exclude_wrap_coord: false) ⇒ Array<Array<number>>

Get all coordinates from any GeoJSON object. ring in its iteration

Parameters:

  • geojson (FeatureCollection|Feature|Geometry)

    any GeoJSON object

  • exclude_wrap_coord (boolean) (defaults to: false)

    whether or not to include the final coordinate of LinearRings that wraps the

Returns:

  • (Array<Array<number>>)

    coordinate position array

See Also:



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/turf/meta.rb', line 25

def coord_all(geojson, exclude_wrap_coord: false)
  geometries = self.geometries(geojson)
  geometries.flat_map do |geometry|
    next [] if geometry.nil?

    case geometry[:type]
    when "Point"
      [geometry[:coordinates]]
    when "LineString", "MultiPoint"
      geometry[:coordinates]
    when "Polygon", "MultiLineString"
      geometry[:coordinates].flat_map do |line_coords|
        (
          exclude_wrap_coord ? line_coords.slice(0...-1) : line_coords
        )
      end
    when "MultiPolygon"
      geometry[:coordinates].flat_map do |polygon_coords|
        polygon_coords.flat_map do |line_coords|
          (
            exclude_wrap_coord ? line_coords.slice(0...-1) : line_coords
          )
        end
      end
    when "Feature"
      [].tap do |feature_coords|
        coord_each geometry, exclude_wrap_coord: exclude_wrap_coord do |coord|
          feature_coords.push coord
        end
      end
    else
      raise Error, "Unknown Geometry Type: #{geometry[:type]}"
    end
  end
end

#coord_each(geojson, exclude_wrap_coord: false) {|current_coord, coord_index| ... } ⇒ Object

Iterate over coordinates in any GeoJSON object, similar to Array.forEach() ring in its iteration

Parameters:

  • geojson (FeatureCollection|Feature|Geometry)

    any GeoJSON object

  • exclude_wrap_coord (boolean) (defaults to: false)

    whether or not to include the final coordinate of LinearRings that wraps the

Yields:

  • (current_coord, coord_index)

    given any coordinate

Yield Parameters:

  • current_coord (Array<number>)

    The current coordinate being processed.

  • coord_index (number)

    The current index of the coordinate being processed.

See Also:



15
16
17
# File 'lib/turf/meta.rb', line 15

def coord_each(geojson, exclude_wrap_coord: false, &block)
  coord_all(geojson, exclude_wrap_coord: exclude_wrap_coord).each_with_index(&block)
end

#coord_reduce(geojson, initial_value: nil, exclude_wrap_coord: false) ⇒ *

Reduce coordinates in any GeoJSON object, similar to Array.reduce() ring in its iteration.

Parameters:

  • geojson (FeatureCollection|Geometry|Feature)

    any GeoJSON object

  • initial_value (*) (defaults to: nil)

    Value to use as the first argument to the first call of the callback.

  • exclude_wrap_coord (Boolean) (defaults to: false)

    whether or not to include the final coordinate of LinearRings that wraps the

Returns:

  • (*)

    The value that results from the reduction.

See Also:



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/turf/meta.rb', line 68

def coord_reduce(geojson, initial_value: nil, exclude_wrap_coord: false)
  previous_value = initial_value

  coord_each(
    geojson,
    exclude_wrap_coord: exclude_wrap_coord,
  ) do |current_coord, coord_index|
    previous_value =
      if coord_index.zero? && initial_value.nil?
        current_coord
      else
        yield(
          previous_value,
          current_coord,
          coord_index
        )
      end
  end

  previous_value
end

#degrees_to_radians(degrees) ⇒ number

Converts an angle in degrees to radians

Parameters:

  • degrees (number)

    angle between 0 and 360 degrees

Returns:

  • (number)

    angle in radians

See Also:



201
202
203
204
# File 'lib/turf/helpers.rb', line 201

def degrees_to_radians(degrees)
  radians = degrees.remainder(360)
  radians * Math::PI / 180
end

#destination(origin, distance, bearing, units: "kilometers", properties: {}) ⇒ Feature<Point>

Takes a Point and calculates the location of a destination point given a distance in degrees, radians, miles, or kilometers; and bearing in degrees. This uses the Haversine formula to account for global curvature.

Parameters:

  • origin (Coord)

    starting point

  • distance (number)

    distance from the origin point

  • bearing (number)

    ranging from -180 to 180

  • units (string) (defaults to: "kilometers")

    miles, kilometers, degrees, or radians

  • properties (Hash) (defaults to: {})

    Translate properties to Point

Returns:

  • (Feature<Point>)

    destination point

See Also:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/turf/destination.rb', line 16

def destination(origin, distance, bearing, units: "kilometers", properties: {})
  coordinates1 = get_coord origin
  longitude1 = degrees_to_radians coordinates1[0]
  latitude1 = degrees_to_radians coordinates1[1]
  bearing_radians = degrees_to_radians bearing
  radians = length_to_radians distance, units

  latitude2 = Math.asin(Math.sin(latitude1) * Math.cos(radians) +
    Math.cos(latitude1) * Math.sin(radians) * Math.cos(bearing_radians))
  longitude2 = longitude1 + Math.atan2(
    Math.sin(bearing_radians) * Math.sin(radians) * Math.cos(latitude1),
    Math.cos(radians) - Math.sin(latitude1) * Math.sin(latitude2),
  )
  lng = radians_to_degrees(longitude2)
  lat = radians_to_degrees(latitude2)

  point([lng, lat], properties: properties)
end

#distance(from, to, units: "kilometers") ⇒ number

Calculates the distance between two points in degrees, radians, miles, or kilometers. This uses the Haversine formula to account for global curvature.

Parameters:

  • from (Coord)

    origin point

  • to (Coord)

    destination point

  • units (string) (defaults to: "kilometers")

    can be degrees, radians, miles, or kilometers

Returns:

  • (number)

    distance between the two points

See Also:



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/turf/distance.rb', line 14

def distance(from, to, units: "kilometers")
  coordinates1 = get_coord from
  coordinates2 = get_coord to

  d_lat = degrees_to_radians coordinates2[1] - coordinates1[1]
  d_lon = degrees_to_radians coordinates2[0] - coordinates1[0]
  lat1 = degrees_to_radians coordinates1[1]
  lat2 = degrees_to_radians coordinates2[1]

  a =
    (
      (Math.sin(d_lat / 2)**2) +
      (Math.sin(d_lon / 2)**2) * Math.cos(lat1) * Math.cos(lat2)
    )

  radians_to_length(
    2 * Math.atan2(
      Math.sqrt(a),
      Math.sqrt(1 - a),
    ),
    units,
  )
end

#feature(geom, properties: {}, bbox: nil, id: nil) ⇒ Feature

Wraps a GeoJSON Geometry in a GeoJSON Feature.

Parameters:

  • geom (Geometry)

    input geometry

  • properties (Hash) (defaults to: {})

    an Object of key-value pairs to add as properties

  • bbox (Array<number>) (defaults to: nil)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string | number) (defaults to: nil)

    Identifier associated with the Feature

Returns:

  • (Feature)

    a GeoJSON Feature

See Also:



35
36
37
38
39
40
41
42
43
44
45
# File 'lib/turf/helpers.rb', line 35

def feature(geom, properties: {}, bbox: nil, id: nil)
  feat = {
    type: "Feature",
    properties: properties,
    geometry: geom
  }
  feat[:id] = options[:id] if id
  feat[:bbox] = options[:bbox] if bbox

  feat
end

#feature_collection(features, bbox: nil, id: nil) ⇒ FeatureCollection

Takes one or more Features and creates a FeatureCollection.

Parameters:

  • features (Array<Feature>)

    input features

  • bbox (Array<number>) (defaults to: nil)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string | number) (defaults to: nil)

    Identifier associated with the Feature

Returns:

  • (FeatureCollection)

    FeatureCollection of Features

See Also:



53
54
55
56
57
58
59
60
# File 'lib/turf/helpers.rb', line 53

def feature_collection(features, bbox: nil, id: nil)
  fc = { type: "FeatureCollection" }
  fc[:id] = options[:id] if id
  fc[:bbox] = options[:bbox] if bbox
  fc[:features] = features

  fc
end

#feature_each(geojson) {|feature| ... } ⇒ Object

Iterate over features in any GeoJSON object, similar to Array.forEach.

Parameters:

  • geojson (FeatureCollection|Feature|Geometry)

    any GeoJSON object

Yields:

  • (feature)

    given any coordinate

Yield Parameters:

  • feature (Feature<any>)

    currentFeature The current Feature being processed.

  • feature_index (number)

    The current index of the Feature being processed.

See Also:



187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/turf/meta.rb', line 187

def feature_each(geojson, &block)
  return unless geojson

  features = []
  geojson = deep_symbolize_keys geojson
  case geojson[:type]
  when "Feature"
    features.push geojson
  when "FeatureCollection"
    features.push(*geojson[:features])
  end

  features.each_with_index(&block)
end

#feature_reduce(geojson, initial_value: nil) {|previous_value, feature, feature_index| ... } ⇒ *

Reduce features in any GeoJSON object, similar to Array.reduce().

Parameters:

  • geojson (FeatureCollection|Feature|Geometry)

    any GeoJSON object

  • initial_value (*) (defaults to: nil)

    Value to use as the first argument to the first call of the callback.

Yield Parameters:

  • previous_value (*)

    Result of previous reduction

  • feature (Feature<any>)

    The current Feature being processed.

  • feature_index (number)

    The current index of the Feature being processed.

Returns:

  • (*)

    The value that results from the reduction.

See Also:



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/turf/meta.rb', line 210

def feature_reduce(geojson, initial_value: nil)
  previous_value = initial_value

  feature_each(
    geojson,
  ) do |feature, feature_index|
    previous_value =
      if feature_index.zero? && initial_value.nil?
        feature
      else
        yield(
          previous_value,
          feature,
          feature_index
        )
      end
  end

  previous_value
end

#flatten_each(geojson) {|feature, feature_index, multi_feature_index| ... } ⇒ Object

Iterate over flattened features in any GeoJSON object, similar to Array.forEach.

Parameters:

  • geojson (FeatureCollection|Feature|Geometry)

    any GeoJSON object

Yield Parameters:

  • feature (Feature<any>)

    The current Feature being processed.

  • feature_index (number)

    The current index of the Feature being processed.

  • multi_feature_index (number)

    The current index of the Feature in the multi-Feature

See Also:



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/turf/meta.rb', line 237

def flatten_each(geojson)
  geom_each(geojson) do |geometry, feature_index, properties, bbox, id|
    if geometry.nil?
      next yield(
        feature(nil, properties: properties, bbox: bbox, id: id),
        feature_index,
        0
      )
    end

    case geometry[:type]
    when "Point", "LineString", "Polygon"
      yield(
        feature(geometry, properties: properties, bbox: bbox, id: id),
        feature_index,
        0
      )
    when "MultiPoint", "MultiLineString", "MultiPolygon"
      geom_type = geometry[:type].sub(/^Multi/, "")
      geometry[:coordinates].each_with_index do |coordinate, multi_feature_index|
        geom = {
          type: geom_type,
          coordinates: coordinate
        }
        yield(
          feature(geom, properties: properties),
          feature_index,
          multi_feature_index
        )
      end
    end
  end
end

#flatten_reduce(geojson, initial_value: nil) {|previous_value, feature, feature_index, multi_feature_index| ... } ⇒ *

Reduce flattened features in any GeoJSON object, similar to Array.reduce().

Parameters:

  • geojson (FeatureCollection|Feature|Geometry)

    any GeoJSON object

  • initial_value (*) (defaults to: nil)

    Value to use as the first argument to the first call of the callback.

Yield Parameters:

  • previous_value (*)

    Result of previous reduction

  • feature (Feature<any>)

    The current Feature being processed.

  • feature_index (number)

    The current index of the Feature being processed.

  • multi_feature_index (number)

    The current index of the Feature in the multi-Feature

Returns:

  • (*)

    The value that results from the reduction.

See Also:



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/turf/meta.rb', line 280

def flatten_reduce(geojson, initial_value: nil)
  previous_value = initial_value

  flatten_each(
    geojson,
  ) do |feature, feature_index, multi_feature_index|
    previous_value =
      if feature_index.zero? && multi_feature_index.zero? && initial_value.nil?
        feature
      else
        yield(
          previous_value,
          feature,
          feature_index,
          multi_feature_index
        )
      end
  end

  previous_value
end

#geom_each(geojson) {|geom, feature_index, properties, bbox, id| ... } ⇒ Object

Iterate over each geometry in any GeoJSON object, similar to Array.forEach()

Parameters:

  • geojson (FeatureCollection|Feature|Geometry)

    any GeoJSON object

Yield Parameters:

  • geom (Geometry)

    The current Feature being processed.

  • feature_index (number)

    The current index of the Feature being processed.

  • properties (Hash)

    an Object of key-value pairs to add as properties

  • bbox (Array<number>)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string|number)

    Identifier associated with the Feature

See Also:



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/turf/meta.rb', line 98

def geom_each(geojson)
  return unless geojson

  geojson = deep_symbolize_keys geojson

  # [geometry, properties, bbox, id]
  entries = []

  case geojson[:type]
  when "FeatureCollection"
    geojson[:features].each do |feature|
      entries.push [feature[:geometry], feature[:properties], feature[:bbox], feature[:id]]
    end
  when "Feature"
    entries.push [geojson[:geometry], geojson[:properties], geojson[:bbox], geojson[:id]]
  else
    entries.push [geojson, {}, nil, nil]
  end

  # flatten GeometryCollection
  entries =
    entries.flat_map do |entry|
      geometry, properties, bbox, id = entry
      next [entry] if geometry.nil?
      next [entry] unless geometry[:type] == "GeometryCollection"

      geometry[:geometries].map do |sub_geometry|
        [sub_geometry, properties, bbox, id]
      end
    end

  entries.each_with_index do |entry, entry_index|
    geometry, properties, bbox, id = entry
    yield geometry, entry_index, properties, bbox, id
  end
end

#geom_reduce(geojson, initial_value: nil) {|previous_value, geom, geom_index, properties, bbox, id| ... } ⇒ *

Reduce geometry in any GeoJSON object, similar to Array.reduce().

Parameters:

  • geojson (FeatureCollection|Feature|Geometry)

    any GeoJSON object

  • initial_value (*) (defaults to: nil)

    Value to use as the first argument to the first call of the callback.

Yield Parameters:

  • previous_value (*)

    Result of previous reduction

  • geom (Geometry)

    The current Feature being processed.

  • geom_index (number)

    The current index of the Feature being processed.

  • properties (Hash)

    an Object of key-value pairs to add as properties

  • bbox (Array<number>)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string|number)

    Identifier associated with the Feature

Returns:

  • (*)

    The value that results from the reduction.

See Also:



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/turf/meta.rb', line 146

def geom_reduce(geojson, initial_value: nil)
  previous_value = initial_value

  geom_each(
    geojson,
  ) do |geom, geom_index, properties, bbox, id|
    previous_value =
      if geom_index.zero? && initial_value.nil?
        geom
      else
        yield(
          previous_value,
          geom,
          geom_index,
          properties,
          bbox,
          id
        )
      end
  end

  previous_value
end

#geometries(geojson) ⇒ Array<Geometry>

Get all Geometry

Parameters:

  • geojson (FeatureCollection|Feature|Geometry)

    any GeoJSON object

Returns:

  • (Array<Geometry>)

    list of Geometry



173
174
175
176
177
178
179
# File 'lib/turf/meta.rb', line 173

def geometries(geojson)
  [].tap do |geometries|
    geom_each(geojson) do |geometry|
      geometries.push(geometry)
    end
  end
end

#geometry_collection(geometries, bbox: nil, id: nil, properties: {}) ⇒ Feature<GeometryCollection>

Creates a Feature based on a coordinate array. Properties can be added optionally.

Parameters:

  • geometries (Array<Geometry>)

    an array of GeoJSON Geometries

  • properties (Hash) (defaults to: {})

    a Hash of key-value pairs to add as properties

  • bbox (Array<number>) (defaults to: nil)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string|number) (defaults to: nil)

    Identifier associated with the Feature

Returns:

  • (Feature<GeometryCollection>)

    a GeoJSON GeometryCollection Feature

See Also:



69
70
71
72
73
74
75
76
# File 'lib/turf/helpers.rb', line 69

def geometry_collection(geometries, bbox: nil, id: nil, properties: {})
  geom = {
    type: "GeometryCollection",
    geometries: geometries
  }

  feature(geom, properties: properties, bbox: bbox, id: id)
end

#get_coord(coord) ⇒ Array

Unwrap a coordinate from a Point Feature, Geometry or a single coordinate.

Parameters:

  • coord (Array|Geometry<Point>|Feature<Point>)

    GeoJSON Point or an Array of numbers

Returns:

  • (Array)

    coordinates

Raises:

See Also:



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/turf/invariant.rb', line 11

def get_coord(coord)
  if !coord
    raise Error, "coord is required"
  end

  is_numeric = ->(i) { i.is_a? Numeric }
  if coord.is_a?(Array) && coord.length >= 2 && coord.all?(&is_numeric)
    return coord
  end

  if coord.is_a? Hash
    coord = deep_symbolize_keys(coord)

    is_feature = coord[:type] == "Feature"
    if is_feature && coord.fetch(:geometry, {})[:type] == "Point"
      return coord[:geometry][:coordinates]
    end

    if coord[:type] == "Point"
      return coord[:coordinates]
    end
  end

  raise Error, "coord must be GeoJSON Point or an Array of numbers"
end

#get_geom(geojson) ⇒ Geometry|null

Get Geometry from Feature or Geometry Object

Parameters:

  • geojson (Feature|Geometry)

    GeoJSON Feature or Geometry Object

Returns:

  • (Geometry|null)

    GeoJSON Geometry Object

See Also:



41
42
43
44
45
46
# File 'lib/turf/invariant.rb', line 41

def get_geom(geojson)
  geojson = deep_symbolize_keys geojson
  return geojson[:geometry] if geojson[:type] == "Feature"

  geojson
end

#length(geojson, units: "kilometers") ⇒ number

Takes a GeoJSON and measures its length in the specified units, (Multi)Point ‘s distance are ignored.

Parameters:

  • geojson (Feature<LineString|MultiLinestring>)

    GeoJSON to measure

  • units (string) (defaults to: "kilometers")

    can be degrees, radians, miles, or kilometers (optional, default “kilometers”)

Returns:

  • (number)

    length of GeoJSON

See Also:



12
13
14
15
16
17
18
19
20
# File 'lib/turf/length.rb', line 12

def length(geojson, units: "kilometers")
  geojson = deep_symbolize_keys(geojson)
  geojson = feature(geojson) if geojson[:geometry].nil?
  segment_reduce(geojson, initial_value: 0) do |previous_value, segment|
    previous_value ||= 0
    coords = segment.dig(:geometry, :coordinates)
    previous_value + Turf.distance(coords[0], coords[1], units: units)
  end
end

#length_to_radians(distance, units = "kilometers") ⇒ number

Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet

Parameters:

  • distance (number)

    in real units

  • units (string) (defaults to: "kilometers")

    can be degrees, radians, miles, inches, yards, metres, meters, kilometres, kilometers.

Returns:

  • (number)

    radians

Raises:

See Also:



225
226
227
228
229
230
# File 'lib/turf/helpers.rb', line 225

def length_to_radians(distance, units = "kilometers")
  factor = FACTORS[units]
  raise Error, "#{units} units is invalid" unless factor

  distance / factor
end

#line_string(coordinates, properties: {}, bbox: nil, id: nil) ⇒ Feature<LineString>

Creates a LineString Feature from an Array of Positions.

Parameters:

  • coordinates (Array<Array<number>>)

    an array of Positions

  • properties (Hash) (defaults to: {})

    an Hash of key-value pairs to add as properties

  • bbox (Array<number>) (defaults to: nil)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string|number) (defaults to: nil)

    Identifier associated with the Feature

Returns:

  • (Feature<LineString>)

    LineString Feature

See Also:



100
101
102
103
104
105
106
107
108
109
110
# File 'lib/turf/helpers.rb', line 100

def line_string(coordinates, properties: {}, bbox: nil, id: nil)
  if coordinates.size < 2
    raise Error, "coordinates must be an array of two or more positions"
  end

  geom = {
    type: "LineString",
    coordinates: coordinates
  }
  feature(geom, properties: properties, bbox: bbox, id: id)
end

#multi_line_string(coordinates, properties: {}, bbox: nil, id: nil) ⇒ Feature<MultiLineString>

Creates a Feature<MultiLineString> based on a coordinate array. Properties can be added optionally.

Parameters:

  • coordinates (Array<Array<Array<number>>>)

    coordinates an array of LineStrings

  • properties (Hash) (defaults to: {})

    a Hash of key-value pairs to add as properties

  • bbox (Array<number>) (defaults to: nil)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string|number) (defaults to: nil)

    Identifier associated with the Feature

Returns:

  • (Feature<MultiLineString>)

    a MultiLineString feature

See Also:



178
179
180
181
182
183
184
# File 'lib/turf/helpers.rb', line 178

def multi_line_string(coordinates, properties: {}, bbox: nil, id: nil)
  geom = {
    type: "MultiLineString",
    coordinates: coordinates
  }
  feature(geom, properties: properties, id: id, bbox: bbox)
end

#multi_point(coordinates, properties: {}, bbox: nil, id: nil) ⇒ Feature<MultiPoint>

Creates a Feature based on a coordinate array. Properties can be added optionally.

Parameters:

  • coordinates (Array<Array<number>>)

    an array of Positions

  • properties (Hash) (defaults to: {})

    a Hash of key-value pairs to add as properties

  • bbox (Array<number>) (defaults to: nil)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string|number) (defaults to: nil)

    Identifier associated with the Feature

Returns:

  • (Feature<MultiPoint>)

    a MultiPoint feature



84
85
86
87
88
89
90
91
# File 'lib/turf/helpers.rb', line 84

def multi_point(coordinates, properties: {}, bbox: nil, id: nil)
  geom = {
    type: "MultiPoint",
    coordinates: coordinates
  }

  feature(geom, properties: properties, bbox: bbox, id: id)
end

#multi_polygon(coordinates, properties: {}, bbox: nil, id: nil) ⇒ Feature<MultiPolygon>

Creates a Feature<MultiPolygon> based on a coordinate array. Properties can be added optionally.

Parameters:

  • coordinates (Array<Array<Array<Array<number>>>>)

    an array of Polygons

  • properties (Hash) (defaults to: {})

    an Hash of key-value pairs to add as properties

  • bbox (Array<number>) (defaults to: nil)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string|number) (defaults to: nil)

    Identifier associated with the Feature

Returns:

  • (Feature<MultiPolygon>)

    a multipolygon feature

See Also:



163
164
165
166
167
168
169
# File 'lib/turf/helpers.rb', line 163

def multi_polygon(coordinates, properties: {}, bbox: nil, id: nil)
  geom = {
    type: "MultiPolygon",
    coordinates: coordinates
  }
  feature(geom, properties: properties, id: id, bbox: bbox)
end

#point(coordinates, properties: {}, id: nil, bbox: nil) ⇒ Feature<Point>

Creates a Point Feature from a Position.

Parameters:

  • coordinates (Array<number>)

    longitude, latitude position (each in decimal degrees)

  • properties (Hash) (defaults to: {})

    an Hash of key-value pairs to add as properties

  • bbox (Array<number>) (defaults to: nil)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string | number) (defaults to: nil)

    Identifier associated with the Feature

Returns:

  • (Feature<Point>)

    a Point feature

See Also:



119
120
121
122
123
124
125
# File 'lib/turf/helpers.rb', line 119

def point(coordinates, properties: {}, id: nil, bbox: nil)
  geom = {
    type: "Point",
    coordinates: coordinates
  }
  feature(geom, properties: properties, id: id, bbox: bbox)
end

#polygon(coordinates, properties: {}, bbox: nil, id: nil) ⇒ Feature<Polygon>

Creates a Polygon Feature from an Array of LinearRings.

Parameters:

  • coordinates (Array<Array<Array<number>>>)

    an array of LinearRings

  • properties (Hash) (defaults to: {})

    an Hash of key-value pairs to add as properties

  • bbox (Array<number>) (defaults to: nil)

    Bounding Box Array [west, south, east, north] associated with the Feature

  • id (string | number) (defaults to: nil)

    Identifier associated with the Feature

Returns:

  • (Feature<Polygon>)

    Polygon feature

See Also:



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/turf/helpers.rb', line 134

def polygon(coordinates, properties: {}, bbox: nil, id: nil)
  coordinates.each do |ring|
    if ring.size < 4
      raise(
        Error,
        "Each LinearRing of a Polygon must have 4 or more Positions.",
      )
    end

    ring.last.each_with_index do |number, idx|
      if ring.first[idx] != number
        raise Error, "First and last Position are not equivalent."
      end
    end
  end
  geom = {
    type: "Polygon",
    coordinates: coordinates
  }
  feature(geom, properties: properties, id: id, bbox: bbox)
end

#radians_to_degrees(radians) ⇒ number

Converts an angle in radians to degrees

Parameters:

  • radians (number)

    angle in radians

Returns:

  • (number)

    degrees between 0 and 360 degrees

See Also:



192
193
194
195
# File 'lib/turf/helpers.rb', line 192

def radians_to_degrees(radians)
  degrees = radians.remainder(2 * Math::PI)
  degrees * 180 / Math::PI
end

#radians_to_length(radians, units = "kilometers") ⇒ number

Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit. Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet

Parameters:

  • radians (number)

    in radians across the sphere

  • units (string) (defaults to: "kilometers")

    can be degrees, radians, miles, inches, yards, metres, meters, kilometres, kilometers.

Returns:

  • (number)

    distance

Raises:

See Also:



212
213
214
215
216
217
# File 'lib/turf/helpers.rb', line 212

def radians_to_length(radians, units = "kilometers")
  factor = FACTORS[units]
  raise Error, "#{units} units is invalid" unless factor

  radians * factor
end

#segment_each(geojson) ⇒ Object

Iterate over 2-vertex line segment in any GeoJSON object, similar to Array.forEach() (Multi)Point geometries do not contain segments therefore they are ignored during this operation.

Parameters:

  • geojson (FeatureCollection|Feature|Geometry)

    any GeoJSON object

See Also:



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/turf/meta.rb', line 306

def segment_each(geojson)
  flatten_each(geojson) do |feature, feature_index|
    # Exclude null Geometries
    return if feature[:geometry].nil?

    # (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
    type = feature[:geometry][:type]
    return if %w[Point MultiPoint].include?(type)

    segment_index = 0

    # Generate 2-vertex line segments
    previous_coords = nil
    previous_feature_index = 0
    coord_each(feature) do |current_coord|
      # Simulating a meta.coord_reduce() since `reduce` operations cannot be stopped by returning `false`
      if previous_coords.nil? || feature_index > previous_feature_index
        previous_coords = current_coord
        previous_feature_index = feature_index
        segment_index = 0
        next
      end

      segment = Turf.line_string([previous_coords, current_coord], properties: feature[:properties])
      next unless yield(segment, feature_index)

      segment_index += 1
      previous_coords = current_coord
    end
  end
end

#segment_reduce(geojson, initial_value: nil) ⇒ Object



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/turf/meta.rb', line 338

def segment_reduce(geojson, initial_value: nil)
  previous_value = initial_value
  started = false

  segment_each(geojson) do |segment, feature_index, multifeature_index, geometry_index, segment_index|
    previous_value =
      if !started && initial_value.nil?
        segment
      else
        yield(
          previous_value,
          segment,
          feature_index,
          multifeature_index,
          geometry_index,
          segment_index
        )
      end
    started = true
  end

  previous_value
end