DYT/Tool/OpenSceneGraph-3.6.5/include/geos/geom/CoordinateSequence.h

808 lines
25 KiB
C
Raw Permalink Normal View History

2024-12-24 23:49:36 +00:00
/**********************************************************************
*
* GEOS - Geometry Engine Open Source
* http://geos.osgeo.org
*
* Copyright (C) 2022 ISciences LLC
* Copyright (C) 2006 Refractions Research Inc.
*
* This is free software; you can redistribute and/or modify it under
* the terms of the GNU Lesser General Public Licence as published
* by the Free Software Foundation.
* See the COPYING file for more information.
*
**********************************************************************/
#pragma once
#include <geos/export.h>
#include <geos/geom/Coordinate.h> // for applyCoordinateFilter
#include <geos/geom/CoordinateSequenceIterator.h>
#include <cassert>
#include <vector>
#include <iostream>
#include <iosfwd> // ostream
#include <memory> // for unique_ptr typedef
// Forward declarations
namespace geos {
namespace geom {
class Envelope;
class CoordinateFilter;
}
}
namespace geos {
namespace geom { // geos::geom
/**
* \class CoordinateSequence geom.h geos.h
*
* \brief
* The internal representation of a list of coordinates inside a Geometry.
*
* A CoordinateSequence is capable of storing XY, XYZ, XYM, or XYZM Coordinates. For efficient
* storage, the dimensionality of the CoordinateSequence should be specified at creation using
* the constructor with `hasz` and `hasm` arguments. Currently most of the GEOS code base
* stores 2D Coordinates and accesses using the Coordinate type. Sequences used by these parts
* of the code must be created with constructors without `hasz` and `hasm` arguments.
*
* If a high-dimension Coordinate coordinate is read from a low-dimension CoordinateSequence,
* the higher dimensions will be populated with incorrect values or a segfault may occur.
*
*/
class GEOS_DLL CoordinateSequence {
public:
/// Standard ordinate index values
enum { X, Y, Z, M };
using iterator = CoordinateSequenceIterator<CoordinateSequence, Coordinate>;
using const_iterator = CoordinateSequenceIterator<const CoordinateSequence, const Coordinate>;
typedef std::unique_ptr<CoordinateSequence> Ptr;
/// \defgroup construct Constructors
/// @{
/**
* Create an CoordinateSequence capable of storing XY or XYZ coordinates.
*/
CoordinateSequence();
/**
* Create a CoordinateSequence capable of storing XY, XYZ or XYZM coordinates.
*
* @param size size of the sequence to create.
* @param dim 2 for 2D, 3 for XYZ, 4 for XYZM, or 0 to determine
* this based on the first coordinate in the sequence
*/
CoordinateSequence(std::size_t size, std::size_t dim = 0);
/**
* Create a CoordinateSequence that packs coordinates of any dimension.
* Code using a CoordinateSequence constructed in this way must not
* attempt to access references to coordinates with dimensions that
* are not actually stored in the sequence.
*
* @param size size of the sequence to create
* @param hasz true if the stored
* @param hasm
* @param initialize
*/
CoordinateSequence(std::size_t size, bool hasz, bool hasm, bool initialize = true);
/**
* Create a CoordinateSequence from a list of XYZ coordinates.
* Code using the sequence may only access references to CoordinateXY
* or Coordinate objects.
*/
CoordinateSequence(const std::initializer_list<Coordinate>&);
/**
* Create a CoordinateSequence from a list of XY coordinates.
* Code using the sequence may only access references to CoordinateXY objects.
*/
CoordinateSequence(const std::initializer_list<CoordinateXY>&);
/**
* Create a CoordinateSequence from a list of XYM coordinates.
* Code using the sequence may only access references to CoordinateXY
* or CoordinateXYM objects.
*/
CoordinateSequence(const std::initializer_list<CoordinateXYM>&);
/**
* Create a CoordinateSequence from a list of XYZM coordinates.
*/
CoordinateSequence(const std::initializer_list<CoordinateXYZM>&);
/**
* Create a CoordinateSequence storing XY values only.
*
* @param size size of the sequence to create
*/
static CoordinateSequence XY(std::size_t size) {
return CoordinateSequence(size, false, false);
}
/**
* Create a CoordinateSequence storing XYZ values only.
*
* @param size size of the sequence to create
*/
static CoordinateSequence XYZ(std::size_t size) {
return CoordinateSequence(size, true, false);
}
/**
* Create a CoordinateSequence storing XYZM values only.
*
* @param size size of the sequence to create
*/
static CoordinateSequence XYZM(std::size_t size) {
return CoordinateSequence(size, true, true);
}
/**
* Create a CoordinateSequence storing XYM values only.
*
* @param size size of the sequence to create
*/
static CoordinateSequence XYM(std::size_t size) {
return CoordinateSequence(size, false, true);
}
/** \brief
* Returns a heap-allocated deep copy of this CoordinateSequence.
*/
std::unique_ptr<CoordinateSequence> clone() const;
/// @}
/// \defgroup prop Properties
/// @{
/**
* Return the Envelope containing all points in this sequence.
* The Envelope is not cached and is computed each time the
* method is called.
*/
Envelope getEnvelope() const;
/** \brief
* Returns the number of Coordinates
*/
std::size_t getSize() const {
return size();
}
/** \brief
* Returns the number of Coordinates
*/
size_t size() const
{
assert(stride() == 2 || stride() == 3 || stride() == 4);
switch(stride()) {
case 2: return m_vect.size() / 2;
case 4: return m_vect.size() / 4;
default : return m_vect.size() / 3;
}
}
/// Returns <code>true</code> if list contains no coordinates.
bool isEmpty() const {
return m_vect.empty();
}
/** \brief
* Tests whether an a {@link CoordinateSequence} forms a ring,
* by checking length and closure. Self-intersection is not checked.
*
* @return true if the coordinate form a ring.
*/
bool isRing() const;
/**
* Returns the dimension (number of ordinates in each coordinate)
* for this sequence.
*
* @return the dimension of the sequence.
*/
std::size_t getDimension() const;
bool hasZ() const {
return m_hasdim ? m_hasz : (m_vect.empty() || !std::isnan(m_vect[2]));
}
bool hasM() const {
return m_hasm;
}
/// Returns true if contains any two consecutive points
bool hasRepeatedPoints() const;
/// Returns true if contains any NaN/Inf coordinates
bool hasRepeatedOrInvalidPoints() const;
/// Get the backing type of this CoordinateSequence. This is not necessarily
/// consistent with the dimensionality of the stored Coordinates; 2D Coordinates
/// may be stored as a XYZ coordinates.
CoordinateType getCoordinateType() const {
switch(stride()) {
case 4: return CoordinateType::XYZM;
case 2: return CoordinateType::XY;
default: return hasM() ? CoordinateType::XYM : CoordinateType::XYZ;
}
}
/// @}
/// \defgroup access Accessors
/// @{
/** \brief
* Returns a read-only reference to Coordinate at position i.
*/
template<typename T=Coordinate>
const T& getAt(std::size_t i) const {
static_assert(std::is_base_of<CoordinateXY, T>::value, "Must be a Coordinate class");
assert(sizeof(T) <= sizeof(double) * stride());
assert(i*stride() < m_vect.size());
const T* orig = reinterpret_cast<const T*>(&m_vect[i*stride()]);
return *orig;
}
/** \brief
* Returns a reference to Coordinate at position i.
*/
template<typename T=Coordinate>
T& getAt(std::size_t i) {
static_assert(std::is_base_of<CoordinateXY, T>::value, "Must be a Coordinate class");
assert(sizeof(T) <= sizeof(double) * stride());
assert(i*stride() < m_vect.size());
T* orig = reinterpret_cast<T*>(&m_vect[i*stride()]);
return *orig;
}
/** \brief
* Write Coordinate at position i to given Coordinate.
*/
template<typename T>
void getAt(std::size_t i, T& c) const {
switch(getCoordinateType()) {
case CoordinateType::XY: c = getAt<CoordinateXY>(i); break;
case CoordinateType::XYZ: c = getAt<Coordinate>(i); break;
case CoordinateType::XYZM: c = getAt<CoordinateXYZM>(i); break;
case CoordinateType::XYM: c = getAt<CoordinateXYM>(i); break;
default: getAt<Coordinate>(i);
}
}
void getAt(std::size_t i, CoordinateXY& c) const {
c = getAt<CoordinateXY>(i);
}
// TODO: change to return CoordinateXY
/**
* Returns a read-only reference to Coordinate at i
*/
const Coordinate& operator[](std::size_t i) const
{
return getAt(i);
}
// TODO: change to return CoordinateXY
/**
* Returns a reference to Coordinate at i
*/
Coordinate&
operator[](std::size_t i)
{
return getAt(i);
}
/**
* Returns the ordinate of a coordinate in this sequence.
* Ordinate indices 0 and 1 are assumed to be X and Y.
* Ordinates indices greater than 1 have user-defined semantics
* (for instance, they may contain other dimensions or measure values).
*
* @param index the coordinate index in the sequence
* @param ordinateIndex the ordinate index in the coordinate
* (in range [0, dimension-1])
*/
double getOrdinate(std::size_t index, std::size_t ordinateIndex) const;
/**
* Returns ordinate X (0) of the specified coordinate.
*
* @param index
* @return the value of the X ordinate in the index'th coordinate
*/
double getX(std::size_t index) const
{
return m_vect[index * stride()];
}
/**
* Returns ordinate Y (1) of the specified coordinate.
*
* @param index
* @return the value of the Y ordinate in the index'th coordinate
*/
double getY(std::size_t index) const
{
return m_vect[index * stride() + 1];
}
/// Return last Coordinate in the sequence
template<typename T=Coordinate>
const T& back() const
{
return getAt<T>(size() - 1);
}
/// Return last Coordinate in the sequence
template<typename T=Coordinate>
T& back()
{
return getAt<T>(size() - 1);
}
/// Return first Coordinate in the sequence
template<typename T=Coordinate>
const T& front() const
{
return *(reinterpret_cast<const T*>(m_vect.data()));
}
/// Return first Coordinate in the sequence
template<typename T=Coordinate>
T& front()
{
return *(reinterpret_cast<T*>(m_vect.data()));
}
/// Pushes all Coordinates of this sequence into the provided vector.
void toVector(std::vector<Coordinate>& coords) const;
void toVector(std::vector<CoordinateXY>& coords) const;
/// @}
/// \defgroup mutate Mutators
/// @{
/// Copy Coordinate c to position pos
template<typename T>
void setAt(const T& c, std::size_t pos) {
switch(getCoordinateType()) {
case CoordinateType::XY: setAtImpl<CoordinateXY>(c, pos); break;
case CoordinateType::XYZ: setAtImpl<Coordinate>(c, pos); break;
case CoordinateType::XYZM: setAtImpl<CoordinateXYZM>(c, pos); break;
case CoordinateType::XYM: setAtImpl<CoordinateXYM>(c, pos); break;
default: setAtImpl<Coordinate>(c, pos);
}
}
/**
* Sets the value for a given ordinate of a coordinate in this sequence.
*
* @param index the coordinate index in the sequence
* @param ordinateIndex the ordinate index in the coordinate
* (in range [0, dimension-1])
* @param value the new ordinate value
*/
void setOrdinate(std::size_t index, std::size_t ordinateIndex, double value);
/// Substitute Coordinate list with a copy of the given vector
void setPoints(const std::vector<Coordinate>& v);
/// @}
/// \defgroup add Adding methods
/// @{
/// Adds the specified coordinate to the end of the sequence. Dimensions
/// present in the coordinate but not in the sequence will be ignored.
/// If multiple coordinates are to be added, a multiple-insert method should
/// be used for best performance.
template<typename T=Coordinate>
void add(const T& c) {
add(c, size());
}
/// Adds the specified coordinate to the end of the sequence. Dimensions
/// present in the coordinate but not in the sequence will be ignored. If
/// allowRepeated is false, the coordinate will not be added if it is the
/// same as the last coordinate in the sequence.
/// If multiple coordinates are to be added, a multiple-insert method should
/// be used for best performance.
template<typename T>
void add(const T& c, bool allowRepeated)
{
if(!allowRepeated && !isEmpty()) {
const CoordinateXY& last = back<CoordinateXY>();
if(last.equals2D(c)) {
return;
}
}
add(c);
}
/** \brief
* Inserts the specified coordinate at the specified position in
* this sequence. If multiple coordinates are to be added, a multiple-
* insert method should be used for best performance.
*
* @param c the coordinate to insert
* @param pos the position at which to insert
*/
template<typename T>
void add(const T& c, std::size_t pos)
{
static_assert(std::is_base_of<CoordinateXY, T>::value, "Must be a Coordinate class");
// c may be a reference inside m_vect, so we make sure it will not
// grow before adding it
if (m_vect.size() + stride() <= m_vect.capacity()) {
make_space(pos, 1);
setAt(c, static_cast<std::size_t>(pos));
} else {
T tmp{c};
make_space(pos, 1);
setAt(tmp, static_cast<std::size_t>(pos));
}
}
/** \brief
* Inserts the specified coordinate at the specified position in
* this list.
*
* @param i the position at which to insert
* @param coord the coordinate to insert
* @param allowRepeated if set to false, repeated coordinates are
* collapsed
*/
template<typename T>
void add(std::size_t i, const T& coord, bool allowRepeated)
{
// don't add duplicate coordinates
if(! allowRepeated) {
std::size_t sz = size();
if(sz > 0) {
if(i > 0) {
const CoordinateXY& prev = getAt<CoordinateXY>(i - 1);
if(prev.equals2D(coord)) {
return;
}
}
if(i < sz) {
const CoordinateXY& next = getAt<CoordinateXY>(i);
if(next.equals2D(coord)) {
return;
}
}
}
}
add(coord, i);
}
void add(double x, double y) {
CoordinateXY c(x, y);
add(c);
}
void add(const CoordinateSequence& cs);
void add(const CoordinateSequence& cs, bool allowRepeated);
void add(const CoordinateSequence& cl, bool allowRepeated, bool forwardDirection);
void add(const CoordinateSequence& cs, std::size_t from, std::size_t to);
void add(const CoordinateSequence& cs, std::size_t from, std::size_t to, bool allowRepeated);
template<typename T, typename... Args>
void add(T begin, T end, Args... args) {
for (auto it = begin; it != end; ++it) {
add(*it, args...);
}
}
template<typename T>
void add(std::size_t i, T from, T to) {
auto npts = static_cast<std::size_t>(std::distance(from, to));
make_space(i, npts);
for (auto it = from; it != to; ++it) {
setAt(*it, i);
i++;
}
}
/// @}
/// \defgroup util Utilities
/// @{
void clear() {
m_vect.clear();
}
void reserve(std::size_t capacity) {
m_vect.reserve(capacity * stride());
}
void resize(std::size_t capacity) {
m_vect.resize(capacity * stride());
}
void pop_back();
/// Get a string representation of CoordinateSequence
std::string toString() const;
/// Returns lower-left Coordinate in list
const CoordinateXY* minCoordinate() const;
/** \brief
* Returns either the given CoordinateSequence if its length
* is greater than the given amount, or an empty CoordinateSequence.
*/
static CoordinateSequence* atLeastNCoordinatesOrNothing(std::size_t n,
CoordinateSequence* c);
/// Return position of a Coordinate
//
/// or numeric_limits<std::size_t>::max() if not found
///
static std::size_t indexOf(const CoordinateXY* coordinate,
const CoordinateSequence* cl);
/**
* \brief
* Returns true if the two arrays are identical, both null,
* or pointwise equal in two dimensions
*/
static bool equals(const CoordinateSequence* cl1,
const CoordinateSequence* cl2);
/**
* \brief
* Returns true if the two sequences are identical (pointwise
* equal in all dimensions, with NaN == NaN).
*/
bool equalsIdentical(const CoordinateSequence& other) const;
/// Scroll given CoordinateSequence so to start with given Coordinate.
static void scroll(CoordinateSequence* cl, const CoordinateXY* firstCoordinate);
/** \brief
* Determines which orientation of the {@link Coordinate} array
* is (overall) increasing.
*
* In other words, determines which end of the array is "smaller"
* (using the standard ordering on {@link Coordinate}).
* Returns an integer indicating the increasing direction.
* If the sequence is a palindrome, it is defined to be
* oriented in a positive direction.
*
* @param pts the array of Coordinates to test
* @return <code>1</code> if the array is smaller at the start
* or is a palindrome,
* <code>-1</code> if smaller at the end
*
* NOTE: this method is found in CoordinateArrays class for JTS
*/
static int increasingDirection(const CoordinateSequence& pts);
/// Reverse Coordinate order in given CoordinateSequence
void reverse();
void sort();
/**
* Expands the given Envelope to include the coordinates in the
* sequence.
* @param env the envelope to expand
*/
void expandEnvelope(Envelope& env) const;
void closeRing(bool allowRepeated = false);
/// @}
/// \defgroup iterate Iteration
/// @{
template<typename Filter>
void apply_rw(const Filter* filter) {
switch(getCoordinateType()) {
case CoordinateType::XY:
for (auto& c : items<CoordinateXY>()) {
if (filter->isDone()) break;
filter->filter_rw(&c);
}
break;
case CoordinateType::XYZ:
for (auto& c : items<Coordinate>()) {
if (filter->isDone()) break;
filter->filter_rw(&c);
}
break;
case CoordinateType::XYM:
for (auto& c : items<CoordinateXYM>()) {
if (filter->isDone()) break;
filter->filter_rw(&c);
}
break;
case CoordinateType::XYZM:
for (auto& c : items<CoordinateXYZM>()) {
if (filter->isDone()) break;
filter->filter_rw(&c);
}
break;
}
m_hasdim = m_hasz = false; // re-check (see http://trac.osgeo.org/geos/ticket/435)
}
template<typename Filter>
void apply_ro(Filter* filter) const {
switch(getCoordinateType()) {
case CoordinateType::XY:
for (const auto& c : items<CoordinateXY>()) {
if (filter->isDone()) break;
filter->filter_ro(&c);
}
break;
case CoordinateType::XYZ:
for (const auto& c : items<Coordinate>()) {
if (filter->isDone()) break;
filter->filter_ro(&c);
}
break;
case CoordinateType::XYM:
for (const auto& c : items<CoordinateXYM>()) {
if (filter->isDone()) break;
filter->filter_ro(&c);
}
break;
case CoordinateType::XYZM:
for (const auto& c : items<CoordinateXYZM>()) {
if (filter->isDone()) break;
filter->filter_ro(&c);
}
break;
}
}
template<typename F>
void forEach(F&& fun) const {
switch(getCoordinateType()) {
case CoordinateType::XY: for (const auto& c : items<CoordinateXY>()) { fun(c); } break;
case CoordinateType::XYZ: for (const auto& c : items<Coordinate>()) { fun(c); } break;
case CoordinateType::XYM: for (const auto& c : items<CoordinateXYM>()) { fun(c); } break;
case CoordinateType::XYZM: for (const auto& c : items<CoordinateXYZM>()) { fun(c); } break;
}
}
template<typename T, typename F>
void forEach(F&& fun) const
{
for (std::size_t i = 0; i < size(); i++) {
fun(getAt<T>(i));
}
}
template<typename T, typename F>
void forEach(std::size_t from, std::size_t to, F&& fun) const
{
for (std::size_t i = from; i <= to; i++) {
fun(getAt<T>(i));
}
}
template<typename T>
class Coordinates {
public:
using SequenceType = typename std::conditional<std::is_const<T>::value, const CoordinateSequence, CoordinateSequence>::type;
explicit Coordinates(SequenceType* seq) : m_seq(seq) {}
CoordinateSequenceIterator<SequenceType, T> begin() {
return {m_seq};
}
CoordinateSequenceIterator<SequenceType, T> end() {
return {m_seq, m_seq->getSize()};
}
CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
begin() const {
return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq};
}
CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
end() const {
return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq, m_seq->getSize()};
}
CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
cbegin() const {
return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq};
}
CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
cend() const {
return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq, m_seq->getSize()};
}
private:
SequenceType* m_seq;
};
template<typename T>
Coordinates<typename std::add_const<T>::type> items() const {
return Coordinates<typename std::add_const<T>::type>(this);
}
template<typename T>
Coordinates<T> items() {
return Coordinates<T>(this);
}
/// @}
double* data() {
return m_vect.data();
}
const double* data() const {
return m_vect.data();
}
private:
std::vector<double> m_vect; // Vector to store values
uint8_t m_stride; // Stride of stored values, corresponding to underlying type
mutable bool m_hasdim; // Has the dimension of this sequence been determined? Or was it created with no
// explicit dimensionality, and we're waiting for getDimension() to be called
// after some coordinates have been added?
mutable bool m_hasz;
bool m_hasm;
void initialize();
template<typename T1, typename T2>
void setAtImpl(const T2& c, std::size_t pos) {
auto& orig = getAt<T1>(pos);
orig = c;
}
void make_space(std::size_t pos, std::size_t n) {
m_vect.insert(std::next(m_vect.begin(), static_cast<std::ptrdiff_t>(pos * stride())),
m_stride * n,
DoubleNotANumber);
}
std::uint8_t stride() const {
return m_stride;
}
};
GEOS_DLL std::ostream& operator<< (std::ostream& os, const CoordinateSequence& cs);
GEOS_DLL bool operator== (const CoordinateSequence& s1, const CoordinateSequence& s2);
GEOS_DLL bool operator!= (const CoordinateSequence& s1, const CoordinateSequence& s2);
} // namespace geos::geom
} // namespace geos