GEOS 3.15.0dev
CoordinateSequence.h
1/**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2022 ISciences LLC
7 * Copyright (C) 2006 Refractions Research Inc.
8 *
9 * This is free software; you can redistribute and/or modify it under
10 * the terms of the GNU Lesser General Public Licence as published
11 * by the Free Software Foundation.
12 * See the COPYING file for more information.
13 *
14 **********************************************************************/
15
16#pragma once
17
18#include <geos/export.h>
19
20#include <geos/geom/Coordinate.h> // for applyCoordinateFilter
21#include <geos/geom/CoordinateSequenceIterator.h>
22
23#include <cassert>
24#include <vector>
25#include <iostream>
26#include <iosfwd> // ostream
27#include <memory> // for unique_ptr typedef
28
29// Forward declarations
30namespace geos {
31namespace geom {
32class Envelope;
33class CoordinateFilter;
34}
35}
36
37namespace geos {
38namespace geom { // geos::geom
39
56class GEOS_DLL CoordinateSequence {
57
58public:
59
61 enum { X, Y, Z, M };
62
63 using iterator = CoordinateSequenceIterator<CoordinateSequence, Coordinate>;
64 using const_iterator = CoordinateSequenceIterator<const CoordinateSequence, const Coordinate>;
65
66 typedef std::unique_ptr<CoordinateSequence> Ptr;
67
70
75
83 CoordinateSequence(std::size_t size, std::size_t dim = 0);
84
96 CoordinateSequence(std::size_t size, bool hasz, bool hasm, bool initialize = true);
97
103 CoordinateSequence(const std::initializer_list<Coordinate>&);
104
109 CoordinateSequence(const std::initializer_list<CoordinateXY>&);
110
116 CoordinateSequence(const std::initializer_list<CoordinateXYM>&);
117
121 CoordinateSequence(const std::initializer_list<CoordinateXYZM>&);
122
128 static CoordinateSequence XY(std::size_t size) {
129 return CoordinateSequence(size, false, false);
130 }
131
137 static CoordinateSequence XYZ(std::size_t size) {
138 return CoordinateSequence(size, true, false);
139 }
140
146 static CoordinateSequence XYZM(std::size_t size) {
147 return CoordinateSequence(size, true, true);
148 }
149
155 static CoordinateSequence XYM(std::size_t size) {
156 return CoordinateSequence(size, false, true);
157 }
158
162 std::unique_ptr<CoordinateSequence> clone() const;
163
167
174
178 std::size_t getSize() const {
179 return size();
180 }
181
185 size_t size() const
186 {
187 assert(stride() == 2 || stride() == 3 || stride() == 4);
188 switch(stride()) {
189 case 2: return m_vect.size() / 2;
190 case 4: return m_vect.size() / 4;
191 default : return m_vect.size() / 3;
192 }
193 }
194
196 bool isEmpty() const {
197 return m_vect.empty();
198 }
199
206 bool isRing() const;
207
214 std::size_t getDimension() const;
215
216 bool hasZ() const {
217 return m_hasdim ? m_hasz : (m_vect.empty() || !std::isnan(m_vect[2]));
218 }
219
220 bool hasM() const {
221 return m_hasm;
222 }
223
225 bool hasRepeatedPoints() const;
226
229
233 CoordinateType getCoordinateType() const {
234 switch(stride()) {
235 case 4: return CoordinateType::XYZM;
236 case 2: return CoordinateType::XY;
237 default: return hasM() ? CoordinateType::XYM : CoordinateType::XYZ;
238 }
239 }
240
244
248 template<typename T=Coordinate>
249 const T& getAt(std::size_t i) const {
250 static_assert(std::is_base_of<CoordinateXY, T>::value, "Must be a Coordinate class");
251 assert(sizeof(T) <= sizeof(double) * stride());
252 assert(i*stride() < m_vect.size());
253 const T* orig = reinterpret_cast<const T*>(&m_vect[i*stride()]);
254 return *orig;
255 }
256
260 template<typename T=Coordinate>
261 T& getAt(std::size_t i) {
262 static_assert(std::is_base_of<CoordinateXY, T>::value, "Must be a Coordinate class");
263 assert(sizeof(T) <= sizeof(double) * stride());
264 assert(i*stride() < m_vect.size());
265 T* orig = reinterpret_cast<T*>(&m_vect[i*stride()]);
266 return *orig;
267 }
268
272 template<typename T>
273 void getAt(std::size_t i, T& c) const {
274 switch(getCoordinateType()) {
275 case CoordinateType::XY: c = getAt<CoordinateXY>(i); break;
276 case CoordinateType::XYZ: c = getAt<Coordinate>(i); break;
277 case CoordinateType::XYZM: c = getAt<CoordinateXYZM>(i); break;
278 case CoordinateType::XYM: c = getAt<CoordinateXYM>(i); break;
279 default: getAt<Coordinate>(i);
280 }
281 }
282
283 void getAt(std::size_t i, CoordinateXY& c) const {
284 c = getAt<CoordinateXY>(i);
285 }
286
287 // TODO: change to return CoordinateXY
291 const Coordinate& operator[](std::size_t i) const
292 {
293 return getAt(i);
294 }
295
296 // TODO: change to return CoordinateXY
301 operator[](std::size_t i)
302 {
303 return getAt(i);
304 }
305
316 double getOrdinate(std::size_t index, std::size_t ordinateIndex) const;
317
324 double getX(std::size_t index) const
325 {
326 return m_vect[index * stride()];
327 }
328
335 double getY(std::size_t index) const
336 {
337 return m_vect[index * stride() + 1];
338 }
339
347 double getZ(std::size_t index) const
348 {
349 return getOrdinate(index, Z);
350 }
351
359 double getM(std::size_t index) const
360 {
361 return getOrdinate(index, M);
362 }
363
370 void setX(std::size_t index, double x)
371 {
372 m_vect[index * stride()] = x;
373 }
374
381 void setY(std::size_t index, double y)
382 {
383 m_vect[index * stride() + 1] = y;
384 }
385
394 void setZ(std::size_t index, double z)
395 {
396 if (hasZ())
397 setOrdinate(index, Z, z);
398 }
399
408 void setM(std::size_t index, double m)
409 {
410 if (hasM())
411 setOrdinate(index, M, m);
412 }
413
415 template<typename T=Coordinate>
416 const T& back() const
417 {
418 return getAt<T>(size() - 1);
419 }
420
422 template<typename T=Coordinate>
423 T& back()
424 {
425 return getAt<T>(size() - 1);
426 }
427
429 template<typename T=Coordinate>
430 const T& front() const
431 {
432 return *(reinterpret_cast<const T*>(m_vect.data()));
433 }
434
436 template<typename T=Coordinate>
437 T& front()
438 {
439 return *(reinterpret_cast<T*>(m_vect.data()));
440 }
441
443 void toVector(std::vector<Coordinate>& coords) const;
444
445 void toVector(std::vector<CoordinateXY>& coords) const;
446
447
451
453 template<typename T>
454 void setAt(const T& c, std::size_t pos) {
455 switch(getCoordinateType()) {
456 case CoordinateType::XY: setAtImpl<CoordinateXY>(c, pos); break;
457 case CoordinateType::XYZ: setAtImpl<Coordinate>(c, pos); break;
458 case CoordinateType::XYZM: setAtImpl<CoordinateXYZM>(c, pos); break;
459 case CoordinateType::XYM: setAtImpl<CoordinateXYM>(c, pos); break;
460 default: setAtImpl<Coordinate>(c, pos);
461 }
462 }
463
472 void setOrdinate(std::size_t index, std::size_t ordinateIndex, double value);
473
475 void setPoints(const std::vector<Coordinate>& v);
476
478 void setPoints(const std::vector<CoordinateXY>& v);
479
483
488 template<typename T=Coordinate>
489 void add(const T& c) {
490 add(c, size());
491 }
492
499 template<typename T>
500 void add(const T& c, bool allowRepeated)
501 {
502 if(!allowRepeated && !isEmpty()) {
503 const CoordinateXY& last = back<CoordinateXY>();
504 if(last.equals2D(c)) {
505 return;
506 }
507 }
508
509 add(c);
510 }
511
520 template<typename T>
521 void add(const T& c, std::size_t pos)
522 {
523 static_assert(std::is_base_of<CoordinateXY, T>::value, "Must be a Coordinate class");
524
525 // c may be a reference inside m_vect, so we make sure it will not
526 // grow before adding it
527 if (m_vect.size() + stride() <= m_vect.capacity()) {
528 make_space(pos, 1);
529 setAt(c, static_cast<std::size_t>(pos));
530 } else {
531 T tmp{c};
532 make_space(pos, 1);
533 setAt(tmp, static_cast<std::size_t>(pos));
534 }
535 }
536
546 template<typename T>
547 void add(std::size_t i, const T& coord, bool allowRepeated)
548 {
549 // don't add duplicate coordinates
550 if(! allowRepeated) {
551 std::size_t sz = size();
552 if(sz > 0) {
553 if(i > 0) {
554 const CoordinateXY& prev = getAt<CoordinateXY>(i - 1);
555 if(prev.equals2D(coord)) {
556 return;
557 }
558 }
559 if(i < sz) {
560 const CoordinateXY& next = getAt<CoordinateXY>(i);
561 if(next.equals2D(coord)) {
562 return;
563 }
564 }
565 }
566 }
567
568 add(coord, i);
569 }
570
571 void add(double x, double y) {
572 CoordinateXY c(x, y);
573 add(c);
574 }
575
576 void add(const CoordinateSequence& cs);
577
578 void add(const CoordinateSequence& cs, bool allowRepeated);
579
580 void add(const CoordinateSequence& cl, bool allowRepeated, bool forwardDirection);
581
583 void add(const CoordinateSequence& cs, std::size_t from, std::size_t to);
584
586 void add(const CoordinateSequence& cs, std::size_t from, std::size_t to, bool allowRepeated);
587
588 template<typename T, typename... Args>
589 void add(T begin, T end, Args... args) {
590 for (auto it = begin; it != end; ++it) {
591 add(*it, args...);
592 }
593 }
594
595 template<typename T>
596 void add(std::size_t i, T from, T to) {
597 auto npts = static_cast<std::size_t>(std::distance(from, to));
598 make_space(i, npts);
599
600 for (auto it = from; it != to; ++it) {
601 setAt(*it, i);
602 i++;
603 }
604 }
605
609
610 void clear() {
611 m_vect.clear();
612 }
613
614 void reserve(std::size_t capacity) {
615 m_vect.reserve(capacity * stride());
616 }
617
618 void resize(std::size_t capacity) {
619 m_vect.resize(capacity * stride());
620 }
621
622 void pop_back();
623
625 std::string toString() const;
626
629 const CoordinateXY* minCoordinate() const;
630
633 std::size_t minCoordinateIndex() const;
634
641
643 //
646 static std::size_t indexOf(const CoordinateXY* coordinate,
647 const CoordinateSequence* cl);
648
654 static bool equals(const CoordinateSequence* cl1,
655 const CoordinateSequence* cl2);
656
662 bool equalsIdentical(const CoordinateSequence& other) const;
663
665 static void scroll(CoordinateSequence* cl, const CoordinateXY* firstCoordinate);
666 void scroll(const CoordinateXY* firstCoordinate);
667 void scroll(std::size_t firstCoordinateIndex);
668
687
689 void reverse();
690
691 void sort();
692
694 void swap(std::size_t i, std::size_t j);
695
701 void expandEnvelope(Envelope& env) const;
702
703 void closeRing(bool allowRepeated = false);
704
708
709 template<typename Filter>
710 void apply_rw(const Filter* filter) {
711 switch(getCoordinateType()) {
712 case CoordinateType::XY:
713 for (auto& c : items<CoordinateXY>()) {
714 if (filter->isDone()) break;
715 filter->filter_rw(&c);
716 }
717 break;
718 case CoordinateType::XYZ:
719 for (auto& c : items<Coordinate>()) {
720 if (filter->isDone()) break;
721 filter->filter_rw(&c);
722 }
723 break;
724 case CoordinateType::XYM:
725 for (auto& c : items<CoordinateXYM>()) {
726 if (filter->isDone()) break;
727 filter->filter_rw(&c);
728 }
729 break;
730 case CoordinateType::XYZM:
731 for (auto& c : items<CoordinateXYZM>()) {
732 if (filter->isDone()) break;
733 filter->filter_rw(&c);
734 }
735 break;
736 }
737 m_hasdim = m_hasz = false; // re-check (see http://trac.osgeo.org/geos/ticket/435)
738 }
739
740 template<typename Filter>
741 void apply_ro(Filter* filter) const {
742 switch(getCoordinateType()) {
743 case CoordinateType::XY:
744 for (const auto& c : items<CoordinateXY>()) {
745 if (filter->isDone()) break;
746 filter->filter_ro(&c);
747 }
748 break;
749 case CoordinateType::XYZ:
750 for (const auto& c : items<Coordinate>()) {
751 if (filter->isDone()) break;
752 filter->filter_ro(&c);
753 }
754 break;
755 case CoordinateType::XYM:
756 for (const auto& c : items<CoordinateXYM>()) {
757 if (filter->isDone()) break;
758 filter->filter_ro(&c);
759 }
760 break;
761 case CoordinateType::XYZM:
762 for (const auto& c : items<CoordinateXYZM>()) {
763 if (filter->isDone()) break;
764 filter->filter_ro(&c);
765 }
766 break;
767 }
768 }
769
770 template<typename F>
771 auto applyAt(size_t i, F&& fun) const {
772 switch(getCoordinateType()) {
773 case CoordinateType::XYZ: return fun(getAt<Coordinate>(i));
774 case CoordinateType::XYM: return fun(getAt<CoordinateXYM>(i));
775 case CoordinateType::XYZM: return fun(getAt<CoordinateXYZM>(i));
776 default: return fun(getAt<CoordinateXY>(i));
777 }
778 }
779
780 template<typename F>
781 void forEach(F&& fun) const {
782 switch(getCoordinateType()) {
783 case CoordinateType::XY: for (const auto& c : items<CoordinateXY>()) { fun(c); } break;
784 case CoordinateType::XYZ: for (const auto& c : items<Coordinate>()) { fun(c); } break;
785 case CoordinateType::XYM: for (const auto& c : items<CoordinateXYM>()) { fun(c); } break;
786 case CoordinateType::XYZM: for (const auto& c : items<CoordinateXYZM>()) { fun(c); } break;
787 }
788 }
789
790 template<typename T, typename F>
791 void forEach(F&& fun) const
792 {
793 for (std::size_t i = 0; i < size(); i++) {
794 fun(getAt<T>(i));
795 }
796 }
797
798 template<typename F>
799 void forEachSegment(F&& fun) const {
800 switch(getCoordinateType()) {
801 case CoordinateType::XY: forEachSegmentImpl<CoordinateXY, F>(std::move(fun)); break;
802 case CoordinateType::XYZ: forEachSegmentImpl<Coordinate, F>(std::move(fun)); break;
803 case CoordinateType::XYM: forEachSegmentImpl<CoordinateXYM, F>(std::move(fun)); break;
804 case CoordinateType::XYZM: forEachSegmentImpl<CoordinateXYZM, F>(std::move(fun)); break;
805 }
806 }
807
808 template<typename T, typename F>
809 void forEach(std::size_t from, std::size_t to, F&& fun) const
810 {
811 for (std::size_t i = from; i <= to; i++) {
812 fun(getAt<T>(i));
813 }
814 }
815
816 template<typename T>
817 class Coordinates {
818 public:
819 using SequenceType = typename std::conditional<std::is_const<T>::value, const CoordinateSequence, CoordinateSequence>::type;
820
821 explicit Coordinates(SequenceType* seq) : m_seq(seq) {}
822
823 CoordinateSequenceIterator<SequenceType, T> begin() {
824 return {m_seq};
825 }
826
827 CoordinateSequenceIterator<SequenceType, T> end() {
828 return {m_seq, m_seq->getSize()};
829 }
830
831 CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
832 begin() const {
833 return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq};
834 }
835
836 CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
837 end() const {
838 return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq, m_seq->getSize()};
839 }
840
841 CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
842 cbegin() const {
843 return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq};
844 }
845
846 CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
847 cend() const {
848 return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq, m_seq->getSize()};
849 }
850
851 private:
852 SequenceType* m_seq;
853 };
854
855 template<typename T>
856 Coordinates<typename std::add_const<T>::type> items() const {
857 return Coordinates<typename std::add_const<T>::type>(this);
858 }
859
860 template<typename T>
861 Coordinates<T> items() {
862 return Coordinates<T>(this);
863 }
864
865
867
868 double* data() {
869 return m_vect.data();
870 }
871
872 const double* data() const {
873 return m_vect.data();
874 }
875
876private:
877 std::vector<double> m_vect; // Vector to store values
878
879 uint8_t m_stride; // Stride of stored values, corresponding to underlying type
880
881 mutable bool m_hasdim; // Has the dimension of this sequence been determined? Or was it created with no
882 // explicit dimensionality, and we're waiting for getDimension() to be called
883 // after some coordinates have been added?
884 mutable bool m_hasz;
885 bool m_hasm;
886
887 void initialize();
888
889 template<typename T1, typename T2>
890 void setAtImpl(const T2& c, std::size_t pos) {
891 auto& orig = getAt<T1>(pos);
892 orig = c;
893 }
894
895 template<typename CoordType, typename F>
896 void forEachSegmentImpl(F&& fun) const {
897 auto p0it = items<CoordType>().cbegin();
898 const auto end = items<CoordType>().end();
899
900 if (p0it == end) {
901 return;
902 }
903
904 auto p1it = std::next(p0it);
905
906 while (p1it != end) {
907 fun(*p0it, *p1it);
908 ++p0it;
909 ++p1it;
910 }
911 }
912
913 void make_space(std::size_t pos, std::size_t n) {
914 m_vect.insert(std::next(m_vect.begin(), static_cast<std::ptrdiff_t>(pos * stride())),
915 m_stride * n,
916 DoubleNotANumber);
917 }
918
919 std::uint8_t stride() const {
920 return m_stride;
921 }
922
923};
924
925GEOS_DLL std::ostream& operator<< (std::ostream& os, const CoordinateSequence& cs);
926
927GEOS_DLL bool operator== (const CoordinateSequence& s1, const CoordinateSequence& s2);
928
929GEOS_DLL bool operator!= (const CoordinateSequence& s1, const CoordinateSequence& s2);
930
931} // namespace geos::geom
932} // namespace geos
933
The internal representation of a list of coordinates inside a Geometry.
Definition CoordinateSequence.h:56
Coordinate is the lightweight class used to store coordinates.
Definition Coordinate.h:220
An Envelope defines a rectangulare region of the 2D coordinate plane.
Definition Envelope.h:59
T & getAt(std::size_t i)
Returns a reference to Coordinate at position i.
Definition CoordinateSequence.h:261
Coordinate & operator[](std::size_t i)
Definition CoordinateSequence.h:301
void setM(std::size_t index, double m)
Definition CoordinateSequence.h:408
void toVector(std::vector< Coordinate > &coords) const
Pushes all Coordinates of this sequence into the provided vector.
double getOrdinate(std::size_t index, std::size_t ordinateIndex) const
const T & front() const
Return first Coordinate in the sequence.
Definition CoordinateSequence.h:430
void setX(std::size_t index, double x)
Definition CoordinateSequence.h:370
T & back()
Return last Coordinate in the sequence.
Definition CoordinateSequence.h:423
void getAt(std::size_t i, T &c) const
Write Coordinate at position i to given Coordinate.
Definition CoordinateSequence.h:273
const T & getAt(std::size_t i) const
Returns a read-only reference to Coordinate at position i.
Definition CoordinateSequence.h:249
T & front()
Return first Coordinate in the sequence.
Definition CoordinateSequence.h:437
double getZ(std::size_t index) const
Definition CoordinateSequence.h:347
double getX(std::size_t index) const
Definition CoordinateSequence.h:324
void setY(std::size_t index, double y)
Definition CoordinateSequence.h:381
double getY(std::size_t index) const
Definition CoordinateSequence.h:335
void setZ(std::size_t index, double z)
Definition CoordinateSequence.h:394
const Coordinate & operator[](std::size_t i) const
Definition CoordinateSequence.h:291
const T & back() const
Return last Coordinate in the sequence.
Definition CoordinateSequence.h:416
double getM(std::size_t index) const
Definition CoordinateSequence.h:359
void add(const CoordinateSequence &cs, std::size_t from, std::size_t to)
Add the portion of a CoordinateSequence between the specified indices (inclusive)
void add(const T &c, bool allowRepeated)
Definition CoordinateSequence.h:500
void add(std::size_t i, const T &coord, bool allowRepeated)
Inserts the specified coordinate at the specified position in this list.
Definition CoordinateSequence.h:547
void add(const T &c)
Definition CoordinateSequence.h:489
void add(const T &c, std::size_t pos)
Inserts the specified coordinate at the specified position in this sequence. If multiple coordinates ...
Definition CoordinateSequence.h:521
void add(const CoordinateSequence &cs, std::size_t from, std::size_t to, bool allowRepeated)
Add the portion of a CoordinateSequence between the specified indices (inclusive)
CoordinateSequence(const std::initializer_list< CoordinateXYZM > &)
static CoordinateSequence XYM(std::size_t size)
Definition CoordinateSequence.h:155
CoordinateSequence(const std::initializer_list< CoordinateXY > &)
std::unique_ptr< CoordinateSequence > clone() const
Returns a heap-allocated deep copy of this CoordinateSequence.
CoordinateSequence(std::size_t size, bool hasz, bool hasm, bool initialize=true)
static CoordinateSequence XYZM(std::size_t size)
Definition CoordinateSequence.h:146
static CoordinateSequence XYZ(std::size_t size)
Definition CoordinateSequence.h:137
static CoordinateSequence XY(std::size_t size)
Definition CoordinateSequence.h:128
CoordinateSequence(std::size_t size, std::size_t dim=0)
CoordinateSequence(const std::initializer_list< CoordinateXYM > &)
CoordinateSequence(const std::initializer_list< Coordinate > &)
void setOrdinate(std::size_t index, std::size_t ordinateIndex, double value)
void setPoints(const std::vector< Coordinate > &v)
Substitute Coordinate list with a copy of the given vector.
void setAt(const T &c, std::size_t pos)
Copy Coordinate c to position pos.
Definition CoordinateSequence.h:454
void setPoints(const std::vector< CoordinateXY > &v)
Substitute Coordinate list with a copy of the given vector.
std::size_t getSize() const
Returns the number of Coordinates.
Definition CoordinateSequence.h:178
bool hasRepeatedOrInvalidPoints() const
Returns true if contains any NaN/Inf coordinates.
size_t size() const
Returns the number of Coordinates.
Definition CoordinateSequence.h:185
bool hasRepeatedPoints() const
Returns true if contains any two consecutive points.
CoordinateType getCoordinateType() const
Definition CoordinateSequence.h:233
bool isEmpty() const
Returns true if list contains no coordinates.
Definition CoordinateSequence.h:196
bool isRing() const
Tests whether an a CoordinateSequence forms a ring, by checking length and closure....
std::size_t getDimension() const
bool equalsIdentical(const CoordinateSequence &other) const
Returns true if the two sequences are identical (pointwise equal in all dimensions,...
void reverse()
Reverse Coordinate order in given CoordinateSequence.
static void scroll(CoordinateSequence *cl, const CoordinateXY *firstCoordinate)
Scroll given CoordinateSequence so to start with given Coordinate.
const CoordinateXY * minCoordinate() const
static int increasingDirection(const CoordinateSequence &pts)
Determines which orientation of the Coordinate array is (overall) increasing.
void expandEnvelope(Envelope &env) const
static CoordinateSequence * atLeastNCoordinatesOrNothing(std::size_t n, CoordinateSequence *c)
Returns either the given CoordinateSequence if its length is greater than the given amount,...
std::size_t minCoordinateIndex() const
std::string toString() const
Get a string representation of CoordinateSequence.
static std::size_t indexOf(const CoordinateXY *coordinate, const CoordinateSequence *cl)
Return position of a Coordinate.
static bool equals(const CoordinateSequence *cl1, const CoordinateSequence *cl2)
Returns true if the two arrays are identical, both null, or pointwise equal in two dimensions.
void swap(std::size_t i, std::size_t j)
Swap the coordinates at two indices.
Basic namespace for all GEOS functionalities.
Definition geos.h:38