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