#ifndef SQL_GIS_GEOMETRIES_H_INCLUDED #define SQL_GIS_GEOMETRIES_H_INCLUDED // Copyright (c) 2017, 2024, Oracle and/or its affiliates. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, version 2.0, // as published by the Free Software Foundation. // // This program is designed to work with certain software (including // but not limited to OpenSSL) that is licensed under separate terms, // as designated in a particular file or component or in included license // documentation. The authors of MySQL hereby grant you an additional // permission to link the program and your derivative works with the // separately licensed software that they have either included with // the program or referenced in the documentation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License, version 2.0, for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. /// @file /// /// This file declares the geometry class hierarchy used by the server as the /// internal representation of geometries. /// /// The hierarchy is closely modelled after the hierarchy in SFA-CA /// (OGC 06-103r4), but since Boost.Geometry depends on type traits to /// know if a geometry is in a Cartesian or geographic SRS, there are /// Cartesian and geographic specializations of each instantiable type /// in the SFA-CA. These are defined in geometries_cs.h. /// /// Because of Boost.Geometry, iterators have to be of coordinate system /// specific types. It is therefore not possible to have the standard begin() /// and end() iterators in the common superclass. Instead, operator[] is /// provided as a coordinate system agnostic option. /// /// @see geometries_cs.h #include #include namespace gis { /// Types of geometry objects. Not all are instantiable. /// /// The values and the storage type are the same as used in WKB. enum class Geometry_type : std::uint32_t { kGeometry = 0, kPoint = 1, kLinestring = 2, kPolygon = 3, kMultipoint = 4, kMultilinestring = 5, kMultipolygon = 6, kGeometrycollection = 7 // kMulticurve = 11, // kMultisurface = 12, // kCurve = 13, // kSurface = 14, }; /// Types of coordinate systems. enum class Coordinate_system { /// A Cartesian plane with the same unit in both directions. kCartesian = 0, /// An ellipsoidal system with longitude and latitude coordinates, /// both in the same unit. kGeographic = 1 }; /// Direction of a ring. enum class Ring_direction { /// Clockwise. kCW = 0, /// Counterclockwise. kCCW = 1, /// Unknown. /// /// Used only as an output value when the function is unable to determine the /// ring direction, e.g., if the ring contains only repetitions of the same /// point, or if there is a spike in the ring. kUnknown = 2 }; class Geometry_visitor; /// Abstract superclass for all geometric objects. /// /// Geometry is a non-instantiable type in SQL. /// /// The hierarchy is first divided into abstract classes according to the SFA-CA /// geometry type hierarchy, and then divided into concrete subclasses for each /// coordinate system. class Geometry { public: Geometry() = default; virtual ~Geometry() = default; Geometry(const Geometry &) = default; Geometry &operator=(const Geometry &) = default; // Returns a copy of an instantiable subclass of Geometry. virtual Geometry *clone() const = 0; /// Gets the geometry type of the object. /// /// @return The type of this object virtual Geometry_type type() const = 0; /// Gets the coordinate system. /// /// @return The coordinate system type. virtual Coordinate_system coordinate_system() const = 0; /// Applies a hierarchical visitor to this geometry. /// /// @see gis::Geometry_visitor /// /// @param v A hierarchical visitor. /// /// @retval true The execution was aborted by the visitor. /// @retval false The execution completed. virtual bool accept(Geometry_visitor *v) = 0; /// Check if this is an empty geometry. /// /// The definition of empty geometries is the one in SFA-CA (OGC 06-103r4, /// Sect. 6.1.2.2), i.e., an empty point set. /// /// @note This is different from the function "empty", which returns true if a /// geometry contains no subgeometries. E.g., a geometry collection may /// contain another geometry collection which is empty. In this case, the /// "empty" function would return false on the outermost geometry collection, /// while "is_empty" would return true. /// /// @retval true The geometry represents the empty point set. /// @retval false The geometry represent a non-empty point set. virtual bool is_empty() const = 0; }; /// A 2d point. /// /// Point is an instantiable type in SQL. class Point : public Geometry { public: Point() : m_x(std::nan("")), m_y(std::nan("")) {} Point(double x, double y) : m_x(x), m_y(y) {} Geometry_type type() const override { return Geometry_type::kPoint; } bool accept(Geometry_visitor *v) override; bool is_empty() const override { return (std::isnan(m_x) || std::isnan(m_y)); } // Returns a copy of a subclass of Point. Point *clone() const override = 0; /// Gets a coordinate value. /// /// This function signature must match the expectations of Boost.Geometry. /// /// @tparam K Coordinate number, zero indexed. /// @return The coordinate value. template double get() const; /// Gets the first coordinate value. /// /// For geographic points, the first coordinate is longitude. /// /// @return The first coordinate value. double x() const; /// Gets the second coordinate value. /// /// For geographic points, the second coordinate is latitude. /// /// @return The second coordinate value. double y() const; /// Sets a coordinate. /// /// This function signature must match the expectations of Boost.Geometry. /// /// @tparam K Coordinate number, zero indexed. /// @param d The coordinate value. template void set(double d); /// Sets the first coordinate value. /// /// For geographic points, the first coordinate is longitude. /// /// @param d The coordinate value. void x(double d); /// Sets the second coordinate value. /// /// For geographic points, the second coordinate is latitude. /// /// @param d The coordinate value. void y(double d); private: /// First coordinate (X or longitude). /// /// Geographic coordinates are in radians, positive to the East of /// Greenwich. Cartesian coordinates are in the SRSs length unit. double m_x; /// Second coordinate (Y or latitude). /// /// Geographic coordinates are in radians, positive to the North of the /// Equator. Cartesian coordinates are in the SRSs length unit. double m_y; }; /// Compares two points. /// /// The point with the lowest X coordinate is the smaller point. If X /// coordinates are equal, the point with the lowest Y coordinate is the /// smaller. /// /// @param lhs Left hand side. /// @param rhs Right hand side. /// /// @retval true Left hand side sorts before right hand side. /// @retval false Left hand side does not sort before right hand side. inline bool operator<(const Point &lhs, const Point &rhs) { return (lhs.x() < rhs.x()) || (lhs.x() == rhs.x() && lhs.y() < rhs.y()); } /// An abstract 2d curve. /// /// Curve is a non-instantiable type in SQL. class Curve : public Geometry { public: Geometry_type type() const override = 0; bool accept(Geometry_visitor *v) override = 0; }; /// A string of connected line segments. /// /// Linestring is an instantiable type in SQL. /// /// According to the SFA-CA, linestrings have a linear interpolation between /// points. In MySQL, a linestring represents the geodesic. On a plane, this is /// linear interpolation, but on an ellipsoid, it's the shortest path along the /// ellipsoid surface. class Linestring : public Curve { public: Geometry_type type() const override { return Geometry_type::kLinestring; } bool accept(Geometry_visitor *v) override = 0; bool is_empty() const override { return empty(); } /// Creates a subclass of Linestring from a Coordinate_system /// /// @param[in] coordinate_system Coordinate system to create a Linestring for. /// @return A pointer to a Linestring that caller must free when it is done /// with it. static Linestring *create_linestring(Coordinate_system coordinate_system); // Returns a copy of a subclass of Linestring. Linestring *clone() const override = 0; /// Adds a point to the end of the linestring. /// /// @param pt The point to add. virtual void push_back(const Point &pt) = 0; virtual void push_back(Point &&pt) = 0; /// Removes a point from the front of the linestring. virtual void pop_front() = 0; /// Checks if the linestring is empty. /// /// Here, the definition of empty is that the linestring does not contain any /// points. An invalid linestring with only one coordinate is not empty. /// /// @retval true The linestring is empty. /// @retval false The linestring is not empty. virtual bool empty() const = 0; /// Returns the size of (number of points in) the linestring. /// /// @return Number of points in the linestring. virtual std::size_t size() const = 0; /// Removes all points from the linestring. virtual void clear() noexcept = 0; /// Returns the last point of the linestring. /// /// @return Last point of linestring virtual Point &back() = 0; virtual const Point &back() const = 0; /// Returns the first point of the linestring. /// /// @return First point of linestring virtual Point &front() = 0; virtual const Point &front() const = 0; virtual Point &operator[](std::size_t i) = 0; virtual const Point &operator[](std::size_t i) const = 0; }; /// A ring-shaped linestring. /// /// Linearring is a non-instantiable type in SQL. It is used to represent /// polygon rings. /// /// The start and end point of the linestring must be the same (assumed, but not /// enforced, by the implementation). class Linearring : public Linestring { public: bool accept(Geometry_visitor *v) override = 0; /// Creates a subclass of Linearring from a Coordinate_system /// /// @param[in] coordinate_system Coordinate system to create a Linearring for. /// @return A pointer to a Linearring that caller must free when it is done /// with it. static Linearring *create_linearring(Coordinate_system coordinate_system); }; /// An abstract 2d surface. /// /// Surface is a non-instantiable type in SQL. class Surface : public Geometry { public: bool accept(Geometry_visitor *v) override = 0; Geometry_type type() const override = 0; }; /// A polygon consisting of an outer ring and zero or more interior rings /// defining holes in the polygon. /// /// Polygon is an instantiable type in SQL. /// /// The interior rings must be inside the exterior ring (not enforced by the /// implementation). class Polygon : public Surface { public: Geometry_type type() const override { return Geometry_type::kPolygon; } bool accept(Geometry_visitor *v) override = 0; bool is_empty() const override { return empty(); } /// Creates a subclass of Polygon from a Coordinate_system /// /// @param[in] coordinate_system Coordinate system to create a Polygon for. /// @return A pointer to a Polygon that caller must free when it is done /// with it. static Polygon *create_polygon(Coordinate_system coordinate_system); // Returns a copy of a subclass of Polygon. Polygon *clone() const override = 0; /// Adds a linear ring to the polygon. /// /// The first ring will become the exterior ring. All following rings will be /// interior rings (holes). /// /// @param lr The linear ring to add. virtual void push_back(const Linearring &lr) = 0; virtual void push_back(Linearring &&lr) = 0; /// Checks if the polygon is empty. /// /// The polygon is considered empty if it has no rings. /// /// @retval true The polygon is empty. /// @retval false The polygon is not empty. virtual bool empty() const = 0; /// Returns the size of the polygon. /// /// @return Number of rings in the polygon (exterior + interior). virtual std::size_t size() const = 0; /// Returns the exterior ring of the polygon. /// /// @note If the polygon currently has no exterior ring, an empty one is /// added. /// /// @return The exterior ring. virtual Linearring &exterior_ring() = 0; /// Returns an interior ring of the polygon. /// /// @param n Ring number, zero indexed. /// /// @return The interior ring. virtual Linearring &interior_ring(std::size_t n) = 0; }; /// A collection of geometries. /// /// Geometrycollection is an instantiable type in SQL. It is the only /// instantiable non-leaf geometry type. /// /// The Geometrycollection class places no restrictions on type, overlapping, /// etc. Subclasses do. class Geometrycollection : public Geometry { public: Geometry_type type() const override { return Geometry_type::kGeometrycollection; } bool accept(Geometry_visitor *v) override = 0; /// Creates a subclass of Geometrycollection from a Coordinate_system /// /// @param[in] coordinate_system Coordinate system to create a /// Geometrycollection for. /// @return A pointer to a Geometrycollection that caller must free when it is /// done with it. static Geometrycollection *CreateGeometrycollection( Coordinate_system coordinate_system); /// Adds a geometry to the collection. /// /// @param g The geometry to add. virtual void push_back(const Geometry &g) = 0; virtual void push_back(Geometry &&g) = 0; /// Removes a geometry from the front of the collection. virtual void pop_front() = 0; /// Checks if the collection is empty. /// /// @retval true The polygon is empty. /// @retval false The polygon is not empty. virtual bool empty() const = 0; /// Returns the size of the geometrycollection. /// /// @return Number of geometries in the geometrycollection. virtual std::size_t size() const = 0; /// Resizes the geometrycollection to contain a given number of elements. /// /// If the new size is smaller than the current size, the remaining geometries /// are discarded. /// /// @param[in] count The new number of geometries. virtual void resize(std::size_t count) = 0; /// Removes all geometries from the geometrycollection. virtual void clear() noexcept = 0; /// Returns the first geometry of the collection. /// /// @return First geometry of the collection virtual Geometry &front() = 0; virtual const Geometry &front() const = 0; virtual Geometry &operator[](std::size_t i) = 0; virtual const Geometry &operator[](std::size_t i) const = 0; /// Clone pattern to easily duplicate a Geometrycollection. /// /// @return A pointer to a copy of the Geometrycollection that caller /// must free when it is done with it. Geometrycollection *clone() const override = 0; /// Creates a subclass from a Coordinate_system /// /// @param[in] coordinate_system Coordinate system to create a /// geometrycollection for /// @return A pointer to a Geometrycollection that caller must free when it is /// done with it. static Geometrycollection *create_geometrycollection( Coordinate_system coordinate_system); }; /// A collection of points. /// /// Multipoint is an instantiable type in SQL. class Multipoint : public Geometrycollection { public: Geometry_type type() const override { return Geometry_type::kMultipoint; } bool accept(Geometry_visitor *v) override = 0; /// Clone pattern to easily duplicate a Multipoint. /// /// @return A pointer to a copy of the Multipoint that caller /// must free when it is done with it. Multipoint *clone() const override = 0; /// Returns the first point of the Multipoint. /// /// @return First point of the Multipoint Point &front() override = 0; const Point &front() const override = 0; /// Creates a subclass of Multipoint from a Coordinate_system /// /// @param[in] coordinate_system Coordinate system to create a Multipoint for. /// @return A pointer to a Multipoint that caller must free when it is done /// with it. static Multipoint *create_multipoint(Coordinate_system coordinate_system); }; /// An abstract collection of curves. /// /// Multicurve is a non-instantiable type in SQL. class Multicurve : public Geometrycollection { public: Geometry_type type() const override = 0; bool accept(Geometry_visitor *v) override = 0; }; /// A collection of linestrings. /// /// Multilinestring is an instantiable type in SQL. class Multilinestring : public Multicurve { public: Geometry_type type() const override { return Geometry_type::kMultilinestring; } bool accept(Geometry_visitor *v) override = 0; /// Clone pattern to easily duplicate a Multilinestring. /// /// @return A pointer to a copy of the Multilinestring that caller /// must free when it is done with it. Multilinestring *clone() const override = 0; /// Returns the first linestring of the Multilinestring. /// /// @return First linestring of the Multilinestring Linestring &front() override = 0; const Linestring &front() const override = 0; /// Creates a subclass of Multilinestring from a Coordinate_system /// /// @param[in] coordinate_system Coordinate system to create a Multilinestring /// for. /// @return A pointer to a Multilinestring that caller must free when it is /// done with it. static Multilinestring *create_multilinestring( Coordinate_system coordinate_system); }; /// An abstract collection of surfaces. /// /// Multisurface is a non-instantiable type in SQL. class Multisurface : public Geometrycollection { public: Geometry_type type() const override = 0; bool accept(Geometry_visitor *v) override = 0; }; /// A collection of polygons. /// /// Multipolygon is an instantiable type in SQL. class Multipolygon : public Multisurface { public: Geometry_type type() const override { return Geometry_type::kMultipolygon; } bool accept(Geometry_visitor *v) override = 0; /// Clone pattern to easily duplicate a Multipolygon. /// /// @return A pointer to a copy of the Multipolygon that caller /// must free when it is done with it. Multipolygon *clone() const override = 0; /// Returns the first polygon of the Multipolygon. /// /// @return First polygon of the Multipolygon Polygon &front() override = 0; const Polygon &front() const override = 0; /// Creates a subclass of Multipolygon from a Coordinate_system /// /// @param[in] coordinate_system Coordinate system to create a Multipolygon /// for. /// @return A pointer to a Multipolygon that caller must free when it is done /// with it. static Multipolygon *create_multipolygon(Coordinate_system coordinate_system); }; /// Get the type name string corresponding to a geometry type. /// /// @param type The geometry type. /// /// @return A string containing the type name (in uppercase). const char *type_to_name(Geometry_type type); } // namespace gis #endif // SQL_GIS_GEOMETRIES_H_INCLUDED