lib/ogr/geometry.rb in ffi-gdal-1.0.0.beta16 vs lib/ogr/geometry.rb in ffi-gdal-1.0.0
- old
+ new
@@ -1,17 +1,17 @@
# frozen_string_literal: true
require_relative '../ogr'
-require_relative 'geometry_mixins/extensions'
require_relative '../gdal'
module OGR
module Geometry
module ClassMethods
def create(type)
geometry_pointer = FFI::OGR::API.OGR_G_CreateGeometry(type)
return if geometry_pointer.null?
+
geometry_pointer.autorelease = false
factory(geometry_pointer)
end
@@ -33,11 +33,11 @@
case geometry.type
when :wkbPoint then OGR::Point.new(new_pointer)
when :wkbPoint25D then OGR::Point25D.new(new_pointer)
when :wkbLineString
- if geometry.to_wkt =~ /^LINEARRING/
+ if /^LINEARRING/.match?(geometry.to_wkt)
OGR::LinearRing.new(new_pointer)
else
OGR::LineString.new(new_pointer)
end
when :wkbLineString25D then OGR::LineString25D.new(new_pointer)
@@ -72,11 +72,11 @@
geometry_ptr = FFI::MemoryPointer.new(:pointer)
geometry_ptr_ptr = FFI::MemoryPointer.new(:pointer)
geometry_ptr_ptr.write_pointer(geometry_ptr)
FFI::OGR::API.OGR_G_CreateFromWkt(wkt_pointer_pointer,
- spatial_ref_pointer, geometry_ptr_ptr)
+ spatial_ref_pointer, geometry_ptr_ptr)
return if geometry_ptr_ptr.null? || geometry_ptr_ptr.read_pointer.null?
factory(geometry_ptr_ptr.read_pointer)
end
@@ -119,11 +119,14 @@
# The human-readable string for the geometry type.
#
# @param type [FFI::OGR::WKBGeometryType]
# @return [String]
def type_to_name(type)
- FFI::OGR::Core.OGRGeometryTypeToName(type)
+ name, ptr = FFI::OGR::Core.OGRGeometryTypeToName(type)
+ ptr.autorelease = false
+
+ name
end
# Finds the most specific common geometry type from the two given types.
# Useful when trying to figure out what geometry type to report for an
# entire layer, when the layer uses multiple types.
@@ -133,17 +136,23 @@
# @return [FFI::OGR::WKBGeometryType] Returns :wkbUnknown when there is
# no type in common.
def merge_geometry_types(main, extra)
FFI::OGR::Core.OGRMergeGeometryTypes(main, extra)
end
+
+ # @param pointer [FFI::Pointer]
+ def release(pointer)
+ return unless pointer && !pointer.null?
+
+ FFI::OGR::API.OGR_G_DestroyGeometry(pointer)
+ end
end
extend ClassMethods
def self.included(base)
base.send(:include, GDAL::Logger)
- base.send(:include, OGR::GeometryMixins::Extensions)
base.send(:extend, ClassMethods)
end
#--------------------------------------------------------------------------
# Instance Methods
@@ -151,13 +160,12 @@
# @return [FFI::Pointer]
attr_reader :c_pointer
def destroy!
- return unless @c_pointer
+ self.class.release(@c_pointer)
- FFI::OGR::API.OGR_G_DestroyGeometry(@c_pointer)
@c_pointer = nil
end
# @return [OGR::Geometry]
def clone
@@ -171,23 +179,23 @@
# @return nil
def empty!
FFI::OGR::API.OGR_G_Empty(@c_pointer)
end
- # @return [Fixnum] 0 for points, 1 for lines, 2 for surfaces.
+ # @return [Integer] 0 for points, 1 for lines, 2 for surfaces.
def dimension
FFI::OGR::API.OGR_G_GetDimension(@c_pointer)
end
# The dimension of coordinates in this geometry (i.e. 2d vs 3d).
#
- # @return [Fixnum] 2 or 3, but 0 in the case of an empty point.
+ # @return [Integer] 2 or 3, but 0 in the case of an empty point.
def coordinate_dimension
FFI::OGR::API.OGR_G_GetCoordinateDimension(@c_pointer)
end
- # @param new_coordinate_dimension [Fixnum]
+ # @param new_coordinate_dimension [Integer]
def coordinate_dimension=(new_coordinate_dimension)
unless [2, 3].include?(new_coordinate_dimension)
raise "Can't set coordinate to #{new_coordinate_dimension}. Must be 2 or 3."
end
@@ -218,37 +226,41 @@
FFI::OGR::API.OGR_G_GetGeometryType(@c_pointer)
end
# @return [String]
def type_to_name
- FFI::OGR::Core.OGRGeometryTypeToName(type)
+ self.class.type_to_name(type)
end
# @return [String]
def name
- FFI::OGR::API.OGR_G_GetGeometryName(@c_pointer)
+ # The returned pointer is to a static internal string and should not be modified or freed.
+ name, ptr = FFI::OGR::API.OGR_G_GetGeometryName(@c_pointer)
+ ptr.autorelease = false
+
+ name
end
- # @return [Fixnum]
+ # @return [Integer]
def geometry_count
FFI::OGR::API.OGR_G_GetGeometryCount(@c_pointer)
end
alias count geometry_count
- # @return [Fixnum]
+ # @return [Integer]
def point_count
return 0 if empty?
FFI::OGR::API.OGR_G_GetPointCount(@c_pointer)
end
- # @return [Fixnum]
+ # @return [Integer]
def centroid
point = is_3d? ? OGR::Point25D.new : OGR::Point.new
ogr_err = FFI::OGR::API.OGR_G_Centroid(@c_pointer, point.c_pointer)
- return if point.c_pointer.null? || ogr_err > 0
+ return if point.c_pointer.null? || ogr_err.positive?
point
end
# Dump as WKT to the given +file_path+; dumps to STDOUT if none is given.
@@ -348,19 +360,18 @@
# TRUE if the geometry has no points, otherwise FALSE.
#
# @return [Boolean]
def ring?
FFI::OGR::API.OGR_G_IsRing(@c_pointer)
- rescue GDAL::Error => ex
- return false if ex.message.include? 'IllegalArgumentException'
+ rescue GDAL::Error => e
+ return false if e.message.include? 'IllegalArgumentException'
raise
end
# @param other_geometry [OGR::Geometry]
# @return [OGR::Geometry]
- # @todo This regularly crashes, so disabling it.
def intersection(other_geometry)
build_geometry do
FFI::OGR::API.OGR_G_Intersection(@c_pointer, other_geometry.c_pointer)
end
end
@@ -414,10 +425,13 @@
# @return [Float] -1 if an error occurs.
def distance_to(geometry)
FFI::OGR::API.OGR_G_Distance(@c_pointer, geometry.c_pointer)
end
+ # NOTE: The returned object may be shared with many geometries, and should
+ # thus not be modified.
+ #
# @return [OGR::SpatialReference]
def spatial_reference
spatial_ref_ptr = FFI::OGR::API.OGR_G_GetSpatialReference(@c_pointer)
return if spatial_ref_ptr.null?
@@ -427,11 +441,13 @@
# Assigns a spatial reference to this geometry. Any existing spatial
# reference is replaced, but this does not reproject the geometry.
#
# @param new_spatial_ref [OGR::SpatialReference, FFI::Pointer]
def spatial_reference=(new_spatial_ref)
- new_spatial_ref_ptr = GDAL._pointer(OGR::SpatialReference, new_spatial_ref)
+ # Note that assigning a spatial reference increments the reference count
+ # on the OGRSpatialReference, but does not copy it.
+ new_spatial_ref_ptr = GDAL._pointer(OGR::SpatialReference, new_spatial_ref, autorelease: false)
FFI::OGR::API.OGR_G_AssignSpatialReference(@c_pointer, new_spatial_ref_ptr)
end
# Transforms the coordinates of this geometry in its current spatial
@@ -442,35 +458,42 @@
# Note that this doesn't require the geometry to have an existing spatial
# reference system.
#
# @param coordinate_transformation [OGR::CoordinateTransformation,
# FFI::Pointer]
- # @return [Boolean]
+ # @raise [OGR::Failure]
def transform!(coordinate_transformation)
coord_trans_ptr = GDAL._pointer(OGR::CoordinateTransformation,
- coordinate_transformation)
+ coordinate_transformation)
return if coord_trans_ptr.nil? || coord_trans_ptr.null?
- ogr_err = FFI::OGR::API.OGR_G_Transform(@c_pointer, coord_trans_ptr)
-
- ogr_err.handle_result
+ OGR::ErrorHandling.handle_ogr_err('Unable to transform geometry') do
+ FFI::OGR::API.OGR_G_Transform(@c_pointer, coord_trans_ptr)
+ end
end
# Similar to +#transform+, but this only works if the geometry already has an
# assigned spatial reference system _and_ is transformable to the target
# coordinate system.
#
+ # Because this function requires internal creation and initialization of an
+ # OGRCoordinateTransformation object it is significantly more expensive to
+ # use this function to transform many geometries than it is to create the
+ # OGRCoordinateTransformation in advance, and call transform() with that
+ # transformation. This function exists primarily for convenience when only
+ # transforming a single geometry.
+ #
# @param new_spatial_ref [OGR::SpatialReference, FFI::Pointer]
- # @return [Boolean]
+ # @raise [OGR::Failure]
def transform_to!(new_spatial_ref)
- new_spatial_ref_ptr = GDAL._pointer(OGR::SpatialReference, new_spatial_ref)
+ new_spatial_ref_ptr = GDAL._pointer(OGR::SpatialReference, new_spatial_ref, autorelease: false)
return if new_spatial_ref_ptr.null?
- ogr_err = FFI::OGR::API.OGR_G_TransformTo(@c_pointer, new_spatial_ref_ptr)
-
- ogr_err.handle_result
+ OGR::ErrorHandling.handle_ogr_err('Unable to transform geometry') do
+ FFI::OGR::API.OGR_G_TransformTo(@c_pointer, new_spatial_ref_ptr)
+ end
end
# Computes and returns a new, simplified geometry.
#
# @param distance_tolerance [Float]
@@ -503,11 +526,11 @@
# Computes the buffer of the geometry by building a new geometry that
# contains the buffer region around the geometry that this was called on.
#
# @param distance [Float] The buffer distance to be applied.
- # @param quad_segments [Fixnum] The number of segments to use to approximate
+ # @param quad_segments [Integer] The number of segments to use to approximate
# a 90 degree (quadrant) of curvature.
# @return [OGR::Polygon]
def buffer(distance, quad_segments = 30)
build_geometry do
FFI::OGR::API.OGR_G_Buffer(@c_pointer, distance, quad_segments)
@@ -525,50 +548,66 @@
def point_on_surface
build_geometry { FFI::OGR::API.OGR_G_PointOnSurface(@c_pointer) }
end
# @param wkb_data [String] Binary WKB data.
- # @return +true+ if successful, otherwise raises an OGR exception.
+ # @raise [OGR::Failure]
def import_from_wkb(wkb_data)
- ogr_err = FFI::OGR::API.OGR_G_ImportFromWkb(@c_pointer, wkb_data, wkb_data.length)
-
- ogr_err.handle_result
+ OGR::ErrorHandling.handle_ogr_err('Unable to import geometry from WKB') do
+ FFI::OGR::API.OGR_G_ImportFromWkb(@c_pointer, wkb_data, wkb_data.length)
+ end
end
# The exact number of bytes required to hold the WKB of this object.
#
- # @return [Fixnum]
+ # @return [Integer]
def wkb_size
FFI::OGR::API.OGR_G_WkbSize(@c_pointer)
end
# @return [String]
+ # @raise [OGR::Failure]
def to_wkb(byte_order = :wkbXDR)
output = FFI::MemoryPointer.new(:uchar, wkb_size)
- ogr_err = FFI::OGR::API.OGR_G_ExportToWkb(@c_pointer, byte_order, output)
- ogr_err.handle_result 'Unable to export geometry to WKB'
+ OGR::ErrorHandling.handle_ogr_err("Unable to export geometry to WKB (using byte order #{byte_order})") do
+ FFI::OGR::API.OGR_G_ExportToWkb(@c_pointer, byte_order, output)
+ end
+
output.read_bytes(wkb_size)
end
# @param wkt_data [String]
+ # @raise [OGR::Failure]
def import_from_wkt(wkt_data)
wkt_data_pointer = FFI::MemoryPointer.from_string(wkt_data)
wkt_pointer_pointer = FFI::MemoryPointer.new(:pointer)
wkt_pointer_pointer.write_pointer(wkt_data_pointer)
- ogr_err = FFI::OGR::API.OGR_G_ImportFromWkt(@c_pointer, wkt_pointer_pointer)
- ogr_err.handle_result "Unable to import: #{wkt_data}"
+ OGR::ErrorHandling.handle_ogr_err("Unable to import from WKT: #{wkt_data}") do
+ FFI::OGR::API.OGR_G_ImportFromWkt(@c_pointer, wkt_pointer_pointer)
+ end
end
# @return [String]
+ # @raise [OGR::Failure]
def to_wkt
- output = FFI::MemoryPointer.new(:string)
- ogr_err = FFI::OGR::API.OGR_G_ExportToWkt(@c_pointer, output)
- ogr_err.handle_result
+ GDAL._cpl_read_and_free_string do |output_ptr|
+ OGR::ErrorHandling.handle_ogr_err('Unable to export to WKT') do
+ FFI::OGR::API.OGR_G_ExportToWkt(@c_pointer, output_ptr)
+ end
+ end
+ end
- output.read_pointer.read_string
+ # @return [String]
+ # @raise [OGR::Failure]
+ def to_iso_wkt
+ GDAL._cpl_read_and_free_string do |output_ptr|
+ OGR::ErrorHandling.handle_ogr_err('Unable to export to WKT') do
+ FFI::OGR::API.OGR_G_ExportToIsoWkt(@c_pointer, output_ptr)
+ end
+ end
end
# This geometry expressed as GML in GML basic data types.
#
# @param [Hash] options
@@ -583,25 +622,48 @@
# @option options [String] :gmlid Use this to write a gml:id attribute at
# the top level of the geometry.
# @return [String]
def to_gml(**options)
options_ptr = GDAL::Options.pointer(options)
- FFI::OGR::API.OGR_G_ExportToGMLEx(@c_pointer, options_ptr)
+ gml, ptr = FFI::OGR::API.OGR_G_ExportToGMLEx(@c_pointer, options_ptr)
+ FFI::CPL::VSI.VSIFree(ptr)
+
+ gml
end
# @param altitude_mode [String] Value to write in the +altitudeMode+
# element.
# @return [String]
def to_kml(altitude_mode = nil)
- FFI::OGR::API.OGR_G_ExportToKML(@c_pointer, altitude_mode)
+ kml, ptr = FFI::OGR::API.OGR_G_ExportToKML(@c_pointer, altitude_mode)
+ FFI::CPL::VSI.VSIFree(ptr)
+
+ kml
end
# @return [String]
def to_geo_json
- FFI::OGR::API.OGR_G_ExportToJson(@c_pointer)
+ json, ptr = FFI::OGR::API.OGR_G_ExportToJson(@c_pointer)
+ FFI::CPL::VSI.VSIFree(ptr)
+
+ json
end
+ # @param [Hash] options
+ # @option options [String] :coordinate_precision Maximum number of figures
+ # after decimal separate to write in coordinates.
+ # @option options [String] :significant_figures Maximum number of
+ # significant figures.
+ # @return [String]
+ def to_geo_json_ex(**options)
+ options_ptr = GDAL::Options.pointer(options)
+ json, ptr = FFI::OGR::API.OGR_G_ExportToJsonEx(@c_pointer, options_ptr)
+ FFI::CPL::VSI.VSIFree(ptr)
+
+ json
+ end
+
# Converts the current geometry to a LineString geometry. The returned
# object is a new OGR::Geometry instance.
#
# @return [OGR::Geometry]
def to_line_string
@@ -610,11 +672,11 @@
# Since GDAL doesn't provide converting to a LinearRing, this is a hackish
# method for doing so.
#
# @return [OGR::LinearRing]
- def to_linear_ring(close_rings = false)
+ def to_linear_ring(close_rings: false)
line_string = to_line_string
return line_string unless line_string.is_a?(OGR::LineString)
linear_ring = OGR::LinearRing.new
@@ -662,10 +724,15 @@
private
# @param geometry_ptr [OGR::Geometry, FFI::Pointer]
def initialize_from_pointer(geometry_ptr)
raise OGR::InvalidHandle, "Must initialize with a valid pointer: #{geometry_ptr}" if geometry_ptr.nil?
- @c_pointer = GDAL._pointer(OGR::Geometry, geometry_ptr)
+
+ pointer = GDAL._pointer(OGR::Geometry, geometry_ptr, autorelease: false)
+
+ raise OGR::InvalidHandle, "Must initialize with a valid pointer: #{geometry_ptr}" if pointer.null?
+
+ @c_pointer = pointer
end
def build_geometry
new_geometry_ptr = yield
return if new_geometry_ptr.nil? || new_geometry_ptr.null? || new_geometry_ptr == @c_pointer