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