# frozen_string_literal: true
# -----------------------------------------------------------------------------
#
# Geometry feature interface
#
# -----------------------------------------------------------------------------
module RGeo
module Feature
# == SFS 1.1 Description
#
# Geometry is the root class of the hierarchy. Geometry is an abstract
# (non-instantiable) class.
#
# The instantiable subclasses of Geometry defined in this International
# Standard are restricted to 0, 1 and 2-dimensional geometric objects
# that exist in 2-dimensional coordinate space (R2).
#
# All instantiable Geometry classes described in this part of ISO 19125
# are defined so that valid instances of a Geometry class are
# topologically closed, i.e. all defined geometries include their
# boundary.
#
# == Notes
#
# Geometry is defined as a module and is provided primarily for the
# sake of documentation. Implementations need not necessarily include
# this module itself. Therefore, you should not depend on the result
# of is_a?(Geometry) to check type. Instead, use the
# provided check_type class method (or === operator) defined in the
# Type module.
#
# Some implementations may support higher dimensional objects or
# coordinate systems, despite the limits of the SFS.
#
# == Forms of equivalence
#
# The Geometry model defines three forms of equivalence.
#
# * Spatial equivalence is the weakest form of equivalence,
# indicating that the objects represent the same region of space,
# but may be different representations of that region. For example,
# POINT(0 0) and a MULTIPOINT(0 0) are spatially equivalent, as are
# LINESTRING(0 0, 10 10) and
# GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(0 0, 10 10, 0 0)).
# As a general rule, objects must have factories that are
# Factory#eql? in order to be spatially equivalent.
#
# * Representational equivalence is a stronger form, indicating
# that the objects have the same representation, but may be
# different objects. All representationally equivalent objects are
# spatially equivalent, but not all spatially equivalent objects are
# representationally equivalent. For example, none of the examples
# in the spatial equivalence section above are representationally
# equivalent. However, two separate objects that both represent
# POINT(1 2) are representationally equivalent as well as spatially
# equivalent.
#
# * Objective equivalence is the strongest form, indicating
# that the references refer to the same object. Of course, all
# pairs of references with the same objective identity are also
# both representationally and spatially equivalent.
#
# Different methods test for different types of equivalence:
#
# * equals? and == test for spatial equivalence.
# * rep_equals? and eql? test for representational
# equivalence.
# * equal? tests for objective equivalence.
#
# All ruby objects must provide a suitable test for objective
# equivalence. Normally, this is simply provided by the Ruby Object
# base class. Geometry implementations should normally also provide
# tests for representational and spatial equivalence, if possible.
# The == operator and the eql? method are standard
# Ruby methods that are often expected to be usable for every object.
# Therefore, if an implementation cannot provide a suitable test for
# their equivalence types, they must degrade to use a stronger form
# of equivalence.
module Geometry
extend Type
# Returns a factory for creating features related to this one.
# This does not necessarily need to be the same factory that created
# this object, but it should create objects that are "compatible"
# with this one. (i.e. they should be in the same spatial reference
# system by default, and it should be possible to perform relational
# operations on them.)
def factory
raise Error::UnsupportedOperation, "Method Geometry#factory not defined."
end
# === SFS 1.1 Description
#
# The inherent dimension of this geometric object, which must be less
# than or equal to the coordinate dimension. This specification is
# restricted to geometries in 2-dimensional coordinate space.
#
# === Notes
#
# Returns an integer. This value is -1 for an empty geometry, 0 for
# point geometries, 1 for curves, and 2 for surfaces.
def dimension
raise Error::UnsupportedOperation, "Method Geometry#dimension not defined."
end
# === SFS 1.1 Description
#
# Returns the instantiable subtype of Geometry of which this
# geometric object is an instantiable member.
#
# === Notes
#
# Returns one of the type modules in RGeo::Feature. e.g. a point
# object would return RGeo::Feature::Point. Note that this is
# different from the SFS specification, which stipulates that the
# string name of the type is returned. To obtain the name string,
# call the +type_name+ method of the returned module.
def geometry_type
raise Error::UnsupportedOperation, "Method Geometry#geometry_type not defined."
end
# === SFS 1.1 Description
#
# Returns the Spatial Reference System ID for this geometric object.
#
# === Notes
#
# Returns an integer.
#
# This will normally be a foreign key to an index of reference systems
# stored in either the same or some other datastore.
def srid
raise Error::UnsupportedOperation, "Method Geometry#srid not defined."
end
# === SFS 1.1 Description
#
# The minimum bounding box for this Geometry, returned as a Geometry.
# The polygon is defined by the corner points of the bounding box
# [(MINX, MINY), (MAXX, MINY), (MAXX, MAXY), (MINX, MAXY), (MINX, MINY)].
#
# === Notes
#
# Returns an object that supports the Geometry interface.
def envelope
raise Error::UnsupportedOperation, "Method Geometry#envelope not defined."
end
# === SFS 1.1 Description
#
# Exports this geometric object to a specific Well-known Text
# Representation of Geometry.
#
# === Notes
#
# Returns an ASCII string.
def as_text
raise Error::UnsupportedOperation, "Method Geometry#as_text not defined."
end
# === SFS 1.1 Description
#
# Exports this geometric object to a specific Well-known Binary
# Representation of Geometry.
#
# === Notes
#
# Returns a binary string.
def as_binary
raise Error::UnsupportedOperation, "Method Geometry#as_binary not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object is the empty Geometry. If true,
# then this geometric object represents the empty point set for the
# coordinate space.
#
# === Notes
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
def is_empty?
raise Error::UnsupportedOperation, "Method Geometry#is_empty? not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object has no anomalous geometric
# points, such as self intersection or self tangency. The description
# of each instantiable geometric class will include the specific
# conditions that cause an instance of that class to be classified as
# not simple.
#
# === Notes
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
def is_simple?
raise Error::UnsupportedOperation, "Method Geometry#is_simple? not defined."
end
# === SFS 1.1 Description
#
# Returns the closure of the combinatorial boundary of this geometric
# object. Because the result of this function is a closure, and hence
# topologically closed, the resulting boundary can be represented using
# representational Geometry primitives.
#
# === Notes
#
# Returns an object that supports the Geometry interface.
def boundary
raise Error::UnsupportedOperation, "Method Geometry#boundary not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object is "spatially equal" to
# another_geometry.
#
# === Notes
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of comparing objects
# from different factories is undefined.
def equals?(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#equals? not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object is "spatially disjoint" from
# another_geometry.
#
# === Notes
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of comparing objects
# from different factories is undefined.
def disjoint?(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#disjoint? not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object "spatially intersects"
# another_geometry.
#
# === Notes
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of comparing objects
# from different factories is undefined.
def intersects?(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#intersects? not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object "spatially touches"
# another_geometry.
#
# === Notes
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of comparing objects
# from different factories is undefined.
def touches?(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#touches? not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object "spatially crosses"
# another_geometry.
#
# === Notes
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of comparing objects
# from different factories is undefined.
def crosses?(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#crosses? not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object is "spatially within"
# another_geometry.
#
# === Notes
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of comparing objects
# from different factories is undefined.
def within?(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#within? not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object "spatially contains"
# another_geometry.
#
# === Notes
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of comparing objects
# from different factories is undefined.
def contains?(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#contains? not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object "spatially overlaps"
# another_geometry.
#
# === Notes
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of comparing objects
# from different factories is undefined.
def overlaps?(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#overlaps? not defined."
end
# === SFS 1.1 Description
#
# Returns true if this geometric object is spatially related to
# another_geometry by testing for intersections between the interior,
# boundary and exterior of the two geometric objects as specified by
# the values in the intersection_pattern_matrix.
#
# === Notes
#
# The intersection_pattern_matrix is provided as a nine-character
# string in row-major order, representing the dimensionalities of
# the different intersections in the DE-9IM. Supported characters
# include T, F, *, 0, 1, and 2.
#
# Returns a boolean value. Note that this is different from the SFS
# specification, which stipulates an integer return value.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of comparing objects
# from different factories is undefined.
def relate?(another_geometry, _intersection_pattern_matrix_)
raise Error::UnsupportedOperation, "Method Geometry#relate not defined."
end
# === SFS 1.1 Description
#
# Returns the shortest distance between any two Points in the two
# geometric objects as calculated in the spatial reference system of
# this geometric object.
#
# === Notes
#
# Returns a floating-point scalar value.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of measuring the
# distance between objects from different factories is undefined.
def distance(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#distance not defined."
end
# === SFS 1.1 Description
#
# Returns a geometric object that represents all Points whose distance
# from this geometric object is less than or equal to distance.
# Calculations are in the spatial reference system of this geometric
# object.
#
# === Notes
#
# Returns an object that supports the Geometry interface.
def buffer(_distance_)
raise Error::UnsupportedOperation, "Method Geometry#buffer not defined."
end
# === SFS 1.1 Description
#
# Returns a geometric object that represents the convex hull of this
# geometric object.
#
# === Notes
#
# Returns an object that supports the Geometry interface.
def convex_hull
raise Error::UnsupportedOperation, "Method Geometry#convex_hull not defined."
end
# === SFS 1.1 Description
#
# Returns a geometric object that represents the Point set
# intersection of this geometric object with another_geometry.
#
# === Notes
#
# Returns an object that supports the Geometry interface.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of performing
# operations on objects from different factories is undefined.
def intersection(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#intersection not defined."
end
# === SFS 1.1 Description
#
# Returns a geometric object that represents the Point set
# union of this geometric object with another_geometry.
#
# === Notes
#
# Returns an object that supports the Geometry interface.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of performing
# operations on objects from different factories is undefined.
def union(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#union not defined."
end
# === SFS 1.1 Description
#
# Returns a geometric object that represents the Point set
# difference of this geometric object with another_geometry.
#
# === Notes
#
# Returns an object that supports the Geometry interface.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of performing
# operations on objects from different factories is undefined.
def difference(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#difference not defined."
end
# === SFS 1.1 Description
#
# Returns a geometric object that represents the Point set symmetric
# difference of this geometric object with another_geometry.
#
# === Notes
#
# Returns an object that supports the Geometry interface.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of performing
# operations on objects from different factories is undefined.
def sym_difference(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#sym_difference not defined."
end
# Returns true if this geometric object is representationally
# equivalent to the given object.
#
# Although implementations are free to attempt to handle
# another_geometry values that do not share the same factory as
# this geometry, strictly speaking, the result of comparing objects
# from different factories is undefined.
def rep_equals?(another_geometry)
raise Error::UnsupportedOperation, "Method Geometry#rep_equals? not defined."
end
# Unions a collection of Geometry or a single Geometry
# (which may be a collection) together. By using this
# special-purpose operation over a collection of geometries
# it is possible to take advantage of various optimizations
# to improve performance. Heterogeneous GeometryCollections
# are fully supported.
#
# This is not a standard SFS method, but when it is available
# in GEOS, it is a very performant way to union all the
# geometries in a collection. GEOS version 3.3 or greater
# is required. If the feature is not available, unary_union
# returns nil.
#
def unary_union
raise Error::UnsupportedOperation, "Method Geometry#unary_union not defined."
end
# This method should behave almost the same as the rep_equals?
# method, with two key differences.
#
# First, the eql? method is required to handle rhs values
# that are not geometry objects (returning false in such cases) in
# order to fulfill the standard Ruby contract for the method,
# whereas the rep_equals? method may assume that any rhs is a
# geometry.
#
# Second, the eql? method should always be defined. That
# is, it should never raise Error::UnsupportedOperation. In cases
# where the underlying implementation cannot provide a
# representational equivalence test, this method must fall back on
# objective equivalence.
def eql?(rhs)
if rhs.is_a?(RGeo::Feature::Instance)
begin
rep_equals?(rhs)
rescue Error::UnsupportedOperation
equal?(rhs)
end
else
false
end
end
# This operator should behave almost the same as the equals? method,
# with two key differences.
#
# First, the == operator is required to handle rhs values that are
# not geometry objects (returning false in such cases) in order to
# fulfill the standard Ruby contract for the == operator, whereas
# the equals? method may assume that any rhs is a geometry.
#
# Second, the == operator should always be defined. That is, it
# should never raise Error::UnsupportedOperation. In cases where
# the underlying implementation cannot provide a spatial equivalence
# test, the == operator must fall back on representational or
# objective equivalence.
def ==(rhs)
if rhs.is_a?(RGeo::Feature::Instance)
begin
equals?(rhs)
rescue Error::UnsupportedOperation
eql?(rhs)
end
else
false
end
end
# If the given rhs is a geometry object, this operator must behave
# the same as the difference method. The behavior for other rhs
# types is not specified; an implementation may choose to provide
# additional capabilities as appropriate.
def -(rhs)
difference(rhs)
end
# If the given rhs is a geometry object, this operator must behave
# the same as the union method. The behavior for other rhs types
# is not specified; an implementation may choose to provide
# additional capabilities as appropriate.
def +(rhs)
union(rhs)
end
# If the given rhs is a geometry object, this operator must behave
# the same as the intersection method. The behavior for other rhs
# types is not specified; an implementation may choose to provide
# additional capabilities as appropriate.
def *(rhs)
intersection(rhs)
end
end
end
end