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