/src/gdal/ogr/ogrcircularstring.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: The OGRCircularString geometry class. |
5 | | * Author: Even Rouault, even dot rouault at spatialys dot com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2010, 2014, Even Rouault <even dot rouault at spatialys dot |
9 | | *com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "ogr_geometry.h" |
16 | | |
17 | | #include <cmath> |
18 | | #include <cstring> |
19 | | |
20 | | #include <algorithm> |
21 | | #include <limits> |
22 | | #include <vector> |
23 | | |
24 | | #include "cpl_error.h" |
25 | | #include "ogr_core.h" |
26 | | #include "ogr_geometry.h" |
27 | | #include "ogr_p.h" |
28 | | |
29 | | static inline double dist(double x0, double y0, double x1, double y1) |
30 | 0 | { |
31 | 0 | return std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); |
32 | 0 | } |
33 | | |
34 | | /************************************************************************/ |
35 | | /* OGRCircularString( const OGRCircularString& ) */ |
36 | | /************************************************************************/ |
37 | | |
38 | | /** |
39 | | * \brief Copy constructor. |
40 | | */ |
41 | | |
42 | 0 | OGRCircularString::OGRCircularString(const OGRCircularString &) = default; |
43 | | |
44 | | /************************************************************************/ |
45 | | /* operator=( const OGRCircularString& ) */ |
46 | | /************************************************************************/ |
47 | | |
48 | | /** |
49 | | * \brief Assignment operator. |
50 | | */ |
51 | | |
52 | | OGRCircularString &OGRCircularString::operator=(const OGRCircularString &other) |
53 | 0 | { |
54 | 0 | if (this != &other) |
55 | 0 | { |
56 | 0 | OGRSimpleCurve::operator=(other); |
57 | 0 | } |
58 | 0 | return *this; |
59 | 0 | } |
60 | | |
61 | | /************************************************************************/ |
62 | | /* getGeometryType() */ |
63 | | /************************************************************************/ |
64 | | |
65 | | OGRwkbGeometryType OGRCircularString::getGeometryType() const |
66 | | |
67 | 9 | { |
68 | 9 | if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED)) |
69 | 5 | return wkbCircularStringZM; |
70 | 4 | else if (flags & OGR_G_MEASURED) |
71 | 0 | return wkbCircularStringM; |
72 | 4 | else if (flags & OGR_G_3D) |
73 | 3 | return wkbCircularStringZ; |
74 | 1 | else |
75 | 1 | return wkbCircularString; |
76 | 9 | } |
77 | | |
78 | | /************************************************************************/ |
79 | | /* getGeometryName() */ |
80 | | /************************************************************************/ |
81 | | |
82 | | const char *OGRCircularString::getGeometryName() const |
83 | | |
84 | 11 | { |
85 | 11 | return "CIRCULARSTRING"; |
86 | 11 | } |
87 | | |
88 | | /************************************************************************/ |
89 | | /* clone() */ |
90 | | /************************************************************************/ |
91 | | |
92 | | OGRCircularString *OGRCircularString::clone() const |
93 | | |
94 | 0 | { |
95 | 0 | return new (std::nothrow) OGRCircularString(*this); |
96 | 0 | } |
97 | | |
98 | | /************************************************************************/ |
99 | | /* importFromWkb() */ |
100 | | /* */ |
101 | | /* Initialize from serialized stream in well known binary */ |
102 | | /* format. */ |
103 | | /************************************************************************/ |
104 | | |
105 | | OGRErr OGRCircularString::importFromWkb(const unsigned char *pabyData, |
106 | | size_t nSize, OGRwkbVariant eWkbVariant, |
107 | | size_t &nBytesConsumedOut) |
108 | | |
109 | 0 | { |
110 | 0 | OGRErr eErr = OGRSimpleCurve::importFromWkb(pabyData, nSize, eWkbVariant, |
111 | 0 | nBytesConsumedOut); |
112 | 0 | if (eErr == OGRERR_NONE) |
113 | 0 | { |
114 | 0 | if (!IsValidFast()) |
115 | 0 | { |
116 | 0 | empty(); |
117 | 0 | return OGRERR_CORRUPT_DATA; |
118 | 0 | } |
119 | 0 | } |
120 | 0 | return eErr; |
121 | 0 | } |
122 | | |
123 | | /************************************************************************/ |
124 | | /* exportToWkb() */ |
125 | | /* */ |
126 | | /* Build a well known binary representation of this object. */ |
127 | | /************************************************************************/ |
128 | | |
129 | | OGRErr |
130 | | OGRCircularString::exportToWkb(unsigned char *pabyData, |
131 | | const OGRwkbExportOptions *psOptions) const |
132 | | |
133 | 0 | { |
134 | 0 | if (!IsValidFast()) |
135 | 0 | { |
136 | 0 | return OGRERR_FAILURE; |
137 | 0 | } |
138 | | |
139 | 0 | OGRwkbExportOptions sOptions(psOptions ? *psOptions |
140 | 0 | : OGRwkbExportOptions()); |
141 | | |
142 | | // Does not make sense for new geometries, so patch it. |
143 | 0 | if (sOptions.eWkbVariant == wkbVariantOldOgc) |
144 | 0 | sOptions.eWkbVariant = wkbVariantIso; |
145 | 0 | return OGRSimpleCurve::exportToWkb(pabyData, &sOptions); |
146 | 0 | } |
147 | | |
148 | | /************************************************************************/ |
149 | | /* importFromWkt() */ |
150 | | /* */ |
151 | | /* Instantiate from well known text format. Currently this is */ |
152 | | /* `CIRCULARSTRING [Z] ( x y [z], x y [z], ...)', */ |
153 | | /************************************************************************/ |
154 | | |
155 | | OGRErr OGRCircularString::importFromWkt(const char **ppszInput) |
156 | | |
157 | 11 | { |
158 | 11 | const OGRErr eErr = OGRSimpleCurve::importFromWkt(ppszInput); |
159 | 11 | if (eErr == OGRERR_NONE) |
160 | 11 | { |
161 | 11 | if (!IsValidFast()) |
162 | 2 | { |
163 | 2 | empty(); |
164 | 2 | return OGRERR_CORRUPT_DATA; |
165 | 2 | } |
166 | 11 | } |
167 | 9 | return eErr; |
168 | 11 | } |
169 | | |
170 | | /************************************************************************/ |
171 | | /* exportToWkt() */ |
172 | | /************************************************************************/ |
173 | | |
174 | | std::string OGRCircularString::exportToWkt(const OGRWktOptions &opts, |
175 | | OGRErr *err) const |
176 | 0 | { |
177 | 0 | if (!IsValidFast()) |
178 | 0 | { |
179 | 0 | if (err) |
180 | 0 | *err = OGRERR_FAILURE; |
181 | 0 | return std::string(); |
182 | 0 | } |
183 | | |
184 | 0 | OGRWktOptions optsModified(opts); |
185 | 0 | optsModified.variant = wkbVariantIso; |
186 | 0 | return OGRSimpleCurve::exportToWkt(optsModified, err); |
187 | 0 | } |
188 | | |
189 | | /************************************************************************/ |
190 | | /* get_Length() */ |
191 | | /* */ |
192 | | /* For now we return a simple euclidean 2D distance. */ |
193 | | /************************************************************************/ |
194 | | |
195 | | double OGRCircularString::get_Length() const |
196 | | |
197 | 0 | { |
198 | 0 | double dfLength = 0.0; |
199 | 0 | for (int i = 0; i < nPointCount - 2; i += 2) |
200 | 0 | { |
201 | 0 | const double x0 = paoPoints[i].x; |
202 | 0 | const double y0 = paoPoints[i].y; |
203 | 0 | const double x1 = paoPoints[i + 1].x; |
204 | 0 | const double y1 = paoPoints[i + 1].y; |
205 | 0 | const double x2 = paoPoints[i + 2].x; |
206 | 0 | const double y2 = paoPoints[i + 2].y; |
207 | 0 | double R = 0.0; |
208 | 0 | double cx = 0.0; |
209 | 0 | double cy = 0.0; |
210 | 0 | double alpha0 = 0.0; |
211 | 0 | double alpha1 = 0.0; |
212 | 0 | double alpha2 = 0.0; |
213 | 0 | if (OGRGeometryFactory::GetCurveParameters( |
214 | 0 | x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2)) |
215 | 0 | { |
216 | 0 | dfLength += fabs(alpha2 - alpha0) * R; |
217 | 0 | } |
218 | 0 | else |
219 | 0 | { |
220 | 0 | dfLength += dist(x0, y0, x2, y2); |
221 | 0 | } |
222 | 0 | } |
223 | 0 | return dfLength; |
224 | 0 | } |
225 | | |
226 | | /************************************************************************/ |
227 | | /* ExtendEnvelopeWithCircular() */ |
228 | | /************************************************************************/ |
229 | | |
230 | | void OGRCircularString::ExtendEnvelopeWithCircular( |
231 | | OGREnvelope *psEnvelope) const |
232 | 0 | { |
233 | 0 | if (!IsValidFast() || nPointCount == 0) |
234 | 0 | return; |
235 | | |
236 | | // Loop through circular portions and determine if they include some |
237 | | // extremities of the circle. |
238 | 0 | for (int i = 0; i < nPointCount - 2; i += 2) |
239 | 0 | { |
240 | 0 | const double x0 = paoPoints[i].x; |
241 | 0 | const double y0 = paoPoints[i].y; |
242 | 0 | const double x1 = paoPoints[i + 1].x; |
243 | 0 | const double y1 = paoPoints[i + 1].y; |
244 | 0 | const double x2 = paoPoints[i + 2].x; |
245 | 0 | const double y2 = paoPoints[i + 2].y; |
246 | 0 | double R = 0.0; |
247 | 0 | double cx = 0.0; |
248 | 0 | double cy = 0.0; |
249 | 0 | double alpha0 = 0.0; |
250 | 0 | double alpha1 = 0.0; |
251 | 0 | double alpha2 = 0.0; |
252 | 0 | if (OGRGeometryFactory::GetCurveParameters( |
253 | 0 | x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2)) |
254 | 0 | { |
255 | 0 | if (std::isnan(alpha0) || std::isnan(alpha2)) |
256 | 0 | { |
257 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
258 | 0 | "GetCurveParameters returned NaN"); |
259 | 0 | continue; |
260 | 0 | } |
261 | 0 | int quadrantStart = |
262 | 0 | static_cast<int>(std::floor(alpha0 / (M_PI / 2))); |
263 | 0 | int quadrantEnd = static_cast<int>(std::floor(alpha2 / (M_PI / 2))); |
264 | 0 | if (quadrantStart > quadrantEnd) |
265 | 0 | { |
266 | 0 | std::swap(quadrantStart, quadrantEnd); |
267 | 0 | } |
268 | | // Transition through quadrants in counter-clock wise direction. |
269 | 0 | for (int j = quadrantStart + 1; j <= quadrantEnd; ++j) |
270 | 0 | { |
271 | 0 | switch ((j + 8) % 4) |
272 | 0 | { |
273 | 0 | case 0: |
274 | 0 | psEnvelope->MaxX = std::max(psEnvelope->MaxX, cx + R); |
275 | 0 | break; |
276 | 0 | case 1: |
277 | 0 | psEnvelope->MaxY = std::max(psEnvelope->MaxY, cy + R); |
278 | 0 | break; |
279 | 0 | case 2: |
280 | 0 | psEnvelope->MinX = std::min(psEnvelope->MinX, cx - R); |
281 | 0 | break; |
282 | 0 | case 3: |
283 | 0 | psEnvelope->MinY = std::min(psEnvelope->MaxY, cy - R); |
284 | 0 | break; |
285 | 0 | default: |
286 | 0 | CPLAssert(false); |
287 | 0 | break; |
288 | 0 | } |
289 | 0 | } |
290 | 0 | } |
291 | 0 | } |
292 | 0 | } |
293 | | |
294 | | /************************************************************************/ |
295 | | /* getEnvelope() */ |
296 | | /************************************************************************/ |
297 | | |
298 | | void OGRCircularString::getEnvelope(OGREnvelope *psEnvelope) const |
299 | | |
300 | 0 | { |
301 | 0 | OGRSimpleCurve::getEnvelope(psEnvelope); |
302 | 0 | ExtendEnvelopeWithCircular(psEnvelope); |
303 | 0 | } |
304 | | |
305 | | /************************************************************************/ |
306 | | /* getEnvelope() */ |
307 | | /************************************************************************/ |
308 | | |
309 | | void OGRCircularString::getEnvelope(OGREnvelope3D *psEnvelope) const |
310 | | |
311 | 0 | { |
312 | 0 | OGRSimpleCurve::getEnvelope(psEnvelope); |
313 | 0 | ExtendEnvelopeWithCircular(psEnvelope); |
314 | 0 | } |
315 | | |
316 | | /************************************************************************/ |
317 | | /* OGRCircularString::segmentize() */ |
318 | | /************************************************************************/ |
319 | | |
320 | | bool OGRCircularString::segmentize(double dfMaxLength) |
321 | 0 | { |
322 | 0 | if (!IsValidFast()) |
323 | 0 | return false; |
324 | 0 | if (nPointCount == 0) |
325 | 0 | return true; |
326 | | |
327 | | // So as to make sure that the same line followed in both directions |
328 | | // result in the same segmentized line. |
329 | 0 | if (paoPoints[0].x < paoPoints[nPointCount - 1].x || |
330 | 0 | (paoPoints[0].x == paoPoints[nPointCount - 1].x && |
331 | 0 | paoPoints[0].y < paoPoints[nPointCount - 1].y)) |
332 | 0 | { |
333 | 0 | reversePoints(); |
334 | 0 | const bool bRet = segmentize(dfMaxLength); |
335 | 0 | reversePoints(); |
336 | 0 | return bRet; |
337 | 0 | } |
338 | | |
339 | 0 | std::vector<OGRRawPoint> aoRawPoint; |
340 | 0 | std::vector<double> adfZ; |
341 | 0 | std::vector<double> adfM; |
342 | 0 | bool bRet = true; |
343 | 0 | for (int i = 0; i < nPointCount - 2; i += 2) |
344 | 0 | { |
345 | 0 | const double x0 = paoPoints[i].x; |
346 | 0 | const double y0 = paoPoints[i].y; |
347 | 0 | const double x1 = paoPoints[i + 1].x; |
348 | 0 | const double y1 = paoPoints[i + 1].y; |
349 | 0 | const double x2 = paoPoints[i + 2].x; |
350 | 0 | const double y2 = paoPoints[i + 2].y; |
351 | 0 | double R = 0.0; |
352 | 0 | double cx = 0.0; |
353 | 0 | double cy = 0.0; |
354 | 0 | double alpha0 = 0.0; |
355 | 0 | double alpha1 = 0.0; |
356 | 0 | double alpha2 = 0.0; |
357 | |
|
358 | 0 | aoRawPoint.emplace_back(x0, y0); |
359 | 0 | if (padfZ) |
360 | 0 | adfZ.emplace_back(padfZ[i]); |
361 | 0 | if (padfM) |
362 | 0 | adfM.emplace_back(padfM[i]); |
363 | |
|
364 | 0 | constexpr int kMax = 2 << 26; |
365 | | |
366 | | // We have strong constraints on the number of intermediate points |
367 | | // we can add. |
368 | |
|
369 | 0 | if (OGRGeometryFactory::GetCurveParameters( |
370 | 0 | x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2)) |
371 | 0 | { |
372 | | // It is an arc circle. |
373 | 0 | const double dfSegmentLength1 = fabs(alpha1 - alpha0) * R; |
374 | 0 | const double dfSegmentLength2 = fabs(alpha2 - alpha1) * R; |
375 | 0 | if (dfSegmentLength1 > dfMaxLength || |
376 | 0 | dfSegmentLength2 > dfMaxLength) |
377 | 0 | { |
378 | 0 | const double dfVal = |
379 | 0 | 1 + 2 * std::floor(dfSegmentLength1 / dfMaxLength / 2.0); |
380 | 0 | if (dfVal < 0.0 || std::isnan(dfVal) || |
381 | 0 | dfVal >= kMax - static_cast<int>(aoRawPoint.size())) |
382 | 0 | { |
383 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
384 | 0 | "segmentize nIntermediatePoints invalid: %lf", |
385 | 0 | dfVal); |
386 | 0 | return false; |
387 | 0 | } |
388 | 0 | const int nIntermediatePoints = static_cast<int>(dfVal); |
389 | 0 | const double dfStep = |
390 | 0 | (alpha1 - alpha0) / (nIntermediatePoints + 1); |
391 | 0 | for (int j = 1; j <= nIntermediatePoints; ++j) |
392 | 0 | { |
393 | 0 | double alpha = alpha0 + dfStep * j; |
394 | 0 | const double x = cx + R * cos(alpha); |
395 | 0 | const double y = cy + R * sin(alpha); |
396 | 0 | aoRawPoint.emplace_back(x, y); |
397 | 0 | if (padfZ) |
398 | 0 | { |
399 | 0 | const double z = padfZ[i] + (padfZ[i + 1] - padfZ[i]) * |
400 | 0 | (alpha - alpha0) / |
401 | 0 | (alpha1 - alpha0); |
402 | 0 | adfZ.emplace_back(z); |
403 | 0 | } |
404 | 0 | if (padfM) |
405 | 0 | { |
406 | 0 | adfM.emplace_back(padfM[i]); |
407 | 0 | } |
408 | 0 | } |
409 | 0 | } |
410 | 0 | aoRawPoint.emplace_back(x1, y1); |
411 | 0 | if (padfZ) |
412 | 0 | adfZ.emplace_back(padfZ[i + 1]); |
413 | 0 | if (padfM) |
414 | 0 | adfM.emplace_back(padfM[i + 1]); |
415 | |
|
416 | 0 | if (dfSegmentLength1 > dfMaxLength || |
417 | 0 | dfSegmentLength2 > dfMaxLength) |
418 | 0 | { |
419 | 0 | const double dfVal = |
420 | 0 | 1 + 2 * std::floor(dfSegmentLength2 / dfMaxLength / 2.0); |
421 | 0 | if (dfVal < 0.0 || std::isnan(dfVal) || |
422 | 0 | dfVal >= kMax - static_cast<int>(aoRawPoint.size())) |
423 | 0 | { |
424 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
425 | 0 | "segmentize nIntermediatePoints invalid 2: %lf", |
426 | 0 | dfVal); |
427 | 0 | return false; |
428 | 0 | } |
429 | 0 | int nIntermediatePoints = static_cast<int>(dfVal); |
430 | 0 | const double dfStep = |
431 | 0 | (alpha2 - alpha1) / (nIntermediatePoints + 1); |
432 | 0 | for (int j = 1; j <= nIntermediatePoints; ++j) |
433 | 0 | { |
434 | 0 | const double alpha = alpha1 + dfStep * j; |
435 | 0 | const double x = cx + R * cos(alpha); |
436 | 0 | const double y = cy + R * sin(alpha); |
437 | 0 | aoRawPoint.emplace_back(x, y); |
438 | 0 | if (padfZ) |
439 | 0 | { |
440 | 0 | const double z = |
441 | 0 | padfZ[i + 1] + (padfZ[i + 2] - padfZ[i + 1]) * |
442 | 0 | (alpha - alpha1) / |
443 | 0 | (alpha2 - alpha1); |
444 | 0 | adfZ.emplace_back(z); |
445 | 0 | } |
446 | 0 | if (padfM) |
447 | 0 | { |
448 | 0 | adfM.emplace_back(padfM[i + 1]); |
449 | 0 | } |
450 | 0 | } |
451 | 0 | } |
452 | 0 | } |
453 | 0 | else |
454 | 0 | { |
455 | | // It is a straight line. |
456 | 0 | const double dfSegmentLength1 = dist(x0, y0, x1, y1); |
457 | 0 | const double dfSegmentLength2 = dist(x1, y1, x2, y2); |
458 | 0 | if (dfSegmentLength1 > dfMaxLength || |
459 | 0 | dfSegmentLength2 > dfMaxLength) |
460 | 0 | { |
461 | 0 | const double dfVal = |
462 | 0 | 1 + 2 * std::ceil(dfSegmentLength1 / dfMaxLength / 2.0); |
463 | 0 | if (dfVal < 0.0 || std::isnan(dfVal) || |
464 | 0 | dfVal >= kMax - static_cast<int>(aoRawPoint.size())) |
465 | 0 | { |
466 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
467 | 0 | "segmentize nIntermediatePoints invalid 2: %lf", |
468 | 0 | dfVal); |
469 | 0 | return false; |
470 | 0 | } |
471 | 0 | int nIntermediatePoints = static_cast<int>(dfVal); |
472 | 0 | for (int j = 1; j <= nIntermediatePoints; ++j) |
473 | 0 | { |
474 | 0 | aoRawPoint.emplace_back( |
475 | 0 | x0 + j * (x1 - x0) / (nIntermediatePoints + 1), |
476 | 0 | y0 + j * (y1 - y0) / (nIntermediatePoints + 1)); |
477 | 0 | if (padfZ) |
478 | 0 | adfZ.emplace_back(padfZ[i] + |
479 | 0 | j * (padfZ[i + 1] - padfZ[i]) / |
480 | 0 | (nIntermediatePoints + 1)); |
481 | 0 | if (padfM) |
482 | 0 | adfM.emplace_back(padfM[i]); |
483 | 0 | } |
484 | 0 | } |
485 | | |
486 | 0 | aoRawPoint.emplace_back(x1, y1); |
487 | 0 | if (padfZ) |
488 | 0 | adfZ.emplace_back(padfZ[i + 1]); |
489 | 0 | if (padfM) |
490 | 0 | adfM.emplace_back(padfM[i + 1]); |
491 | |
|
492 | 0 | if (dfSegmentLength1 > dfMaxLength || |
493 | 0 | dfSegmentLength2 > dfMaxLength) |
494 | 0 | { |
495 | 0 | const double dfVal = |
496 | 0 | 1 + 2 * std::ceil(dfSegmentLength2 / dfMaxLength / 2.0); |
497 | 0 | if (dfVal < 0.0 || std::isnan(dfVal) || |
498 | 0 | dfVal >= kMax - static_cast<int>(aoRawPoint.size())) |
499 | 0 | { |
500 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
501 | 0 | "segmentize nIntermediatePoints invalid 3: %lf", |
502 | 0 | dfVal); |
503 | 0 | return false; |
504 | 0 | } |
505 | 0 | const int nIntermediatePoints = static_cast<int>(dfVal); |
506 | |
|
507 | 0 | for (int j = 1; j <= nIntermediatePoints; ++j) |
508 | 0 | { |
509 | 0 | aoRawPoint.emplace_back( |
510 | 0 | x1 + j * (x2 - x1) / (nIntermediatePoints + 1), |
511 | 0 | y1 + j * (y2 - y1) / (nIntermediatePoints + 1)); |
512 | 0 | if (padfZ) |
513 | 0 | adfZ.emplace_back(padfZ[i + 1] + |
514 | 0 | j * (padfZ[i + 2] - padfZ[i + 1]) / |
515 | 0 | (nIntermediatePoints + 1)); |
516 | 0 | if (padfM) |
517 | 0 | adfM.emplace_back(padfM[i + 1]); |
518 | 0 | } |
519 | 0 | } |
520 | 0 | } |
521 | 0 | } |
522 | 0 | aoRawPoint.push_back(paoPoints[nPointCount - 1]); |
523 | 0 | if (padfZ) |
524 | 0 | adfZ.push_back(padfZ[nPointCount - 1]); |
525 | 0 | if (padfM) |
526 | 0 | adfM.push_back(padfM[nPointCount - 1]); |
527 | |
|
528 | 0 | CPLAssert(aoRawPoint.empty() || |
529 | 0 | (aoRawPoint.size() >= 3 && (aoRawPoint.size() % 2) == 1)); |
530 | 0 | if (padfZ) |
531 | 0 | { |
532 | 0 | CPLAssert(adfZ.size() == aoRawPoint.size()); |
533 | 0 | } |
534 | 0 | if (padfM) |
535 | 0 | { |
536 | 0 | CPLAssert(adfM.size() == aoRawPoint.size()); |
537 | 0 | } |
538 | | |
539 | | // Is there actually something to modify? |
540 | 0 | if (nPointCount < static_cast<int>(aoRawPoint.size())) |
541 | 0 | { |
542 | 0 | nPointCount = static_cast<int>(aoRawPoint.size()); |
543 | 0 | paoPoints = static_cast<OGRRawPoint *>( |
544 | 0 | CPLRealloc(paoPoints, sizeof(OGRRawPoint) * nPointCount)); |
545 | 0 | memcpy(paoPoints, &aoRawPoint[0], sizeof(OGRRawPoint) * nPointCount); |
546 | 0 | if (padfZ) |
547 | 0 | { |
548 | 0 | padfZ = static_cast<double *>( |
549 | 0 | CPLRealloc(padfZ, sizeof(double) * aoRawPoint.size())); |
550 | 0 | memcpy(padfZ, &adfZ[0], sizeof(double) * nPointCount); |
551 | 0 | } |
552 | 0 | if (padfM) |
553 | 0 | { |
554 | 0 | padfM = static_cast<double *>( |
555 | 0 | CPLRealloc(padfM, sizeof(double) * aoRawPoint.size())); |
556 | 0 | memcpy(padfM, &adfM[0], sizeof(double) * nPointCount); |
557 | 0 | } |
558 | 0 | } |
559 | 0 | return bRet; |
560 | 0 | } |
561 | | |
562 | | /************************************************************************/ |
563 | | /* Value() */ |
564 | | /* */ |
565 | | /* Get an interpolated point at some distance along the curve. */ |
566 | | /************************************************************************/ |
567 | | |
568 | | void OGRCircularString::Value(double dfDistance, OGRPoint *poPoint) const |
569 | | |
570 | 0 | { |
571 | 0 | if (dfDistance < 0) |
572 | 0 | { |
573 | 0 | StartPoint(poPoint); |
574 | 0 | return; |
575 | 0 | } |
576 | | |
577 | 0 | double dfLength = 0; |
578 | |
|
579 | 0 | for (int i = 0; i < nPointCount - 2; i += 2) |
580 | 0 | { |
581 | 0 | const double x0 = paoPoints[i].x; |
582 | 0 | const double y0 = paoPoints[i].y; |
583 | 0 | const double x1 = paoPoints[i + 1].x; |
584 | 0 | const double y1 = paoPoints[i + 1].y; |
585 | 0 | const double x2 = paoPoints[i + 2].x; |
586 | 0 | const double y2 = paoPoints[i + 2].y; |
587 | 0 | double R = 0.0; |
588 | 0 | double cx = 0.0; |
589 | 0 | double cy = 0.0; |
590 | 0 | double alpha0 = 0.0; |
591 | 0 | double alpha1 = 0.0; |
592 | 0 | double alpha2 = 0.0; |
593 | | |
594 | | // We have strong constraints on the number of intermediate points |
595 | | // we can add. |
596 | |
|
597 | 0 | if (OGRGeometryFactory::GetCurveParameters( |
598 | 0 | x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2)) |
599 | 0 | { |
600 | | // It is an arc circle. |
601 | 0 | const double dfSegLength = fabs(alpha2 - alpha0) * R; |
602 | 0 | if (dfSegLength > 0) |
603 | 0 | { |
604 | 0 | if ((dfLength <= dfDistance) && |
605 | 0 | ((dfLength + dfSegLength) >= dfDistance)) |
606 | 0 | { |
607 | 0 | const double dfRatio = |
608 | 0 | (dfDistance - dfLength) / dfSegLength; |
609 | |
|
610 | 0 | const double alpha = |
611 | 0 | alpha0 * (1 - dfRatio) + alpha2 * dfRatio; |
612 | 0 | const double x = cx + R * cos(alpha); |
613 | 0 | const double y = cy + R * sin(alpha); |
614 | |
|
615 | 0 | poPoint->setX(x); |
616 | 0 | poPoint->setY(y); |
617 | |
|
618 | 0 | if (getCoordinateDimension() == 3) |
619 | 0 | poPoint->setZ(padfZ[i] * (1 - dfRatio) + |
620 | 0 | padfZ[i + 2] * dfRatio); |
621 | |
|
622 | 0 | return; |
623 | 0 | } |
624 | | |
625 | 0 | dfLength += dfSegLength; |
626 | 0 | } |
627 | 0 | } |
628 | 0 | else |
629 | 0 | { |
630 | | // It is a straight line. |
631 | 0 | const double dfSegLength = dist(x0, y0, x2, y2); |
632 | 0 | if (dfSegLength > 0) |
633 | 0 | { |
634 | 0 | if ((dfLength <= dfDistance) && |
635 | 0 | ((dfLength + dfSegLength) >= dfDistance)) |
636 | 0 | { |
637 | 0 | const double dfRatio = |
638 | 0 | (dfDistance - dfLength) / dfSegLength; |
639 | |
|
640 | 0 | poPoint->setX(paoPoints[i].x * (1 - dfRatio) + |
641 | 0 | paoPoints[i + 2].x * dfRatio); |
642 | 0 | poPoint->setY(paoPoints[i].y * (1 - dfRatio) + |
643 | 0 | paoPoints[i + 2].y * dfRatio); |
644 | |
|
645 | 0 | if (getCoordinateDimension() == 3) |
646 | 0 | poPoint->setZ(padfZ[i] * (1 - dfRatio) + |
647 | 0 | padfZ[i + 2] * dfRatio); |
648 | |
|
649 | 0 | return; |
650 | 0 | } |
651 | | |
652 | 0 | dfLength += dfSegLength; |
653 | 0 | } |
654 | 0 | } |
655 | 0 | } |
656 | | |
657 | 0 | EndPoint(poPoint); |
658 | 0 | } |
659 | | |
660 | | /************************************************************************/ |
661 | | /* CurveToLine() */ |
662 | | /************************************************************************/ |
663 | | |
664 | | OGRLineString * |
665 | | OGRCircularString::CurveToLine(double dfMaxAngleStepSizeDegrees, |
666 | | const char *const *papszOptions) const |
667 | 0 | { |
668 | 0 | OGRLineString *poLine = new OGRLineString(); |
669 | 0 | poLine->assignSpatialReference(getSpatialReference()); |
670 | |
|
671 | 0 | const bool bHasZ = getCoordinateDimension() == 3; |
672 | 0 | for (int i = 0; i < nPointCount - 2; i += 2) |
673 | 0 | { |
674 | 0 | OGRLineString *poArc = OGRGeometryFactory::curveToLineString( |
675 | 0 | paoPoints[i].x, paoPoints[i].y, padfZ ? padfZ[i] : 0.0, |
676 | 0 | paoPoints[i + 1].x, paoPoints[i + 1].y, padfZ ? padfZ[i + 1] : 0.0, |
677 | 0 | paoPoints[i + 2].x, paoPoints[i + 2].y, padfZ ? padfZ[i + 2] : 0.0, |
678 | 0 | bHasZ, dfMaxAngleStepSizeDegrees, papszOptions); |
679 | 0 | poLine->addSubLineString(poArc, (i == 0) ? 0 : 1); |
680 | 0 | delete poArc; |
681 | 0 | } |
682 | 0 | return poLine; |
683 | 0 | } |
684 | | |
685 | | /************************************************************************/ |
686 | | /* IsValidFast() */ |
687 | | /************************************************************************/ |
688 | | |
689 | | OGRBoolean OGRCircularString::IsValidFast() const |
690 | | |
691 | 11 | { |
692 | 11 | if (nPointCount == 1 || nPointCount == 2 || |
693 | 9 | (nPointCount >= 3 && (nPointCount % 2) == 0)) |
694 | 2 | { |
695 | 2 | CPLError(CE_Failure, CPLE_NotSupported, |
696 | 2 | "Bad number of points in circular string : %d", nPointCount); |
697 | 2 | return FALSE; |
698 | 2 | } |
699 | 9 | return TRUE; |
700 | 11 | } |
701 | | |
702 | | /************************************************************************/ |
703 | | /* IsValid() */ |
704 | | /************************************************************************/ |
705 | | |
706 | | OGRBoolean OGRCircularString::IsValid() const |
707 | | |
708 | 0 | { |
709 | 0 | return IsValidFast() && OGRGeometry::IsValid(); |
710 | 0 | } |
711 | | |
712 | | /************************************************************************/ |
713 | | /* hasCurveGeometry() */ |
714 | | /************************************************************************/ |
715 | | |
716 | | OGRBoolean |
717 | | OGRCircularString::hasCurveGeometry(int /* bLookForNonLinear */) const |
718 | 9 | { |
719 | 9 | return TRUE; |
720 | 9 | } |
721 | | |
722 | | /************************************************************************/ |
723 | | /* getLinearGeometry() */ |
724 | | /************************************************************************/ |
725 | | |
726 | | OGRGeometry * |
727 | | OGRCircularString::getLinearGeometry(double dfMaxAngleStepSizeDegrees, |
728 | | const char *const *papszOptions) const |
729 | 0 | { |
730 | 0 | return CurveToLine(dfMaxAngleStepSizeDegrees, papszOptions); |
731 | 0 | } |
732 | | |
733 | | //! @cond Doxygen_Suppress |
734 | | /************************************************************************/ |
735 | | /* GetCasterToLineString() */ |
736 | | /************************************************************************/ |
737 | | |
738 | | static OGRLineString *CasterToLineString(OGRCurve *poGeom) |
739 | 0 | { |
740 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible", |
741 | 0 | poGeom->getGeometryName()); |
742 | 0 | delete poGeom; |
743 | 0 | return nullptr; |
744 | 0 | } |
745 | | |
746 | | OGRCurveCasterToLineString OGRCircularString::GetCasterToLineString() const |
747 | 0 | { |
748 | 0 | return ::CasterToLineString; |
749 | 0 | } |
750 | | |
751 | | /************************************************************************/ |
752 | | /* GetCasterToLinearRing() */ |
753 | | /************************************************************************/ |
754 | | |
755 | | static OGRLinearRing *CasterToLinearRing(OGRCurve *poGeom) |
756 | 0 | { |
757 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "%s found. Conversion impossible", |
758 | 0 | poGeom->getGeometryName()); |
759 | 0 | delete poGeom; |
760 | 0 | return nullptr; |
761 | 0 | } |
762 | | |
763 | | OGRCurveCasterToLinearRing OGRCircularString::GetCasterToLinearRing() const |
764 | 0 | { |
765 | 0 | return ::CasterToLinearRing; |
766 | 0 | } |
767 | | |
768 | | //! @endcond |
769 | | |
770 | | /************************************************************************/ |
771 | | /* IsFullCircle() */ |
772 | | /************************************************************************/ |
773 | | |
774 | | int OGRCircularString::IsFullCircle(double &cx, double &cy, |
775 | | double &square_R) const |
776 | 0 | { |
777 | 0 | if (getNumPoints() == 3 && get_IsClosed()) |
778 | 0 | { |
779 | 0 | const double x0 = getX(0); |
780 | 0 | const double y0 = getY(0); |
781 | 0 | const double x1 = getX(1); |
782 | 0 | const double y1 = getY(1); |
783 | 0 | cx = (x0 + x1) / 2; |
784 | 0 | cy = (y0 + y1) / 2; |
785 | 0 | square_R = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy); |
786 | 0 | return TRUE; |
787 | 0 | } |
788 | | // Full circle defined by 2 arcs? |
789 | 0 | else if (getNumPoints() == 5 && get_IsClosed()) |
790 | 0 | { |
791 | 0 | double R_1 = 0.0; |
792 | 0 | double cx_1 = 0.0; |
793 | 0 | double cy_1 = 0.0; |
794 | 0 | double alpha0_1 = 0.0; |
795 | 0 | double alpha1_1 = 0.0; |
796 | 0 | double alpha2_1 = 0.0; |
797 | 0 | double R_2 = 0.0; |
798 | 0 | double cx_2 = 0.0; |
799 | 0 | double cy_2 = 0.0; |
800 | 0 | double alpha0_2 = 0.0; |
801 | 0 | double alpha1_2 = 0.0; |
802 | 0 | double alpha2_2 = 0.0; |
803 | 0 | if (OGRGeometryFactory::GetCurveParameters( |
804 | 0 | getX(0), getY(0), getX(1), getY(1), getX(2), getY(2), R_1, cx_1, |
805 | 0 | cy_1, alpha0_1, alpha1_1, alpha2_1) && |
806 | 0 | OGRGeometryFactory::GetCurveParameters( |
807 | 0 | getX(2), getY(2), getX(3), getY(3), getX(4), getY(4), R_2, cx_2, |
808 | 0 | cy_2, alpha0_2, alpha1_2, alpha2_2) && |
809 | 0 | fabs(R_1 - R_2) < 1e-10 && fabs(cx_1 - cx_2) < 1e-10 && |
810 | 0 | fabs(cy_1 - cy_2) < 1e-10 && |
811 | 0 | (alpha2_1 - alpha0_1) * (alpha2_2 - alpha0_2) > 0) |
812 | 0 | { |
813 | 0 | cx = cx_1; |
814 | 0 | cy = cy_1; |
815 | 0 | square_R = R_1 * R_1; |
816 | 0 | return TRUE; |
817 | 0 | } |
818 | 0 | } |
819 | 0 | return FALSE; |
820 | 0 | } |
821 | | |
822 | | /************************************************************************/ |
823 | | /* get_AreaOfCurveSegments() */ |
824 | | /************************************************************************/ |
825 | | |
826 | | //! @cond Doxygen_Suppress |
827 | | double OGRCircularString::get_AreaOfCurveSegments() const |
828 | 0 | { |
829 | 0 | double dfArea = 0.0; |
830 | 0 | for (int i = 0; i < getNumPoints() - 2; i += 2) |
831 | 0 | { |
832 | 0 | const double x0 = getX(i); |
833 | 0 | const double y0 = getY(i); |
834 | 0 | const double x1 = getX(i + 1); |
835 | 0 | const double y1 = getY(i + 1); |
836 | 0 | const double x2 = getX(i + 2); |
837 | 0 | const double y2 = getY(i + 2); |
838 | 0 | double R = 0.0; |
839 | 0 | double cx = 0.0; |
840 | 0 | double cy = 0.0; |
841 | 0 | double alpha0 = 0.0; |
842 | 0 | double alpha1 = 0.0; |
843 | 0 | double alpha2 = 0.0; |
844 | 0 | if (OGRGeometryFactory::GetCurveParameters( |
845 | 0 | x0, y0, x1, y1, x2, y2, R, cx, cy, alpha0, alpha1, alpha2)) |
846 | 0 | { |
847 | | // Should be <= PI in absolute value. |
848 | 0 | const double delta_alpha01 = alpha1 - alpha0; |
849 | 0 | const double delta_alpha12 = alpha2 - alpha1; // Same. |
850 | | // http://en.wikipedia.org/wiki/Circular_segment |
851 | 0 | dfArea += 0.5 * R * R * |
852 | 0 | fabs(delta_alpha01 - sin(delta_alpha01) + delta_alpha12 - |
853 | 0 | sin(delta_alpha12)); |
854 | 0 | } |
855 | 0 | } |
856 | 0 | return dfArea; |
857 | 0 | } |
858 | | |
859 | | //! @endcond |
860 | | |
861 | | /************************************************************************/ |
862 | | /* get_Area() */ |
863 | | /************************************************************************/ |
864 | | |
865 | | double OGRCircularString::get_Area() const |
866 | 0 | { |
867 | 0 | if (IsEmpty() || !get_IsClosed()) |
868 | 0 | return 0; |
869 | | |
870 | 0 | double cx = 0.0; |
871 | 0 | double cy = 0.0; |
872 | 0 | double square_R = 0.0; |
873 | |
|
874 | 0 | if (IsFullCircle(cx, cy, square_R)) |
875 | 0 | { |
876 | 0 | return M_PI * square_R; |
877 | 0 | } |
878 | | |
879 | | // Optimization for convex rings. |
880 | 0 | if (IsConvex()) |
881 | 0 | { |
882 | | // Compute area of shape without the circular segments. |
883 | 0 | double dfArea = get_LinearArea(); |
884 | | |
885 | | // Add the area of the spherical segments. |
886 | 0 | dfArea += get_AreaOfCurveSegments(); |
887 | |
|
888 | 0 | return dfArea; |
889 | 0 | } |
890 | | |
891 | 0 | OGRLineString *poLS = CurveToLine(); |
892 | 0 | const double dfArea = poLS->get_Area(); |
893 | 0 | delete poLS; |
894 | |
|
895 | 0 | return dfArea; |
896 | 0 | } |
897 | | |
898 | | /************************************************************************/ |
899 | | /* get_GeodesicArea() */ |
900 | | /************************************************************************/ |
901 | | |
902 | | double OGRCircularString::get_GeodesicArea( |
903 | | const OGRSpatialReference *poSRSOverride) const |
904 | 0 | { |
905 | 0 | if (IsEmpty()) |
906 | 0 | return 0; |
907 | | |
908 | 0 | if (!get_IsClosed()) |
909 | 0 | { |
910 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Non-closed geometry"); |
911 | 0 | return -1; |
912 | 0 | } |
913 | | |
914 | 0 | if (!poSRSOverride) |
915 | 0 | poSRSOverride = getSpatialReference(); |
916 | |
|
917 | 0 | auto poLS = std::unique_ptr<OGRLineString>(CurveToLine()); |
918 | 0 | return poLS->get_GeodesicArea(poSRSOverride); |
919 | 0 | } |
920 | | |
921 | | /************************************************************************/ |
922 | | /* get_GeodesicLength() */ |
923 | | /************************************************************************/ |
924 | | |
925 | | double OGRCircularString::get_GeodesicLength( |
926 | | const OGRSpatialReference *poSRSOverride) const |
927 | 0 | { |
928 | 0 | if (IsEmpty()) |
929 | 0 | return 0; |
930 | | |
931 | 0 | if (!poSRSOverride) |
932 | 0 | poSRSOverride = getSpatialReference(); |
933 | |
|
934 | 0 | auto poLS = std::unique_ptr<OGRLineString>(CurveToLine()); |
935 | 0 | return poLS->get_GeodesicLength(poSRSOverride); |
936 | 0 | } |
937 | | |
938 | | //! @cond Doxygen_Suppress |
939 | | |
940 | | /************************************************************************/ |
941 | | /* ContainsPoint() */ |
942 | | /************************************************************************/ |
943 | | |
944 | | int OGRCircularString::ContainsPoint(const OGRPoint *p) const |
945 | 0 | { |
946 | 0 | double cx = 0.0; |
947 | 0 | double cy = 0.0; |
948 | 0 | double square_R = 0.0; |
949 | 0 | if (IsFullCircle(cx, cy, square_R)) |
950 | 0 | { |
951 | 0 | const double square_dist = (p->getX() - cx) * (p->getX() - cx) + |
952 | 0 | (p->getY() - cy) * (p->getY() - cy); |
953 | 0 | return square_dist < square_R; |
954 | 0 | } |
955 | 0 | return -1; |
956 | 0 | } |
957 | | |
958 | | /************************************************************************/ |
959 | | /* IntersectsPoint() */ |
960 | | /************************************************************************/ |
961 | | |
962 | | int OGRCircularString::IntersectsPoint(const OGRPoint *p) const |
963 | 0 | { |
964 | 0 | double cx = 0.0; |
965 | 0 | double cy = 0.0; |
966 | 0 | double square_R = 0.0; |
967 | 0 | if (IsFullCircle(cx, cy, square_R)) |
968 | 0 | { |
969 | 0 | const double square_dist = (p->getX() - cx) * (p->getX() - cx) + |
970 | 0 | (p->getY() - cy) * (p->getY() - cy); |
971 | 0 | return square_dist <= square_R; |
972 | 0 | } |
973 | 0 | return -1; |
974 | 0 | } |
975 | | |
976 | | //! @endcond |