Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: KML Translator
4 : * Purpose: Implements OGRLIBKMLDriver
5 : * Author: Brian Case, rush at winkey dot org
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010, Brian Case
9 : * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : *****************************************************************************/
13 :
14 : #include "libkml_headers.h"
15 :
16 : #include "ogr_geometry.h"
17 : #include "ogr_p.h"
18 : #include "ogrlibkmlgeometry.h"
19 :
20 : using kmlbase::Vec3;
21 : using kmldom::CoordinatesPtr;
22 : using kmldom::ElementPtr;
23 : using kmldom::GeometryPtr;
24 : using kmldom::GxLatLonQuadPtr;
25 : using kmldom::GxMultiTrackPtr;
26 : using kmldom::GxTrackPtr;
27 : using kmldom::InnerBoundaryIsPtr;
28 : using kmldom::KmlFactory;
29 : using kmldom::LatLonBoxPtr;
30 : using kmldom::LinearRingPtr;
31 : using kmldom::LineStringPtr;
32 : using kmldom::MultiGeometryPtr;
33 : using kmldom::OuterBoundaryIsPtr;
34 : using kmldom::PointPtr;
35 : using kmldom::PolygonPtr;
36 :
37 794 : static bool NormalizeLongLat(double &x, double &y)
38 : {
39 794 : if (x >= -180 && x <= 180)
40 : {
41 : // nominal
42 : }
43 6 : else if (x > 180 && x <= 180 + 360)
44 2 : x -= 360;
45 4 : else if (x < -180 && x >= -180 - 360)
46 2 : x += 360;
47 : else
48 : {
49 : const bool bStrictCompliance =
50 2 : CPLTestBool(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE"));
51 2 : CPLError(bStrictCompliance ? CE_Failure : CE_Warning, CPLE_AppDefined,
52 : "Invalid longitude %g", x);
53 2 : if (bStrictCompliance)
54 2 : return false;
55 : }
56 :
57 792 : constexpr double EPSILON = 1e-8;
58 792 : if (y >= -90 && y <= 90)
59 : {
60 : // nominal
61 : }
62 11 : else if (y > 90 && y < 90 + EPSILON)
63 : {
64 1 : y = 90;
65 : }
66 10 : else if (y < -90 && y > -90 - EPSILON)
67 : {
68 1 : y = -90;
69 : }
70 : else
71 : {
72 : const bool bStrictCompliance =
73 9 : CPLTestBool(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE"));
74 9 : CPLError(bStrictCompliance ? CE_Failure : CE_Warning, CPLE_AppDefined,
75 : "Invalid latitude %g", y);
76 9 : if (bStrictCompliance)
77 9 : return false;
78 : }
79 783 : return true;
80 : }
81 :
82 : /******************************************************************************
83 : Function to write out a ogr geometry to kml.
84 :
85 : Args:
86 : poOgrGeom the ogr geometry
87 : extra used in recursion, just pass -1
88 : poKmlFactory pointer to the libkml dom factory
89 :
90 : Returns:
91 : ElementPtr to the geometry created
92 :
93 : ******************************************************************************/
94 :
95 320 : ElementPtr geom2kml(OGRGeometry *poOgrGeom, int extra, KmlFactory *poKmlFactory)
96 : {
97 320 : if (!poOgrGeom)
98 : {
99 0 : return nullptr;
100 : }
101 :
102 : /***** libkml geom vars *****/
103 640 : CoordinatesPtr coordinates = nullptr;
104 :
105 : // This will be the return value.
106 640 : ElementPtr poKmlGeometry = nullptr;
107 :
108 : /***** Other vars *****/
109 320 : int numpoints = 0;
110 320 : const OGRwkbGeometryType type = poOgrGeom->getGeometryType();
111 :
112 320 : switch (type)
113 : {
114 61 : case wkbPoint:
115 : {
116 61 : const OGRPoint *poOgrPoint = poOgrGeom->toPoint();
117 61 : PointPtr poKmlPoint = nullptr;
118 61 : if (poOgrPoint->getCoordinateDimension() == 0)
119 : {
120 0 : poKmlPoint = poKmlFactory->CreatePoint();
121 0 : poKmlGeometry = poKmlPoint;
122 : }
123 : else
124 : {
125 61 : double x = poOgrPoint->getX();
126 61 : double y = poOgrPoint->getY();
127 :
128 61 : if (!NormalizeLongLat(x, y))
129 6 : return nullptr;
130 :
131 55 : coordinates = poKmlFactory->CreateCoordinates();
132 55 : coordinates->add_latlng(y, x);
133 55 : poKmlPoint = poKmlFactory->CreatePoint();
134 55 : poKmlGeometry = poKmlPoint;
135 55 : poKmlPoint->set_coordinates(coordinates);
136 : }
137 :
138 55 : break;
139 : }
140 26 : case wkbPoint25D:
141 : {
142 26 : const OGRPoint *poOgrPoint = poOgrGeom->toPoint();
143 :
144 26 : double x = poOgrPoint->getX();
145 26 : double y = poOgrPoint->getY();
146 26 : const double z = poOgrPoint->getZ();
147 :
148 26 : if (!NormalizeLongLat(x, y))
149 1 : return nullptr;
150 :
151 25 : coordinates = poKmlFactory->CreateCoordinates();
152 25 : coordinates->add_latlngalt(y, x, z);
153 50 : PointPtr poKmlPoint = poKmlFactory->CreatePoint();
154 25 : poKmlGeometry = poKmlPoint;
155 25 : poKmlPoint->set_coordinates(coordinates);
156 :
157 25 : break;
158 : }
159 53 : case wkbLineString:
160 : {
161 53 : OGRLineString *poOgrLineString = poOgrGeom->toLineString();
162 :
163 53 : if (extra >= 0)
164 : {
165 23 : poOgrGeom->toLinearRing()->closeRings();
166 : }
167 :
168 53 : numpoints = poOgrLineString->getNumPoints();
169 53 : if (extra >= 0)
170 : {
171 23 : if (numpoints < 4 && CPLTestBool(CPLGetConfigOption(
172 : "LIBKML_STRICT_COMPLIANCE", "TRUE")))
173 : {
174 0 : CPLError(CE_Failure, CPLE_NotSupported,
175 : "A linearring should have at least 4 points");
176 2 : return nullptr;
177 : }
178 : }
179 : else
180 : {
181 30 : if (numpoints < 2 && CPLTestBool(CPLGetConfigOption(
182 : "LIBKML_STRICT_COMPLIANCE", "TRUE")))
183 : {
184 0 : CPLError(CE_Failure, CPLE_NotSupported,
185 : "A linestring should have at least 2 points");
186 0 : return nullptr;
187 : }
188 : }
189 :
190 53 : coordinates = poKmlFactory->CreateCoordinates();
191 :
192 53 : OGRPoint point;
193 :
194 419 : for (int i = 0; i < numpoints; i++)
195 : {
196 368 : poOgrLineString->getPoint(i, &point);
197 :
198 368 : double x = point.getX();
199 368 : double y = point.getY();
200 :
201 368 : if (!NormalizeLongLat(x, y))
202 2 : return nullptr;
203 :
204 366 : coordinates->add_latlng(y, x);
205 : }
206 :
207 : /***** Check if its a wkbLinearRing *****/
208 51 : if (extra < 0)
209 : {
210 : LineStringPtr poKmlLineString =
211 58 : poKmlFactory->CreateLineString();
212 29 : poKmlGeometry = poKmlLineString;
213 29 : poKmlLineString->set_coordinates(coordinates);
214 :
215 29 : break;
216 : }
217 : [[fallthrough]];
218 : }
219 : /***** fallthrough *****/
220 :
221 : case wkbLinearRing: // This case is for readability only.
222 : {
223 44 : LinearRingPtr poKmlLinearRing = poKmlFactory->CreateLinearRing();
224 22 : poKmlLinearRing->set_coordinates(coordinates);
225 :
226 22 : if (!extra)
227 : {
228 : OuterBoundaryIsPtr poKmlOuterRing =
229 42 : poKmlFactory->CreateOuterBoundaryIs();
230 21 : poKmlOuterRing->set_linearring(poKmlLinearRing);
231 21 : poKmlGeometry = poKmlOuterRing;
232 : }
233 : else
234 : {
235 : InnerBoundaryIsPtr poKmlInnerRing =
236 2 : poKmlFactory->CreateInnerBoundaryIs();
237 1 : poKmlGeometry = poKmlInnerRing;
238 1 : poKmlInnerRing->set_linearring(poKmlLinearRing);
239 : }
240 :
241 22 : break;
242 : }
243 66 : case wkbLineString25D:
244 : {
245 66 : const OGRLineString *poOgrLineString = poOgrGeom->toLineString();
246 :
247 66 : if (extra >= 0)
248 : {
249 49 : poOgrGeom->toLinearRing()->closeRings();
250 : }
251 :
252 66 : numpoints = poOgrLineString->getNumPoints();
253 66 : if (extra >= 0)
254 : {
255 49 : if (numpoints < 4 && CPLTestBool(CPLGetConfigOption(
256 : "LIBKML_STRICT_COMPLIANCE", "TRUE")))
257 : {
258 0 : CPLError(CE_Failure, CPLE_NotSupported,
259 : "A linearring should have at least 4 points");
260 2 : return nullptr;
261 : }
262 : }
263 : else
264 : {
265 17 : if (numpoints < 2 && CPLTestBool(CPLGetConfigOption(
266 : "LIBKML_STRICT_COMPLIANCE", "TRUE")))
267 : {
268 0 : CPLError(CE_Failure, CPLE_NotSupported,
269 : "A linestring should have at least 2 points");
270 0 : return nullptr;
271 : }
272 : }
273 :
274 66 : coordinates = poKmlFactory->CreateCoordinates();
275 66 : OGRPoint point;
276 :
277 403 : for (int i = 0; i < numpoints; i++)
278 : {
279 339 : poOgrLineString->getPoint(i, &point);
280 :
281 339 : double x = point.getX();
282 339 : double y = point.getY();
283 339 : const double z = point.getZ();
284 :
285 339 : if (!NormalizeLongLat(x, y))
286 2 : return nullptr;
287 :
288 337 : coordinates->add_latlngalt(y, x, z);
289 : }
290 :
291 : /***** Check if its a wkbLinearRing *****/
292 64 : if (extra < 0)
293 : {
294 : LineStringPtr poKmlLineString =
295 32 : poKmlFactory->CreateLineString();
296 16 : poKmlGeometry = poKmlLineString;
297 16 : poKmlLineString->set_coordinates(coordinates);
298 :
299 16 : break;
300 : }
301 : /***** fallthrough *****/
302 :
303 : // case wkbLinearRing25D: // This case is for readability only.
304 :
305 96 : LinearRingPtr poKmlLinearRing = poKmlFactory->CreateLinearRing();
306 48 : poKmlLinearRing->set_coordinates(coordinates);
307 :
308 48 : if (!extra)
309 : {
310 : OuterBoundaryIsPtr poKmlOuterRing =
311 70 : poKmlFactory->CreateOuterBoundaryIs();
312 35 : poKmlGeometry = poKmlOuterRing;
313 35 : poKmlOuterRing->set_linearring(poKmlLinearRing);
314 : }
315 : else
316 : {
317 : InnerBoundaryIsPtr poKmlInnerRing =
318 26 : poKmlFactory->CreateInnerBoundaryIs();
319 13 : poKmlGeometry = poKmlInnerRing;
320 13 : poKmlInnerRing->set_linearring(poKmlLinearRing);
321 : }
322 :
323 48 : break;
324 : }
325 23 : case wkbPolygon:
326 : {
327 23 : if (CPLTestBool(
328 46 : CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) &&
329 23 : OGRGeometryFactory::haveGEOS())
330 : {
331 : bool bError;
332 : {
333 23 : CPLErrorStateBackuper oErrorStateBackuper;
334 23 : bError = !poOgrGeom->IsValid();
335 : }
336 23 : if (bError)
337 : {
338 1 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid polygon");
339 3 : return nullptr;
340 : }
341 : }
342 :
343 22 : PolygonPtr poKmlPolygon = poKmlFactory->CreatePolygon();
344 22 : poKmlGeometry = poKmlPolygon;
345 :
346 22 : OGRPolygon *poOgrPolygon = poOgrGeom->toPolygon();
347 : ElementPtr poKmlTmpGeometry =
348 22 : geom2kml(poOgrPolygon->getExteriorRing(), 0, poKmlFactory);
349 22 : if (!poKmlTmpGeometry)
350 1 : return nullptr;
351 42 : poKmlPolygon->set_outerboundaryis(
352 42 : AsOuterBoundaryIs(poKmlTmpGeometry));
353 :
354 21 : const int nGeom = poOgrPolygon->getNumInteriorRings();
355 22 : for (int i = 0; i < nGeom; i++)
356 : {
357 2 : poKmlTmpGeometry = geom2kml(poOgrPolygon->getInteriorRing(i),
358 1 : i + 1, poKmlFactory);
359 1 : if (!poKmlTmpGeometry)
360 0 : return nullptr;
361 2 : poKmlPolygon->add_innerboundaryis(
362 2 : AsInnerBoundaryIs(poKmlTmpGeometry));
363 : }
364 :
365 21 : break;
366 : }
367 37 : case wkbPolygon25D:
368 : {
369 37 : if (CPLTestBool(
370 74 : CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) &&
371 37 : OGRGeometryFactory::haveGEOS())
372 : {
373 : bool bError;
374 : {
375 37 : CPLErrorStateBackuper oErrorStateBackuper;
376 37 : bError = !poOgrGeom->IsValid();
377 : }
378 37 : if (bError)
379 : {
380 1 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid polygon");
381 3 : return nullptr;
382 : }
383 : }
384 :
385 36 : PolygonPtr poKmlPolygon = poKmlFactory->CreatePolygon();
386 36 : poKmlGeometry = poKmlPolygon;
387 :
388 36 : OGRPolygon *poOgrPolygon = poOgrGeom->toPolygon();
389 : ElementPtr poKmlTmpGeometry =
390 36 : geom2kml(poOgrPolygon->getExteriorRing(), 0, poKmlFactory);
391 36 : if (!poKmlTmpGeometry)
392 1 : return nullptr;
393 70 : poKmlPolygon->set_outerboundaryis(
394 70 : AsOuterBoundaryIs(poKmlTmpGeometry));
395 :
396 35 : const int nGeom = poOgrPolygon->getNumInteriorRings();
397 48 : for (int i = 0; i < nGeom; i++)
398 : {
399 26 : poKmlTmpGeometry = geom2kml(poOgrPolygon->getInteriorRing(i),
400 13 : i + 1, poKmlFactory);
401 13 : if (!poKmlTmpGeometry)
402 0 : return nullptr;
403 26 : poKmlPolygon->add_innerboundaryis(
404 26 : AsInnerBoundaryIs(poKmlTmpGeometry));
405 : }
406 :
407 35 : break;
408 : }
409 54 : case wkbMultiPoint:
410 : case wkbMultiLineString:
411 : case wkbMultiPolygon:
412 : case wkbGeometryCollection:
413 : case wkbMultiPoint25D:
414 : case wkbMultiLineString25D:
415 : case wkbMultiPolygon25D:
416 : case wkbGeometryCollection25D:
417 : {
418 : OGRGeometryCollection *poOgrMultiGeom =
419 54 : poOgrGeom->toGeometryCollection();
420 :
421 54 : const int nGeom = poOgrMultiGeom->getNumGeometries();
422 :
423 54 : if (nGeom == 1 && CPLTestBool(CPLGetConfigOption(
424 : "LIBKML_STRICT_COMPLIANCE", "TRUE")))
425 : {
426 21 : CPLDebug("LIBKML",
427 : "Turning multiple geometry into single geometry");
428 42 : poKmlGeometry = geom2kml(poOgrMultiGeom->getGeometryRef(0), -1,
429 21 : poKmlFactory);
430 21 : if (!poKmlGeometry)
431 1 : return nullptr;
432 : }
433 : else
434 : {
435 33 : if (nGeom == 0 && CPLTestBool(CPLGetConfigOption(
436 : "LIBKML_STRICT_COMPLIANCE", "TRUE")))
437 : {
438 1 : CPLError(CE_Warning, CPLE_AppDefined,
439 : "Empty multi geometry are not recommended");
440 : }
441 :
442 : MultiGeometryPtr poKmlMultiGeometry =
443 33 : poKmlFactory->CreateMultiGeometry();
444 33 : poKmlGeometry = poKmlMultiGeometry;
445 :
446 102 : for (int i = 0; i < nGeom; i++)
447 : {
448 : ElementPtr poKmlTmpGeometry = geom2kml(
449 70 : poOgrMultiGeom->getGeometryRef(i), -1, poKmlFactory);
450 70 : if (!poKmlTmpGeometry)
451 1 : return nullptr;
452 138 : poKmlMultiGeometry->add_geometry(
453 138 : AsGeometry(std::move(poKmlTmpGeometry)));
454 : }
455 : }
456 :
457 52 : break;
458 : }
459 0 : case wkbUnknown:
460 : case wkbNone:
461 : default:
462 0 : break;
463 : }
464 :
465 303 : return poKmlGeometry;
466 : }
467 :
468 : /******************************************************************************
469 : Recursive function to read a kml geometry and translate to ogr.
470 :
471 : Args:
472 : poKmlGeometry pointer to the kml geometry to translate
473 : poOgrSRS pointer to the spatial ref to set on the geometry
474 :
475 : Returns:
476 : pointer to the new ogr geometry object
477 :
478 : ******************************************************************************/
479 :
480 2027 : static OGRGeometry *kml2geom_rec(const GeometryPtr &poKmlGeometry,
481 : OGRSpatialReference *poOgrSRS)
482 : {
483 : /***** ogr geom vars *****/
484 2027 : OGRPoint *poOgrPoint = nullptr;
485 2027 : OGRLineString *poOgrLineString = nullptr;
486 2027 : OGRLinearRing *poOgrLinearRing = nullptr;
487 2027 : OGRPolygon *poOgrPolygon = nullptr;
488 2027 : OGRGeometryCollection *poOgrMultiGeometry = nullptr;
489 2027 : OGRGeometry *poOgrGeometry = nullptr;
490 2027 : OGRGeometry *poOgrTmpGeometry = nullptr;
491 :
492 2027 : switch (poKmlGeometry->Type())
493 : {
494 212 : case kmldom::Type_Point:
495 : {
496 424 : PointPtr poKmlPoint = AsPoint(poKmlGeometry);
497 212 : if (poKmlPoint->has_coordinates())
498 : {
499 422 : CoordinatesPtr poKmlCoordinates = poKmlPoint->get_coordinates();
500 : const size_t nCoords =
501 211 : poKmlCoordinates->get_coordinates_array_size();
502 211 : if (nCoords > 0)
503 : {
504 : const Vec3 oKmlVec =
505 210 : poKmlCoordinates->get_coordinates_array_at(0);
506 :
507 210 : if (oKmlVec.has_altitude())
508 402 : poOgrPoint = new OGRPoint(oKmlVec.get_longitude(),
509 201 : oKmlVec.get_latitude(),
510 201 : oKmlVec.get_altitude());
511 : else
512 18 : poOgrPoint = new OGRPoint(oKmlVec.get_longitude(),
513 9 : oKmlVec.get_latitude());
514 :
515 210 : poOgrGeometry = poOgrPoint;
516 : }
517 : else
518 : {
519 1 : poOgrGeometry = new OGRPoint();
520 : }
521 : }
522 : else
523 : {
524 1 : poOgrGeometry = new OGRPoint();
525 : }
526 :
527 212 : break;
528 : }
529 240 : case kmldom::Type_LineString:
530 : {
531 480 : LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
532 240 : poOgrLineString = new OGRLineString();
533 240 : if (poKmlLineString->has_coordinates())
534 : {
535 : CoordinatesPtr poKmlCoordinates =
536 478 : poKmlLineString->get_coordinates();
537 :
538 : const size_t nCoords =
539 239 : poKmlCoordinates->get_coordinates_array_size();
540 1865 : for (size_t i = 0; i < nCoords; i++)
541 : {
542 : const Vec3 oKmlVec =
543 1626 : poKmlCoordinates->get_coordinates_array_at(i);
544 1626 : if (oKmlVec.has_altitude())
545 1618 : poOgrLineString->addPoint(oKmlVec.get_longitude(),
546 : oKmlVec.get_latitude(),
547 : oKmlVec.get_altitude());
548 : else
549 8 : poOgrLineString->addPoint(oKmlVec.get_longitude(),
550 : oKmlVec.get_latitude());
551 : }
552 : }
553 240 : poOgrGeometry = poOgrLineString;
554 :
555 240 : break;
556 : }
557 799 : case kmldom::Type_LinearRing:
558 : {
559 1598 : LinearRingPtr poKmlLinearRing = AsLinearRing(poKmlGeometry);
560 799 : poOgrLinearRing = new OGRLinearRing();
561 799 : if (poKmlLinearRing->has_coordinates())
562 : {
563 : CoordinatesPtr poKmlCoordinates =
564 1592 : poKmlLinearRing->get_coordinates();
565 :
566 : const size_t nCoords =
567 796 : poKmlCoordinates->get_coordinates_array_size();
568 15569 : for (size_t i = 0; i < nCoords; i++)
569 : {
570 : const Vec3 oKmlVec =
571 14773 : poKmlCoordinates->get_coordinates_array_at(i);
572 14773 : if (oKmlVec.has_altitude())
573 14731 : poOgrLinearRing->addPoint(oKmlVec.get_longitude(),
574 : oKmlVec.get_latitude(),
575 : oKmlVec.get_altitude());
576 : else
577 42 : poOgrLinearRing->addPoint(oKmlVec.get_longitude(),
578 : oKmlVec.get_latitude());
579 : }
580 : }
581 799 : poOgrGeometry = poOgrLinearRing;
582 :
583 799 : break;
584 : }
585 745 : case kmldom::Type_Polygon:
586 : {
587 1490 : PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
588 :
589 745 : poOgrPolygon = new OGRPolygon();
590 745 : if (poKmlPolygon->has_outerboundaryis())
591 : {
592 : OuterBoundaryIsPtr poKmlOuterRing =
593 1488 : poKmlPolygon->get_outerboundaryis();
594 : LinearRingPtr poKmlLinearRing =
595 1488 : poKmlOuterRing->get_linearring();
596 744 : if (poKmlLinearRing)
597 : {
598 743 : poOgrTmpGeometry = kml2geom_rec(poKmlLinearRing, poOgrSRS);
599 :
600 743 : poOgrPolygon->addRingDirectly(
601 743 : (OGRLinearRing *)poOgrTmpGeometry);
602 : }
603 : }
604 : const size_t nRings =
605 745 : poKmlPolygon->get_innerboundaryis_array_size();
606 799 : for (size_t i = 0; i < nRings; i++)
607 : {
608 : InnerBoundaryIsPtr poKmlInnerRing =
609 108 : poKmlPolygon->get_innerboundaryis_array_at(i);
610 : LinearRingPtr poKmlLinearRing =
611 108 : poKmlInnerRing->get_linearring();
612 54 : if (poKmlLinearRing)
613 : {
614 53 : poOgrTmpGeometry = kml2geom_rec(poKmlLinearRing, poOgrSRS);
615 :
616 53 : poOgrPolygon->addRingDirectly(
617 53 : (OGRLinearRing *)poOgrTmpGeometry);
618 : }
619 : }
620 745 : poOgrGeometry = poOgrPolygon;
621 :
622 745 : break;
623 : }
624 28 : case kmldom::Type_MultiGeometry:
625 : {
626 : MultiGeometryPtr poKmlMultiGeometry =
627 56 : AsMultiGeometry(poKmlGeometry);
628 28 : const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
629 :
630 : // Detect subgeometry type to instantiate appropriate
631 : // multi geometry type.
632 28 : kmldom::KmlDomType type = kmldom::Type_Unknown;
633 67 : for (size_t i = 0; i < nGeom; i++)
634 : {
635 : GeometryPtr poKmlTmpGeometry =
636 44 : poKmlMultiGeometry->get_geometry_array_at(i);
637 44 : if (type == kmldom::Type_Unknown)
638 : {
639 25 : type = poKmlTmpGeometry->Type();
640 : }
641 19 : else if (type != poKmlTmpGeometry->Type())
642 : {
643 5 : type = kmldom::Type_Unknown;
644 5 : break;
645 : }
646 : }
647 :
648 28 : if (type == kmldom::Type_Point)
649 8 : poOgrMultiGeometry = new OGRMultiPoint();
650 20 : else if (type == kmldom::Type_LineString)
651 6 : poOgrMultiGeometry = new OGRMultiLineString();
652 14 : else if (type == kmldom::Type_Polygon)
653 6 : poOgrMultiGeometry = new OGRMultiPolygon();
654 : else
655 8 : poOgrMultiGeometry = new OGRGeometryCollection();
656 :
657 72 : for (size_t i = 0; i < nGeom; i++)
658 : {
659 : GeometryPtr poKmlTmpGeometry =
660 88 : poKmlMultiGeometry->get_geometry_array_at(i);
661 44 : poOgrTmpGeometry = kml2geom_rec(poKmlTmpGeometry, poOgrSRS);
662 :
663 44 : poOgrMultiGeometry->addGeometryDirectly(poOgrTmpGeometry);
664 : }
665 28 : poOgrGeometry = poOgrMultiGeometry;
666 28 : break;
667 : }
668 2 : case kmldom::Type_GxTrack:
669 : {
670 4 : GxTrackPtr poKmlGxTrack = AsGxTrack(poKmlGeometry);
671 2 : const size_t nCoords = poKmlGxTrack->get_gx_coord_array_size();
672 2 : poOgrLineString = new OGRLineString();
673 11 : for (size_t i = 0; i < nCoords; i++)
674 : {
675 9 : const Vec3 oKmlVec = poKmlGxTrack->get_gx_coord_array_at(i);
676 9 : if (oKmlVec.has_altitude())
677 7 : poOgrLineString->addPoint(oKmlVec.get_longitude(),
678 : oKmlVec.get_latitude(),
679 : oKmlVec.get_altitude());
680 : else
681 2 : poOgrLineString->addPoint(oKmlVec.get_longitude(),
682 : oKmlVec.get_latitude());
683 : }
684 2 : poOgrGeometry = poOgrLineString;
685 2 : break;
686 : }
687 1 : case kmldom::Type_GxMultiTrack:
688 : {
689 2 : GxMultiTrackPtr poKmlGxMultiTrack = AsGxMultiTrack(poKmlGeometry);
690 1 : const size_t nGeom = poKmlGxMultiTrack->get_gx_track_array_size();
691 1 : poOgrMultiGeometry = new OGRMultiLineString();
692 2 : for (size_t j = 0; j < nGeom; j++)
693 : {
694 : GxTrackPtr poKmlGxTrack =
695 2 : poKmlGxMultiTrack->get_gx_track_array_at(j);
696 1 : const size_t nCoords = poKmlGxTrack->get_gx_coord_array_size();
697 1 : poOgrLineString = new OGRLineString();
698 3 : for (size_t i = 0; i < nCoords; i++)
699 : {
700 2 : const Vec3 oKmlVec = poKmlGxTrack->get_gx_coord_array_at(i);
701 2 : if (oKmlVec.has_altitude())
702 0 : poOgrLineString->addPoint(oKmlVec.get_longitude(),
703 : oKmlVec.get_latitude(),
704 : oKmlVec.get_altitude());
705 : else
706 2 : poOgrLineString->addPoint(oKmlVec.get_longitude(),
707 : oKmlVec.get_latitude());
708 : }
709 1 : poOgrMultiGeometry->addGeometryDirectly(poOgrLineString);
710 : }
711 1 : poOgrGeometry = poOgrMultiGeometry;
712 1 : break;
713 : }
714 :
715 0 : default:
716 : {
717 0 : break;
718 : }
719 : }
720 :
721 2027 : if (poOgrGeometry)
722 2027 : poOgrGeometry->assignSpatialReference(poOgrSRS);
723 :
724 2027 : return poOgrGeometry;
725 : }
726 :
727 42 : static OGRGeometry *kml2geom_latlonbox_int(const LatLonBoxPtr &poKmlLatLonBox,
728 : OGRSpatialReference *poOgrSRS)
729 : {
730 84 : if (!poKmlLatLonBox->has_north() || !poKmlLatLonBox->has_south() ||
731 84 : !poKmlLatLonBox->has_east() || !poKmlLatLonBox->has_west())
732 : {
733 0 : return nullptr;
734 : }
735 42 : const double north = poKmlLatLonBox->get_north();
736 42 : const double south = poKmlLatLonBox->get_south();
737 42 : const double east = poKmlLatLonBox->get_east();
738 42 : const double west = poKmlLatLonBox->get_west();
739 :
740 42 : OGRLinearRing *poOgrRing = new OGRLinearRing();
741 42 : poOgrRing->addPoint(east, north, 0.0);
742 42 : poOgrRing->addPoint(east, south, 0.0);
743 42 : poOgrRing->addPoint(west, south, 0.0);
744 42 : poOgrRing->addPoint(west, north, 0.0);
745 42 : poOgrRing->addPoint(east, north, 0.0);
746 :
747 42 : OGRPolygon *poOgrPolygon = new OGRPolygon();
748 42 : poOgrPolygon->addRingDirectly(poOgrRing);
749 42 : poOgrPolygon->assignSpatialReference(poOgrSRS);
750 :
751 42 : return poOgrPolygon;
752 : }
753 :
754 : static OGRGeometry *
755 0 : kml2geom_latlonquad_int(const GxLatLonQuadPtr &poKmlLatLonQuad,
756 : OGRSpatialReference *poOgrSRS)
757 : {
758 0 : if (!poKmlLatLonQuad->has_coordinates())
759 0 : return nullptr;
760 :
761 0 : const CoordinatesPtr &poKmlCoordinates = poKmlLatLonQuad->get_coordinates();
762 :
763 0 : OGRLinearRing *poOgrLinearRing = new OGRLinearRing();
764 :
765 0 : size_t nCoords = poKmlCoordinates->get_coordinates_array_size();
766 0 : for (size_t i = 0; i < nCoords; i++)
767 : {
768 0 : Vec3 oKmlVec = poKmlCoordinates->get_coordinates_array_at(i);
769 0 : if (oKmlVec.has_altitude())
770 0 : poOgrLinearRing->addPoint(oKmlVec.get_longitude(),
771 : oKmlVec.get_latitude(),
772 : oKmlVec.get_altitude());
773 : else
774 0 : poOgrLinearRing->addPoint(oKmlVec.get_longitude(),
775 : oKmlVec.get_latitude());
776 : }
777 0 : poOgrLinearRing->closeRings();
778 :
779 0 : OGRPolygon *poOgrPolygon = new OGRPolygon();
780 0 : poOgrPolygon->addRingDirectly(poOgrLinearRing);
781 0 : poOgrPolygon->assignSpatialReference(poOgrSRS);
782 :
783 0 : return poOgrPolygon;
784 : }
785 :
786 : /******************************************************************************
787 : Main function to read a kml geometry and translate to ogr.
788 :
789 : Args:
790 : poKmlGeometry pointer to the kml geometry to translate
791 : poOgrSRS pointer to the spatial ref to set on the geometry
792 :
793 : Returns:
794 : pointer to the new ogr geometry object
795 :
796 : ******************************************************************************/
797 :
798 1187 : OGRGeometry *kml2geom(GeometryPtr poKmlGeometry, OGRSpatialReference *poOgrSRS)
799 : {
800 : /***** Get the geometry *****/
801 : OGRGeometry *poOgrGeometry =
802 1187 : kml2geom_rec(std::move(poKmlGeometry), poOgrSRS);
803 :
804 : /***** Split the geometry at the dateline? *****/
805 1187 : const char *pszWrap = CPLGetConfigOption("LIBKML_WRAPDATELINE", "no");
806 1187 : if (!CPLTestBool(pszWrap))
807 1187 : return poOgrGeometry;
808 :
809 0 : char **papszTransformOptions = CSLAddString(nullptr, "WRAPDATELINE=YES");
810 :
811 : /***** Transform *****/
812 0 : OGRGeometry *poOgrDstGeometry = OGRGeometryFactory::transformWithOptions(
813 : poOgrGeometry, nullptr, papszTransformOptions);
814 :
815 : /***** Replace the original geom *****/
816 0 : if (poOgrDstGeometry)
817 : {
818 0 : delete poOgrGeometry;
819 0 : poOgrGeometry = poOgrDstGeometry;
820 : }
821 :
822 0 : CSLDestroy(papszTransformOptions);
823 :
824 0 : return poOgrGeometry;
825 : }
826 :
827 42 : OGRGeometry *kml2geom_latlonbox(LatLonBoxPtr poKmlLatLonBox,
828 : OGRSpatialReference *poOgrSRS)
829 : {
830 : /***** Get the geometry *****/
831 : OGRGeometry *poOgrGeometry =
832 42 : kml2geom_latlonbox_int(poKmlLatLonBox, poOgrSRS);
833 :
834 : /***** Split the geometry at the dateline? *****/
835 42 : const char *pszWrap = CPLGetConfigOption("LIBKML_WRAPDATELINE", "no");
836 :
837 42 : if (!CPLTestBool(pszWrap))
838 42 : return poOgrGeometry;
839 :
840 0 : char **papszTransformOptions = CSLAddString(nullptr, "WRAPDATELINE=YES");
841 :
842 : /***** Transform *****/
843 0 : OGRGeometry *poOgrDstGeometry = OGRGeometryFactory::transformWithOptions(
844 : poOgrGeometry, nullptr, papszTransformOptions);
845 :
846 : /***** Replace the original geom *****/
847 0 : if (poOgrDstGeometry)
848 : {
849 0 : delete poOgrGeometry;
850 0 : poOgrGeometry = poOgrDstGeometry;
851 : }
852 :
853 0 : CSLDestroy(papszTransformOptions);
854 :
855 0 : return poOgrGeometry;
856 : }
857 :
858 0 : OGRGeometry *kml2geom_latlonquad(GxLatLonQuadPtr poKmlLatLonQuad,
859 : OGRSpatialReference *poOgrSRS)
860 : {
861 : /***** Get the geometry *****/
862 : OGRGeometry *poOgrGeometry =
863 0 : kml2geom_latlonquad_int(poKmlLatLonQuad, poOgrSRS);
864 :
865 : /***** Split the geometry at the dateline? *****/
866 0 : const char *pszWrap = CPLGetConfigOption("LIBKML_WRAPDATELINE", "no");
867 0 : if (!CPLTestBool(pszWrap))
868 0 : return poOgrGeometry;
869 :
870 0 : char **papszTransformOptions = CSLAddString(nullptr, "WRAPDATELINE=YES");
871 :
872 : /***** Transform *****/
873 0 : OGRGeometry *poOgrDstGeometry = OGRGeometryFactory::transformWithOptions(
874 : poOgrGeometry, nullptr, papszTransformOptions);
875 :
876 : /***** Replace the original geom *****/
877 0 : if (poOgrDstGeometry)
878 : {
879 0 : delete poOgrGeometry;
880 0 : poOgrGeometry = poOgrDstGeometry;
881 : }
882 :
883 0 : CSLDestroy(papszTransformOptions);
884 :
885 0 : return poOgrGeometry;
886 : }
|