module OGR::Layer::Extensions

Methods not part of the C Layer API.

Public Instance Methods

any_geometries_with_z?() click to toggle source

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

@return [Boolean]

# 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() { |feature| ... } click to toggle source

Enumerates through all associated features. Beware: it calls {#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.

@return [Enumerator] @yieldparam [OGR::Feature]

# 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() { |feature_ptr| ... } click to toggle source

@return [Enumerator] @yieldparam [FFI::Pointer] A pointer to each feature in the layer.

# 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
features() click to toggle source

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

@return [Array<OGR::Feature>]

# File lib/ogr/extensions/layer/extensions.rb, line 70
def features
  each_feature.map(&:clone)
end
geometry_from_extent() click to toggle source

@return [OGR::Polygon] A polygon derived from a LinearRing that connects

the 4 bounding box points (from the extent).
# 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 = {}) click to toggle source

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.

@example Not passing with_attributes

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

@example 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']]

@param with_attributes [String, Array<String>] @return [Array<Array>] @raise [OGR::UnsupportedGeometryType] if a geometry of some type is

encountered that the method doesn't know how to extract point values
from.
# 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

Private Instance Methods

collect_point_values(geometry_ptr, field_values) click to toggle source

@param geometry_ptr [FFI::Pointer] @param field_values [Array] @return [Array]

# File lib/ogr/extensions/layer/extensions.rb, line 246
def collect_point_values(geometry_ptr, field_values)
  [FFI::OGR::API.OGR_G_GetX(geometry_ptr, 0), FFI::OGR::API.OGR_G_GetY(geometry_ptr, 0), *field_values]
end
extract_ring_points(ring_ptr, x_ptr, y_ptr) { |read_double, read_double| ... } click to toggle source

@param ring_ptr [FFI::Pointer] Pointer to the LineString/LinearRing to

extract points from.

@param x_ptr [FFI::Pointer] Pointer to use for writing the x value to. @param y_ptr [FFI::Pointer] Pointer to use for writing the y value to. @yieldparam [Array<Float>] (x, y)

# File lib/ogr/extensions/layer/extensions.rb, line 255
def extract_ring_points(ring_ptr, x_ptr, y_ptr)
  point_count = FFI::OGR::API.OGR_G_GetPointCount(ring_ptr)

  point_count.times do |point_num|
    FFI::OGR::API.OGR_G_GetPoint(ring_ptr, point_num, x_ptr, y_ptr, nil)

    yield [x_ptr.read_double, y_ptr.read_double]

    x_ptr.clear
    y_ptr.clear
  end
end