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 130 : static char *OGRLIBKMLSanitizeUTF8String(const char *pszString)
209 : {
210 130 : 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 130 : 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 205 : void field2kml(OGRFeature *poOgrFeat, OGRLIBKMLLayer *poOgrLayer,
259 : KmlFactory *poKmlFactory, FeaturePtr poKmlFeature,
260 : int bUseSimpleFieldIn, const fieldconfig &oFC)
261 : {
262 205 : const bool bUseSimpleField = CPL_TO_BOOL(bUseSimpleFieldIn);
263 410 : SchemaDataPtr poKmlSchemaData = nullptr;
264 205 : if (bUseSimpleField)
265 : {
266 204 : poKmlSchemaData = poKmlFactory->CreateSchemaData();
267 408 : SchemaPtr poKmlSchema = poOgrLayer->GetKmlSchema();
268 :
269 : /***** set the url to the schema *****/
270 204 : if (poKmlSchema && poKmlSchema->has_id())
271 : {
272 296 : std::string oKmlSchemaID = poKmlSchema->get_id();
273 296 : std::string oKmlSchemaURL = "#";
274 148 : oKmlSchemaURL.append(oKmlSchemaID);
275 :
276 148 : poKmlSchemaData->set_schemaurl(oKmlSchemaURL);
277 : }
278 : }
279 :
280 410 : TimeSpanPtr poKmlTimeSpan = nullptr;
281 :
282 205 : const int nFields = poOgrFeat->GetFieldCount();
283 205 : int iSkip1 = -1;
284 205 : int iSkip2 = -1;
285 205 : int iAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND;
286 205 : int isGX = false;
287 :
288 410 : ExtendedDataPtr poKmlExtendedData = nullptr;
289 :
290 1063 : for (int i = 0; i < nFields; i++)
291 : {
292 : /***** If the field is set to skip, do so *****/
293 858 : if (i == iSkip1 || i == iSkip2)
294 544 : continue;
295 :
296 : /***** If the field isn't set just bail now *****/
297 858 : if (!poOgrFeat->IsFieldSetAndNotNull(i))
298 427 : continue;
299 :
300 431 : const OGRFieldDefn *poOgrFieldDef = poOgrFeat->GetFieldDefnRef(i);
301 431 : const OGRFieldType type = poOgrFieldDef->GetType();
302 431 : const char *name = poOgrFieldDef->GetNameRef();
303 :
304 431 : SimpleDataPtr poKmlSimpleData = nullptr;
305 431 : DataPtr poKmlData = nullptr;
306 : OGRField sFieldDT;
307 :
308 : // TODO(schwehr): Refactor to get rid of gotos.
309 431 : switch (type)
310 : {
311 130 : case OFTString: // String of ASCII chars
312 : {
313 : char *const pszUTF8String =
314 130 : OGRLIBKMLSanitizeUTF8String(poOgrFeat->GetFieldAsString(i));
315 130 : if (pszUTF8String[0] == '\0')
316 : {
317 0 : CPLFree(pszUTF8String);
318 0 : continue;
319 : }
320 :
321 : /***** id *****/
322 130 : if (EQUAL(name, oFC.idfield))
323 : {
324 11 : poKmlFeature->set_id(pszUTF8String);
325 11 : CPLFree(pszUTF8String);
326 11 : continue;
327 : }
328 : /***** name *****/
329 119 : if (EQUAL(name, oFC.namefield))
330 : {
331 15 : poKmlFeature->set_name(pszUTF8String);
332 15 : CPLFree(pszUTF8String);
333 15 : continue;
334 : }
335 : /***** description *****/
336 104 : else if (EQUAL(name, oFC.descfield))
337 : {
338 6 : poKmlFeature->set_description(pszUTF8String);
339 6 : CPLFree(pszUTF8String);
340 6 : continue;
341 : }
342 : /***** altitudemode *****/
343 98 : 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 96 : 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 96 : 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 96 : 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 96 : 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 95 : else if (EQUAL(name, oFC.iconfield) ||
423 95 : EQUAL(name, oFC.modelfield) ||
424 93 : EQUAL(name, oFC.networklinkfield) ||
425 90 : EQUAL(name, oFC.networklink_refreshMode_field) ||
426 89 : EQUAL(name, oFC.networklink_viewRefreshMode_field) ||
427 88 : EQUAL(name, oFC.networklink_viewFormat_field) ||
428 87 : EQUAL(name, oFC.networklink_httpQuery_field) ||
429 86 : EQUAL(name, oFC.camera_altitudemode_field) ||
430 84 : EQUAL(name, oFC.photooverlayfield) ||
431 82 : EQUAL(name, oFC.photooverlay_shape_field) ||
432 80 : EQUAL(name, oFC.imagepyramid_gridorigin_field))
433 : {
434 15 : CPLFree(pszUTF8String);
435 :
436 15 : continue;
437 : }
438 :
439 : /***** other *****/
440 :
441 80 : if (bUseSimpleField)
442 : {
443 79 : poKmlSimpleData = poKmlFactory->CreateSimpleData();
444 79 : poKmlSimpleData->set_name(name);
445 79 : 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 80 : CPLFree(pszUTF8String);
455 :
456 80 : 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 83 : case OFTInteger: // Simple 32bit integer
614 : {
615 : /***** extrude *****/
616 83 : 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 74 : 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 65 : 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 56 : else if (EQUAL(name, oFC.drawOrderfield) ||
707 56 : EQUAL(name, oFC.networklink_refreshvisibility_field) ||
708 55 : EQUAL(name, oFC.networklink_flytoview_field) ||
709 54 : EQUAL(name, oFC.networklink_refreshInterval_field) ||
710 54 : EQUAL(name, oFC.networklink_viewRefreshMode_field) ||
711 54 : EQUAL(name, oFC.networklink_viewRefreshTime_field) ||
712 54 : EQUAL(name, oFC.imagepyramid_tilesize_field) ||
713 53 : EQUAL(name, oFC.imagepyramid_maxwidth_field) ||
714 52 : EQUAL(name, oFC.imagepyramid_maxheight_field))
715 : {
716 5 : continue;
717 : }
718 :
719 : /***** other *****/
720 : const char *value =
721 51 : poOgrFieldDef->GetSubType() == OFSTBoolean
722 51 : ? (poOgrFeat->GetFieldAsInteger(i) ? "true" : "false")
723 49 : : poOgrFeat->GetFieldAsString(i);
724 51 : if (bUseSimpleField)
725 : {
726 51 : poKmlSimpleData = poKmlFactory->CreateSimpleData();
727 51 : poKmlSimpleData->set_name(name);
728 51 : poKmlSimpleData->set_text(value);
729 : }
730 : else
731 : {
732 0 : poKmlData = poKmlFactory->CreateData();
733 0 : poKmlData->set_name(name);
734 0 : poKmlData->set_value(value);
735 : }
736 :
737 51 : break;
738 : }
739 :
740 100 : case OFTReal: // Double Precision floating point
741 : {
742 100 : if (EQUAL(name, oFC.headingfield) ||
743 95 : EQUAL(name, oFC.tiltfield) || EQUAL(name, oFC.rollfield) ||
744 87 : EQUAL(name, oFC.scalexfield) ||
745 86 : EQUAL(name, oFC.scaleyfield) ||
746 85 : EQUAL(name, oFC.scalezfield) ||
747 84 : EQUAL(name, oFC.networklink_refreshInterval_field) ||
748 83 : EQUAL(name, oFC.networklink_viewRefreshMode_field) ||
749 83 : EQUAL(name, oFC.networklink_viewRefreshTime_field) ||
750 82 : EQUAL(name, oFC.networklink_viewBoundScale_field) ||
751 81 : EQUAL(name, oFC.camera_longitude_field) ||
752 79 : EQUAL(name, oFC.camera_latitude_field) ||
753 77 : EQUAL(name, oFC.camera_altitude_field) ||
754 75 : EQUAL(name, oFC.leftfovfield) ||
755 73 : EQUAL(name, oFC.rightfovfield) ||
756 71 : EQUAL(name, oFC.bottomfovfield) ||
757 69 : EQUAL(name, oFC.topfovfield) ||
758 67 : EQUAL(name, oFC.nearfield) ||
759 65 : EQUAL(name, oFC.camera_altitude_field))
760 : {
761 35 : continue;
762 : }
763 :
764 65 : char *pszStr = CPLStrdup(poOgrFeat->GetFieldAsString(i));
765 :
766 65 : if (bUseSimpleField)
767 : {
768 65 : poKmlSimpleData = poKmlFactory->CreateSimpleData();
769 65 : poKmlSimpleData->set_name(name);
770 65 : poKmlSimpleData->set_text(pszStr);
771 : }
772 : else
773 : {
774 0 : poKmlData = poKmlFactory->CreateData();
775 0 : poKmlData->set_name(name);
776 0 : poKmlData->set_value(pszStr);
777 : }
778 :
779 65 : CPLFree(pszStr);
780 :
781 65 : break;
782 : }
783 :
784 10 : case OFTStringList: // Array of strings
785 : case OFTIntegerList: // List of 32bit integers
786 : case OFTRealList: // List of doubles
787 : case OFTBinary: // Raw Binary data
788 : case OFTWideStringList: // deprecated
789 : default:
790 :
791 : /***** other *****/
792 :
793 10 : if (bUseSimpleField)
794 : {
795 10 : poKmlSimpleData = poKmlFactory->CreateSimpleData();
796 10 : poKmlSimpleData->set_name(name);
797 10 : poKmlSimpleData->set_text(poOgrFeat->GetFieldAsString(i));
798 : }
799 : else
800 : {
801 0 : poKmlData = poKmlFactory->CreateData();
802 0 : poKmlData->set_name(name);
803 0 : poKmlData->set_value(poOgrFeat->GetFieldAsString(i));
804 : }
805 :
806 10 : break;
807 : }
808 :
809 314 : if (poKmlSimpleData)
810 : {
811 313 : poKmlSchemaData->add_simpledata(poKmlSimpleData);
812 : }
813 1 : else if (poKmlData)
814 : {
815 1 : if (!poKmlExtendedData)
816 1 : poKmlExtendedData = poKmlFactory->CreateExtendedData();
817 : #if defined(__GNUC__)
818 : #pragma GCC diagnostic push
819 : #pragma GCC diagnostic ignored "-Wnull-dereference"
820 : #endif
821 1 : poKmlExtendedData->add_data(poKmlData);
822 : #if defined(__GNUC__)
823 : #pragma GCC diagnostic pop
824 : #endif
825 : }
826 : }
827 :
828 : // Do not add it to the placemark unless there is data.
829 205 : if (bUseSimpleField && poKmlSchemaData->get_simpledata_array_size() > 0)
830 : {
831 75 : poKmlExtendedData = poKmlFactory->CreateExtendedData();
832 75 : poKmlExtendedData->add_schemadata(poKmlSchemaData);
833 : }
834 205 : if (poKmlExtendedData)
835 : {
836 76 : poKmlFeature->set_extendeddata(poKmlExtendedData);
837 : }
838 205 : }
839 :
840 : /******************************************************************************
841 : Recursive function to read altitude mode from the geometry.
842 : ******************************************************************************/
843 :
844 1233 : static bool kml2altitudemode_rec(GeometryPtr poKmlGeometry, int *pnAltitudeMode,
845 : int *pbIsGX)
846 : {
847 1233 : switch (poKmlGeometry->Type())
848 : {
849 214 : case kmldom::Type_Point:
850 : {
851 214 : PointPtr poKmlPoint = AsPoint(poKmlGeometry);
852 :
853 214 : if (poKmlPoint->has_altitudemode())
854 : {
855 60 : *pnAltitudeMode = poKmlPoint->get_altitudemode();
856 60 : return true;
857 : }
858 154 : else if (poKmlPoint->has_gx_altitudemode())
859 : {
860 0 : *pnAltitudeMode = poKmlPoint->get_gx_altitudemode();
861 0 : *pbIsGX = true;
862 0 : return true;
863 : }
864 :
865 154 : break;
866 : }
867 240 : case kmldom::Type_LineString:
868 : {
869 240 : LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
870 :
871 240 : if (poKmlLineString->has_altitudemode())
872 : {
873 128 : *pnAltitudeMode = poKmlLineString->get_altitudemode();
874 128 : return true;
875 : }
876 112 : else if (poKmlLineString->has_gx_altitudemode())
877 : {
878 0 : *pnAltitudeMode = poKmlLineString->get_gx_altitudemode();
879 0 : *pbIsGX = true;
880 0 : return true;
881 : }
882 112 : break;
883 : }
884 3 : case kmldom::Type_LinearRing:
885 : {
886 3 : break;
887 : }
888 745 : case kmldom::Type_Polygon:
889 : {
890 745 : PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
891 :
892 745 : if (poKmlPolygon->has_altitudemode())
893 : {
894 296 : *pnAltitudeMode = poKmlPolygon->get_altitudemode();
895 296 : return true;
896 : }
897 449 : else if (poKmlPolygon->has_gx_altitudemode())
898 : {
899 0 : *pnAltitudeMode = poKmlPolygon->get_gx_altitudemode();
900 0 : *pbIsGX = true;
901 0 : return true;
902 : }
903 :
904 449 : break;
905 : }
906 28 : case kmldom::Type_MultiGeometry:
907 : {
908 : MultiGeometryPtr poKmlMultiGeometry =
909 28 : AsMultiGeometry(poKmlGeometry);
910 :
911 28 : const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
912 72 : for (size_t i = 0; i < nGeom; i++)
913 : {
914 44 : if (kml2altitudemode_rec(
915 44 : poKmlMultiGeometry->get_geometry_array_at(i),
916 : pnAltitudeMode, pbIsGX))
917 0 : return true;
918 : }
919 :
920 28 : break;
921 : }
922 3 : default:
923 : {
924 3 : break;
925 : }
926 : }
927 :
928 749 : return false;
929 : }
930 :
931 : /******************************************************************************
932 : Recursive function to read extrude from the geometry.
933 : ******************************************************************************/
934 :
935 1233 : static bool kml2extrude_rec(GeometryPtr poKmlGeometry, bool *pbExtrude)
936 : {
937 1233 : switch (poKmlGeometry->Type())
938 : {
939 214 : case kmldom::Type_Point:
940 : {
941 214 : PointPtr poKmlPoint = AsPoint(poKmlGeometry);
942 :
943 214 : if (poKmlPoint->has_extrude())
944 : {
945 32 : *pbExtrude = poKmlPoint->get_extrude();
946 32 : return true;
947 : }
948 :
949 182 : break;
950 : }
951 240 : case kmldom::Type_LineString:
952 : {
953 240 : LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
954 :
955 240 : if (poKmlLineString->has_extrude())
956 : {
957 60 : *pbExtrude = poKmlLineString->get_extrude();
958 60 : return true;
959 : }
960 :
961 180 : break;
962 : }
963 3 : case kmldom::Type_LinearRing:
964 : {
965 3 : break;
966 : }
967 745 : case kmldom::Type_Polygon:
968 : {
969 745 : PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
970 :
971 745 : if (poKmlPolygon->has_extrude())
972 : {
973 280 : *pbExtrude = poKmlPolygon->get_extrude();
974 280 : return true;
975 : }
976 :
977 465 : break;
978 : }
979 28 : case kmldom::Type_MultiGeometry:
980 : {
981 : MultiGeometryPtr poKmlMultiGeometry =
982 28 : AsMultiGeometry(poKmlGeometry);
983 :
984 28 : const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
985 72 : for (size_t i = 0; i < nGeom; i++)
986 : {
987 44 : if (kml2extrude_rec(
988 44 : poKmlMultiGeometry->get_geometry_array_at(i),
989 : pbExtrude))
990 0 : return true;
991 : }
992 :
993 28 : break;
994 : }
995 3 : default:
996 : {
997 3 : break;
998 : }
999 : }
1000 :
1001 861 : return false;
1002 : }
1003 :
1004 : /******************************************************************************
1005 : Recursive function to read tessellate from the geometry.
1006 : ******************************************************************************/
1007 :
1008 1233 : static bool kml2tessellate_rec(GeometryPtr poKmlGeometry, int *pnTessellate)
1009 : {
1010 1233 : switch (poKmlGeometry->Type())
1011 : {
1012 214 : case kmldom::Type_Point:
1013 : {
1014 214 : break;
1015 : }
1016 240 : case kmldom::Type_LineString:
1017 : {
1018 240 : LineStringPtr poKmlLineString = AsLineString(poKmlGeometry);
1019 :
1020 240 : if (poKmlLineString->has_tessellate())
1021 : {
1022 214 : *pnTessellate = poKmlLineString->get_tessellate();
1023 214 : return true;
1024 : }
1025 :
1026 26 : break;
1027 : }
1028 3 : case kmldom::Type_LinearRing:
1029 : {
1030 3 : break;
1031 : }
1032 745 : case kmldom::Type_Polygon:
1033 : {
1034 745 : PolygonPtr poKmlPolygon = AsPolygon(poKmlGeometry);
1035 :
1036 745 : if (poKmlPolygon->has_tessellate())
1037 : {
1038 124 : *pnTessellate = poKmlPolygon->get_tessellate();
1039 124 : return true;
1040 : }
1041 :
1042 621 : break;
1043 : }
1044 28 : case kmldom::Type_MultiGeometry:
1045 : {
1046 : MultiGeometryPtr poKmlMultiGeometry =
1047 28 : AsMultiGeometry(poKmlGeometry);
1048 :
1049 28 : const size_t nGeom = poKmlMultiGeometry->get_geometry_array_size();
1050 72 : for (size_t i = 0; i < nGeom; i++)
1051 : {
1052 44 : if (kml2tessellate_rec(
1053 44 : poKmlMultiGeometry->get_geometry_array_at(i),
1054 : pnTessellate))
1055 0 : return true;
1056 : }
1057 :
1058 28 : break;
1059 : }
1060 3 : default:
1061 3 : break;
1062 : }
1063 :
1064 895 : return false;
1065 : }
1066 :
1067 : /************************************************************************/
1068 : /* ogrkmlSetAltitudeMode() */
1069 : /************************************************************************/
1070 :
1071 485 : static void ogrkmlSetAltitudeMode(OGRFeature *poOgrFeat, int iField,
1072 : int nAltitudeMode, bool bIsGX)
1073 : {
1074 485 : if (!bIsGX)
1075 : {
1076 485 : switch (nAltitudeMode)
1077 : {
1078 0 : case kmldom::ALTITUDEMODE_CLAMPTOGROUND:
1079 0 : poOgrFeat->SetField(iField, "clampToGround");
1080 0 : break;
1081 :
1082 349 : case kmldom::ALTITUDEMODE_RELATIVETOGROUND:
1083 349 : poOgrFeat->SetField(iField, "relativeToGround");
1084 349 : break;
1085 :
1086 136 : case kmldom::ALTITUDEMODE_ABSOLUTE:
1087 136 : poOgrFeat->SetField(iField, "absolute");
1088 136 : break;
1089 : }
1090 : }
1091 : else
1092 : {
1093 0 : switch (nAltitudeMode)
1094 : {
1095 0 : case kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR:
1096 0 : poOgrFeat->SetField(iField, "relativeToSeaFloor");
1097 0 : break;
1098 :
1099 0 : case kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR:
1100 0 : poOgrFeat->SetField(iField, "clampToSeaFloor");
1101 0 : break;
1102 : }
1103 : }
1104 485 : }
1105 :
1106 : /************************************************************************/
1107 : /* TrimSpaces() */
1108 : /************************************************************************/
1109 :
1110 1299 : static const char *TrimSpaces(CPLString &oText)
1111 : {
1112 : // SerializePretty() adds a new line before the data
1113 : // ands trailing spaces. I believe this is wrong
1114 : // as it breaks round-tripping.
1115 :
1116 : // Trim trailing spaces.
1117 1299 : while (!oText.empty() && oText.back() == ' ')
1118 0 : oText.pop_back();
1119 :
1120 : // Skip leading newline and spaces.
1121 1299 : const char *pszText = oText.c_str();
1122 1299 : if (pszText[0] == '\n')
1123 0 : pszText++;
1124 1299 : while (pszText[0] == ' ')
1125 0 : pszText++;
1126 :
1127 1299 : return pszText;
1128 : }
1129 :
1130 : /************************************************************************/
1131 : /* kmldatetime2ogr() */
1132 : /************************************************************************/
1133 :
1134 7 : static void kmldatetime2ogr(OGRFeature *poOgrFeat, const char *pszOGRField,
1135 : const std::string &osKmlDateTime)
1136 : {
1137 7 : const int iField = poOgrFeat->GetFieldIndex(pszOGRField);
1138 :
1139 7 : if (iField > -1)
1140 : {
1141 : OGRField sField;
1142 :
1143 7 : if (OGRParseXMLDateTime(osKmlDateTime.c_str(), &sField))
1144 7 : poOgrFeat->SetField(iField, &sField);
1145 : }
1146 7 : }
1147 :
1148 : /******************************************************************************
1149 : function to read kml into ogr fields
1150 : ******************************************************************************/
1151 :
1152 1295 : void kml2field(OGRFeature *poOgrFeat, FeaturePtr poKmlFeature,
1153 : const fieldconfig &oFC)
1154 : {
1155 : /***** id *****/
1156 :
1157 1295 : if (poKmlFeature->has_id())
1158 : {
1159 1052 : const std::string oKmlId = poKmlFeature->get_id();
1160 526 : int iField = poOgrFeat->GetFieldIndex(oFC.idfield);
1161 :
1162 526 : if (iField > -1)
1163 510 : poOgrFeat->SetField(iField, oKmlId.c_str());
1164 : }
1165 : /***** name *****/
1166 :
1167 1295 : if (poKmlFeature->has_name())
1168 : {
1169 1518 : const std::string oKmlName = poKmlFeature->get_name();
1170 759 : int iField = poOgrFeat->GetFieldIndex(oFC.namefield);
1171 :
1172 759 : if (iField > -1)
1173 759 : poOgrFeat->SetField(iField, oKmlName.c_str());
1174 : }
1175 :
1176 : /***** description *****/
1177 :
1178 1295 : if (poKmlFeature->has_description())
1179 : {
1180 788 : const std::string oKmlDesc = poKmlFeature->get_description();
1181 394 : int iField = poOgrFeat->GetFieldIndex(oFC.descfield);
1182 :
1183 394 : if (iField > -1)
1184 394 : poOgrFeat->SetField(iField, oKmlDesc.c_str());
1185 : }
1186 :
1187 1295 : if (poKmlFeature->has_timeprimitive())
1188 : {
1189 4 : TimePrimitivePtr poKmlTimePrimitive = poKmlFeature->get_timeprimitive();
1190 :
1191 : /***** timestamp *****/
1192 :
1193 2 : if (poKmlTimePrimitive->IsA(kmldom::Type_TimeStamp))
1194 : {
1195 : // Probably a libkml bug: AsTimeStamp should really return not NULL
1196 : // on a gx:TimeStamp.
1197 2 : TimeStampPtr poKmlTimeStamp = AsTimeStamp(poKmlTimePrimitive);
1198 1 : if (!poKmlTimeStamp)
1199 1 : poKmlTimeStamp = AsGxTimeStamp(poKmlTimePrimitive);
1200 :
1201 1 : if (poKmlTimeStamp && poKmlTimeStamp->has_when())
1202 : {
1203 2 : const std::string oKmlWhen = poKmlTimeStamp->get_when();
1204 1 : kmldatetime2ogr(poOgrFeat, oFC.tsfield, oKmlWhen);
1205 : }
1206 : }
1207 :
1208 : /***** timespan *****/
1209 :
1210 2 : if (poKmlTimePrimitive->IsA(kmldom::Type_TimeSpan))
1211 : {
1212 : // Probably a libkml bug: AsTimeSpan should really return not NULL
1213 : // on a gx:TimeSpan.
1214 2 : TimeSpanPtr poKmlTimeSpan = AsTimeSpan(poKmlTimePrimitive);
1215 1 : if (!poKmlTimeSpan)
1216 1 : poKmlTimeSpan = AsGxTimeSpan(poKmlTimePrimitive);
1217 :
1218 : /***** begin *****/
1219 :
1220 1 : if (poKmlTimeSpan && poKmlTimeSpan->has_begin())
1221 : {
1222 2 : const std::string oKmlWhen = poKmlTimeSpan->get_begin();
1223 1 : kmldatetime2ogr(poOgrFeat, oFC.beginfield, oKmlWhen);
1224 : }
1225 :
1226 : /***** end *****/
1227 :
1228 1 : if (poKmlTimeSpan && poKmlTimeSpan->has_end())
1229 : {
1230 2 : const std::string oKmlWhen = poKmlTimeSpan->get_end();
1231 1 : kmldatetime2ogr(poOgrFeat, oFC.endfield, oKmlWhen);
1232 : }
1233 : }
1234 : }
1235 :
1236 : /***** placemark *****/
1237 2590 : PlacemarkPtr poKmlPlacemark = AsPlacemark(poKmlFeature);
1238 2590 : GroundOverlayPtr poKmlGroundOverlay = AsGroundOverlay(poKmlFeature);
1239 1295 : if (poKmlPlacemark && poKmlPlacemark->has_geometry())
1240 : {
1241 2378 : GeometryPtr poKmlGeometry = poKmlPlacemark->get_geometry();
1242 :
1243 : /***** altitudeMode *****/
1244 1189 : int bIsGX = false;
1245 1189 : int nAltitudeMode = -1;
1246 :
1247 1189 : int iField = poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
1248 :
1249 1189 : if (iField > -1)
1250 : {
1251 1189 : if (kml2altitudemode_rec(poKmlGeometry, &nAltitudeMode, &bIsGX))
1252 : {
1253 484 : ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode,
1254 484 : CPL_TO_BOOL(bIsGX));
1255 : }
1256 : }
1257 :
1258 : /***** tessellate *****/
1259 1189 : int nTessellate = -1;
1260 :
1261 1189 : kml2tessellate_rec(poKmlGeometry, &nTessellate);
1262 :
1263 1189 : iField = poOgrFeat->GetFieldIndex(oFC.tessellatefield);
1264 1189 : if (iField > -1)
1265 1189 : poOgrFeat->SetField(iField, nTessellate);
1266 :
1267 : /***** extrude *****/
1268 :
1269 1189 : bool bExtrude = false;
1270 :
1271 1189 : kml2extrude_rec(poKmlGeometry, &bExtrude);
1272 :
1273 1189 : iField = poOgrFeat->GetFieldIndex(oFC.extrudefield);
1274 1189 : if (iField > -1)
1275 1189 : poOgrFeat->SetField(iField, bExtrude ? 1 : 0);
1276 :
1277 : /***** special case for gx:Track ******/
1278 : /* we set the first timestamp as begin and the last one as end */
1279 1191 : if (poKmlGeometry->Type() == kmldom::Type_GxTrack &&
1280 2 : !poKmlFeature->has_timeprimitive())
1281 : {
1282 4 : GxTrackPtr poKmlGxTrack = AsGxTrack(poKmlGeometry);
1283 2 : if (poKmlGxTrack)
1284 : {
1285 2 : const size_t nCoords = poKmlGxTrack->get_when_array_size();
1286 2 : if (nCoords > 0)
1287 : {
1288 2 : kmldatetime2ogr(poOgrFeat, oFC.beginfield,
1289 1 : poKmlGxTrack->get_when_array_at(0).c_str());
1290 3 : kmldatetime2ogr(
1291 1 : poOgrFeat, oFC.endfield,
1292 1 : poKmlGxTrack->get_when_array_at(nCoords - 1).c_str());
1293 : }
1294 : }
1295 : }
1296 :
1297 : /***** special case for gx:MultiTrack ******/
1298 : /* we set the first timestamp as begin and the last one as end */
1299 1188 : else if (poKmlGeometry->Type() == kmldom::Type_GxMultiTrack &&
1300 1 : !poKmlFeature->has_timeprimitive())
1301 : {
1302 2 : GxMultiTrackPtr poKmlGxMultiTrack = AsGxMultiTrack(poKmlGeometry);
1303 1 : if (poKmlGxMultiTrack)
1304 : {
1305 : const size_t nGeom =
1306 1 : poKmlGxMultiTrack->get_gx_track_array_size();
1307 1 : if (nGeom >= 1)
1308 : {
1309 : {
1310 : GxTrackPtr poKmlGxTrack =
1311 2 : poKmlGxMultiTrack->get_gx_track_array_at(0);
1312 : const size_t nCoords =
1313 1 : poKmlGxTrack->get_when_array_size();
1314 1 : if (nCoords > 0)
1315 : {
1316 3 : kmldatetime2ogr(
1317 1 : poOgrFeat, oFC.beginfield,
1318 1 : poKmlGxTrack->get_when_array_at(0).c_str());
1319 : }
1320 : }
1321 :
1322 : {
1323 : GxTrackPtr poKmlGxTrack =
1324 2 : poKmlGxMultiTrack->get_gx_track_array_at(nGeom - 1);
1325 : const size_t nCoords =
1326 1 : poKmlGxTrack->get_when_array_size();
1327 1 : if (nCoords > 0)
1328 : {
1329 3 : kmldatetime2ogr(
1330 1 : poOgrFeat, oFC.endfield,
1331 1 : poKmlGxTrack->get_when_array_at(nCoords - 1)
1332 : .c_str());
1333 : }
1334 : }
1335 : }
1336 : }
1337 : }
1338 : }
1339 :
1340 : /***** camera *****/
1341 :
1342 108 : else if (poKmlPlacemark && poKmlPlacemark->has_abstractview() &&
1343 2 : poKmlPlacemark->get_abstractview()->IsA(kmldom::Type_Camera))
1344 : {
1345 4 : const CameraPtr &camera = AsCamera(poKmlPlacemark->get_abstractview());
1346 :
1347 2 : if (camera->has_heading())
1348 : {
1349 2 : int iField = poOgrFeat->GetFieldIndex(oFC.headingfield);
1350 2 : if (iField > -1)
1351 2 : poOgrFeat->SetField(iField, camera->get_heading());
1352 : }
1353 :
1354 2 : if (camera->has_tilt())
1355 : {
1356 1 : int iField = poOgrFeat->GetFieldIndex(oFC.tiltfield);
1357 1 : if (iField > -1)
1358 1 : poOgrFeat->SetField(iField, camera->get_tilt());
1359 : }
1360 :
1361 2 : if (camera->has_roll())
1362 : {
1363 1 : int iField = poOgrFeat->GetFieldIndex(oFC.rollfield);
1364 1 : if (iField > -1)
1365 1 : poOgrFeat->SetField(iField, camera->get_roll());
1366 : }
1367 :
1368 2 : int iField = poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
1369 :
1370 2 : if (iField > -1)
1371 : {
1372 2 : if (camera->has_altitudemode())
1373 : {
1374 1 : const int nAltitudeMode = camera->get_altitudemode();
1375 1 : ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, false);
1376 : }
1377 1 : else if (camera->has_gx_altitudemode())
1378 : {
1379 0 : const int nAltitudeMode = camera->get_gx_altitudemode();
1380 0 : ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, true);
1381 : }
1382 : }
1383 : }
1384 : /***** ground overlay *****/
1385 104 : else if (poKmlGroundOverlay)
1386 : {
1387 : /***** icon *****/
1388 42 : int iField = poOgrFeat->GetFieldIndex(oFC.iconfield);
1389 42 : if (iField > -1)
1390 : {
1391 42 : if (poKmlGroundOverlay->has_icon())
1392 : {
1393 84 : IconPtr icon = poKmlGroundOverlay->get_icon();
1394 42 : if (icon->has_href())
1395 : {
1396 42 : poOgrFeat->SetField(iField, icon->get_href().c_str());
1397 : }
1398 : }
1399 : }
1400 :
1401 : /***** drawOrder *****/
1402 42 : iField = poOgrFeat->GetFieldIndex(oFC.drawOrderfield);
1403 42 : if (iField > -1)
1404 : {
1405 42 : if (poKmlGroundOverlay->has_draworder())
1406 : {
1407 0 : poOgrFeat->SetField(iField,
1408 0 : poKmlGroundOverlay->get_draworder());
1409 : }
1410 : }
1411 :
1412 : /***** altitudeMode *****/
1413 :
1414 42 : iField = poOgrFeat->GetFieldIndex(oFC.altitudeModefield);
1415 :
1416 42 : if (iField > -1)
1417 : {
1418 42 : if (poKmlGroundOverlay->has_altitudemode())
1419 : {
1420 0 : switch (poKmlGroundOverlay->get_altitudemode())
1421 : {
1422 0 : case kmldom::ALTITUDEMODE_CLAMPTOGROUND:
1423 0 : poOgrFeat->SetField(iField, "clampToGround");
1424 0 : break;
1425 :
1426 0 : case kmldom::ALTITUDEMODE_RELATIVETOGROUND:
1427 0 : poOgrFeat->SetField(iField, "relativeToGround");
1428 0 : break;
1429 :
1430 0 : case kmldom::ALTITUDEMODE_ABSOLUTE:
1431 0 : poOgrFeat->SetField(iField, "absolute");
1432 0 : break;
1433 : }
1434 : }
1435 42 : else if (poKmlGroundOverlay->has_gx_altitudemode())
1436 : {
1437 0 : switch (poKmlGroundOverlay->get_gx_altitudemode())
1438 : {
1439 0 : case kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR:
1440 0 : poOgrFeat->SetField(iField, "relativeToSeaFloor");
1441 0 : break;
1442 :
1443 0 : case kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR:
1444 0 : poOgrFeat->SetField(iField, "clampToSeaFloor");
1445 0 : break;
1446 : }
1447 : }
1448 : }
1449 : }
1450 :
1451 : /***** visibility *****/
1452 : const int nVisibility =
1453 1295 : poKmlFeature->has_visibility() ? poKmlFeature->get_visibility() : -1;
1454 :
1455 1295 : int iField = poOgrFeat->GetFieldIndex(oFC.visibilityfield);
1456 :
1457 1295 : if (iField > -1)
1458 1279 : poOgrFeat->SetField(iField, nVisibility);
1459 :
1460 : /***** snippet *****/
1461 :
1462 1295 : if (poKmlFeature->has_snippet())
1463 : {
1464 2 : CPLString oText = poKmlFeature->get_snippet()->get_text();
1465 :
1466 1 : iField = poOgrFeat->GetFieldIndex(oFC.snippetfield);
1467 :
1468 1 : if (iField > -1)
1469 1 : poOgrFeat->SetField(iField, TrimSpaces(oText));
1470 : }
1471 :
1472 : /***** extended schema *****/
1473 2590 : ExtendedDataPtr poKmlExtendedData = nullptr;
1474 :
1475 1295 : if (poKmlFeature->has_extendeddata())
1476 : {
1477 441 : poKmlExtendedData = poKmlFeature->get_extendeddata();
1478 :
1479 : /***** loop over the schemadata_arrays *****/
1480 :
1481 : const size_t nSchemaData =
1482 441 : poKmlExtendedData->get_schemadata_array_size();
1483 :
1484 879 : for (size_t iSchemaData = 0; iSchemaData < nSchemaData; iSchemaData++)
1485 : {
1486 : SchemaDataPtr poKmlSchemaData =
1487 876 : poKmlExtendedData->get_schemadata_array_at(iSchemaData);
1488 :
1489 : /***** loop over the simpledata array *****/
1490 :
1491 : const size_t nSimpleData =
1492 438 : poKmlSchemaData->get_simpledata_array_size();
1493 :
1494 1736 : for (size_t iSimpleData = 0; iSimpleData < nSimpleData;
1495 : iSimpleData++)
1496 : {
1497 : SimpleDataPtr poKmlSimpleData =
1498 2596 : poKmlSchemaData->get_simpledata_array_at(iSimpleData);
1499 :
1500 : /***** find the field index *****/
1501 :
1502 1298 : iField = -1;
1503 :
1504 1298 : if (poKmlSimpleData->has_name())
1505 : {
1506 1298 : const string oName = poKmlSimpleData->get_name();
1507 1298 : const char *pszName = oName.c_str();
1508 :
1509 1298 : iField = poOgrFeat->GetFieldIndex(pszName);
1510 : }
1511 :
1512 : /***** if it has trxt set the field *****/
1513 :
1514 1298 : if (iField > -1 && poKmlSimpleData->has_text())
1515 : {
1516 2596 : CPLString oText = poKmlSimpleData->get_text();
1517 :
1518 1298 : poOgrFeat->SetField(iField, TrimSpaces(oText));
1519 : }
1520 : }
1521 : }
1522 :
1523 441 : if (nSchemaData == 0 && poKmlExtendedData->get_data_array_size() > 0)
1524 : {
1525 3 : const bool bLaunderFieldNames = CPLTestBool(
1526 : CPLGetConfigOption("LIBKML_LAUNDER_FIELD_NAMES", "YES"));
1527 : const size_t nDataArraySize =
1528 3 : poKmlExtendedData->get_data_array_size();
1529 8 : for (size_t i = 0; i < nDataArraySize; i++)
1530 : {
1531 5 : const DataPtr &data = poKmlExtendedData->get_data_array_at(i);
1532 5 : if (data->has_name() && data->has_value())
1533 : {
1534 10 : CPLString osName = std::string(data->get_name());
1535 5 : if (bLaunderFieldNames)
1536 5 : osName = OGRLIBKMLLayer::LaunderFieldNames(osName);
1537 5 : iField = poOgrFeat->GetFieldIndex(osName);
1538 5 : if (iField >= 0)
1539 : {
1540 5 : poOgrFeat->SetField(iField, data->get_value().c_str());
1541 : }
1542 : }
1543 : }
1544 : }
1545 : }
1546 1295 : }
1547 :
1548 : /******************************************************************************
1549 : function create a simplefield from a FieldDefn
1550 : ******************************************************************************/
1551 :
1552 179 : SimpleFieldPtr FieldDef2kml(const OGRFieldDefn *poOgrFieldDef,
1553 : KmlFactory *poKmlFactory, bool bApproxOK,
1554 : const fieldconfig &oFC)
1555 : {
1556 179 : const char *pszFieldName = poOgrFieldDef->GetNameRef();
1557 :
1558 179 : if (EQUAL(pszFieldName, oFC.idfield) ||
1559 176 : EQUAL(pszFieldName, oFC.namefield) ||
1560 165 : EQUAL(pszFieldName, oFC.descfield) ||
1561 159 : EQUAL(pszFieldName, oFC.tsfield) ||
1562 159 : EQUAL(pszFieldName, oFC.beginfield) ||
1563 159 : EQUAL(pszFieldName, oFC.endfield) ||
1564 159 : EQUAL(pszFieldName, oFC.altitudeModefield) ||
1565 157 : EQUAL(pszFieldName, oFC.tessellatefield) ||
1566 157 : EQUAL(pszFieldName, oFC.extrudefield) ||
1567 157 : EQUAL(pszFieldName, oFC.visibilityfield) ||
1568 157 : EQUAL(pszFieldName, oFC.drawOrderfield) ||
1569 157 : EQUAL(pszFieldName, oFC.iconfield) ||
1570 157 : EQUAL(pszFieldName, oFC.headingfield) ||
1571 154 : EQUAL(pszFieldName, oFC.tiltfield) ||
1572 151 : EQUAL(pszFieldName, oFC.rollfield) ||
1573 148 : EQUAL(pszFieldName, oFC.snippetfield) ||
1574 147 : EQUAL(pszFieldName, oFC.modelfield) ||
1575 146 : EQUAL(pszFieldName, oFC.scalexfield) ||
1576 145 : EQUAL(pszFieldName, oFC.scaleyfield) ||
1577 144 : EQUAL(pszFieldName, oFC.scalezfield) ||
1578 143 : EQUAL(pszFieldName, oFC.networklinkfield) ||
1579 142 : EQUAL(pszFieldName, oFC.networklink_refreshvisibility_field) ||
1580 141 : EQUAL(pszFieldName, oFC.networklink_flytoview_field) ||
1581 140 : EQUAL(pszFieldName, oFC.networklink_refreshMode_field) ||
1582 139 : EQUAL(pszFieldName, oFC.networklink_refreshInterval_field) ||
1583 138 : EQUAL(pszFieldName, oFC.networklink_viewRefreshMode_field) ||
1584 137 : EQUAL(pszFieldName, oFC.networklink_viewRefreshTime_field) ||
1585 136 : EQUAL(pszFieldName, oFC.networklink_viewBoundScale_field) ||
1586 135 : EQUAL(pszFieldName, oFC.networklink_viewFormat_field) ||
1587 134 : EQUAL(pszFieldName, oFC.networklink_httpQuery_field) ||
1588 133 : EQUAL(pszFieldName, oFC.camera_longitude_field) ||
1589 132 : EQUAL(pszFieldName, oFC.camera_latitude_field) ||
1590 131 : EQUAL(pszFieldName, oFC.camera_altitude_field) ||
1591 130 : EQUAL(pszFieldName, oFC.camera_altitudemode_field) ||
1592 129 : EQUAL(pszFieldName, oFC.photooverlayfield) ||
1593 128 : EQUAL(pszFieldName, oFC.leftfovfield) ||
1594 127 : EQUAL(pszFieldName, oFC.rightfovfield) ||
1595 126 : EQUAL(pszFieldName, oFC.bottomfovfield) ||
1596 125 : EQUAL(pszFieldName, oFC.topfovfield) ||
1597 124 : EQUAL(pszFieldName, oFC.nearfield) ||
1598 123 : EQUAL(pszFieldName, oFC.photooverlay_shape_field) ||
1599 122 : EQUAL(pszFieldName, oFC.imagepyramid_tilesize_field) ||
1600 121 : EQUAL(pszFieldName, oFC.imagepyramid_maxwidth_field) ||
1601 120 : EQUAL(pszFieldName, oFC.imagepyramid_maxheight_field) ||
1602 119 : EQUAL(pszFieldName, oFC.imagepyramid_gridorigin_field))
1603 : {
1604 60 : return nullptr;
1605 : }
1606 :
1607 238 : SimpleFieldPtr poKmlSimpleField = poKmlFactory->CreateSimpleField();
1608 119 : poKmlSimpleField->set_name(pszFieldName);
1609 :
1610 119 : switch (poOgrFieldDef->GetType())
1611 : {
1612 17 : case OFTInteger:
1613 : case OFTIntegerList:
1614 34 : poKmlSimpleField->set_type(
1615 17 : poOgrFieldDef->GetSubType() == OFSTBoolean ? "bool" : "int");
1616 17 : return poKmlSimpleField;
1617 :
1618 17 : case OFTReal:
1619 : case OFTRealList:
1620 34 : poKmlSimpleField->set_type(
1621 17 : poOgrFieldDef->GetSubType() == OFSTFloat32 ? "float"
1622 : : "double");
1623 17 : return poKmlSimpleField;
1624 :
1625 40 : case OFTString:
1626 : case OFTStringList:
1627 40 : poKmlSimpleField->set_type("string");
1628 40 : return poKmlSimpleField;
1629 :
1630 1 : case OFTInteger64:
1631 1 : if (bApproxOK)
1632 : {
1633 1 : poKmlSimpleField->set_type("string");
1634 1 : return poKmlSimpleField;
1635 : }
1636 0 : break;
1637 :
1638 : /***** kml has these types but as timestamp/timespan *****/
1639 44 : case OFTDate:
1640 : case OFTTime:
1641 : case OFTDateTime:
1642 44 : if (bApproxOK)
1643 : {
1644 44 : poKmlSimpleField->set_type("string");
1645 44 : return poKmlSimpleField;
1646 : }
1647 0 : break;
1648 :
1649 0 : default:
1650 0 : poKmlSimpleField->set_type("string");
1651 0 : return poKmlSimpleField;
1652 : }
1653 :
1654 0 : return nullptr;
1655 : }
1656 :
1657 : /******************************************************************************
1658 : function to add the simpleFields in a schema to a featuredefn
1659 : ******************************************************************************/
1660 :
1661 35 : void kml2FeatureDef(SchemaPtr poKmlSchema, OGRFeatureDefn *poOgrFeatureDefn)
1662 : {
1663 35 : const size_t nSimpleFields = poKmlSchema->get_simplefield_array_size();
1664 :
1665 156 : for (size_t iSimpleField = 0; iSimpleField < nSimpleFields; iSimpleField++)
1666 : {
1667 : SimpleFieldPtr poKmlSimpleField =
1668 242 : poKmlSchema->get_simplefield_array_at(iSimpleField);
1669 :
1670 121 : const char *pszType = "string";
1671 242 : string osName = "Unknown";
1672 242 : string osType;
1673 :
1674 121 : if (poKmlSimpleField->has_type())
1675 : {
1676 121 : osType = poKmlSimpleField->get_type();
1677 :
1678 121 : pszType = osType.c_str();
1679 : }
1680 :
1681 : // TODO: We cannot set displayname as the field name because in
1682 : // kml2field() we make the lookup on fields based on their name. We
1683 : // would need some map if we really want to use displayname, but that
1684 : // might not be a good idea because displayname may have HTML
1685 : // formatting, which makes it impractical when converting to other
1686 : // drivers or to make requests.
1687 : // Example: http://www.jasonbirch.com/files/newt_combined.kml
1688 :
1689 : // if( poKmlSimpleField->has_displayname() )
1690 : // {
1691 : // osName = poKmlSimpleField->get_displayname();
1692 : // }
1693 : // else
1694 121 : if (poKmlSimpleField->has_name())
1695 : {
1696 121 : osName = poKmlSimpleField->get_name();
1697 : }
1698 121 : if (poOgrFeatureDefn->GetFieldIndex(osName.c_str()) < 0)
1699 : {
1700 116 : if (EQUAL(pszType, "bool") || EQUAL(pszType, "boolean"))
1701 : {
1702 2 : OGRFieldDefn ogrFieldDefn(osName.c_str(), OFTInteger);
1703 1 : ogrFieldDefn.SetSubType(OFSTBoolean);
1704 2 : poOgrFeatureDefn->AddFieldDefn(&ogrFieldDefn);
1705 : }
1706 115 : else if (EQUAL(pszType, "int") || EQUAL(pszType, "short") ||
1707 99 : EQUAL(pszType, "ushort"))
1708 : {
1709 32 : OGRFieldDefn ogrFieldDefn(osName.c_str(), OFTInteger);
1710 32 : poOgrFeatureDefn->AddFieldDefn(&ogrFieldDefn);
1711 : }
1712 99 : else if (EQUAL(pszType, "uint"))
1713 : {
1714 0 : OGRFieldDefn ogrFieldDefn(osName.c_str(), OFTInteger64);
1715 0 : poOgrFeatureDefn->AddFieldDefn(&ogrFieldDefn);
1716 : }
1717 99 : else if (EQUAL(pszType, "float") || EQUAL(pszType, "double"))
1718 : {
1719 : // We write correctly 'double' for 64-bit since GDAL 3.11.1.
1720 : // In prior versions we wrote 'float', so it is premature
1721 : // on reading to set OFSTFloat32 when reading 'float'
1722 40 : OGRFieldDefn ogrFieldDefn(osName.c_str(), OFTReal);
1723 40 : poOgrFeatureDefn->AddFieldDefn(&ogrFieldDefn);
1724 : }
1725 : else // string, or any other unrecognized type.
1726 : {
1727 158 : OGRFieldDefn ogrFieldDefn(osName.c_str(), OFTString);
1728 79 : poOgrFeatureDefn->AddFieldDefn(&ogrFieldDefn);
1729 : }
1730 : }
1731 : }
1732 35 : }
1733 :
1734 : /*******************************************************************************
1735 : * function to fetch the field config options
1736 : *
1737 : *******************************************************************************/
1738 :
1739 366 : void get_fieldconfig(struct fieldconfig *oFC)
1740 : {
1741 366 : oFC->idfield = CPLGetConfigOption("LIBKML_ID_FIELD", "id");
1742 366 : oFC->namefield = CPLGetConfigOption("LIBKML_NAME_FIELD", "Name");
1743 366 : oFC->descfield =
1744 366 : CPLGetConfigOption("LIBKML_DESCRIPTION_FIELD", "description");
1745 366 : oFC->tsfield = CPLGetConfigOption("LIBKML_TIMESTAMP_FIELD", "timestamp");
1746 366 : oFC->beginfield = CPLGetConfigOption("LIBKML_BEGIN_FIELD", "begin");
1747 366 : oFC->endfield = CPLGetConfigOption("LIBKML_END_FIELD", "end");
1748 366 : oFC->altitudeModefield =
1749 366 : CPLGetConfigOption("LIBKML_ALTITUDEMODE_FIELD", "altitudeMode");
1750 366 : oFC->tessellatefield =
1751 366 : CPLGetConfigOption("LIBKML_TESSELLATE_FIELD", "tessellate");
1752 366 : oFC->extrudefield = CPLGetConfigOption("LIBKML_EXTRUDE_FIELD", "extrude");
1753 366 : oFC->visibilityfield =
1754 366 : CPLGetConfigOption("LIBKML_VISIBILITY_FIELD", "visibility");
1755 366 : oFC->drawOrderfield =
1756 366 : CPLGetConfigOption("LIBKML_DRAWORDER_FIELD", "drawOrder");
1757 366 : oFC->iconfield = CPLGetConfigOption("LIBKML_ICON_FIELD", "icon");
1758 366 : oFC->headingfield = CPLGetConfigOption("LIBKML_HEADING_FIELD", "heading");
1759 366 : oFC->tiltfield = CPLGetConfigOption("LIBKML_TILT_FIELD", "tilt");
1760 366 : oFC->rollfield = CPLGetConfigOption("LIBKML_ROLL_FIELD", "roll");
1761 366 : oFC->snippetfield = CPLGetConfigOption("LIBKML_SNIPPET_FIELD", "snippet");
1762 366 : oFC->modelfield = CPLGetConfigOption("LIBKML_MODEL_FIELD", "model");
1763 366 : oFC->scalexfield = CPLGetConfigOption("LIBKML_SCALE_X_FIELD", "scale_x");
1764 366 : oFC->scaleyfield = CPLGetConfigOption("LIBKML_SCALE_Y_FIELD", "scale_y");
1765 366 : oFC->scalezfield = CPLGetConfigOption("LIBKML_SCALE_Z_FIELD", "scale_z");
1766 366 : oFC->networklinkfield =
1767 366 : CPLGetConfigOption("LIBKML_NETWORKLINK_FIELD", "networklink");
1768 366 : oFC->networklink_refreshvisibility_field =
1769 366 : CPLGetConfigOption("LIBKML_NETWORKLINK_REFRESHVISIBILITY_FIELD",
1770 : "networklink_refreshvisibility");
1771 366 : oFC->networklink_flytoview_field = CPLGetConfigOption(
1772 : "LIBKML_NETWORKLINK_FLYTOVIEW_FIELD", "networklink_flytoview");
1773 366 : oFC->networklink_refreshMode_field = CPLGetConfigOption(
1774 : "LIBKML_NETWORKLINK_REFRESHMODE_FIELD", "networklink_refreshmode");
1775 366 : oFC->networklink_refreshInterval_field =
1776 366 : CPLGetConfigOption("LIBKML_NETWORKLINK_REFRESHINTERVAL_FIELD",
1777 : "networklink_refreshinterval");
1778 366 : oFC->networklink_viewRefreshMode_field =
1779 366 : CPLGetConfigOption("LIBKML_NETWORKLINK_VIEWREFRESHMODE_FIELD",
1780 : "networklink_viewrefreshmode");
1781 366 : oFC->networklink_viewRefreshTime_field =
1782 366 : CPLGetConfigOption("LIBKML_NETWORKLINK_VIEWREFRESHTIME_FIELD",
1783 : "networklink_viewrefreshtime");
1784 366 : oFC->networklink_viewBoundScale_field =
1785 366 : CPLGetConfigOption("LIBKML_NETWORKLINK_VIEWBOUNDSCALE_FIELD",
1786 : "networklink_viewboundscale");
1787 366 : oFC->networklink_viewFormat_field = CPLGetConfigOption(
1788 : "LIBKML_NETWORKLINK_VIEWFORMAT_FIELD", "networklink_viewformat");
1789 366 : oFC->networklink_httpQuery_field = CPLGetConfigOption(
1790 : "LIBKML_NETWORKLINK_HTTPQUERY_FIELD", "networklink_httpquery");
1791 366 : oFC->camera_longitude_field =
1792 366 : CPLGetConfigOption("LIBKML_CAMERA_LONGITUDE_FIELD", "camera_longitude");
1793 366 : oFC->camera_latitude_field =
1794 366 : CPLGetConfigOption("LIBKML_CAMERA_LATITUDE_FIELD", "camera_latitude");
1795 366 : oFC->camera_altitude_field =
1796 366 : CPLGetConfigOption("LIBKML_CAMERA_ALTITUDE_FIELD", "camera_altitude");
1797 366 : oFC->camera_altitudemode_field = CPLGetConfigOption(
1798 : "LIBKML_CAMERA_ALTITUDEMODE_FIELD", "camera_altitudemode");
1799 366 : oFC->photooverlayfield =
1800 366 : CPLGetConfigOption("LIBKML_PHOTOOVERLAY_FIELD", "photooverlay");
1801 366 : oFC->leftfovfield = CPLGetConfigOption("LIBKML_LEFTFOV_FIELD", "leftfov");
1802 366 : oFC->rightfovfield =
1803 366 : CPLGetConfigOption("LIBKML_RIGHTFOV_FIELD", "rightfov");
1804 366 : oFC->bottomfovfield =
1805 366 : CPLGetConfigOption("LIBKML_BOTTOMFOV_FIELD", "bottomfov");
1806 366 : oFC->topfovfield = CPLGetConfigOption("LIBKML_TOPFOV_FIELD", "topfov");
1807 366 : oFC->nearfield = CPLGetConfigOption("LIBKML_NEARFOV_FIELD", "near");
1808 366 : oFC->photooverlay_shape_field = CPLGetConfigOption(
1809 : "LIBKML_PHOTOOVERLAY_SHAPE_FIELD", "photooverlay_shape");
1810 366 : oFC->imagepyramid_tilesize_field = CPLGetConfigOption(
1811 : "LIBKML_IMAGEPYRAMID_TILESIZE", "imagepyramid_tilesize");
1812 366 : oFC->imagepyramid_maxwidth_field = CPLGetConfigOption(
1813 : "LIBKML_IMAGEPYRAMID_MAXWIDTH", "imagepyramid_maxwidth");
1814 366 : oFC->imagepyramid_maxheight_field = CPLGetConfigOption(
1815 : "LIBKML_IMAGEPYRAMID_MAXHEIGHT", "imagepyramid_maxheight");
1816 366 : oFC->imagepyramid_gridorigin_field = CPLGetConfigOption(
1817 : "LIBKML_IMAGEPYRAMID_GRIDORIGIN", "imagepyramid_gridorigin");
1818 366 : }
1819 :
1820 : /************************************************************************/
1821 : /* kmlAltitudeModeFromString() */
1822 : /************************************************************************/
1823 :
1824 8 : int kmlAltitudeModeFromString(const char *pszAltitudeMode, int &isGX)
1825 : {
1826 8 : isGX = FALSE;
1827 8 : int iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_CLAMPTOGROUND);
1828 :
1829 8 : if (EQUAL(pszAltitudeMode, "clampToGround"))
1830 : {
1831 0 : iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_CLAMPTOGROUND);
1832 : }
1833 8 : else if (EQUAL(pszAltitudeMode, "relativeToGround"))
1834 : {
1835 8 : iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_RELATIVETOGROUND);
1836 : }
1837 0 : else if (EQUAL(pszAltitudeMode, "absolute"))
1838 : {
1839 0 : iAltitudeMode = static_cast<int>(kmldom::ALTITUDEMODE_ABSOLUTE);
1840 : }
1841 0 : else if (EQUAL(pszAltitudeMode, "relativeToSeaFloor"))
1842 : {
1843 0 : iAltitudeMode =
1844 : static_cast<int>(kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR);
1845 0 : isGX = TRUE;
1846 : }
1847 0 : else if (EQUAL(pszAltitudeMode, "clampToSeaFloor"))
1848 : {
1849 0 : iAltitudeMode =
1850 : static_cast<int>(kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR);
1851 0 : isGX = TRUE;
1852 : }
1853 : else
1854 : {
1855 0 : CPLError(CE_Warning, CPLE_NotSupported,
1856 : "Unrecognized value for altitudeMode: %s", pszAltitudeMode);
1857 : }
1858 :
1859 8 : return iAltitudeMode;
1860 : }
|