/src/geos/src/geom/CoordinateSequence.cpp
Line | Count | Source |
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 | | * Copyright (C) 2001-2002 Vivid Solutions Inc. |
9 | | * |
10 | | * This is free software; you can redistribute and/or modify it under |
11 | | * the terms of the GNU Lesser General Public Licence as published |
12 | | * by the Free Software Foundation. |
13 | | * See the COPYING file for more information. |
14 | | * |
15 | | **********************************************************************/ |
16 | | |
17 | | #include <geos/constants.h> |
18 | | #include <geos/profiler.h> |
19 | | #include <geos/geom/CoordinateFilter.h> |
20 | | #include <geos/geom/CoordinateSequence.h> |
21 | | #include <geos/geom/Coordinate.h> |
22 | | #include <geos/geom/Envelope.h> |
23 | | #include <geos/util/IllegalArgumentException.h> |
24 | | #include <geos/util.h> |
25 | | |
26 | | #include <cstdio> |
27 | | #include <cstring> |
28 | | #include <algorithm> |
29 | | #include <vector> |
30 | | #include <cassert> |
31 | | #include <iterator> |
32 | | #include <sstream> |
33 | | |
34 | | namespace geos { |
35 | | namespace geom { // geos::geom |
36 | | |
37 | | #if PROFILE |
38 | | static Profiler* profiler = Profiler::instance(); |
39 | | #endif |
40 | | |
41 | | // If GEOS_COORDSEQ_PADZ is defined: |
42 | | // - XY sequences will be stored as XYZ |
43 | | // - XYM sequences will be stored as XYZM |
44 | | // This prevents incorrect results when an XYZ Coordinate is read from |
45 | | // a sequence storing XY or XYM. When GEOS is changed to check |
46 | | // coordinate types throughout the library, this can be undefined to |
47 | | // store coordinates efficiently. |
48 | | #define GEOS_COORDSEQ_PADZ |
49 | | |
50 | | CoordinateSequence::CoordinateSequence() : |
51 | 985k | CoordinateSequence(0, 0) {} |
52 | | |
53 | | CoordinateSequence::CoordinateSequence(std::size_t sz, bool hasz, bool hasm, bool init) : |
54 | | #ifdef GEOS_COORDSEQ_PADZ |
55 | 151M | m_vect(sz * (3u + hasm)), |
56 | 151M | m_stride(static_cast<std::uint8_t>(3u + hasm)), |
57 | | #else |
58 | | m_vect(sz * (2u + hasm + hasz)), |
59 | | m_stride(static_cast<std::uint8_t>(2u + hasm + hasz)), |
60 | | #endif |
61 | 151M | m_hasdim(true), |
62 | 151M | m_hasz(hasz), |
63 | 151M | m_hasm(hasm) |
64 | 151M | { |
65 | 151M | if (init) { |
66 | 146M | initialize(); |
67 | 146M | } |
68 | 151M | } |
69 | | |
70 | | CoordinateSequence::CoordinateSequence(std::size_t sz, std::size_t dim) : |
71 | | #ifdef GEOS_COORDSEQ_PADZ |
72 | 2.47M | m_vect(sz * std::max(static_cast<std::uint8_t>(dim), static_cast<std::uint8_t>(3))), |
73 | 2.47M | m_stride(std::max(static_cast<std::uint8_t>(dim), static_cast<std::uint8_t>(3))), |
74 | | #else |
75 | | m_vect(sz * static_cast<std::uint8_t>(dim)), |
76 | | m_stride(static_cast<std::uint8_t>(dim)), |
77 | | #endif |
78 | 2.47M | m_hasdim(dim > 0), |
79 | 2.47M | m_hasz(dim >= 3), |
80 | 2.47M | m_hasm(dim == 4) |
81 | 2.47M | { |
82 | 2.47M | if (dim == 1 || dim > 4) { |
83 | 0 | throw util::IllegalArgumentException("Declared dimension must be 2, 3, or 4"); |
84 | 0 | } |
85 | 2.47M | initialize(); |
86 | 2.47M | } |
87 | | |
88 | | CoordinateSequence::CoordinateSequence(const std::initializer_list<Coordinate>& list) : |
89 | 9.60M | m_stride(3), |
90 | 9.60M | m_hasdim(false), |
91 | 9.60M | m_hasz(false), |
92 | 9.60M | m_hasm(false) |
93 | 9.60M | { |
94 | 9.60M | reserve(list.size()); |
95 | 9.60M | add(list.begin(), list.end()); |
96 | 9.60M | } |
97 | | |
98 | | CoordinateSequence::CoordinateSequence(const std::initializer_list<CoordinateXY>& list) : |
99 | | #ifdef GEOS_COORDSEQ_PADZ |
100 | 7.26k | m_stride(3), |
101 | | #else |
102 | | m_stride(2), |
103 | | #endif |
104 | 7.26k | m_hasdim(true), |
105 | 7.26k | m_hasz(false), |
106 | 7.26k | m_hasm(false) |
107 | 7.26k | { |
108 | 7.26k | reserve(list.size()); |
109 | 7.26k | add(list.begin(), list.end()); |
110 | 7.26k | } |
111 | | |
112 | | CoordinateSequence::CoordinateSequence(const std::initializer_list<CoordinateXYM>& list) : |
113 | | #ifdef GEOS_COORDSEQ_PADZ |
114 | 0 | m_stride(4), |
115 | | #else |
116 | | m_stride(3), |
117 | | #endif |
118 | 0 | m_hasdim(true), |
119 | 0 | m_hasz(false), |
120 | 0 | m_hasm(true) |
121 | 0 | { |
122 | 0 | reserve(list.size()); |
123 | 0 | add(list.begin(), list.end()); |
124 | 0 | } |
125 | | |
126 | | CoordinateSequence::CoordinateSequence(const std::initializer_list<CoordinateXYZM>& list) : |
127 | 0 | m_stride(4), |
128 | 0 | m_hasdim(true), |
129 | 0 | m_hasz(true), |
130 | 0 | m_hasm(true) |
131 | 0 | { |
132 | 0 | reserve(list.size()); |
133 | 0 | add(list.begin(), list.end()); |
134 | 0 | } |
135 | | |
136 | | template<typename T> |
137 | | void fillVector(std::vector<double> & v) |
138 | 149M | { |
139 | 149M | const T c; |
140 | 149M | T* from = reinterpret_cast<T*>(v.data()); |
141 | 149M | T* to = reinterpret_cast<T*>(v.data() + v.size()); |
142 | 149M | std::fill(from, to, c); |
143 | 149M | } void geos::geom::fillVector<geos::geom::Coordinate>(std::__1::vector<double, std::__1::allocator<double> >&) Line | Count | Source | 138 | 129M | { | 139 | 129M | const T c; | 140 | 129M | T* from = reinterpret_cast<T*>(v.data()); | 141 | 129M | T* to = reinterpret_cast<T*>(v.data() + v.size()); | 142 | 129M | std::fill(from, to, c); | 143 | 129M | } |
void geos::geom::fillVector<geos::geom::CoordinateXYZM>(std::__1::vector<double, std::__1::allocator<double> >&) Line | Count | Source | 138 | 19.9M | { | 139 | 19.9M | const T c; | 140 | 19.9M | T* from = reinterpret_cast<T*>(v.data()); | 141 | 19.9M | T* to = reinterpret_cast<T*>(v.data() + v.size()); | 142 | 19.9M | std::fill(from, to, c); | 143 | 19.9M | } |
Unexecuted instantiation: void geos::geom::fillVector<geos::geom::CoordinateXY>(std::__1::vector<double, std::__1::allocator<double> >&) Unexecuted instantiation: void geos::geom::fillVector<geos::geom::CoordinateXYM>(std::__1::vector<double, std::__1::allocator<double> >&) |
144 | | |
145 | | void |
146 | | CoordinateSequence::initialize() |
147 | 149M | { |
148 | 149M | switch(getCoordinateType()) { |
149 | 129M | case CoordinateType::XYZ: fillVector<Coordinate>(m_vect); break; |
150 | 19.9M | case CoordinateType::XYZM: fillVector<CoordinateXYZM>(m_vect); break; |
151 | 0 | case CoordinateType::XY: fillVector<CoordinateXY>(m_vect); break; |
152 | 0 | case CoordinateType::XYM: fillVector<CoordinateXYM>(m_vect); break; |
153 | 149M | } |
154 | 149M | } |
155 | | |
156 | | void |
157 | | CoordinateSequence::add(const CoordinateSequence& cs, std::size_t from, std::size_t to) |
158 | 67.8M | { |
159 | 67.8M | if (cs.stride() == stride() && cs.hasM() == hasM()) { |
160 | 67.5M | m_vect.insert(m_vect.end(), |
161 | 67.5M | std::next(cs.m_vect.cbegin(), static_cast<std::ptrdiff_t>(from * stride())), |
162 | 67.5M | std::next(cs.m_vect.cbegin(), static_cast<std::ptrdiff_t>((to + 1u)*stride()))); |
163 | 67.5M | } else { |
164 | 283k | std::size_t pos = size(); |
165 | 283k | make_space(pos, to - from + 1); |
166 | | |
167 | 283k | switch(cs.getCoordinateType()) { |
168 | 0 | case CoordinateType::XY: cs.forEach<CoordinateXY>(from, to, [this, &pos](const CoordinateXY& c) { setAt(c, pos++); }); break; |
169 | 555k | case CoordinateType::XYZ: cs.forEach<Coordinate>(from, to, [this, &pos](const Coordinate& c) { setAt(c, pos++); }); break; |
170 | 204 | case CoordinateType::XYZM: cs.forEach<CoordinateXYZM>(from, to, [this, &pos](const CoordinateXYZM& c) { setAt(c, pos++); }); break; |
171 | 0 | case CoordinateType::XYM: cs.forEach<CoordinateXYM>(from, to, [this, &pos](const CoordinateXYM& c) { setAt(c, pos++); }); break; |
172 | 283k | } |
173 | 283k | } |
174 | 67.8M | } |
175 | | |
176 | | void |
177 | | CoordinateSequence::add(const CoordinateSequence& cs) |
178 | 177k | { |
179 | 177k | add(cs, 0, cs.size() - 1); |
180 | 177k | } |
181 | | |
182 | | void |
183 | | CoordinateSequence::add(const CoordinateSequence& cs, std::size_t from, std::size_t to, bool allowRepeated) |
184 | 46.1M | { |
185 | 46.1M | if (allowRepeated) { |
186 | 0 | add(cs, from, to); |
187 | 0 | return; |
188 | 0 | } |
189 | | |
190 | 46.1M | std::size_t first = from; |
191 | | |
192 | | // Check for case where first point(s) of `cs` duplicate last points of `this` |
193 | 46.1M | if (!isEmpty()) { |
194 | 11.1M | while(first <= to && cs.getAt<CoordinateXY>(first).equals2D(back<CoordinateXY>())) { |
195 | 5.42M | first++; |
196 | 5.42M | } |
197 | 5.72M | } |
198 | | |
199 | 46.1M | if (first > to) { |
200 | | // No unique points to add. |
201 | 898 | return; |
202 | 898 | } |
203 | | |
204 | 46.1M | std::size_t last = first + 1; |
205 | 46.1M | const CoordinateXY* last_unique = &cs.getAt<CoordinateXY>(first); |
206 | 120M | while(last <= to) { |
207 | 74.7M | const CoordinateXY* curr = &cs.getAt<CoordinateXY>(last); |
208 | 74.7M | if (curr->equals2D(*last_unique)) { |
209 | | // End of block |
210 | 769k | add(cs, first, last - 1); |
211 | 13.9M | do { |
212 | 13.9M | last++; |
213 | 13.9M | } while (last <= to && cs.getAt<CoordinateXY>(last).equals2D(*last_unique)); |
214 | | |
215 | 769k | if (last != (to + 1) ) { |
216 | 705k | first = last; |
217 | 705k | last_unique = &cs.getAt<CoordinateXY>(first); |
218 | 705k | } |
219 | 769k | last++; |
220 | 74.0M | } else { |
221 | 74.0M | last_unique = curr; |
222 | 74.0M | last++; |
223 | 74.0M | } |
224 | 74.7M | } |
225 | | |
226 | 46.1M | if (last == (to + 1)) { |
227 | 46.1M | add(cs, first, to); |
228 | 46.1M | } |
229 | 46.1M | } |
230 | | |
231 | | void |
232 | 39.0M | CoordinateSequence::add(const CoordinateSequence& cs, bool allowRepeated) { |
233 | 39.0M | if (!cs.isEmpty()) { |
234 | 39.0M | add(cs, 0, cs.size() - 1, allowRepeated); |
235 | 39.0M | } |
236 | 39.0M | } |
237 | | |
238 | | void |
239 | | CoordinateSequence::add(const CoordinateSequence& cl, bool allowRepeated, bool forwardDirection) |
240 | 831 | { |
241 | 831 | if (forwardDirection) { |
242 | 831 | add(cl, allowRepeated); |
243 | 831 | } else { |
244 | 0 | CoordinateSequence rev(cl); |
245 | 0 | rev.reverse(); |
246 | 0 | add(rev, allowRepeated); |
247 | 0 | return; |
248 | 0 | } |
249 | | |
250 | 831 | } |
251 | | |
252 | | /*public*/ |
253 | | |
254 | | std::unique_ptr<CoordinateSequence> |
255 | | CoordinateSequence::clone() const |
256 | 9.68M | { |
257 | 9.68M | return detail::make_unique<CoordinateSequence>(*this); |
258 | 9.68M | } |
259 | | |
260 | | void |
261 | | CoordinateSequence::closeRing(bool allowRepeated) |
262 | 62.5k | { |
263 | 62.5k | if(!isEmpty() && (allowRepeated || front<CoordinateXY>() != back<CoordinateXY>())) { |
264 | 43.5k | const std::size_t n = stride(); |
265 | 43.5k | const std::size_t old = m_vect.size(); |
266 | 43.5k | m_vect.resize(old + n); |
267 | 43.5k | std::copy_n(m_vect.data(), n, m_vect.data() + old); |
268 | 43.5k | } |
269 | 62.5k | } |
270 | | |
271 | | std::size_t |
272 | | CoordinateSequence::getDimension() const |
273 | 7.87M | { |
274 | 7.87M | if (m_hasdim) { |
275 | 5.83M | return static_cast<std::size_t>(2 + hasM() + hasZ()); |
276 | 5.83M | } |
277 | | |
278 | 2.04M | if (m_vect.empty()) { |
279 | 765 | return 3; |
280 | 765 | } |
281 | | |
282 | 2.04M | assert(stride() >= 3); |
283 | 2.04M | m_hasdim = true; |
284 | 2.04M | if (!std::isnan(getAt<Coordinate>(0).z)) { |
285 | 1.10M | m_hasz = true; |
286 | 1.10M | } |
287 | | |
288 | 2.04M | return getDimension(); |
289 | 2.04M | } |
290 | | |
291 | | |
292 | | double |
293 | | CoordinateSequence::getOrdinate(std::size_t index, std::size_t ordinateIndex) const |
294 | 7.68k | { |
295 | 7.68k | switch(ordinateIndex) { |
296 | 0 | case CoordinateSequence::X: |
297 | 0 | return getAt<CoordinateXY>(index).x; |
298 | 0 | case CoordinateSequence::Y: |
299 | 0 | return getAt<CoordinateXY>(index).y; |
300 | 3.93k | case CoordinateSequence::Z: |
301 | 3.93k | return hasZ() ? getAt<Coordinate>(index).z : DoubleNotANumber; |
302 | 3.74k | case CoordinateSequence::M: |
303 | 3.74k | return getCoordinateType() == CoordinateType::XYZM ? getAt<CoordinateXYZM>(index).m : |
304 | 3.74k | getCoordinateType() == CoordinateType::XYM ? getAt<CoordinateXYM>(index).m : |
305 | 0 | DoubleNotANumber; |
306 | 0 | default: |
307 | 0 | return DoubleNotANumber; |
308 | 7.68k | } |
309 | 7.68k | } |
310 | | |
311 | | bool |
312 | | CoordinateSequence::hasRepeatedPoints() const |
313 | 4.86M | { |
314 | | // Iterate over the array of doubles and check x/y values directly. |
315 | | // This is about 30% faster than retrieving/comparing CoordinateXY&. |
316 | 28.9M | for (std::size_t i = stride(); i < m_vect.size(); i += stride()) { |
317 | 24.1M | if (m_vect[i - stride()] == m_vect[i] && m_vect[i + 1 - stride()] == m_vect[i+1]) { |
318 | 53.8k | return true; |
319 | 53.8k | } |
320 | 24.1M | } |
321 | 4.81M | return false; |
322 | 4.86M | } |
323 | | |
324 | | bool |
325 | | CoordinateSequence::hasRepeatedOrInvalidPoints() const |
326 | 0 | { |
327 | | // Check first points |
328 | 0 | if (! (std::isfinite(m_vect[0]) && std::isfinite(m_vect[1]) )) { |
329 | 0 | return true; |
330 | 0 | } |
331 | | // Iterate over the array of doubles and check x/y values directly. |
332 | | // This is about 30% faster than retrieving/comparing CoordinateXY&. |
333 | 0 | for (std::size_t i = stride(); i < m_vect.size(); i += stride()) { |
334 | 0 | if (! (std::isfinite(m_vect[i]) && std::isfinite(m_vect[i+1]) )) { |
335 | 0 | return true; |
336 | 0 | } |
337 | 0 | if (m_vect[i - stride()] == m_vect[i] && m_vect[i + 1 - stride()] == m_vect[i+1]) { |
338 | 0 | return true; |
339 | 0 | } |
340 | 0 | } |
341 | 0 | return false; |
342 | 0 | } |
343 | | |
344 | | |
345 | | /* |
346 | | * Returns either the given coordinate array if its length is greater than the |
347 | | * given amount, or an empty coordinate array. |
348 | | */ |
349 | | CoordinateSequence* |
350 | | CoordinateSequence::atLeastNCoordinatesOrNothing(std::size_t n, |
351 | | CoordinateSequence* c) |
352 | 0 | { |
353 | 0 | if(c->getSize() >= n) { |
354 | 0 | return c; |
355 | 0 | } |
356 | 0 | else { |
357 | | // FIXME: return NULL rather then empty coordinate array |
358 | 0 | return new CoordinateSequence(0, c->getDimension()); |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | | const CoordinateXY* |
363 | | CoordinateSequence::minCoordinate() const |
364 | 0 | { |
365 | 0 | if (isEmpty()) { |
366 | 0 | return nullptr; |
367 | 0 | } |
368 | 0 | return &getAt<CoordinateXY>(minCoordinateIndex()); |
369 | 0 | } |
370 | | |
371 | | std::size_t |
372 | | CoordinateSequence::minCoordinateIndex() const |
373 | 0 | { |
374 | 0 | const CoordinateXY* minCoord = nullptr; |
375 | 0 | std::size_t minIndex = std::numeric_limits<std::size_t>::max(); |
376 | 0 | const std::size_t p_size = getSize(); |
377 | 0 | for(std::size_t i = 0; i < p_size; i++) { |
378 | 0 | if(minCoord == nullptr || minCoord->compareTo(getAt<CoordinateXY>(i)) > 0) { |
379 | 0 | minCoord = &getAt<CoordinateXY>(i); |
380 | 0 | minIndex = i; |
381 | 0 | } |
382 | 0 | } |
383 | 0 | return minIndex; |
384 | 0 | } |
385 | | |
386 | | size_t |
387 | | CoordinateSequence::indexOf(const CoordinateXY* coordinate, |
388 | | const CoordinateSequence* cl) |
389 | 12 | { |
390 | 12 | std::size_t p_size = cl->size(); |
391 | 29 | for(std::size_t i = 0; i < p_size; ++i) { |
392 | 29 | if((*coordinate) == cl->getAt<CoordinateXY>(i)) { |
393 | 12 | return i; |
394 | 12 | } |
395 | 29 | } |
396 | 0 | return NO_COORD_INDEX; |
397 | 12 | } |
398 | | |
399 | | void |
400 | | CoordinateSequence::scroll(CoordinateSequence* cl, const CoordinateXY* firstCoordinate) |
401 | 0 | { |
402 | 0 | cl->scroll(firstCoordinate); |
403 | 0 | } |
404 | | |
405 | | void |
406 | | CoordinateSequence::scroll(const CoordinateXY* firstCoordinate) |
407 | 0 | { |
408 | 0 | std::size_t ind = indexOf(firstCoordinate, this); |
409 | 0 | if(ind == 0 || ind == std::numeric_limits<std::size_t>::max()) { |
410 | 0 | return; // not found or already first |
411 | 0 | } |
412 | | |
413 | 0 | scroll(ind); |
414 | 0 | } |
415 | | |
416 | | void |
417 | | CoordinateSequence::scroll(size_t ind) |
418 | 0 | { |
419 | 0 | std::rotate(m_vect.begin(), |
420 | 0 | std::next(m_vect.begin(), static_cast<std::ptrdiff_t>(ind * stride())), |
421 | 0 | m_vect.end()); |
422 | 0 | } |
423 | | |
424 | | int |
425 | | CoordinateSequence::increasingDirection(const CoordinateSequence& pts) |
426 | 51.0M | { |
427 | 51.0M | std::size_t ptsize = pts.size(); |
428 | 51.1M | for(std::size_t i = 0, n = ptsize / 2; i < n; ++i) { |
429 | 51.1M | std::size_t j = ptsize - 1 - i; |
430 | | // skip equal points on both ends |
431 | 51.1M | int comp = pts[i].compareTo(pts[j]); |
432 | 51.1M | if(comp != 0) { |
433 | 51.0M | return comp; |
434 | 51.0M | } |
435 | 51.1M | } |
436 | | // array must be a palindrome - defined to be in positive direction |
437 | 45.1k | return 1; |
438 | 51.0M | } |
439 | | |
440 | | /* public */ |
441 | | bool |
442 | | CoordinateSequence::isRing() const |
443 | 139k | { |
444 | 139k | if (size() < 4) |
445 | 4.24k | return false; |
446 | | |
447 | 135k | if (front<CoordinateXY>() != back<CoordinateXY>()) |
448 | 25.8k | return false; |
449 | | |
450 | 109k | return true; |
451 | 135k | } |
452 | | |
453 | | void |
454 | | CoordinateSequence::reverse() |
455 | 24.4k | { |
456 | 24.4k | auto mid = m_vect.size() / 2; |
457 | 24.4k | auto last = m_vect.size() - stride(); |
458 | 880k | for (std::size_t i = 0; i < mid; i += stride()) { |
459 | 856k | switch(stride()) { |
460 | 78.7k | case 4: std::swap(m_vect[i + 3], m_vect[last - i + 3]); // fall-through |
461 | 856k | case 3: std::swap(m_vect[i + 2], m_vect[last - i + 2]); // fall-through |
462 | 856k | case 2: std::swap(m_vect[i + 1], m_vect[last - i + 1]); |
463 | 856k | std::swap(m_vect[i], m_vect[last - i]); |
464 | | |
465 | 856k | } |
466 | 856k | } |
467 | 24.4k | } |
468 | | |
469 | | void |
470 | | CoordinateSequence::sort() |
471 | 37.9k | { |
472 | 37.9k | switch(getCoordinateType()) { |
473 | 0 | case CoordinateType::XY: std::sort(items<CoordinateXY>().begin(), items<CoordinateXY>().end()); return; |
474 | 26.7k | case CoordinateType::XYZ: std::sort(items<Coordinate>().begin(), items<Coordinate>().end()); return; |
475 | 11.1k | case CoordinateType::XYZM: std::sort(items<CoordinateXYZM>().begin(), items<CoordinateXYZM>().end()); return; |
476 | 0 | case CoordinateType::XYM: std::sort(items<CoordinateXYM>().begin(), items<CoordinateXYM>().end()); return; |
477 | 37.9k | } |
478 | 37.9k | } |
479 | | |
480 | | bool |
481 | | CoordinateSequence::equals(const CoordinateSequence* cl1, |
482 | | const CoordinateSequence* cl2) |
483 | 0 | { |
484 | 0 | if(cl1 == cl2) { |
485 | 0 | return true; |
486 | 0 | } |
487 | | |
488 | 0 | if(cl1 == nullptr || cl2 == nullptr) { |
489 | 0 | return false; |
490 | 0 | } |
491 | | |
492 | 0 | std::size_t npts1 = cl1->getSize(); |
493 | 0 | if(npts1 != cl2->getSize()) { |
494 | 0 | return false; |
495 | 0 | } |
496 | 0 | for(std::size_t i = 0; i < npts1; i++) { |
497 | 0 | if(!(cl1->getAt<CoordinateXY>(i) == cl2->getAt<CoordinateXY>(i))) { |
498 | 0 | return false; |
499 | 0 | } |
500 | 0 | } |
501 | 0 | return true; |
502 | 0 | } |
503 | | |
504 | | bool |
505 | | CoordinateSequence::equalsIdentical(const CoordinateSequence& other) const |
506 | 0 | { |
507 | 0 | if (this == &other) { |
508 | 0 | return true; |
509 | 0 | } |
510 | | |
511 | 0 | if (size() != other.size()) { |
512 | 0 | return false; |
513 | 0 | } |
514 | | |
515 | 0 | if (hasZ() != other.hasZ()) { |
516 | 0 | return false; |
517 | 0 | } |
518 | | |
519 | 0 | if (hasM() != other.hasM()) { |
520 | 0 | return false; |
521 | 0 | } |
522 | | |
523 | 0 | assert(getCoordinateType() == other.getCoordinateType()); |
524 | |
|
525 | 0 | for (std::size_t i = 0; i < m_vect.size(); i++) { |
526 | 0 | const double& a = m_vect[i]; |
527 | 0 | const double& b = other.m_vect[i]; |
528 | 0 | if (a != b && !(std::isnan(a) && std::isnan(b))) { |
529 | 0 | return false; |
530 | 0 | } |
531 | 0 | } |
532 | | |
533 | 0 | return true; |
534 | 0 | } |
535 | | |
536 | | void |
537 | | CoordinateSequence::expandEnvelope(Envelope& env) const |
538 | 757 | { |
539 | 757 | const std::size_t p_size = getSize(); |
540 | 19.4k | for(std::size_t i = 0; i < p_size; i++) { |
541 | 18.6k | env.expandToInclude(getAt<CoordinateXY>(i)); |
542 | 18.6k | } |
543 | 757 | } |
544 | | |
545 | | Envelope |
546 | 40.9M | CoordinateSequence::getEnvelope() const { |
547 | 40.9M | if (isEmpty()) { |
548 | 0 | return {}; |
549 | 0 | } |
550 | | |
551 | 40.9M | double xmin = std::numeric_limits<double>::infinity(); |
552 | 40.9M | double ymin = std::numeric_limits<double>::infinity(); |
553 | 40.9M | double xmax = -std::numeric_limits<double>::infinity(); |
554 | 40.9M | double ymax = -std::numeric_limits<double>::infinity(); |
555 | | |
556 | 140M | for (std::size_t i = 0; i < m_vect.size(); i += stride()) { |
557 | 99.8M | xmin = std::min(xmin, m_vect[i]); |
558 | 99.8M | xmax = std::max(xmax, m_vect[i]); |
559 | 99.8M | ymin = std::min(ymin, m_vect[i+1]); |
560 | 99.8M | ymax = std::max(ymax, m_vect[i+1]); |
561 | 99.8M | } |
562 | | |
563 | 40.9M | return {xmin, xmax, ymin, ymax}; |
564 | 40.9M | } |
565 | | |
566 | | |
567 | | void |
568 | | CoordinateSequence::setOrdinate(std::size_t index, std::size_t ordinateIndex, double value) |
569 | 17.3k | { |
570 | 17.3k | switch(ordinateIndex) { |
571 | 0 | case CoordinateSequence::X: |
572 | 0 | getAt<CoordinateXY>(index).x = value; |
573 | 0 | break; |
574 | 0 | case CoordinateSequence::Y: |
575 | 0 | getAt<CoordinateXY>(index).y = value; |
576 | 0 | break; |
577 | 7.74k | case CoordinateSequence::Z: |
578 | 7.74k | { |
579 | 7.74k | if (!hasZ()) { |
580 | 0 | throw util::IllegalArgumentException("Coordinate type is not XYZ or XYZM"); |
581 | 0 | } |
582 | 7.74k | getAt<Coordinate>(index).z = value; |
583 | 7.74k | } |
584 | 0 | break; |
585 | 9.61k | case CoordinateSequence::M: |
586 | 9.61k | { |
587 | 9.61k | if (!hasM()) { |
588 | 0 | throw util::IllegalArgumentException("Coordinate type is not XYM or XYZM."); |
589 | 0 | } |
590 | 9.61k | if (getCoordinateType() == CoordinateType::XYZM) { |
591 | 9.61k | getAt<CoordinateXYZM>(index).m = value; |
592 | 9.61k | } else { |
593 | 0 | getAt<CoordinateXYM>(index).m = value; |
594 | 0 | } |
595 | 9.61k | break; |
596 | 9.61k | } |
597 | 0 | default: { |
598 | 0 | std::stringstream ss; |
599 | 0 | ss << "Unknown ordinate index " << ordinateIndex; |
600 | 0 | throw util::IllegalArgumentException(ss.str()); |
601 | 0 | break; |
602 | 9.61k | } |
603 | 17.3k | } |
604 | 17.3k | } |
605 | | |
606 | | void |
607 | | CoordinateSequence::setPoints(const std::vector<Coordinate>& v) |
608 | 0 | { |
609 | 0 | m_stride = 3; |
610 | 0 | m_hasdim = false; |
611 | 0 | m_hasz = false; |
612 | 0 | m_hasm = false; |
613 | |
|
614 | 0 | m_vect.resize(m_stride * v.size()); |
615 | 0 | const double* cbuf = reinterpret_cast<const double*>(v.data()); |
616 | 0 | m_vect.assign(cbuf, cbuf + m_vect.size()); |
617 | 0 | } |
618 | | |
619 | | void |
620 | | CoordinateSequence::setPoints(const std::vector<CoordinateXY>& v) |
621 | 0 | { |
622 | 0 | m_stride = 3; |
623 | 0 | m_hasdim = false; |
624 | 0 | m_hasz = false; |
625 | 0 | m_hasm = false; |
626 | |
|
627 | 0 | m_vect.resize(m_stride * v.size()); |
628 | 0 | for (std::size_t i = 0; i < v.size(); i++) { |
629 | 0 | setAt(v[i], i); |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | | void |
634 | | CoordinateSequence::setZM(bool p_hasZ, bool p_hasM) |
635 | 0 | { |
636 | 0 | if (p_hasZ != hasZ() || p_hasM != hasM()) { |
637 | 0 | CoordinateSequence newSeq(0, p_hasZ, p_hasM); |
638 | 0 | newSeq.add(*this); |
639 | 0 | *this = std::move(newSeq); |
640 | 0 | } |
641 | | |
642 | | // Make sure we don't carry over Z values that were hiding in a |
643 | | // self-declared 2D sequence. |
644 | 0 | if (!p_hasZ && (getCoordinateType() == CoordinateType::XYZ || getCoordinateType() == CoordinateType::XYZM)) { |
645 | 0 | const Coordinate& nullCoord = Coordinate::getNull(); |
646 | 0 | for (std::size_t i = 2; i < m_vect.size(); i += stride()) { |
647 | 0 | m_vect[i] = nullCoord.z; |
648 | 0 | } |
649 | 0 | } |
650 | 0 | } |
651 | | |
652 | | void |
653 | | CoordinateSequence::swap(std::size_t i, std::size_t j) |
654 | 0 | { |
655 | 0 | using difference_type = decltype(m_vect)::difference_type; |
656 | |
|
657 | 0 | if (i != j) { |
658 | 0 | std::swap_ranges(std::next(m_vect.begin(), static_cast<difference_type>(i*stride())), |
659 | 0 | std::next(m_vect.begin(), static_cast<difference_type>(i+1) * stride()), |
660 | 0 | std::next(m_vect.begin(), static_cast<difference_type>(j*stride()))); |
661 | 0 | } |
662 | 0 | } |
663 | | |
664 | | void |
665 | | CoordinateSequence::toVector(std::vector<Coordinate>& out) const |
666 | 9.90k | { |
667 | 9.90k | if (getCoordinateType() == CoordinateType::XYZ) { |
668 | 7.19k | const Coordinate* cbuf = reinterpret_cast<const Coordinate*>(m_vect.data()); |
669 | 7.19k | out.insert(out.end(), cbuf, cbuf + size()); |
670 | 7.19k | } else if (hasZ()) { |
671 | 42.5k | for (const auto& c : items<Coordinate>()) { |
672 | 42.5k | out.emplace_back(c.x, c.y, c.z); |
673 | 42.5k | } |
674 | 2.17k | } else { |
675 | 14.2k | for (const auto& c : items<CoordinateXY>()) { |
676 | 14.2k | out.emplace_back(c.x, c.y); |
677 | 14.2k | } |
678 | 539 | } |
679 | 9.90k | } |
680 | | |
681 | | void |
682 | | CoordinateSequence::toVector(std::vector<CoordinateXY>& out) const |
683 | 0 | { |
684 | 0 | if (stride() == 2) { |
685 | 0 | const CoordinateXY* cbuf = reinterpret_cast<const CoordinateXY*>(m_vect.data()); |
686 | 0 | out.insert(out.end(), cbuf, cbuf + size()); |
687 | 0 | } else { |
688 | 0 | for (const CoordinateXY& c : items<CoordinateXY>()) { |
689 | 0 | out.emplace_back(c.x, c.y); |
690 | 0 | } |
691 | 0 | } |
692 | 0 | } |
693 | | |
694 | | void |
695 | | CoordinateSequence::pop_back() |
696 | 0 | { |
697 | 0 | switch (stride()) { |
698 | 0 | case 4: m_vect.pop_back(); // fall through |
699 | 0 | case 3: m_vect.pop_back(); // fall through |
700 | 0 | case 2: m_vect.pop_back(); |
701 | 0 | m_vect.pop_back(); |
702 | 0 | break; |
703 | 0 | default: |
704 | 0 | assert(0); |
705 | 0 | } |
706 | 0 | } |
707 | | |
708 | | std::ostream& |
709 | | operator<< (std::ostream& os, const CoordinateSequence& cs) |
710 | 0 | { |
711 | 0 | os << "("; |
712 | |
|
713 | 0 | bool writeComma = false; |
714 | 0 | auto write = [&os, &writeComma](const auto& coord) { |
715 | 0 | if (writeComma) { |
716 | 0 | os << ", "; |
717 | 0 | } else { |
718 | 0 | writeComma = true; |
719 | 0 | } |
720 | |
|
721 | 0 | os << coord; |
722 | 0 | }; Unexecuted instantiation: CoordinateSequence.cpp:auto geos::geom::operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, geos::geom::CoordinateSequence const&)::$_0::operator()<geos::geom::CoordinateXY>(geos::geom::CoordinateXY const&) const Unexecuted instantiation: CoordinateSequence.cpp:auto geos::geom::operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, geos::geom::CoordinateSequence const&)::$_0::operator()<geos::geom::Coordinate>(geos::geom::Coordinate const&) const Unexecuted instantiation: CoordinateSequence.cpp:auto geos::geom::operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, geos::geom::CoordinateSequence const&)::$_0::operator()<geos::geom::CoordinateXYM>(geos::geom::CoordinateXYM const&) const Unexecuted instantiation: CoordinateSequence.cpp:auto geos::geom::operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, geos::geom::CoordinateSequence const&)::$_0::operator()<geos::geom::CoordinateXYZM>(geos::geom::CoordinateXYZM const&) const |
723 | |
|
724 | 0 | cs.forEach(write); |
725 | 0 | os << ")"; |
726 | |
|
727 | 0 | return os; |
728 | 0 | } |
729 | | |
730 | | std::string |
731 | | CoordinateSequence::toString() const |
732 | 0 | { |
733 | 0 | std::ostringstream ss; |
734 | 0 | ss << *this; |
735 | 0 | return ss.str(); |
736 | 0 | } |
737 | | |
738 | | bool |
739 | | operator== (const CoordinateSequence& s1, const CoordinateSequence& s2) |
740 | 0 | { |
741 | 0 | return CoordinateSequence::equals(&s1, &s2); |
742 | 0 | } |
743 | | |
744 | | bool |
745 | | operator!= (const CoordinateSequence& s1, const CoordinateSequence& s2) |
746 | 0 | { |
747 | 0 | return ! CoordinateSequence::equals(&s1, &s2); |
748 | 0 | } |
749 | | |
750 | | } // namespace geos::geom |
751 | | } // namespace geos |