GEOS 3.16.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#include <type_traits> // for add_const
29
30// Forward declarations
31namespace geos {
32namespace geom {
33class Envelope;
34class CoordinateFilter;
35}
36}
37
38namespace geos {
39namespace geom { // geos::geom
40
57class GEOS_DLL CoordinateSequence {
58
59public:
60
62 enum { X, Y, Z, M };
63
64 using iterator = CoordinateSequenceIterator<CoordinateSequence, Coordinate>;
65 using const_iterator = CoordinateSequenceIterator<const CoordinateSequence, const Coordinate>;
66
67 typedef std::unique_ptr<CoordinateSequence> Ptr;
68
71
76
84 CoordinateSequence(std::size_t size, std::size_t dim = 0);
85
97 CoordinateSequence(std::size_t size, bool hasz, bool hasm, bool initialize = true);
98
104 CoordinateSequence(const std::initializer_list<Coordinate>&);
105
110 CoordinateSequence(const std::initializer_list<CoordinateXY>&);
111
117 CoordinateSequence(const std::initializer_list<CoordinateXYM>&);
118
122 CoordinateSequence(const std::initializer_list<CoordinateXYZM>&);
123
129 static CoordinateSequence XY(std::size_t size) {
130 return CoordinateSequence(size, false, false);
131 }
132
138 static CoordinateSequence XYZ(std::size_t size) {
139 return CoordinateSequence(size, true, false);
140 }
141
147 static CoordinateSequence XYZM(std::size_t size) {
148 return CoordinateSequence(size, true, true);
149 }
150
156 static CoordinateSequence XYM(std::size_t size) {
157 return CoordinateSequence(size, false, true);
158 }
159
163 std::unique_ptr<CoordinateSequence> clone() const;
164
168
175
179 std::size_t getSize() const {
180 return size();
181 }
182
186 size_t size() const
187 {
188 assert(stride() == 2 || stride() == 3 || stride() == 4);
189 switch(stride()) {
190 case 2: return m_vect.size() / 2;
191 case 4: return m_vect.size() / 4;
192 default : return m_vect.size() / 3;
193 }
194 }
195
197 bool isEmpty() const {
198 return m_vect.empty();
199 }
200
207 bool isRing() const;
208
215 std::size_t getDimension() const;
216
217 bool hasZ() const {
218 return m_hasdim ? m_hasz : (m_vect.empty() || !std::isnan(m_vect[2]));
219 }
220
221 bool hasM() const {
222 return m_hasm;
223 }
224
230 void setZM(bool hasZ, bool hasM);
231
233 bool hasRepeatedPoints() const;
234
237
241 CoordinateType getCoordinateType() const {
242 switch(stride()) {
243 case 4: return CoordinateType::XYZM;
244 case 2: return CoordinateType::XY;
245 default: return hasM() ? CoordinateType::XYM : CoordinateType::XYZ;
246 }
247 }
248
252
256 template<typename T=Coordinate>
257 const T& getAt(std::size_t i) const {
258 static_assert(std::is_base_of<CoordinateXY, T>::value, "Must be a Coordinate class");
259 assert(sizeof(T) <= sizeof(double) * stride());
260 assert(i*stride() < m_vect.size());
261 const T* orig = reinterpret_cast<const T*>(&m_vect[i*stride()]);
262 return *orig;
263 }
264
268 template<typename T=Coordinate>
269 T& getAt(std::size_t i) {
270 static_assert(std::is_base_of<CoordinateXY, T>::value, "Must be a Coordinate class");
271 assert(sizeof(T) <= sizeof(double) * stride());
272 assert(i*stride() < m_vect.size());
273 T* orig = reinterpret_cast<T*>(&m_vect[i*stride()]);
274 return *orig;
275 }
276
280 template<typename T>
281 void getAt(std::size_t i, T& c) const {
282 switch(getCoordinateType()) {
283 case CoordinateType::XY: c = getAt<CoordinateXY>(i); break;
284 case CoordinateType::XYZ: c = getAt<Coordinate>(i); break;
285 case CoordinateType::XYZM: c = getAt<CoordinateXYZM>(i); break;
286 case CoordinateType::XYM: c = getAt<CoordinateXYM>(i); break;
287 default: getAt<Coordinate>(i);
288 }
289 }
290
291 void getAt(std::size_t i, CoordinateXY& c) const {
292 c = getAt<CoordinateXY>(i);
293 }
294
295 // TODO: change to return CoordinateXY
299 const Coordinate& operator[](std::size_t i) const
300 {
301 return getAt(i);
302 }
303
304 // TODO: change to return CoordinateXY
309 operator[](std::size_t i)
310 {
311 return getAt(i);
312 }
313
324 double getOrdinate(std::size_t index, std::size_t ordinateIndex) const;
325
332 double getX(std::size_t index) const
333 {
334 return m_vect[index * stride()];
335 }
336
343 double getY(std::size_t index) const
344 {
345 return m_vect[index * stride() + 1];
346 }
347
355 double getZ(std::size_t index) const
356 {
357 return getOrdinate(index, Z);
358 }
359
367 double getM(std::size_t index) const
368 {
369 return getOrdinate(index, M);
370 }
371
378 void setX(std::size_t index, double x)
379 {
380 m_vect[index * stride()] = x;
381 }
382
389 void setY(std::size_t index, double y)
390 {
391 m_vect[index * stride() + 1] = y;
392 }
393
402 void setZ(std::size_t index, double z)
403 {
404 if (hasZ())
405 setOrdinate(index, Z, z);
406 }
407
416 void setM(std::size_t index, double m)
417 {
418 if (hasM())
419 setOrdinate(index, M, m);
420 }
421
423 template<typename T=Coordinate>
424 const T& back() const
425 {
426 return getAt<T>(size() - 1);
427 }
428
430 template<typename T=Coordinate>
431 T& back()
432 {
433 return getAt<T>(size() - 1);
434 }
435
437 template<typename T=Coordinate>
438 const T& front() const
439 {
440 return *(reinterpret_cast<const T*>(m_vect.data()));
441 }
442
444 template<typename T=Coordinate>
445 T& front()
446 {
447 return *(reinterpret_cast<T*>(m_vect.data()));
448 }
449
451 void toVector(std::vector<Coordinate>& coords) const;
452
453 void toVector(std::vector<CoordinateXY>& coords) const;
454
455
459
461 template<typename T>
462 void setAt(const T& c, std::size_t pos) {
463 switch(getCoordinateType()) {
464 case CoordinateType::XY: setAtImpl<CoordinateXY>(c, pos); break;
465 case CoordinateType::XYZ: setAtImpl<Coordinate>(c, pos); break;
466 case CoordinateType::XYZM: setAtImpl<CoordinateXYZM>(c, pos); break;
467 case CoordinateType::XYM: setAtImpl<CoordinateXYM>(c, pos); break;
468 default: setAtImpl<Coordinate>(c, pos);
469 }
470 }
471
480 void setOrdinate(std::size_t index, std::size_t ordinateIndex, double value);
481
483 void setPoints(const std::vector<Coordinate>& v);
484
486 void setPoints(const std::vector<CoordinateXY>& v);
487
491
496 template<typename T=Coordinate>
497 void add(const T& c) {
498 add(c, size());
499 }
500
507 template<typename T>
508 void add(const T& c, bool allowRepeated)
509 {
510 if(!allowRepeated && !isEmpty()) {
511 const CoordinateXY& last = back<CoordinateXY>();
512 if(last.equals2D(c)) {
513 return;
514 }
515 }
516
517 add(c);
518 }
519
528 template<typename T>
529 void add(const T& c, std::size_t pos)
530 {
531 static_assert(std::is_base_of<CoordinateXY, T>::value, "Must be a Coordinate class");
532
533 // c may be a reference inside m_vect, so we make sure it will not
534 // grow before adding it
535 if (m_vect.size() + stride() <= m_vect.capacity()) {
536 make_space(pos, 1);
537 setAt(c, static_cast<std::size_t>(pos));
538 } else {
539 T tmp{c};
540 make_space(pos, 1);
541 setAt(tmp, static_cast<std::size_t>(pos));
542 }
543 }
544
554 template<typename T>
555 void add(std::size_t i, const T& coord, bool allowRepeated)
556 {
557 // don't add duplicate coordinates
558 if(! allowRepeated) {
559 std::size_t sz = size();
560 if(sz > 0) {
561 if(i > 0) {
562 const CoordinateXY& prev = getAt<CoordinateXY>(i - 1);
563 if(prev.equals2D(coord)) {
564 return;
565 }
566 }
567 if(i < sz) {
568 const CoordinateXY& next = getAt<CoordinateXY>(i);
569 if(next.equals2D(coord)) {
570 return;
571 }
572 }
573 }
574 }
575
576 add(coord, i);
577 }
578
579 void add(double x, double y) {
580 CoordinateXY c(x, y);
581 add(c);
582 }
583
584 void add(const CoordinateSequence& cs);
585
586 void add(const CoordinateSequence& cs, bool allowRepeated);
587
588 void add(const CoordinateSequence& cl, bool allowRepeated, bool forwardDirection);
589
591 void add(const CoordinateSequence& cs, std::size_t from, std::size_t to);
592
594 void add(const CoordinateSequence& cs, std::size_t from, std::size_t to, bool allowRepeated);
595
596 template<typename T, typename... Args>
597 void add(T begin, T end, Args... args) {
598 for (auto it = begin; it != end; ++it) {
599 add(*it, args...);
600 }
601 }
602
603 template<typename T>
604 void add(std::size_t i, T from, T to) {
605 auto npts = static_cast<std::size_t>(std::distance(from, to));
606 make_space(i, npts);
607
608 for (auto it = from; it != to; ++it) {
609 setAt(*it, i);
610 i++;
611 }
612 }
613
617
618 void clear() {
619 m_vect.clear();
620 }
621
622 void reserve(std::size_t capacity) {
623 m_vect.reserve(capacity * stride());
624 }
625
626 void resize(std::size_t capacity) {
627 m_vect.resize(capacity * stride());
628 }
629
630 void pop_back();
631
633 std::string toString() const;
634
637 const CoordinateXY* minCoordinate() const;
638
641 std::size_t minCoordinateIndex() const;
642
649
651 //
654 static std::size_t indexOf(const CoordinateXY* coordinate,
655 const CoordinateSequence* cl);
656
662 static bool equals(const CoordinateSequence* cl1,
663 const CoordinateSequence* cl2);
664
670 bool equalsIdentical(const CoordinateSequence& other) const;
671
673 static void scroll(CoordinateSequence* cl, const CoordinateXY* firstCoordinate);
674 void scroll(const CoordinateXY* firstCoordinate);
675 void scroll(std::size_t firstCoordinateIndex);
676
695
697 void reverse();
698
699 void sort();
700
702 void swap(std::size_t i, std::size_t j);
703
709 void expandEnvelope(Envelope& env) const;
710
711 void closeRing(bool allowRepeated = false);
712
716
717 template<typename Filter>
718 void apply_rw(const Filter* filter) {
719 switch(getCoordinateType()) {
720 case CoordinateType::XY:
721 for (auto& c : items<CoordinateXY>()) {
722 if (filter->isDone()) break;
723 filter->filter_rw(&c);
724 }
725 break;
726 case CoordinateType::XYZ:
727 for (auto& c : items<Coordinate>()) {
728 if (filter->isDone()) break;
729 filter->filter_rw(&c);
730 }
731 break;
732 case CoordinateType::XYM:
733 for (auto& c : items<CoordinateXYM>()) {
734 if (filter->isDone()) break;
735 filter->filter_rw(&c);
736 }
737 break;
738 case CoordinateType::XYZM:
739 for (auto& c : items<CoordinateXYZM>()) {
740 if (filter->isDone()) break;
741 filter->filter_rw(&c);
742 }
743 break;
744 }
745 m_hasdim = m_hasz = false; // re-check (see http://trac.osgeo.org/geos/ticket/435)
746 }
747
748 template<typename Filter>
749 void apply_ro(Filter* filter) const {
750 switch(getCoordinateType()) {
751 case CoordinateType::XY:
752 for (const auto& c : items<CoordinateXY>()) {
753 if (filter->isDone()) break;
754 filter->filter_ro(&c);
755 }
756 break;
757 case CoordinateType::XYZ:
758 for (const auto& c : items<Coordinate>()) {
759 if (filter->isDone()) break;
760 filter->filter_ro(&c);
761 }
762 break;
763 case CoordinateType::XYM:
764 for (const auto& c : items<CoordinateXYM>()) {
765 if (filter->isDone()) break;
766 filter->filter_ro(&c);
767 }
768 break;
769 case CoordinateType::XYZM:
770 for (const auto& c : items<CoordinateXYZM>()) {
771 if (filter->isDone()) break;
772 filter->filter_ro(&c);
773 }
774 break;
775 }
776 }
777
778 template<typename F>
779 auto applyAt(size_t i, F&& fun) const {
780 switch(getCoordinateType()) {
781 case CoordinateType::XYZ: return fun(getAt<Coordinate>(i));
782 case CoordinateType::XYM: return fun(getAt<CoordinateXYM>(i));
783 case CoordinateType::XYZM: return fun(getAt<CoordinateXYZM>(i));
784 default: return fun(getAt<CoordinateXY>(i));
785 }
786 }
787
788 template<typename F>
789 void forEach(F&& fun) const {
790 switch(getCoordinateType()) {
791 case CoordinateType::XY: for (const auto& c : items<CoordinateXY>()) { fun(c); } break;
792 case CoordinateType::XYZ: for (const auto& c : items<Coordinate>()) { fun(c); } break;
793 case CoordinateType::XYM: for (const auto& c : items<CoordinateXYM>()) { fun(c); } break;
794 case CoordinateType::XYZM: for (const auto& c : items<CoordinateXYZM>()) { fun(c); } break;
795 }
796 }
797
798 template<typename T, typename F>
799 void forEach(F&& fun) const
800 {
801 for (std::size_t i = 0; i < size(); i++) {
802 fun(getAt<T>(i));
803 }
804 }
805
806 template<typename F>
807 void forEachSegment(F&& fun) const {
808 switch(getCoordinateType()) {
809 case CoordinateType::XY: forEachSegmentImpl<CoordinateXY, F>(std::move(fun)); break;
810 case CoordinateType::XYZ: forEachSegmentImpl<Coordinate, F>(std::move(fun)); break;
811 case CoordinateType::XYM: forEachSegmentImpl<CoordinateXYM, F>(std::move(fun)); break;
812 case CoordinateType::XYZM: forEachSegmentImpl<CoordinateXYZM, F>(std::move(fun)); break;
813 }
814 }
815
816 template<typename T, typename F>
817 void forEach(std::size_t from, std::size_t to, F&& fun) const
818 {
819 for (std::size_t i = from; i <= to; i++) {
820 fun(getAt<T>(i));
821 }
822 }
823
824 template<typename T>
825 class Coordinates {
826 public:
827 using SequenceType = typename std::conditional<std::is_const<T>::value, const CoordinateSequence, CoordinateSequence>::type;
828
829 explicit Coordinates(SequenceType* seq) : m_seq(seq) {}
830
831 CoordinateSequenceIterator<SequenceType, T> begin() {
832 return {m_seq};
833 }
834
835 CoordinateSequenceIterator<SequenceType, T> end() {
836 return {m_seq, m_seq->getSize()};
837 }
838
839 CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
840 begin() const {
841 return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq};
842 }
843
844 CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
845 end() const {
846 return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq, m_seq->getSize()};
847 }
848
849 CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
850 cbegin() const {
851 return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq};
852 }
853
854 CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>
855 cend() const {
856 return CoordinateSequenceIterator<const SequenceType, typename std::add_const<T>::type>{m_seq, m_seq->getSize()};
857 }
858
859 private:
860 SequenceType* m_seq;
861 };
862
863 template<typename T>
864 Coordinates<typename std::add_const<T>::type> items() const {
865 return Coordinates<typename std::add_const<T>::type>(this);
866 }
867
868 template<typename T>
869 Coordinates<T> items() {
870 return Coordinates<T>(this);
871 }
872
873
875
876 double* data() {
877 return m_vect.data();
878 }
879
880 const double* data() const {
881 return m_vect.data();
882 }
883
884private:
885 std::vector<double> m_vect; // Vector to store values
886
887 uint8_t m_stride; // Stride of stored values, corresponding to underlying type
888
889 mutable bool m_hasdim; // Has the dimension of this sequence been determined? Or was it created with no
890 // explicit dimensionality, and we're waiting for getDimension() to be called
891 // after some coordinates have been added?
892 mutable bool m_hasz;
893 bool m_hasm;
894
895 void initialize();
896
897 template<typename T1, typename T2>
898 void setAtImpl(const T2& c, std::size_t pos) {
899 auto& orig = getAt<T1>(pos);
900 orig = c;
901 }
902
903 template<typename CoordType, typename F>
904 void forEachSegmentImpl(F&& fun) const {
905 auto p0it = items<CoordType>().cbegin();
906 const auto end = items<CoordType>().end();
907
908 if (p0it == end) {
909 return;
910 }
911
912 auto p1it = std::next(p0it);
913
914 while (p1it != end) {
915 fun(*p0it, *p1it);
916 ++p0it;
917 ++p1it;
918 }
919 }
920
921 void make_space(std::size_t pos, std::size_t n) {
922 m_vect.insert(std::next(m_vect.begin(), static_cast<std::ptrdiff_t>(pos * stride())),
923 m_stride * n,
924 DoubleNotANumber);
925 }
926
927 std::uint8_t stride() const {
928 return m_stride;
929 }
930
931};
932
933GEOS_DLL std::ostream& operator<< (std::ostream& os, const CoordinateSequence& cs);
934
935GEOS_DLL bool operator== (const CoordinateSequence& s1, const CoordinateSequence& s2);
936
937GEOS_DLL bool operator!= (const CoordinateSequence& s1, const CoordinateSequence& s2);
938
939} // namespace geos::geom
940} // namespace geos
941
The internal representation of a list of coordinates inside a Geometry.
Definition CoordinateSequence.h:57
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:269
Coordinate & operator[](std::size_t i)
Definition CoordinateSequence.h:309
void setM(std::size_t index, double m)
Definition CoordinateSequence.h:416
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:438
void setX(std::size_t index, double x)
Definition CoordinateSequence.h:378
T & back()
Return last Coordinate in the sequence.
Definition CoordinateSequence.h:431
void getAt(std::size_t i, T &c) const
Write Coordinate at position i to given Coordinate.
Definition CoordinateSequence.h:281
const T & getAt(std::size_t i) const
Returns a read-only reference to Coordinate at position i.
Definition CoordinateSequence.h:257
T & front()
Return first Coordinate in the sequence.
Definition CoordinateSequence.h:445
double getZ(std::size_t index) const
Definition CoordinateSequence.h:355
double getX(std::size_t index) const
Definition CoordinateSequence.h:332
void setY(std::size_t index, double y)
Definition CoordinateSequence.h:389
double getY(std::size_t index) const
Definition CoordinateSequence.h:343
void setZ(std::size_t index, double z)
Definition CoordinateSequence.h:402
const Coordinate & operator[](std::size_t i) const
Definition CoordinateSequence.h:299
const T & back() const
Return last Coordinate in the sequence.
Definition CoordinateSequence.h:424
double getM(std::size_t index) const
Definition CoordinateSequence.h:367
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:508
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:555
void add(const T &c)
Definition CoordinateSequence.h:497
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:529
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:156
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:147
static CoordinateSequence XYZ(std::size_t size)
Definition CoordinateSequence.h:138
static CoordinateSequence XY(std::size_t size)
Definition CoordinateSequence.h:129
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:462
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:179
bool hasRepeatedOrInvalidPoints() const
Returns true if contains any NaN/Inf coordinates.
size_t size() const
Returns the number of Coordinates.
Definition CoordinateSequence.h:186
bool hasRepeatedPoints() const
Returns true if contains any two consecutive points.
void setZM(bool hasZ, bool hasM)
CoordinateType getCoordinateType() const
Definition CoordinateSequence.h:241
bool isEmpty() const
Returns true if list contains no coordinates.
Definition CoordinateSequence.h:197
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