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