Module: OGR::Layer::Extensions

Included in:
OGR::Layer
Defined in:
lib/ogr/extensions/layer/extensions.rb

Overview

Methods not part of the C Layer API.

Instance Method Summary collapse

Instance Method Details

#any_geometries_with_z?Boolean

Iterates through features to see if any of them are 3d.

Returns:

  • (Boolean)


224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/ogr/extensions/layer/extensions.rb', line 224

def any_geometries_with_z?
  found_z_geom = false

  each_feature_pointer do |feature_ptr|
    break if found_z_geom

    geom_ptr = FFI::OGR::API.OGR_F_GetGeometryRef(feature_ptr)
    geom_ptr.autorelease = false
    coordinate_dimension = FFI::OGR::API.OGR_G_GetCoordinateDimension(geom_ptr)
    found_z_geom = coordinate_dimension == 3
  end

  feature.destroy! if feature.c_pointer

  found_z_geom
end

#each_feature {|| ... } ⇒ Enumerator

Enumerates through all associated features. Beware: it calls OGR::LayerMixins::OGRFeatureMethods#reset_reading both before and after it’s called. If you’re using OGR::LayerMixins::OGRFeatureMethods#next_feature for iterating through features somewhere in your code, this will reset that reading.

Yield Parameters:

Returns:

  • (Enumerator)


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

def each_feature
  return enum_for(:each_feature) unless block_given?

  FFI::OGR::API.OGR_L_ResetReading(@c_pointer)

  loop do
    feature = next_feature
    break unless feature

    begin
      yield feature
    rescue StandardError
      feature.destroy!
      raise
    end

    feature.destroy!
  end

  FFI::OGR::API.OGR_L_ResetReading(@c_pointer)
end

#each_feature_pointer {|A| ... } ⇒ Enumerator

Yield Parameters:

  • A (FFI::Pointer)

    pointer to each feature in the layer.

Returns:

  • (Enumerator)


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/ogr/extensions/layer/extensions.rb', line 40

def each_feature_pointer
  return enum_for(:each_feature_pointer) unless block_given?

  FFI::OGR::API.OGR_L_ResetReading(@c_pointer)

  loop do
    feature_ptr = FFI::OGR::API.OGR_L_GetNextFeature(@c_pointer)

    if feature_ptr.null?
      FFI::OGR::API.OGR_F_Destroy(feature_ptr)
      break
    end

    begin
      yield feature_ptr
    rescue StandardError
      FFI::OGR::API.OGR_F_Destroy(feature_ptr)
      raise
    end

    FFI::OGR::API.OGR_F_Destroy(feature_ptr)
  end

  FFI::OGR::API.OGR_L_ResetReading(@c_pointer)
end

#featuresArray<OGR::Feature>

Returns all features as an Array. Note that if you just want to iterate through features, OGR::Layer::Extensions.{{#each_feature} will perform better.

Returns:



70
71
72
# File 'lib/ogr/extensions/layer/extensions.rb', line 70

def features
  each_feature.map(&:clone)
end

#geometry_from_extentOGR::Polygon

Returns A polygon derived from a LinearRing that connects the 4 bounding box points (from the extent).

Returns:

  • (OGR::Polygon)

    A polygon derived from a LinearRing that connects the 4 bounding box points (from the extent).



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/ogr/extensions/layer/extensions.rb', line 76

def geometry_from_extent
  ring = OGR::LinearRing.new

  ring.point_count = 5
  ring.set_point(0, extent.x_min, extent.y_min)
  ring.set_point(1, extent.x_min, extent.y_max)
  ring.set_point(2, extent.x_max, extent.y_max)
  ring.set_point(3, extent.x_max, extent.y_min)
  ring.set_point(4, extent.x_min, extent.y_min)

  polygon = OGR::Polygon.new spatial_reference: spatial_reference.dup
  polygon.add_geometry(ring)

  polygon
end

#point_values(with_attributes = {}) ⇒ Array<Array>

Iterates through all geometries in the Layer and extracts the point values to an Array. The result will be an Array of Arrays where the inner Array is the point values. If with_attributes is given, it will extract the field values for each given field names.

Examples:

Not passing with_attributes

points = layer.point_values
# => [[100, 100], [100, 120], [110, 110], [110, 100], [100, 100]]

With with_attributes

points = layer.point_values('Moisture' => :double, 'Color' => :string)
# => [[100, 100, 74.2, 'Red'],
      [100, 120, 19.0, 'Blue'],
      [110, 110, 21.1, 'Red'],
      [110, 100, 54.99, 'Green'],
      [100, 100, 3.3, 'Red']]

Parameters:

Returns:

  • (Array<Array>)

Raises:

  • (OGR::UnsupportedGeometryType)

    if a geometry of some type is encountered that the method doesn’t know how to extract point values from.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/ogr/extensions/layer/extensions.rb', line 114

def point_values(with_attributes = {})
  return [] if feature_count.zero?

  field_indices = with_attributes.keys.map { |field_name| find_field_index(field_name) }
  values = Array.new(feature_count) { Array.new(with_attributes.size + 2) }
  start = Time.now
  i = 0

  # Initing these once and only once in the case the geom type is _not_
  # wkbPoint.
  x_ptr = FFI::MemoryPointer.new(:double)
  y_ptr = FFI::MemoryPointer.new(:double)

  # This block is intentionally long simply for the sake of performance.
  # I've tried refactoring chunks of this out to separate methods and
  # performance suffers greatly. Since this is a key part of gridding (at
  # least at this point), it needs to be as fast as possible.
  each_feature_pointer do |feature_ptr| # rubocop:todo Metrics/BlockLength
    field_values = field_indices.map.with_index do |j, attribute_index|
      FFI::OGR::API.send("OGR_F_GetFieldAs#{with_attributes.values[attribute_index].capitalize}", feature_ptr, j)
    end

    geom_ptr = FFI::OGR::API.OGR_F_GetGeometryRef(feature_ptr)
    geom_ptr.autorelease = false
    FFI::OGR::API.OGR_G_FlattenTo2D(geom_ptr)
    geom_type = FFI::OGR::API.OGR_G_GetGeometryType(geom_ptr)

    case geom_type
    when :wkbPoint
      values[i] = collect_point_values(geom_ptr, field_values)
      i += 1
    when :wkbLineString, :wkbLinearRing
      extract_ring_points(geom_ptr, x_ptr, y_ptr) do |points|
        values[i] = points.push(*field_values)
        i += 1
      end
    when :wkbPolygon
      exterior_ring_ptr = FFI::OGR::API.OGR_G_GetGeometryRef(geom_ptr, 0)

      extract_ring_points(exterior_ring_ptr, x_ptr, y_ptr) do |points|
        values[i] = points.push(*field_values)
        i += 1
      end

      count = FFI::OGR::API.OGR_G_GetGeometryCount(geom_ptr)
      next if count > 1

      count.times do |ring_num|
        next if ring_num.zero?

        ring_ptr = FFI::OGR::API.OGR_G_GetGeometryRef(geom_ptr, ring_num)

        extract_ring_points(ring_ptr, x_ptr, y_ptr) do |points|
          values[i] = points.push(*field_values)
          i += 1
        end
      end
    when :wkbMultiPoint, :wkbMultiLineString
      count = FFI::OGR::API.OGR_G_GetGeometryCount(geom_ptr)

      count.times do |geom_num|
        ring_ptr = FFI::OGR::API.OGR_G_GetGeometryRef(geom_ptr, geom_num)

        extract_ring_points(ring_ptr, x_ptr, y_ptr) do |points|
          values[i] = points.push(*field_values)
          i += 1
        end
      end
    when :wkbMultiPolygon
      polygon_count = FFI::OGR::API.OGR_G_GetGeometryCount(geom_ptr)

      polygon_count.times do |polygon_num|
        polygon_ptr = FFI::OGR::API.OGR_G_GetGeometryRef(geom_ptr, polygon_num)
        polygon_ptr.autorelease = false
        exterior_ring_ptr = FFI::OGR::API.OGR_G_GetGeometryRef(polygon_ptr, 0)
        exterior_ring_ptr.autorelease = false

        extract_ring_points(exterior_ring_ptr, x_ptr, y_ptr) do |points|
          values[i] = points.push(*field_values)
          i += 1
        end

        count = FFI::OGR::API.OGR_G_GetGeometryCount(polygon_ptr)
        next if count > 1

        count.times do |ring_num|
          next if ring_num.zero?

          ring_ptr = FFI::OGR::API.OGR_G_GetGeometryRef(polygon_ptr, ring_num)
          ring_ptr.autorelease = false

          extract_ring_points(ring_ptr, x_ptr, y_ptr) do |points|
            values[i] = points.push(*field_values)
            i += 1
          end
        end
      end
    else raise OGR::UnsupportedGeometryType,
               "Not sure how to extract point_values for a #{geom_type}"
    end
  end

  log "#point_values took #{Time.now - start}s"

  values
end