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 mines-paris dot org>
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  <ogrsf_frmts.h>
31 #include <ogr_feature.h>
32 #include "ogr_p.h"
33 
34 #include <kml/dom.h>
35 #include <iostream>
36 
37 using kmldom::ExtendedDataPtr;
38 using kmldom::SchemaPtr;
39 using kmldom::SchemaDataPtr;
40 using kmldom::SimpleDataPtr;
41 using kmldom::DataPtr;
42 
43 using kmldom::TimeStampPtr;
44 using kmldom::TimeSpanPtr;
45 using kmldom::TimePrimitivePtr;
46 using kmldom::SnippetPtr;
47 
48 using kmldom::PointPtr;
49 using kmldom::LineStringPtr;
50 using kmldom::PolygonPtr;
51 using kmldom::MultiGeometryPtr;
52 using kmldom::GeometryPtr;
53 
54 using kmldom::FeaturePtr;
55 using kmldom::GroundOverlayPtr;
56 using kmldom::IconPtr;
57 using kmldom::CameraPtr;
58 
59 using kmldom::GxTrackPtr;
60 using kmldom::GxMultiTrackPtr;
61 
62 #include "ogr_libkml.h"
63 
64 #include "ogrlibkmlfield.h"
65 
ogr2altitudemode_rec(GeometryPtr poKmlGeometry,int iAltitudeMode,int isGX)66 void ogr2altitudemode_rec (
67     GeometryPtr poKmlGeometry,
68     int iAltitudeMode,
69     int isGX )
70 {
71 
72     PointPtr poKmlPoint;
73     LineStringPtr poKmlLineString;
74     PolygonPtr poKmlPolygon;
75     MultiGeometryPtr poKmlMultiGeometry;
76 
77     size_t nGeom;
78     size_t i;
79 
80     switch ( poKmlGeometry->Type (  ) ) {
81 
82     case kmldom::Type_Point:
83         poKmlPoint = AsPoint ( poKmlGeometry );
84 
85         if ( !isGX )
86             poKmlPoint->set_altitudemode ( iAltitudeMode );
87         else
88             poKmlPoint->set_gx_altitudemode ( iAltitudeMode );
89 
90         break;
91 
92     case kmldom::Type_LineString:
93         poKmlLineString = AsLineString ( poKmlGeometry );
94 
95         if ( !isGX )
96             poKmlLineString->set_altitudemode ( iAltitudeMode );
97         else
98             poKmlLineString->set_gx_altitudemode ( iAltitudeMode );
99 
100         break;
101 
102     case kmldom::Type_LinearRing:
103         break;
104 
105     case kmldom::Type_Polygon:
106         poKmlPolygon = AsPolygon ( poKmlGeometry );
107 
108         if ( !isGX )
109             poKmlPolygon->set_altitudemode ( iAltitudeMode );
110         else
111             poKmlPolygon->set_gx_altitudemode ( iAltitudeMode );
112 
113         break;
114 
115     case kmldom::Type_MultiGeometry:
116         poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry );
117 
118         nGeom = poKmlMultiGeometry->get_geometry_array_size (  );
119         for ( i = 0; i < nGeom; i++ ) {
120             ogr2altitudemode_rec ( poKmlMultiGeometry->
121                                    get_geometry_array_at ( i ), iAltitudeMode,
122                                    isGX );
123         }
124 
125         break;
126 
127     default:
128         break;
129 
130     }
131 
132 }
133 
ogr2extrude_rec(int nExtrude,GeometryPtr poKmlGeometry)134 void ogr2extrude_rec (
135     int nExtrude,
136     GeometryPtr poKmlGeometry )
137 {
138 
139     PointPtr poKmlPoint;
140     LineStringPtr poKmlLineString;
141     PolygonPtr poKmlPolygon;
142     MultiGeometryPtr poKmlMultiGeometry;
143 
144     size_t nGeom;
145     size_t i;
146 
147     switch ( poKmlGeometry->Type (  ) ) {
148     case kmldom::Type_Point:
149         poKmlPoint = AsPoint ( poKmlGeometry );
150         poKmlPoint->set_extrude ( nExtrude );
151         break;
152 
153     case kmldom::Type_LineString:
154         poKmlLineString = AsLineString ( poKmlGeometry );
155         poKmlLineString->set_extrude ( nExtrude );
156         break;
157 
158     case kmldom::Type_LinearRing:
159         break;
160 
161     case kmldom::Type_Polygon:
162         poKmlPolygon = AsPolygon ( poKmlGeometry );
163         poKmlPolygon->set_extrude ( nExtrude );
164         break;
165 
166     case kmldom::Type_MultiGeometry:
167         poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry );
168 
169         nGeom = poKmlMultiGeometry->get_geometry_array_size (  );
170         for ( i = 0; i < nGeom; i++ ) {
171             ogr2extrude_rec ( nExtrude,
172                               poKmlMultiGeometry->
173                               get_geometry_array_at ( i ) );
174         }
175         break;
176 
177     default:
178         break;
179 
180     }
181 }
182 
ogr2tessellate_rec(int nTessellate,GeometryPtr poKmlGeometry)183 void ogr2tessellate_rec (
184     int nTessellate,
185     GeometryPtr poKmlGeometry )
186 {
187 
188     LineStringPtr poKmlLineString;
189     PolygonPtr poKmlPolygon;
190     MultiGeometryPtr poKmlMultiGeometry;
191 
192     size_t nGeom;
193     size_t i;
194 
195     switch ( poKmlGeometry->Type (  ) ) {
196 
197     case kmldom::Type_Point:
198         break;
199 
200     case kmldom::Type_LineString:
201         poKmlLineString = AsLineString ( poKmlGeometry );
202         poKmlLineString->set_tessellate ( nTessellate );
203         break;
204 
205     case kmldom::Type_LinearRing:
206         break;
207 
208     case kmldom::Type_Polygon:
209         poKmlPolygon = AsPolygon ( poKmlGeometry );
210 
211         poKmlPolygon->set_tessellate ( nTessellate );
212         break;
213 
214     case kmldom::Type_MultiGeometry:
215         poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry );
216 
217         nGeom = poKmlMultiGeometry->get_geometry_array_size (  );
218         for ( i = 0; i < nGeom; i++ ) {
219             ogr2tessellate_rec ( nTessellate,
220                                  poKmlMultiGeometry->
221                                  get_geometry_array_at ( i ) );
222         }
223 
224         break;
225 
226     default:
227         break;
228 
229     }
230 }
231 
232 
233 /************************************************************************/
234 /*                 OGRLIBKMLSanitizeUTF8String()                        */
235 /************************************************************************/
236 
OGRLIBKMLSanitizeUTF8String(const char * pszString)237 static char* OGRLIBKMLSanitizeUTF8String(const char* pszString)
238 {
239     if (!CPLIsUTF8(pszString, -1) &&
240          CSLTestBoolean(CPLGetConfigOption("OGR_FORCE_ASCII", "YES")))
241     {
242         static int bFirstTime = TRUE;
243         if (bFirstTime)
244         {
245             bFirstTime = FALSE;
246             CPLError(CE_Warning, CPLE_AppDefined,
247                     "%s is not a valid UTF-8 string. Forcing it to ASCII.\n"
248                     "If you still want the original string and change the XML file encoding\n"
249                     "afterwards, you can define OGR_FORCE_ASCII=NO as configuration option.\n"
250                     "This warning won't be issued anymore", pszString);
251         }
252         else
253         {
254             CPLDebug("OGR", "%s is not a valid UTF-8 string. Forcing it to ASCII",
255                     pszString);
256         }
257         return CPLForceToASCII(pszString, -1, '?');
258     }
259     else
260         return CPLStrdup(pszString);
261 }
262 
263 /******************************************************************************
264  function to output ogr fields in kml
265 
266  args:
267         poOgrFeat       pointer to the feature the field is in
268         poOgrLayer      pointer to the layer the feature is in
269         poKmlFactory    pointer to the libkml dom factory
270         poKmlPlacemark  pointer to the placemark to add to
271 
272  returns:
273         nothing
274 
275  env vars:
276   LIBKML_TIMESTAMP_FIELD         default: OFTDate or OFTDateTime named timestamp
277   LIBKML_TIMESPAN_BEGIN_FIELD    default: OFTDate or OFTDateTime named begin
278   LIBKML_TIMESPAN_END_FIELD      default: OFTDate or OFTDateTime named end
279   LIBKML_DESCRIPTION_FIELD       default: none
280   LIBKML_NAME_FIELD              default: OFTString field named name
281 
282 
283 ******************************************************************************/
284 
285 
286 
field2kml(OGRFeature * poOgrFeat,OGRLIBKMLLayer * poOgrLayer,KmlFactory * poKmlFactory,FeaturePtr poKmlFeature,int bUseSimpleField)287 void field2kml (
288     OGRFeature * poOgrFeat,
289     OGRLIBKMLLayer * poOgrLayer,
290     KmlFactory * poKmlFactory,
291     FeaturePtr poKmlFeature,
292     int bUseSimpleField)
293 {
294     int i;
295 
296     ExtendedDataPtr poKmlExtendedData = NULL;
297     SchemaDataPtr poKmlSchemaData = NULL;
298     if( bUseSimpleField )
299     {
300         poKmlSchemaData = poKmlFactory->CreateSchemaData (  );
301         SchemaPtr poKmlSchema = poOgrLayer->GetKmlSchema (  );
302 
303         /***** set the url to the schema *****/
304 
305         if ( poKmlSchema && poKmlSchema->has_id (  ) ) {
306             std::string oKmlSchemaID = poKmlSchema->get_id (  );
307 
308 
309             std::string oKmlSchemaURL = "#";
310             oKmlSchemaURL.append ( oKmlSchemaID );
311 
312             poKmlSchemaData->set_schemaurl ( oKmlSchemaURL );
313         }
314     }
315 
316     /***** get the field config *****/
317 
318     struct fieldconfig oFC;
319     get_fieldconfig( &oFC );
320 
321     TimeSpanPtr poKmlTimeSpan = NULL;
322 
323     int nFields = poOgrFeat->GetFieldCount (  );
324     int iSkip1 = -1;
325     int iSkip2 = -1;
326     int iAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND;
327     int isGX = FALSE;
328 
329     for ( i = 0; i < nFields; i++ ) {
330 
331         /***** if the field is set to skip, do so *****/
332 
333         if ( i == iSkip1 || i == iSkip2 )
334             continue;
335 
336         /***** if the field isn't set just bail now *****/
337 
338         if ( !poOgrFeat->IsFieldSet ( i ) )
339             continue;
340 
341         OGRFieldDefn *poOgrFieldDef = poOgrFeat->GetFieldDefnRef ( i );
342         OGRFieldType type = poOgrFieldDef->GetType (  );
343         const char *name = poOgrFieldDef->GetNameRef (  );
344 
345         SimpleDataPtr poKmlSimpleData = NULL;
346         DataPtr poKmlData = NULL;
347         OGRField sFieldDT;
348 
349         switch ( type ) {
350 
351         case OFTString:        //     String of ASCII chars
352             {
353                 char* pszUTF8String = OGRLIBKMLSanitizeUTF8String(
354                                         poOgrFeat->GetFieldAsString ( i ));
355                 if( pszUTF8String[0] == '\0' )
356                 {
357                     CPLFree( pszUTF8String );
358                     continue;
359                 }
360 
361                 /***** name *****/
362 
363                 if ( EQUAL ( name, oFC.namefield ) ) {
364                     poKmlFeature->set_name ( pszUTF8String );
365                     CPLFree( pszUTF8String );
366                     continue;
367                 }
368 
369                 /***** description *****/
370 
371                 else if ( EQUAL ( name, oFC.descfield ) ) {
372                     poKmlFeature->set_description ( pszUTF8String );
373                     CPLFree( pszUTF8String );
374                     continue;
375                 }
376 
377                 /***** altitudemode *****/
378 
379                 else if ( EQUAL ( name, oFC.altitudeModefield ) ) {
380                     const char *pszAltitudeMode = pszUTF8String ;
381 
382                     iAltitudeMode = kmlAltitudeModeFromString(pszAltitudeMode, isGX);
383 
384                     if ( poKmlFeature->IsA ( kmldom::Type_Placemark ) ) {
385                         PlacemarkPtr poKmlPlacemark = AsPlacemark ( poKmlFeature );
386                         if ( poKmlPlacemark->has_geometry (  ) ) {
387                             GeometryPtr poKmlGeometry =
388                                 poKmlPlacemark->get_geometry (  );
389 
390                             ogr2altitudemode_rec ( poKmlGeometry, iAltitudeMode,
391                                                 isGX );
392 
393                         }
394                     }
395 
396                     CPLFree( pszUTF8String );
397 
398                     continue;
399                 }
400 
401                 /***** timestamp *****/
402 
403                 else if ( EQUAL ( name, oFC.tsfield ) ) {
404 
405                     TimeStampPtr poKmlTimeStamp =
406                         poKmlFactory->CreateTimeStamp (  );
407                     poKmlTimeStamp->set_when ( pszUTF8String  );
408                     poKmlFeature->set_timeprimitive ( poKmlTimeStamp );
409 
410                     CPLFree( pszUTF8String );
411 
412                     continue;
413                 }
414 
415                 /***** begin *****/
416 
417                 if ( EQUAL ( name, oFC.beginfield ) ) {
418 
419                     if ( !poKmlTimeSpan ) {
420                         poKmlTimeSpan = poKmlFactory->CreateTimeSpan (  );
421                         poKmlFeature->set_timeprimitive ( poKmlTimeSpan );
422                     }
423 
424                     poKmlTimeSpan->set_begin ( pszUTF8String );
425 
426                     CPLFree( pszUTF8String );
427 
428                     continue;
429 
430                 }
431 
432                 /***** end *****/
433 
434                 else if ( EQUAL ( name, oFC.endfield ) ) {
435 
436                     if ( !poKmlTimeSpan ) {
437                         poKmlTimeSpan = poKmlFactory->CreateTimeSpan (  );
438                         poKmlFeature->set_timeprimitive ( poKmlTimeSpan );
439                     }
440 
441                     poKmlTimeSpan->set_end ( pszUTF8String );
442 
443                     CPLFree( pszUTF8String );
444 
445                     continue;
446                 }
447 
448                 /***** snippet *****/
449 
450                 else if  ( EQUAL ( name, oFC.snippetfield ) ) {
451 
452                     SnippetPtr snippet = poKmlFactory->CreateSnippet (  );
453                     snippet->set_text(pszUTF8String);
454                     poKmlFeature->set_snippet ( snippet );
455 
456                     CPLFree( pszUTF8String );
457 
458                     continue;
459 
460                 }
461 
462                 /***** other special fields *****/
463 
464                 else if (  EQUAL ( name, oFC.iconfield ) ||
465                            EQUAL ( name, oFC.modelfield ) ||
466                            EQUAL ( name, oFC.networklinkfield ) ||
467                            EQUAL ( name, oFC.networklink_refreshMode_field ) ||
468                            EQUAL ( name, oFC.networklink_viewRefreshMode_field ) ||
469                            EQUAL ( name, oFC.networklink_viewFormat_field ) ||
470                            EQUAL ( name, oFC.networklink_httpQuery_field ) ||
471                            EQUAL ( name, oFC.camera_altitudemode_field ) ||
472                            EQUAL ( name, oFC.photooverlayfield ) ||
473                            EQUAL ( name, oFC.photooverlay_shape_field ) ||
474                            EQUAL ( name, oFC.imagepyramid_gridorigin_field ) ) {
475 
476                     CPLFree( pszUTF8String );
477 
478                     continue;
479                 }
480 
481                 /***** other *****/
482 
483                 if( bUseSimpleField )
484                 {
485                     poKmlSimpleData = poKmlFactory->CreateSimpleData (  );
486                     poKmlSimpleData->set_name ( name );
487                     poKmlSimpleData->set_text ( pszUTF8String );
488                 }
489                 else
490                 {
491                     poKmlData = poKmlFactory->CreateData (  );
492                     poKmlData->set_name ( name );
493                     poKmlData->set_value ( pszUTF8String );
494                 }
495 
496                 CPLFree( pszUTF8String );
497 
498                 break;
499             }
500 
501         /* This code checks if there's a OFTTime field with the same name */
502         /* that could be used to compose a DateTime. Not sure this is really */
503         /* supported in OGR data model to have 2 fields with same name... */
504         case OFTDate:          //   Date
505             {
506                 memcpy(&sFieldDT, poOgrFeat->GetRawFieldRef(i), sizeof(OGRField));
507 
508                 int iTimeField;
509 
510                 for ( iTimeField = i + 1; iTimeField < nFields; iTimeField++ ) {
511                     if ( iTimeField == iSkip1 || iTimeField == iSkip2 )
512                         continue;
513 
514                     OGRFieldDefn *poOgrFieldDef2 =
515                         poOgrFeat->GetFieldDefnRef ( i );
516                     OGRFieldType type2 = poOgrFieldDef2->GetType (  );
517                     const char *name2 = poOgrFieldDef2->GetNameRef (  );
518 
519                     if ( EQUAL ( name2, name ) && type2 == OFTTime &&
520                          ( EQUAL ( name, oFC.tsfield ) ||
521                            EQUAL ( name, oFC.beginfield ) ||
522                            EQUAL ( name, oFC.endfield ) ) ) {
523 
524                         const OGRField* psField2 = poOgrFeat->GetRawFieldRef(iTimeField);
525                         sFieldDT.Date.Hour = psField2->Date.Hour;
526                         sFieldDT.Date.Minute = psField2->Date.Minute;
527                         sFieldDT.Date.Second = psField2->Date.Second;
528                         sFieldDT.Date.TZFlag = psField2->Date.TZFlag;
529 
530                         if ( 0 > iSkip1 )
531                             iSkip1 = iTimeField;
532                         else
533                             iSkip2 = iTimeField;
534                     }
535                 }
536 
537                 goto Do_DateTime;
538 
539             }
540 
541         /* This code checks if there's a OFTTime field with the same name */
542         /* that could be used to compose a DateTime. Not sure this is really */
543         /* supported in OGR data model to have 2 fields with same name... */
544         case OFTTime:          //   Time
545             {
546                 memcpy(&sFieldDT, poOgrFeat->GetRawFieldRef(i), sizeof(OGRField));
547 
548                 int iTimeField;
549 
550                 for ( iTimeField = i + 1; iTimeField < nFields; iTimeField++ ) {
551                     if ( iTimeField == iSkip1 || iTimeField == iSkip2 )
552                         continue;
553 
554                     OGRFieldDefn *poOgrFieldDef2 =
555                         poOgrFeat->GetFieldDefnRef ( i );
556                     OGRFieldType type2 = poOgrFieldDef2->GetType (  );
557                     const char *name2 = poOgrFieldDef2->GetNameRef (  );
558 
559                     if ( EQUAL ( name2, name ) && type2 == OFTDate &&
560                          ( EQUAL ( name, oFC.tsfield ) ||
561                            EQUAL ( name, oFC.beginfield ) ||
562                            EQUAL ( name, oFC.endfield ) ) ) {
563 
564                         const OGRField* psField2 = poOgrFeat->GetRawFieldRef(iTimeField);
565                         sFieldDT.Date.Year = psField2->Date.Year;
566                         sFieldDT.Date.Month = psField2->Date.Month;
567                         sFieldDT.Date.Day = psField2->Date.Day;
568 
569                         if ( 0 > iSkip1 )
570                             iSkip1 = iTimeField;
571                         else
572                             iSkip2 = iTimeField;
573                     }
574                 }
575 
576                 goto Do_DateTime;
577 
578             }
579 
580         case OFTDateTime:      //  Date and Time
581             {
582               memcpy(&sFieldDT, poOgrFeat->GetRawFieldRef(i), sizeof(OGRField));
583 
584               Do_DateTime:
585                 /***** timestamp *****/
586 
587                 if ( EQUAL ( name, oFC.tsfield ) ) {
588 
589                     char *timebuf = OGRGetXMLDateTime ( &sFieldDT );
590 
591                     TimeStampPtr poKmlTimeStamp =
592                         poKmlFactory->CreateTimeStamp (  );
593                     poKmlTimeStamp->set_when ( timebuf );
594                     poKmlFeature->set_timeprimitive ( poKmlTimeStamp );
595                     CPLFree( timebuf );
596 
597                     continue;
598                 }
599 
600                 /***** begin *****/
601 
602                 if ( EQUAL ( name, oFC.beginfield ) ) {
603 
604                     char *timebuf = OGRGetXMLDateTime ( &sFieldDT );
605 
606                     if ( !poKmlTimeSpan ) {
607                         poKmlTimeSpan = poKmlFactory->CreateTimeSpan (  );
608                         poKmlFeature->set_timeprimitive ( poKmlTimeSpan );
609                     }
610 
611                     poKmlTimeSpan->set_begin ( timebuf );
612                     CPLFree( timebuf );
613 
614                     continue;
615 
616                 }
617 
618                 /***** end *****/
619 
620                 else if ( EQUAL ( name, oFC.endfield ) ) {
621 
622                     char *timebuf = OGRGetXMLDateTime ( &sFieldDT );
623 
624 
625                     if ( !poKmlTimeSpan ) {
626                         poKmlTimeSpan = poKmlFactory->CreateTimeSpan (  );
627                         poKmlFeature->set_timeprimitive ( poKmlTimeSpan );
628                     }
629 
630                     poKmlTimeSpan->set_end ( timebuf );
631                     CPLFree( timebuf );
632 
633                     continue;
634                 }
635 
636                 /***** other *****/
637 
638                 if( bUseSimpleField )
639                 {
640                     poKmlSimpleData = poKmlFactory->CreateSimpleData (  );
641                     poKmlSimpleData->set_name ( name );
642                     poKmlSimpleData->set_text ( poOgrFeat->
643                                                 GetFieldAsString ( i ) );
644                 }
645                 else
646                 {
647                     poKmlData = poKmlFactory->CreateData (  );
648                     poKmlData->set_name ( name );
649                     poKmlData->set_value ( poOgrFeat->
650                                                 GetFieldAsString ( i ) );
651                 }
652 
653                 break;
654             }
655 
656         case OFTInteger:       //    Simple 32bit integer
657 
658             /***** extrude *****/
659 
660             if ( EQUAL ( name, oFC.extrudefield ) ) {
661 
662                 if ( poKmlFeature->IsA ( kmldom::Type_Placemark ) ) {
663                     PlacemarkPtr poKmlPlacemark = AsPlacemark ( poKmlFeature );
664                     if ( poKmlPlacemark->has_geometry (  )
665                         && -1 < poOgrFeat->GetFieldAsInteger ( i ) ) {
666                         int iExtrude = poOgrFeat->GetFieldAsInteger ( i );
667                         if( iExtrude &&
668                             isGX == FALSE && iAltitudeMode == kmldom::ALTITUDEMODE_CLAMPTOGROUND &&
669                             CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) )
670                         {
671                             CPLError(CE_Warning, CPLE_NotSupported,
672                                 "altitudeMode=clampToGround unsupported with extrude=1");
673                         }
674                         else
675                         {
676                             GeometryPtr poKmlGeometry =
677                                 poKmlPlacemark->get_geometry (  );
678                             ogr2extrude_rec ( iExtrude,
679                                             poKmlGeometry );
680                         }
681                     }
682                 }
683                 continue;
684             }
685 
686             /***** tessellate *****/
687 
688 
689             if ( EQUAL ( name, oFC.tessellatefield ) ) {
690 
691                 if ( poKmlFeature->IsA ( kmldom::Type_Placemark ) ) {
692                     PlacemarkPtr poKmlPlacemark = AsPlacemark ( poKmlFeature );
693                     if ( poKmlPlacemark->has_geometry (  )
694                         && -1 < poOgrFeat->GetFieldAsInteger ( i ) ) {
695                         int iTesselate = poOgrFeat->GetFieldAsInteger ( i );
696                         if( iTesselate &&
697                             !(isGX == FALSE && iAltitudeMode == kmldom::ALTITUDEMODE_CLAMPTOGROUND) &&
698                             !(isGX == TRUE && iAltitudeMode == kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR) &&
699                             CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) )
700                         {
701                             CPLError(CE_Warning, CPLE_NotSupported,
702                                 "altitudeMode!=clampToGround && altitudeMode!=clampToSeaFloor unsupported with tesselate=1");
703                         }
704                         else
705                         {
706                             GeometryPtr poKmlGeometry =
707                                 poKmlPlacemark->get_geometry (  );
708                             ogr2tessellate_rec ( iTesselate,
709                                                 poKmlGeometry );
710                             if( isGX == FALSE && iAltitudeMode == kmldom::ALTITUDEMODE_CLAMPTOGROUND )
711                                 ogr2altitudemode_rec ( poKmlGeometry, iAltitudeMode,
712                                                     isGX );
713                         }
714                     }
715                 }
716 
717                 continue;
718             }
719 
720 
721             /***** visibility *****/
722 
723             if ( EQUAL ( name, oFC.visibilityfield ) ) {
724                 if ( -1 < poOgrFeat->GetFieldAsInteger ( i ) )
725                     poKmlFeature->set_visibility ( poOgrFeat->
726                                                      GetFieldAsInteger ( i ) );
727 
728                 continue;
729             }
730 
731 
732             /***** other special fields *****/
733 
734             else if (  EQUAL ( name, oFC.drawOrderfield ) ||
735                         EQUAL ( name, oFC.networklink_refreshvisibility_field ) ||
736                         EQUAL ( name, oFC.networklink_flytoview_field ) ||
737                         EQUAL ( name, oFC.networklink_refreshInterval_field ) ||
738                         EQUAL ( name, oFC.networklink_viewRefreshMode_field ) ||
739                         EQUAL ( name, oFC.networklink_viewRefreshTime_field ) ||
740                         EQUAL ( name, oFC.imagepyramid_tilesize_field ) ||
741                         EQUAL ( name, oFC.imagepyramid_maxwidth_field ) ||
742                         EQUAL ( name, oFC.imagepyramid_maxheight_field ) ) {
743 
744                 continue;
745             }
746 
747             /***** other *****/
748 
749             if( bUseSimpleField )
750             {
751                 poKmlSimpleData = poKmlFactory->CreateSimpleData (  );
752                 poKmlSimpleData->set_name ( name );
753                 poKmlSimpleData->set_text ( poOgrFeat->GetFieldAsString ( i ) );
754             }
755             else
756             {
757                 poKmlData = poKmlFactory->CreateData (  );
758                 poKmlData->set_name ( name );
759                 poKmlData->set_value ( poOgrFeat->GetFieldAsString ( i ) );
760             }
761 
762             break;
763 
764         case OFTReal:          //   Double Precision floating point
765         {
766             if( EQUAL(name, oFC.headingfield) ||
767                 EQUAL(name, oFC.tiltfield) ||
768                 EQUAL(name, oFC.rollfield) ||
769                 EQUAL(name, oFC.scalexfield) ||
770                 EQUAL(name, oFC.scaleyfield) ||
771                 EQUAL(name, oFC.scalezfield) ||
772                 EQUAL(name, oFC.networklink_refreshInterval_field ) ||
773                 EQUAL(name, oFC.networklink_viewRefreshMode_field) ||
774                 EQUAL(name, oFC.networklink_viewRefreshTime_field) ||
775                 EQUAL(name, oFC.networklink_viewBoundScale_field) ||
776                 EQUAL(name, oFC.camera_longitude_field) ||
777                 EQUAL(name, oFC.camera_latitude_field) ||
778                 EQUAL(name, oFC.camera_altitude_field) ||
779                 EQUAL(name, oFC.leftfovfield) ||
780                 EQUAL(name, oFC.rightfovfield) ||
781                 EQUAL(name, oFC.bottomfovfield) ||
782                 EQUAL(name, oFC.topfovfield) ||
783                 EQUAL(name, oFC.nearfield) ||
784                 EQUAL(name, oFC.camera_altitude_field) )
785             {
786                 continue;
787             }
788 
789             char* pszStr = CPLStrdup( poOgrFeat->GetFieldAsString ( i ) );
790 
791             if( bUseSimpleField )
792             {
793                 poKmlSimpleData = poKmlFactory->CreateSimpleData (  );
794                 poKmlSimpleData->set_name ( name );
795                 poKmlSimpleData->set_text ( pszStr );
796             }
797             else
798             {
799                 poKmlData = poKmlFactory->CreateData (  );
800                 poKmlData->set_name ( name );
801                 poKmlData->set_value ( pszStr );
802             }
803 
804             CPLFree(pszStr);
805 
806             break;
807         }
808 
809         case OFTStringList:    //     Array of strings
810         case OFTIntegerList:   //    List of 32bit integers
811         case OFTRealList:   //    List of doubles
812         case OFTBinary:        //     Raw Binary data
813         case OFTWideStringList:    //     deprecated
814         default:
815 
816         /***** other *****/
817 
818             if( bUseSimpleField )
819             {
820                 poKmlSimpleData = poKmlFactory->CreateSimpleData (  );
821                 poKmlSimpleData->set_name ( name );
822                 poKmlSimpleData->set_text ( poOgrFeat->GetFieldAsString ( i ) );
823             }
824             else
825             {
826                 poKmlData = poKmlFactory->CreateData (  );
827                 poKmlData->set_name ( name );
828                 poKmlData->set_value ( poOgrFeat->GetFieldAsString ( i ) );
829             }
830 
831             break;
832         }
833 
834         if( poKmlSimpleData )
835         {
836             poKmlSchemaData->add_simpledata ( poKmlSimpleData );
837         }
838         else if( poKmlData )
839         {
840             if( poKmlExtendedData == NULL )
841                 poKmlExtendedData = poKmlFactory->CreateExtendedData (  );
842             poKmlExtendedData->add_data ( poKmlData );
843         }
844     }
845 
846     /***** dont add it to the placemark unless there is data *****/
847 
848     if ( bUseSimpleField && poKmlSchemaData->get_simpledata_array_size (  ) > 0 ) {
849         poKmlExtendedData = poKmlFactory->CreateExtendedData (  );
850         poKmlExtendedData->add_schemadata ( poKmlSchemaData );
851     }
852     if( poKmlExtendedData != NULL )
853     {
854         poKmlFeature->set_extendeddata ( poKmlExtendedData );
855     }
856 
857     return;
858 }
859 
860 /******************************************************************************
861  recursive function to read altitude mode from the geometry
862 ******************************************************************************/
863 
kml2altitudemode_rec(GeometryPtr poKmlGeometry,int * pnAltitudeMode,int * pbIsGX)864 int kml2altitudemode_rec (
865     GeometryPtr poKmlGeometry,
866     int *pnAltitudeMode,
867     int *pbIsGX )
868 {
869 
870     PointPtr poKmlPoint;
871     LineStringPtr poKmlLineString;
872     PolygonPtr poKmlPolygon;
873     MultiGeometryPtr poKmlMultiGeometry;
874 
875     size_t nGeom;
876     size_t i;
877 
878     switch ( poKmlGeometry->Type (  ) ) {
879 
880     case kmldom::Type_Point:
881         poKmlPoint = AsPoint ( poKmlGeometry );
882 
883         if ( poKmlPoint->has_altitudemode (  ) ) {
884             *pnAltitudeMode = poKmlPoint->get_altitudemode (  );
885             return TRUE;
886         }
887         else if ( poKmlPoint->has_gx_altitudemode (  ) ) {
888             *pnAltitudeMode = poKmlPoint->get_gx_altitudemode (  );
889             *pbIsGX = TRUE;
890             return TRUE;
891         }
892 
893         break;
894 
895     case kmldom::Type_LineString:
896         poKmlLineString = AsLineString ( poKmlGeometry );
897 
898         if ( poKmlLineString->has_altitudemode (  ) ) {
899             *pnAltitudeMode = poKmlLineString->get_altitudemode (  );
900             return TRUE;
901         }
902         else if ( poKmlLineString->has_gx_altitudemode (  ) ) {
903             *pnAltitudeMode = poKmlLineString->get_gx_altitudemode (  );
904             *pbIsGX = TRUE;
905             return TRUE;
906         }
907         break;
908 
909     case kmldom::Type_LinearRing:
910         break;
911 
912     case kmldom::Type_Polygon:
913         poKmlPolygon = AsPolygon ( poKmlGeometry );
914 
915         if ( poKmlPolygon->has_altitudemode (  ) ) {
916             *pnAltitudeMode = poKmlPolygon->get_altitudemode (  );
917             return TRUE;
918         }
919         else if ( poKmlPolygon->has_gx_altitudemode (  ) ) {
920             *pnAltitudeMode = poKmlPolygon->get_gx_altitudemode (  );
921             *pbIsGX = TRUE;
922             return TRUE;
923         }
924 
925         break;
926 
927     case kmldom::Type_MultiGeometry:
928         poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry );
929 
930         nGeom = poKmlMultiGeometry->get_geometry_array_size (  );
931         for ( i = 0; i < nGeom; i++ ) {
932             if ( kml2altitudemode_rec ( poKmlMultiGeometry->
933                                         get_geometry_array_at ( i ),
934                                         pnAltitudeMode, pbIsGX ) )
935                 return TRUE;
936         }
937 
938         break;
939 
940     default:
941         break;
942 
943     }
944 
945     return FALSE;
946 }
947 
948 /******************************************************************************
949  recursive function to read extrude from the geometry
950 ******************************************************************************/
951 
kml2extrude_rec(GeometryPtr poKmlGeometry,int * pnExtrude)952 int kml2extrude_rec (
953     GeometryPtr poKmlGeometry,
954     int *pnExtrude )
955 {
956 
957     PointPtr poKmlPoint;
958     LineStringPtr poKmlLineString;
959     PolygonPtr poKmlPolygon;
960     MultiGeometryPtr poKmlMultiGeometry;
961 
962     size_t nGeom;
963     size_t i;
964 
965     switch ( poKmlGeometry->Type (  ) ) {
966 
967     case kmldom::Type_Point:
968         poKmlPoint = AsPoint ( poKmlGeometry );
969 
970         if ( poKmlPoint->has_extrude (  ) ) {
971             *pnExtrude = poKmlPoint->get_extrude (  );
972             return TRUE;
973         }
974 
975         break;
976 
977     case kmldom::Type_LineString:
978         poKmlLineString = AsLineString ( poKmlGeometry );
979 
980         if ( poKmlLineString->has_extrude (  ) ) {
981             *pnExtrude = poKmlLineString->get_extrude (  );
982             return TRUE;
983         }
984 
985         break;
986 
987     case kmldom::Type_LinearRing:
988         break;
989 
990     case kmldom::Type_Polygon:
991         poKmlPolygon = AsPolygon ( poKmlGeometry );
992 
993         if ( poKmlPolygon->has_extrude (  ) ) {
994             *pnExtrude = poKmlPolygon->get_extrude (  );
995             return TRUE;
996         }
997 
998         break;
999 
1000     case kmldom::Type_MultiGeometry:
1001         poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry );
1002 
1003         nGeom = poKmlMultiGeometry->get_geometry_array_size (  );
1004         for ( i = 0; i < nGeom; i++ ) {
1005             if ( kml2extrude_rec ( poKmlMultiGeometry->
1006                                    get_geometry_array_at ( i ), pnExtrude ) )
1007                 return TRUE;
1008         }
1009 
1010         break;
1011 
1012     default:
1013         break;
1014 
1015     }
1016 
1017     return FALSE;
1018 }
1019 
1020 /******************************************************************************
1021  recursive function to read tessellate from the geometry
1022 ******************************************************************************/
1023 
kml2tessellate_rec(GeometryPtr poKmlGeometry,int * pnTessellate)1024 int kml2tessellate_rec (
1025     GeometryPtr poKmlGeometry,
1026     int *pnTessellate )
1027 {
1028 
1029     LineStringPtr poKmlLineString;
1030     PolygonPtr poKmlPolygon;
1031     MultiGeometryPtr poKmlMultiGeometry;
1032 
1033     size_t nGeom;
1034     size_t i;
1035 
1036     switch ( poKmlGeometry->Type (  ) ) {
1037 
1038     case kmldom::Type_Point:
1039         break;
1040 
1041     case kmldom::Type_LineString:
1042         poKmlLineString = AsLineString ( poKmlGeometry );
1043 
1044         if ( poKmlLineString->has_tessellate (  ) ) {
1045             *pnTessellate = poKmlLineString->get_tessellate (  );
1046             return TRUE;
1047         }
1048 
1049         break;
1050 
1051     case kmldom::Type_LinearRing:
1052         break;
1053 
1054     case kmldom::Type_Polygon:
1055         poKmlPolygon = AsPolygon ( poKmlGeometry );
1056 
1057         if ( poKmlPolygon->has_tessellate (  ) ) {
1058             *pnTessellate = poKmlPolygon->get_tessellate (  );
1059             return TRUE;
1060         }
1061 
1062         break;
1063 
1064     case kmldom::Type_MultiGeometry:
1065         poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry );
1066 
1067         nGeom = poKmlMultiGeometry->get_geometry_array_size (  );
1068         for ( i = 0; i < nGeom; i++ ) {
1069             if ( kml2tessellate_rec ( poKmlMultiGeometry->
1070                                       get_geometry_array_at ( i ),
1071                                       pnTessellate ) )
1072                 return TRUE;
1073         }
1074 
1075         break;
1076 
1077     default:
1078         break;
1079 
1080     }
1081 
1082     return FALSE;
1083 }
1084 
1085 /************************************************************************/
1086 /*                     ogrkmlSetAltitudeMode()                          */
1087 /************************************************************************/
1088 
ogrkmlSetAltitudeMode(OGRFeature * poOgrFeat,int iField,int nAltitudeMode,int bIsGX)1089 static void ogrkmlSetAltitudeMode(OGRFeature* poOgrFeat, int iField,
1090                                   int nAltitudeMode, int bIsGX)
1091 {
1092     if ( !bIsGX ) {
1093         switch ( nAltitudeMode ) {
1094         case kmldom::ALTITUDEMODE_CLAMPTOGROUND:
1095             poOgrFeat->SetField ( iField, "clampToGround" );
1096             break;
1097 
1098         case kmldom::ALTITUDEMODE_RELATIVETOGROUND:
1099             poOgrFeat->SetField ( iField, "relativeToGround" );
1100             break;
1101 
1102         case kmldom::ALTITUDEMODE_ABSOLUTE:
1103             poOgrFeat->SetField ( iField, "absolute" );
1104             break;
1105 
1106         }
1107     }
1108 
1109     else {
1110         switch ( nAltitudeMode ) {
1111         case kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR:
1112             poOgrFeat->SetField ( iField, "relativeToSeaFloor" );
1113             break;
1114 
1115         case kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR:
1116             poOgrFeat->SetField ( iField, "clampToSeaFloor" );
1117             break;
1118         }
1119     }
1120 }
1121 
1122 /************************************************************************/
1123 /*                            TrimSpaces()                              */
1124 /************************************************************************/
1125 
TrimSpaces(string & oText)1126 static const char* TrimSpaces(string& oText)
1127 {
1128 
1129     /* SerializePretty() adds a new line before the data */
1130     /* ands trailing spaces. I believe this is wrong */
1131     /* as it breaks round-tripping */
1132 
1133     /* Trim trailing spaces */
1134     while (oText.size() != 0 && oText[oText.size()-1] == ' ')
1135         oText.resize(oText.size()-1);
1136 
1137     /* Skip leading newline and spaces */
1138     const char* pszText = oText.c_str (  );
1139     if (pszText[0] == '\n')
1140         pszText ++;
1141     while (pszText[0] == ' ')
1142         pszText ++;
1143 
1144     return pszText;
1145 }
1146 
1147 /************************************************************************/
1148 /*                            kmldatetime2ogr()                         */
1149 /************************************************************************/
1150 
kmldatetime2ogr(OGRFeature * poOgrFeat,const char * pszOGRField,const std::string & osKmlDateTime)1151 static void kmldatetime2ogr( OGRFeature* poOgrFeat,
1152                              const char* pszOGRField,
1153                              const std::string& osKmlDateTime )
1154 {
1155     int iField = poOgrFeat->GetFieldIndex ( pszOGRField );
1156 
1157     if ( iField > -1 ) {
1158         OGRField sField;
1159 
1160         if ( OGRParseXMLDateTime( osKmlDateTime.c_str (  ), &sField ) )
1161             poOgrFeat->SetField ( iField, &sField );
1162     }
1163 }
1164 
1165 /******************************************************************************
1166  function to read kml into ogr fields
1167 ******************************************************************************/
1168 
kml2field(OGRFeature * poOgrFeat,FeaturePtr poKmlFeature)1169 void kml2field (
1170     OGRFeature * poOgrFeat,
1171     FeaturePtr poKmlFeature )
1172 {
1173 
1174     /***** get the field config *****/
1175 
1176     struct fieldconfig oFC;
1177     get_fieldconfig( &oFC );
1178 
1179     /***** name *****/
1180 
1181     if ( poKmlFeature->has_name (  ) ) {
1182         const std::string oKmlName = poKmlFeature->get_name (  );
1183         int iField = poOgrFeat->GetFieldIndex ( oFC.namefield );
1184 
1185         if ( iField > -1 )
1186             poOgrFeat->SetField ( iField, oKmlName.c_str (  ) );
1187     }
1188 
1189     /***** description *****/
1190 
1191     if ( poKmlFeature->has_description (  ) ) {
1192         const std::string oKmlDesc = poKmlFeature->get_description (  );
1193         int iField = poOgrFeat->GetFieldIndex ( oFC.descfield );
1194 
1195         if ( iField > -1 )
1196             poOgrFeat->SetField ( iField, oKmlDesc.c_str (  ) );
1197     }
1198 
1199     if ( poKmlFeature->has_timeprimitive (  ) ) {
1200         TimePrimitivePtr poKmlTimePrimitive =
1201             poKmlFeature->get_timeprimitive (  );
1202 
1203         /***** timestamp *****/
1204 
1205         if ( poKmlTimePrimitive->IsA ( kmldom::Type_TimeStamp ) ) {
1206             TimeStampPtr poKmlTimeStamp = AsTimeStamp ( poKmlTimePrimitive );
1207 
1208             if ( poKmlTimeStamp->has_when (  ) ) {
1209                 const std::string oKmlWhen = poKmlTimeStamp->get_when (  );
1210                 kmldatetime2ogr(poOgrFeat, oFC.tsfield, oKmlWhen );
1211             }
1212         }
1213 
1214         /***** timespan *****/
1215 
1216         if ( poKmlTimePrimitive->IsA ( kmldom::Type_TimeSpan ) ) {
1217             TimeSpanPtr poKmlTimeSpan = AsTimeSpan ( poKmlTimePrimitive );
1218 
1219             /***** begin *****/
1220 
1221             if ( poKmlTimeSpan->has_begin (  ) ) {
1222                 const std::string oKmlWhen = poKmlTimeSpan->get_begin (  );
1223                 kmldatetime2ogr(poOgrFeat, oFC.beginfield, oKmlWhen );
1224             }
1225 
1226             /***** end *****/
1227 
1228             if ( poKmlTimeSpan->has_end (  ) ) {
1229                 const std::string oKmlWhen = poKmlTimeSpan->get_end (  );
1230                 kmldatetime2ogr(poOgrFeat, oFC.endfield, oKmlWhen );
1231             }
1232         }
1233     }
1234 
1235     /***** placemark *****/
1236 
1237     PlacemarkPtr poKmlPlacemark = AsPlacemark ( poKmlFeature );
1238     GroundOverlayPtr poKmlGroundOverlay = AsGroundOverlay ( poKmlFeature );
1239     if ( poKmlPlacemark && poKmlPlacemark->has_geometry (  ) ) {
1240         GeometryPtr poKmlGeometry = poKmlPlacemark->get_geometry (  );
1241 
1242         /***** altitudeMode *****/
1243 
1244 
1245         int bIsGX = FALSE;
1246         int nAltitudeMode = -1;
1247 
1248         int iField = poOgrFeat->GetFieldIndex ( oFC.altitudeModefield );
1249 
1250         if ( iField > -1 ) {
1251 
1252             if ( kml2altitudemode_rec ( poKmlGeometry,
1253                                         &nAltitudeMode, &bIsGX ) ) {
1254                 ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, bIsGX);
1255             }
1256 
1257         }
1258 
1259         /***** tessellate *****/
1260 
1261         int nTessellate = -1;
1262 
1263         kml2tessellate_rec ( poKmlGeometry, &nTessellate );
1264 
1265         iField = poOgrFeat->GetFieldIndex ( oFC.tessellatefield );
1266         if ( iField > -1 )
1267             poOgrFeat->SetField ( iField, nTessellate );
1268 
1269         /***** extrude *****/
1270 
1271         int nExtrude = -1;
1272 
1273         kml2extrude_rec ( poKmlGeometry, &nExtrude );
1274 
1275         iField = poOgrFeat->GetFieldIndex ( oFC.extrudefield );
1276         if ( iField > -1 )
1277             poOgrFeat->SetField ( iField, nExtrude );
1278 
1279         /***** special case for gx:Track ******/
1280         /* we set the first timestamp as begin and the last one as end */
1281         if ( poKmlGeometry->Type (  )  == kmldom::Type_GxTrack &&
1282              !poKmlFeature->has_timeprimitive (  ) ) {
1283             GxTrackPtr poKmlGxTrack = AsGxTrack ( poKmlGeometry );
1284             size_t nCoords = poKmlGxTrack->get_gx_coord_array_size();
1285             if( nCoords > 0 )
1286             {
1287                 kmldatetime2ogr(poOgrFeat, oFC.beginfield,
1288                             poKmlGxTrack->get_when_array_at ( 0 ).c_str() );
1289                 kmldatetime2ogr(poOgrFeat, oFC.endfield,
1290                             poKmlGxTrack->get_when_array_at ( nCoords - 1 ).c_str() );
1291             }
1292         }
1293 
1294         /***** special case for gx:MultiTrack ******/
1295         /* we set the first timestamp as begin and the last one as end */
1296         else if ( poKmlGeometry->Type (  )  == kmldom::Type_GxMultiTrack &&
1297              !poKmlFeature->has_timeprimitive (  ) ) {
1298             GxMultiTrackPtr poKmlGxMultiTrack = AsGxMultiTrack ( poKmlGeometry );
1299             size_t nGeom = poKmlGxMultiTrack->get_gx_track_array_size (  );
1300             if( nGeom >= 1 )
1301             {
1302                 GxTrackPtr poKmlGxTrack = poKmlGxMultiTrack->get_gx_track_array_at ( 0 );
1303                 size_t nCoords = poKmlGxTrack->get_gx_coord_array_size();
1304                 if( nCoords > 0 )
1305                 {
1306                     kmldatetime2ogr(poOgrFeat, oFC.beginfield,
1307                                 poKmlGxTrack->get_when_array_at ( 0 ).c_str() );
1308                 }
1309 
1310                 poKmlGxTrack = poKmlGxMultiTrack->get_gx_track_array_at (nGeom -1);
1311                 nCoords = poKmlGxTrack->get_gx_coord_array_size();
1312                 if( nCoords > 0 )
1313                 {
1314                     kmldatetime2ogr(poOgrFeat, oFC.endfield,
1315                                 poKmlGxTrack->get_when_array_at ( nCoords - 1 ).c_str() );
1316                 }
1317             }
1318         }
1319     }
1320 
1321     /***** camera *****/
1322 
1323     else if ( poKmlPlacemark &&
1324               poKmlPlacemark->has_abstractview (  ) &&
1325               poKmlPlacemark->get_abstractview()->IsA( kmldom::Type_Camera) ) {
1326 
1327         const CameraPtr& camera = AsCamera(poKmlPlacemark->get_abstractview());
1328 
1329         if( camera->has_heading() )
1330         {
1331             int iField = poOgrFeat->GetFieldIndex ( oFC.headingfield );
1332             if ( iField > -1 )
1333                 poOgrFeat->SetField ( iField, camera->get_heading() );
1334         }
1335 
1336         if( camera->has_tilt() )
1337         {
1338             int iField = poOgrFeat->GetFieldIndex ( oFC.tiltfield );
1339             if ( iField > -1 )
1340                 poOgrFeat->SetField ( iField, camera->get_tilt() );
1341         }
1342 
1343         if( camera->has_roll() )
1344         {
1345             int iField = poOgrFeat->GetFieldIndex ( oFC.rollfield );
1346             if ( iField > -1 )
1347                 poOgrFeat->SetField ( iField, camera->get_roll() );
1348         }
1349 
1350         int nAltitudeMode = -1;
1351 
1352         int iField = poOgrFeat->GetFieldIndex ( oFC.altitudeModefield );
1353 
1354         if ( iField > -1 ) {
1355 
1356             if ( camera->has_altitudemode (  ) ) {
1357                 nAltitudeMode = camera->get_altitudemode (  );
1358                 ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, FALSE);
1359             }
1360             else if ( camera->has_gx_altitudemode (  ) ) {
1361                 nAltitudeMode = camera->get_gx_altitudemode (  );
1362                 ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, TRUE);
1363             }
1364         }
1365     }
1366 
1367     /***** ground overlay *****/
1368 
1369     else if ( poKmlGroundOverlay ) {
1370 
1371         /***** icon *****/
1372 
1373         int iField = poOgrFeat->GetFieldIndex ( oFC.iconfield );
1374         if ( iField > -1 ) {
1375 
1376             if ( poKmlGroundOverlay->has_icon (  ) ) {
1377                 IconPtr icon = poKmlGroundOverlay->get_icon (  );
1378                 if ( icon->has_href (  ) ) {
1379                     poOgrFeat->SetField ( iField, icon->get_href (  ).c_str (  ) );
1380                 }
1381             }
1382         }
1383 
1384         /***** drawOrder *****/
1385 
1386 
1387         iField = poOgrFeat->GetFieldIndex ( oFC.drawOrderfield );
1388         if ( iField > -1 ) {
1389 
1390             if ( poKmlGroundOverlay->has_draworder (  ) ) {
1391                 poOgrFeat->SetField ( iField, poKmlGroundOverlay->get_draworder (  ) );
1392             }
1393         }
1394 
1395         /***** altitudeMode *****/
1396 
1397         iField = poOgrFeat->GetFieldIndex ( oFC.altitudeModefield );
1398 
1399         if ( iField > -1 ) {
1400 
1401             if ( poKmlGroundOverlay->has_altitudemode (  ) ) {
1402                 switch ( poKmlGroundOverlay->get_altitudemode (  ) ) {
1403                 case kmldom::ALTITUDEMODE_CLAMPTOGROUND:
1404                     poOgrFeat->SetField ( iField, "clampToGround" );
1405                     break;
1406 
1407                 case kmldom::ALTITUDEMODE_RELATIVETOGROUND:
1408                     poOgrFeat->SetField ( iField, "relativeToGround" );
1409                     break;
1410 
1411                 case kmldom::ALTITUDEMODE_ABSOLUTE:
1412                     poOgrFeat->SetField ( iField, "absolute" );
1413                     break;
1414 
1415                 }
1416             } else if ( poKmlGroundOverlay->has_gx_altitudemode (  ) ) {
1417                 switch ( poKmlGroundOverlay->get_gx_altitudemode ( ) ) {
1418                 case kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR:
1419                     poOgrFeat->SetField ( iField, "relativeToSeaFloor" );
1420                     break;
1421 
1422                 case kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR:
1423                     poOgrFeat->SetField ( iField, "clampToSeaFloor" );
1424                     break;
1425                 }
1426             }
1427 
1428         }
1429     }
1430 
1431     /***** visibility *****/
1432 
1433     int nVisibility = -1;
1434 
1435     if ( poKmlFeature->has_visibility (  ) )
1436         nVisibility = poKmlFeature->get_visibility (  );
1437 
1438     int iField = poOgrFeat->GetFieldIndex ( oFC.visibilityfield );
1439 
1440     if ( iField > -1 )
1441         poOgrFeat->SetField ( iField, nVisibility );
1442 
1443     /***** snippet *****/
1444 
1445     if ( poKmlFeature->has_snippet (  ) )
1446     {
1447         string oText = poKmlFeature->get_snippet (  )->get_text();
1448 
1449         iField = poOgrFeat->GetFieldIndex ( oFC.snippetfield );
1450 
1451         if ( iField > -1 )
1452             poOgrFeat->SetField ( iField, TrimSpaces(oText) );
1453     }
1454 
1455     /***** extended schema *****/
1456     ExtendedDataPtr poKmlExtendedData = NULL;
1457 
1458     if ( poKmlFeature->has_extendeddata (  ) ) {
1459         poKmlExtendedData = poKmlFeature->get_extendeddata (  );
1460 
1461         /***** loop over the schemadata_arrays *****/
1462 
1463         size_t nSchemaData = poKmlExtendedData->get_schemadata_array_size (  );
1464 
1465         size_t iSchemaData;
1466 
1467         for ( iSchemaData = 0; iSchemaData < nSchemaData; iSchemaData++ ) {
1468             SchemaDataPtr poKmlSchemaData =
1469                 poKmlExtendedData->get_schemadata_array_at ( iSchemaData );
1470 
1471             /***** loop over the simpledata array *****/
1472 
1473             size_t nSimpleData =
1474                 poKmlSchemaData->get_simpledata_array_size (  );
1475 
1476             size_t iSimpleData;
1477 
1478             for ( iSimpleData = 0; iSimpleData < nSimpleData; iSimpleData++ ) {
1479                 SimpleDataPtr poKmlSimpleData =
1480                     poKmlSchemaData->get_simpledata_array_at ( iSimpleData );
1481 
1482                 /***** find the field index *****/
1483 
1484                 int iField = -1;
1485 
1486                 if ( poKmlSimpleData->has_name (  ) ) {
1487                     const string oName = poKmlSimpleData->get_name (  );
1488                     const char *pszName = oName.c_str (  );
1489 
1490                     iField = poOgrFeat->GetFieldIndex ( pszName );
1491                 }
1492 
1493                 /***** if it has trxt set the field *****/
1494 
1495                 if ( iField > -1 && poKmlSimpleData->has_text (  ) ) {
1496                     string oText = poKmlSimpleData->get_text (  );
1497 
1498                     poOgrFeat->SetField ( iField, TrimSpaces(oText) );
1499                 }
1500             }
1501         }
1502 
1503         if (nSchemaData == 0 &&  poKmlExtendedData->get_data_array_size() > 0 )
1504         {
1505             int bLaunderFieldNames =
1506                         CSLTestBoolean(CPLGetConfigOption("LIBKML_LAUNDER_FIELD_NAMES", "YES"));
1507             size_t nDataArraySize = poKmlExtendedData->get_data_array_size();
1508             for(size_t i=0; i < nDataArraySize; i++)
1509             {
1510                 const DataPtr& data = poKmlExtendedData->get_data_array_at(i);
1511                 if (data->has_name() && data->has_value())
1512                 {
1513                     CPLString osName = data->get_name();
1514                     if (bLaunderFieldNames)
1515                         osName = OGRLIBKMLLayer::LaunderFieldNames(osName);
1516                     int iField = poOgrFeat->GetFieldIndex ( osName );
1517                     if (iField >= 0)
1518                     {
1519                         poOgrFeat->SetField ( iField, data->get_value().c_str() );
1520                     }
1521                 }
1522             }
1523         }
1524     }
1525 
1526 }
1527 
1528 /******************************************************************************
1529  function create a simplefield from a FieldDefn
1530 ******************************************************************************/
1531 
FieldDef2kml(OGRFieldDefn * poOgrFieldDef,KmlFactory * poKmlFactory)1532 SimpleFieldPtr FieldDef2kml (
1533     OGRFieldDefn * poOgrFieldDef,
1534     KmlFactory * poKmlFactory )
1535 {
1536     /***** get the field config *****/
1537 
1538     struct fieldconfig oFC;
1539     get_fieldconfig( &oFC );
1540 
1541     const char *pszFieldName = poOgrFieldDef->GetNameRef (  );
1542 
1543     if ( EQUAL ( pszFieldName, oFC.namefield ) ||
1544          EQUAL ( pszFieldName, oFC.descfield ) ||
1545          EQUAL ( pszFieldName, oFC.tsfield ) ||
1546          EQUAL ( pszFieldName, oFC.beginfield ) ||
1547          EQUAL ( pszFieldName, oFC.endfield ) ||
1548          EQUAL ( pszFieldName, oFC.altitudeModefield ) ||
1549          EQUAL ( pszFieldName, oFC.tessellatefield ) ||
1550          EQUAL ( pszFieldName, oFC.extrudefield ) ||
1551          EQUAL ( pszFieldName, oFC.visibilityfield ) ||
1552          EQUAL ( pszFieldName, oFC.drawOrderfield ) ||
1553          EQUAL ( pszFieldName, oFC.iconfield ) ||
1554          EQUAL ( pszFieldName, oFC.headingfield ) ||
1555          EQUAL ( pszFieldName, oFC.tiltfield ) ||
1556          EQUAL ( pszFieldName, oFC.rollfield ) ||
1557          EQUAL ( pszFieldName, oFC.snippetfield ) ||
1558          EQUAL ( pszFieldName, oFC.modelfield ) ||
1559          EQUAL ( pszFieldName, oFC.scalexfield ) ||
1560          EQUAL ( pszFieldName, oFC.scaleyfield ) ||
1561          EQUAL ( pszFieldName, oFC.scalezfield ) ||
1562          EQUAL ( pszFieldName, oFC.networklinkfield ) ||
1563          EQUAL ( pszFieldName, oFC.networklink_refreshvisibility_field ) ||
1564          EQUAL ( pszFieldName, oFC.networklink_flytoview_field ) ||
1565          EQUAL ( pszFieldName, oFC.networklink_refreshMode_field ) ||
1566          EQUAL ( pszFieldName, oFC.networklink_refreshInterval_field ) ||
1567          EQUAL ( pszFieldName, oFC.networklink_viewRefreshMode_field ) ||
1568          EQUAL ( pszFieldName, oFC.networklink_viewRefreshTime_field ) ||
1569          EQUAL ( pszFieldName, oFC.networklink_viewBoundScale_field ) ||
1570          EQUAL ( pszFieldName, oFC.networklink_viewFormat_field ) ||
1571          EQUAL ( pszFieldName, oFC.networklink_httpQuery_field ) ||
1572          EQUAL ( pszFieldName, oFC.camera_longitude_field ) ||
1573          EQUAL ( pszFieldName, oFC.camera_latitude_field ) ||
1574          EQUAL ( pszFieldName, oFC.camera_altitude_field ) ||
1575          EQUAL ( pszFieldName, oFC.camera_altitudemode_field ) ||
1576          EQUAL ( pszFieldName, oFC.photooverlayfield ) ||
1577          EQUAL ( pszFieldName, oFC.leftfovfield ) ||
1578          EQUAL ( pszFieldName, oFC.rightfovfield ) ||
1579          EQUAL ( pszFieldName, oFC.bottomfovfield ) ||
1580          EQUAL ( pszFieldName, oFC.topfovfield ) ||
1581          EQUAL ( pszFieldName, oFC.nearfield ) ||
1582          EQUAL ( pszFieldName, oFC.photooverlay_shape_field ) ||
1583          EQUAL ( pszFieldName, oFC.imagepyramid_tilesize_field) ||
1584          EQUAL ( pszFieldName, oFC.imagepyramid_maxwidth_field) ||
1585          EQUAL ( pszFieldName, oFC.imagepyramid_maxheight_field) ||
1586          EQUAL ( pszFieldName, oFC.imagepyramid_gridorigin_field) )
1587     {
1588         return NULL;
1589     }
1590 
1591     SimpleFieldPtr poKmlSimpleField = poKmlFactory->CreateSimpleField (  );
1592     poKmlSimpleField->set_name ( pszFieldName );
1593 
1594 
1595     SimpleDataPtr poKmlSimpleData = NULL;
1596 
1597     switch ( poOgrFieldDef->GetType (  ) ) {
1598     case OFTInteger:
1599     case OFTIntegerList:
1600         poKmlSimpleField->set_type ( "int" );
1601         return poKmlSimpleField;
1602 
1603     case OFTReal:
1604     case OFTRealList:
1605         poKmlSimpleField->set_type ( "float" );
1606         return poKmlSimpleField;
1607 
1608     case OFTString:
1609     case OFTStringList:
1610         poKmlSimpleField->set_type ( "string" );
1611         return poKmlSimpleField;
1612 
1613     /***** kml has these types but as timestamp/timespan *****/
1614 
1615     case OFTDate:
1616     case OFTTime:
1617     case OFTDateTime:
1618         break;
1619 
1620     default:
1621         poKmlSimpleField->set_type ( "string" );
1622         return poKmlSimpleField;
1623     }
1624 
1625     return NULL;
1626 }
1627 
1628 /******************************************************************************
1629  function to add the simpleFields in a schema to a featuredefn
1630 ******************************************************************************/
1631 
kml2FeatureDef(SchemaPtr poKmlSchema,OGRFeatureDefn * poOgrFeatureDefn)1632 void kml2FeatureDef (
1633     SchemaPtr poKmlSchema,
1634     OGRFeatureDefn * poOgrFeatureDefn )
1635 {
1636 
1637     size_t nSimpleFields = poKmlSchema->get_simplefield_array_size (  );
1638     size_t iSimpleField;
1639 
1640     for ( iSimpleField = 0; iSimpleField < nSimpleFields; iSimpleField++ ) {
1641         SimpleFieldPtr poKmlSimpleField =
1642             poKmlSchema->get_simplefield_array_at ( iSimpleField );
1643 
1644         const char *pszType = "string";
1645         string osName = "Unknown";
1646         string osType;
1647 
1648         if ( poKmlSimpleField->has_type (  ) ) {
1649             osType = poKmlSimpleField->get_type (  );
1650 
1651             pszType = osType.c_str (  );
1652         }
1653 
1654         /* FIXME? We cannot set displayname as the field name because in kml2field() we make the */
1655         /* lookup on fields based on their name. We would need some map if we really */
1656         /* want to use displayname, but that might not be a good idea because displayname */
1657         /* may have HTML formatting, which makes it impractical when converting to other */
1658         /* drivers or to make requests */
1659         /* Example: http://www.jasonbirch.com/files/newt_combined.kml */
1660         /*if ( poKmlSimpleField->has_displayname (  ) ) {
1661             osName = poKmlSimpleField->get_displayname (  );
1662         }
1663 
1664         else*/ if ( poKmlSimpleField->has_name (  ) ) {
1665             osName = poKmlSimpleField->get_name (  );
1666         }
1667 
1668         if ( EQUAL ( pszType, "bool" ) ||
1669              EQUAL ( pszType, "boolean" ) ||
1670              EQUAL ( pszType, "int" ) ||
1671              EQUAL ( pszType, "short" ) ||
1672              EQUAL ( pszType, "ushort" ) ) {
1673             OGRFieldDefn oOgrFieldName ( osName.c_str(), OFTInteger );
1674             poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldName );
1675         }
1676         else if ( EQUAL ( pszType, "uint" ) )  {
1677             OGRFieldDefn oOgrFieldName ( osName.c_str(), OFTInteger64 );
1678             poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldName );
1679         }
1680         else if ( EQUAL ( pszType, "float" ) ||
1681                   EQUAL ( pszType, "double" ) ) {
1682             OGRFieldDefn oOgrFieldName ( osName.c_str(), OFTReal );
1683             poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldName );
1684         }
1685         else /* string, or any other unrecognized type */
1686         {
1687             OGRFieldDefn oOgrFieldName ( osName.c_str(), OFTString );
1688             poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldName );
1689         }
1690     }
1691 
1692     return;
1693 }
1694 
1695 /*******************************************************************************
1696  * function to fetch the field config options
1697  *
1698 *******************************************************************************/
1699 
get_fieldconfig(struct fieldconfig * oFC)1700 void get_fieldconfig( struct fieldconfig *oFC) {
1701 
1702     oFC->namefield = CPLGetConfigOption ( "LIBKML_NAME_FIELD",
1703                                                   "Name" );
1704     oFC->descfield = CPLGetConfigOption ( "LIBKML_DESCRIPTION_FIELD",
1705                                                   "description" );
1706     oFC->tsfield = CPLGetConfigOption ( "LIBKML_TIMESTAMP_FIELD",
1707                                                 "timestamp" );
1708     oFC->beginfield = CPLGetConfigOption ( "LIBKML_BEGIN_FIELD",
1709 	                                               "begin" );
1710 	oFC->endfield = CPLGetConfigOption ( "LIBKML_END_FIELD",
1711 	                                             "end" );
1712     oFC->altitudeModefield = CPLGetConfigOption ( "LIBKML_ALTITUDEMODE_FIELD",
1713                                                           "altitudeMode" );
1714     oFC->tessellatefield = CPLGetConfigOption ( "LIBKML_TESSELLATE_FIELD",
1715                                                         "tessellate" );
1716     oFC->extrudefield = CPLGetConfigOption ( "LIBKML_EXTRUDE_FIELD",
1717                                                      "extrude" );
1718     oFC->visibilityfield = CPLGetConfigOption ( "LIBKML_VISIBILITY_FIELD",
1719                                                         "visibility" );
1720     oFC->drawOrderfield = CPLGetConfigOption ( "LIBKML_DRAWORDER_FIELD",
1721                                                        "drawOrder" );
1722     oFC->iconfield = CPLGetConfigOption ( "LIBKML_ICON_FIELD",
1723                                                   "icon" );
1724     oFC->headingfield = CPLGetConfigOption( "LIBKML_HEADING_FIELD", "heading");
1725     oFC->tiltfield = CPLGetConfigOption( "LIBKML_TILT_FIELD", "tilt");
1726     oFC->rollfield = CPLGetConfigOption( "LIBKML_ROLL_FIELD", "roll");
1727     oFC->snippetfield = CPLGetConfigOption( "LIBKML_SNIPPET_FIELD", "snippet");
1728     oFC->modelfield = CPLGetConfigOption( "LIBKML_MODEL_FIELD", "model");
1729     oFC->scalexfield = CPLGetConfigOption( "LIBKML_SCALE_X_FIELD", "scale_x");
1730     oFC->scaleyfield = CPLGetConfigOption( "LIBKML_SCALE_Y_FIELD", "scale_y");
1731     oFC->scalezfield = CPLGetConfigOption( "LIBKML_SCALE_Z_FIELD", "scale_z");
1732     oFC->networklinkfield = CPLGetConfigOption( "LIBKML_NETWORKLINK_FIELD", "networklink");
1733     oFC->networklink_refreshvisibility_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_REFRESHVISIBILITY_FIELD", "networklink_refreshvisibility");
1734     oFC->networklink_flytoview_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_FLYTOVIEW_FIELD", "networklink_flytoview");
1735     oFC->networklink_refreshMode_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_REFRESHMODE_FIELD", "networklink_refreshmode");
1736     oFC->networklink_refreshInterval_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_REFRESHINTERVAL_FIELD", "networklink_refreshinterval");
1737     oFC->networklink_viewRefreshMode_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_VIEWREFRESHMODE_FIELD", "networklink_viewrefreshmode");
1738     oFC->networklink_viewRefreshTime_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_VIEWREFRESHTIME_FIELD", "networklink_viewrefreshtime");
1739     oFC->networklink_viewBoundScale_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_VIEWBOUNDSCALE_FIELD", "networklink_viewboundscale");
1740     oFC->networklink_viewFormat_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_VIEWFORMAT_FIELD", "networklink_viewformat");
1741     oFC->networklink_httpQuery_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_HTTPQUERY_FIELD", "networklink_httpquery");
1742     oFC->camera_longitude_field = CPLGetConfigOption( "LIBKML_CAMERA_LONGITUDE_FIELD", "camera_longitude");
1743     oFC->camera_latitude_field = CPLGetConfigOption( "LIBKML_CAMERA_LATITUDE_FIELD", "camera_latitude");
1744     oFC->camera_altitude_field = CPLGetConfigOption( "LIBKML_CAMERA_ALTITUDE_FIELD", "camera_altitude");
1745     oFC->camera_altitudemode_field = CPLGetConfigOption( "LIBKML_CAMERA_ALTITUDEMODE_FIELD", "camera_altitudemode");
1746     oFC->photooverlayfield = CPLGetConfigOption( "LIBKML_PHOTOOVERLAY_FIELD", "photooverlay");
1747     oFC->leftfovfield = CPLGetConfigOption( "LIBKML_LEFTFOV_FIELD", "leftfov");
1748     oFC->rightfovfield = CPLGetConfigOption( "LIBKML_RIGHTFOV_FIELD", "rightfov");
1749     oFC->bottomfovfield = CPLGetConfigOption( "LIBKML_BOTTOMFOV_FIELD", "bottomfov");
1750     oFC->topfovfield = CPLGetConfigOption( "LIBKML_TOPFOV_FIELD", "topfov");
1751     oFC->nearfield = CPLGetConfigOption( "LIBKML_NEARFOV_FIELD", "near");
1752     oFC->photooverlay_shape_field = CPLGetConfigOption( "LIBKML_PHOTOOVERLAY_SHAPE_FIELD", "photooverlay_shape");
1753     oFC->imagepyramid_tilesize_field = CPLGetConfigOption( "LIBKML_IMAGEPYRAMID_TILESIZE", "imagepyramid_tilesize");
1754     oFC->imagepyramid_maxwidth_field = CPLGetConfigOption( "LIBKML_IMAGEPYRAMID_MAXWIDTH", "imagepyramid_maxwidth");
1755     oFC->imagepyramid_maxheight_field = CPLGetConfigOption( "LIBKML_IMAGEPYRAMID_MAXHEIGHT", "imagepyramid_maxheight");
1756     oFC->imagepyramid_gridorigin_field = CPLGetConfigOption( "LIBKML_IMAGEPYRAMID_GRIDORIGIN", "imagepyramid_gridorigin");
1757 }
1758 
1759 /************************************************************************/
1760 /*                 kmlAltitudeModeFromString()                          */
1761 /************************************************************************/
1762 
kmlAltitudeModeFromString(const char * pszAltitudeMode,int & isGX)1763 int kmlAltitudeModeFromString(const char* pszAltitudeMode,
1764                               int& isGX)
1765 {
1766     isGX = FALSE;
1767     int iAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND;
1768 
1769     if ( EQUAL ( pszAltitudeMode, "clampToGround" ) )
1770         iAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND;
1771 
1772     else if ( EQUAL ( pszAltitudeMode, "relativeToGround" ) )
1773         iAltitudeMode = kmldom::ALTITUDEMODE_RELATIVETOGROUND;
1774 
1775     else if ( EQUAL ( pszAltitudeMode, "absolute" ) )
1776         iAltitudeMode = kmldom::ALTITUDEMODE_ABSOLUTE;
1777 
1778     else if ( EQUAL ( pszAltitudeMode, "relativeToSeaFloor" ) ) {
1779         iAltitudeMode =
1780             kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR;
1781         isGX = TRUE;
1782     }
1783 
1784     else if ( EQUAL ( pszAltitudeMode, "clampToSeaFloor" ) ) {
1785         iAltitudeMode =
1786             kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR;
1787         isGX = TRUE;
1788     }
1789     else
1790     {
1791         CPLError(CE_Warning, CPLE_NotSupported,
1792                  "Unrecognized value for altitudeMode: %s",
1793                  pszAltitudeMode);
1794     }
1795 
1796     return iAltitudeMode;
1797 }
1798