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 :
16 : #include <string>
17 :
18 : #include "ogr_libkml.h"
19 : #include "cpl_error.h"
20 : #include "ogrlibkmlfeature.h"
21 : #include "ogrlibkmlfield.h"
22 : #include "ogrlibkmlstyle.h"
23 :
24 : #include <algorithm>
25 : #include <set>
26 :
27 : using kmldom::CameraPtr;
28 : using kmldom::ChangePtr;
29 : using kmldom::ContainerPtr;
30 : using kmldom::CreatePtr;
31 : using kmldom::DataPtr;
32 : using kmldom::DeletePtr;
33 : using kmldom::DocumentPtr;
34 : using kmldom::ElementPtr;
35 : using kmldom::ExtendedDataPtr;
36 : using kmldom::FeaturePtr;
37 : using kmldom::GroundOverlayPtr;
38 : using kmldom::IconPtr;
39 : using kmldom::KmlFactory;
40 : using kmldom::KmlPtr;
41 : using kmldom::LatLonAltBoxPtr;
42 : using kmldom::LodPtr;
43 : using kmldom::LookAtPtr;
44 : using kmldom::PlacemarkPtr;
45 : using kmldom::RegionPtr;
46 : using kmldom::SchemaDataPtr;
47 : using kmldom::ScreenOverlayPtr;
48 : using kmldom::SimpleFieldPtr;
49 : using kmldom::UpdatePtr;
50 : using kmlengine::Bbox;
51 :
52 : /************************************************************************/
53 : /* OGRLIBKMLGetSanitizedNCName() */
54 : /************************************************************************/
55 :
56 499 : CPLString OGRLIBKMLGetSanitizedNCName(const char *pszName)
57 : {
58 499 : CPLString osName;
59 : // (Approximate) validation rules for a valid NCName.
60 :
61 : // If the first character is illegal as a first character, but allowed in
62 : // later positions, prepend an initial underscore
63 : // (cf https://github.com/OSGeo/gdal/issues/9538)
64 499 : if (pszName[0] == '-' || pszName[0] == '.' ||
65 499 : (pszName[0] >= '0' && pszName[0] <= '9'))
66 : {
67 2 : osName = "_";
68 : }
69 499 : osName += pszName;
70 :
71 5537 : for (size_t i = 0; i < osName.size(); i++)
72 : {
73 5038 : char ch = osName[i];
74 5038 : if ((ch >= 'A' && ch <= 'Z') || ch == '_' || (ch >= 'a' && ch <= 'z') ||
75 898 : (ch == '-' || ch == '.' || (ch >= '0' && ch <= '9')))
76 : {
77 : /* ok */
78 : }
79 : // Always false: ch > 127.
80 : else
81 : {
82 192 : osName[i] = '_';
83 : }
84 : }
85 499 : return osName;
86 : }
87 :
88 : /******************************************************************************
89 : OGRLIBKMLLayer constructor
90 :
91 : Args: pszLayerName the name of the layer
92 : eGType the layers geometry type
93 : poOgrDS pointer to the datasource the layer is in
94 : poKmlRoot pointer to the root kml element of the layer
95 : poKmlContainer pointer to the kml container of the layer
96 : pszFileName the filename of the layer
97 : bNew true if its a new layer
98 : bUpdate true if the layer is writable
99 :
100 : Returns: nothing
101 :
102 : ******************************************************************************/
103 :
104 367 : OGRLIBKMLLayer::OGRLIBKMLLayer(
105 : const char *pszLayerName, OGRwkbGeometryType eGType,
106 : const OGRSpatialReference *poSRSIn, OGRLIBKMLDataSource *poOgrDS,
107 : ElementPtr poKmlRoot, ContainerPtr poKmlContainer, UpdatePtr poKmlUpdate,
108 367 : const char *pszFileName, bool bNew, bool bUpdateIn)
109 734 : : m_bNew(bNew), bUpdate(bUpdateIn), m_pszName(CPLStrdup(pszLayerName)),
110 734 : m_pszFileName(CPLStrdup(pszFileName)),
111 367 : m_poKmlLayer(std::move(poKmlContainer)), // Store the layers container.
112 : m_poKmlLayerRoot(
113 367 : std::move(poKmlRoot)), // Store the root element pointer.
114 367 : m_poKmlUpdate(std::move(poKmlUpdate)), m_poOgrDS(poOgrDS),
115 367 : m_poOgrFeatureDefn(new OGRFeatureDefn(pszLayerName)),
116 367 : m_poKmlSchema(nullptr), m_poOgrSRS(new OGRSpatialReference(nullptr)),
117 : m_bReadGroundOverlay(
118 367 : CPLTestBool(CPLGetConfigOption("LIBKML_READ_GROUND_OVERLAY", "YES"))),
119 : m_bUseSimpleField(
120 367 : CPLTestBool(CPLGetConfigOption("LIBKML_USE_SIMPLEFIELD", "YES"))),
121 : m_bWriteRegion(false), m_bRegionBoundsAuto(false),
122 : m_dfRegionMinLodPixels(0), m_dfRegionMaxLodPixels(-1),
123 : m_dfRegionMinFadeExtent(0), m_dfRegionMaxFadeExtent(0),
124 : m_dfRegionMinX(200), m_dfRegionMinY(200), m_dfRegionMaxX(-200),
125 1835 : m_dfRegionMaxY(-200), m_bUpdateIsFolder(false)
126 : {
127 367 : get_fieldconfig(&m_oFieldConfig);
128 :
129 367 : m_poStyleTable = nullptr;
130 :
131 367 : m_poOgrSRS->SetWellKnownGeogCS("WGS84");
132 367 : m_poOgrSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
133 :
134 : // KML should be created as WGS84.
135 367 : if (poSRSIn != nullptr)
136 : {
137 8 : if (!m_poOgrSRS->IsSame(poSRSIn))
138 : {
139 6 : m_poCT.reset(
140 6 : OGRCreateCoordinateTransformation(poSRSIn, m_poOgrSRS));
141 6 : if (m_poCT == nullptr && poOgrDS->IsFirstCTError())
142 : {
143 : // If we can't create a transformation, issue a warning - but
144 : // continue the transformation.
145 0 : char *pszWKT = nullptr;
146 :
147 0 : poSRSIn->exportToPrettyWkt(&pszWKT, FALSE);
148 :
149 0 : CPLError(
150 : CE_Warning, CPLE_AppDefined,
151 : "Failed to create coordinate transformation between the "
152 : "input coordinate system and WGS84. This may be because "
153 : "they are not transformable. "
154 : "KML geometries may not render correctly. "
155 : "This message will not be issued any more."
156 : "\nSource:\n%s\n",
157 : pszWKT);
158 :
159 0 : CPLFree(pszWKT);
160 0 : poOgrDS->IssuedFirstCTError();
161 : }
162 : }
163 : }
164 :
165 : m_osSanitizedNCName =
166 367 : OGRLIBKMLGetSanitizedNCName(m_poOgrFeatureDefn->GetName());
167 :
168 367 : SetDescription(m_poOgrFeatureDefn->GetName());
169 367 : m_poOgrFeatureDefn->Reference();
170 367 : m_poOgrFeatureDefn->SetGeomType(eGType);
171 367 : if (m_poOgrFeatureDefn->GetGeomFieldCount() != 0)
172 364 : m_poOgrFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poOgrSRS);
173 :
174 : /***** was the layer created from a DS::Open *****/
175 367 : if (!bNew)
176 : {
177 : /***** get the number of features on the layer *****/
178 240 : nFeatures = static_cast<int>(m_poKmlLayer->get_feature_array_size());
179 :
180 : /***** id field *****/
181 480 : OGRFieldDefn oOgrFieldId(m_oFieldConfig.idfield, OFTString);
182 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldId);
183 :
184 : /***** name field *****/
185 480 : OGRFieldDefn oOgrFieldName(m_oFieldConfig.namefield, OFTString);
186 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldName);
187 :
188 : /***** description field *****/
189 480 : OGRFieldDefn oOgrFieldDesc(m_oFieldConfig.descfield, OFTString);
190 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldDesc);
191 :
192 : /***** timestamp field *****/
193 480 : OGRFieldDefn oOgrFieldTs(m_oFieldConfig.tsfield, OFTDateTime);
194 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldTs);
195 :
196 : /***** timespan begin field *****/
197 480 : OGRFieldDefn oOgrFieldBegin(m_oFieldConfig.beginfield, OFTDateTime);
198 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldBegin);
199 :
200 : /***** timespan end field *****/
201 480 : OGRFieldDefn oOgrFieldEnd(m_oFieldConfig.endfield, OFTDateTime);
202 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldEnd);
203 :
204 : /***** altitudeMode field *****/
205 : OGRFieldDefn oOgrFieldAltitudeMode(m_oFieldConfig.altitudeModefield,
206 480 : OFTString);
207 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldAltitudeMode);
208 :
209 : /***** tessellate field *****/
210 : OGRFieldDefn oOgrFieldTessellate(m_oFieldConfig.tessellatefield,
211 480 : OFTInteger);
212 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldTessellate);
213 :
214 : /***** extrude field *****/
215 480 : OGRFieldDefn oOgrFieldExtrude(m_oFieldConfig.extrudefield, OFTInteger);
216 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldExtrude);
217 :
218 : /***** visibility field *****/
219 : OGRFieldDefn oOgrFieldVisibility(m_oFieldConfig.visibilityfield,
220 480 : OFTInteger);
221 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldVisibility);
222 :
223 : /***** draw order field *****/
224 : OGRFieldDefn oOgrFieldDrawOrder(m_oFieldConfig.drawOrderfield,
225 480 : OFTInteger);
226 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldDrawOrder);
227 :
228 : /***** icon field *****/
229 480 : OGRFieldDefn oOgrFieldIcon(m_oFieldConfig.iconfield, OFTString);
230 240 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrFieldIcon);
231 :
232 : /***** get the styles *****/
233 240 : if (m_poKmlLayer->IsA(kmldom::Type_Document))
234 87 : ParseStyles(AsDocument(m_poKmlLayer), &m_poStyleTable);
235 :
236 240 : bool bCanSetKmlSchema = true;
237 :
238 480 : std::set<std::string> oSetSimpleFields;
239 :
240 : /***** get the schema if the layer is a Document *****/
241 240 : if (m_poKmlLayer->IsA(kmldom::Type_Document))
242 : {
243 174 : DocumentPtr poKmlDocument = AsDocument(m_poKmlLayer);
244 :
245 87 : if (poKmlDocument->get_schema_array_size())
246 : {
247 15 : for (size_t i = 0; i < poKmlDocument->get_schema_array_size();
248 : i++)
249 : {
250 9 : auto schema = poKmlDocument->get_schema_array_at(i);
251 9 : if (bCanSetKmlSchema && !m_poKmlSchema)
252 : {
253 6 : m_poKmlSchema = schema;
254 6 : bCanSetKmlSchema = false;
255 : }
256 : else
257 : {
258 3 : m_poKmlSchema = nullptr;
259 : }
260 9 : kml2FeatureDef(std::move(schema), m_poOgrFeatureDefn,
261 9 : m_oMapSimpleFieldNameToOgrFieldIx,
262 : oSetSimpleFields);
263 : }
264 : }
265 : }
266 :
267 : /***** the schema is somewhere else *****/
268 240 : if (bCanSetKmlSchema)
269 : {
270 : /***** try to find the correct schema *****/
271 234 : bool bHasHeading = false;
272 234 : bool bHasTilt = false;
273 234 : bool bHasRoll = false;
274 234 : bool bHasSnippet = false;
275 468 : FeaturePtr poKmlFeature = nullptr;
276 234 : const bool bLaunderFieldNames = CPLTestBool(
277 : CPLGetConfigOption("LIBKML_LAUNDER_FIELD_NAMES", "YES"));
278 234 : std::set<std::string> oSetSchemaAlreadyVisited;
279 :
280 : /***** find the first placemark *****/
281 805 : for (iFeature = 0; iFeature < nFeatures; iFeature++)
282 : {
283 571 : poKmlFeature = m_poKmlLayer->get_feature_array_at(iFeature);
284 :
285 571 : if (poKmlFeature->Type() == kmldom::Type_Placemark)
286 : {
287 900 : PlacemarkPtr poKmlPlacemark = AsPlacemark(poKmlFeature);
288 517 : if (!poKmlPlacemark->has_geometry() &&
289 517 : poKmlPlacemark->has_abstractview() &&
290 4 : poKmlPlacemark->get_abstractview()->IsA(
291 2 : kmldom::Type_Camera))
292 : {
293 : const CameraPtr &camera =
294 4 : AsCamera(poKmlPlacemark->get_abstractview());
295 2 : if (camera->has_heading() && !bHasHeading)
296 : {
297 1 : bHasHeading = true;
298 : OGRFieldDefn oOgrField(m_oFieldConfig.headingfield,
299 2 : OFTReal);
300 1 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrField);
301 : }
302 2 : if (camera->has_tilt() && !bHasTilt)
303 : {
304 1 : bHasTilt = true;
305 : OGRFieldDefn oOgrField(m_oFieldConfig.tiltfield,
306 2 : OFTReal);
307 1 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrField);
308 : }
309 2 : if (camera->has_roll() && !bHasRoll)
310 : {
311 1 : bHasRoll = true;
312 : OGRFieldDefn oOgrField(m_oFieldConfig.rollfield,
313 2 : OFTReal);
314 1 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrField);
315 : }
316 : }
317 :
318 450 : if (poKmlFeature->has_extendeddata())
319 : {
320 : const ExtendedDataPtr poKmlExtendedData =
321 234 : poKmlFeature->get_extendeddata();
322 :
323 117 : if (poKmlExtendedData->get_schemadata_array_size() > 0)
324 : {
325 : const SchemaDataPtr poKmlSchemaData =
326 228 : poKmlExtendedData->get_schemadata_array_at(0);
327 :
328 114 : if (poKmlSchemaData->has_schemaurl())
329 : {
330 : std::string oKmlSchemaUrl =
331 226 : poKmlSchemaData->get_schemaurl();
332 113 : if (oSetSchemaAlreadyVisited.find(
333 113 : oKmlSchemaUrl) ==
334 226 : oSetSchemaAlreadyVisited.end())
335 : {
336 : oSetSchemaAlreadyVisited.insert(
337 29 : oKmlSchemaUrl);
338 29 : auto schema = m_poOgrDS->FindSchema(
339 58 : oKmlSchemaUrl.c_str());
340 29 : if (schema)
341 : {
342 27 : if (bCanSetKmlSchema && !m_poKmlSchema)
343 : {
344 26 : m_poKmlSchema = schema;
345 26 : bCanSetKmlSchema = false;
346 : }
347 : else
348 : {
349 1 : m_poKmlSchema = nullptr;
350 : }
351 27 : kml2FeatureDef(
352 27 : std::move(schema),
353 : m_poOgrFeatureDefn,
354 27 : m_oMapSimpleFieldNameToOgrFieldIx,
355 : oSetSimpleFields);
356 : }
357 : }
358 : }
359 : }
360 3 : else if (poKmlExtendedData->get_data_array_size() > 0)
361 : {
362 : const size_t nDataArraySize =
363 3 : poKmlExtendedData->get_data_array_size();
364 8 : for (size_t i = 0; i < nDataArraySize; i++)
365 : {
366 : const DataPtr &data =
367 5 : poKmlExtendedData->get_data_array_at(i);
368 5 : if (data->has_name())
369 : {
370 : CPLString osName =
371 5 : std::string(data->get_name());
372 5 : if (bLaunderFieldNames)
373 5 : osName = LaunderFieldNames(osName);
374 :
375 5 : int counter = 2;
376 5 : const std::string osNameRadix = osName;
377 : const CPLString osNameLower =
378 5 : CPLString(osName).tolower();
379 5 : if (cpl::contains(oSetSimpleFields,
380 : osNameLower))
381 1 : continue;
382 4 : oSetSimpleFields.insert(osNameLower);
383 :
384 : m_oMapSimpleFieldNameToOgrFieldIx
385 4 : [osNameRadix] =
386 4 : m_poOgrFeatureDefn->GetFieldCount();
387 :
388 4 : while (m_poOgrFeatureDefn->GetFieldIndex(
389 8 : osName.c_str()) >= 0)
390 : {
391 0 : osName = osNameRadix +
392 0 : std::to_string(counter);
393 0 : ++counter;
394 : }
395 :
396 8 : OGRFieldDefn oOgrField(osName, OFTString);
397 4 : m_poOgrFeatureDefn->AddFieldDefn(
398 4 : &oOgrField);
399 : }
400 : }
401 : }
402 : }
403 : }
404 571 : if (!bHasSnippet && poKmlFeature->has_snippet())
405 : {
406 1 : bHasSnippet = true;
407 : OGRFieldDefn oOgrField(m_oFieldConfig.snippetfield,
408 2 : OFTString);
409 1 : m_poOgrFeatureDefn->AddFieldDefn(&oOgrField);
410 : }
411 : }
412 :
413 234 : iFeature = 0;
414 : }
415 : }
416 367 : }
417 :
418 : /******************************************************************************
419 : OGRLIBKMLLayer Destructor
420 :
421 : Args: none
422 :
423 : Returns: nothing
424 :
425 : ******************************************************************************/
426 :
427 734 : OGRLIBKMLLayer::~OGRLIBKMLLayer()
428 : {
429 367 : CPLFree(const_cast<char *>(m_pszName));
430 367 : CPLFree(const_cast<char *>(m_pszFileName));
431 367 : m_poOgrSRS->Release();
432 :
433 367 : m_poOgrFeatureDefn->Release();
434 734 : }
435 :
436 : /******************************************************************************
437 : Method to get the next feature on the layer.
438 :
439 : Args: none
440 :
441 : Returns: The next feature, or NULL if there is no more
442 :
443 : ******************************************************************************/
444 :
445 1681 : OGRFeature *OGRLIBKMLLayer::GetNextRawFeature()
446 : {
447 1681 : OGRFeature *poOgrFeature = nullptr;
448 :
449 1681 : if (!m_poKmlLayer)
450 0 : return nullptr;
451 :
452 : /***** loop over the kml features to find the next placemark *****/
453 :
454 1681 : std::string id;
455 244 : do
456 : {
457 1925 : if (iFeature >= nFeatures)
458 : {
459 345 : m_bAllReadAtLeastOnce = true;
460 345 : break;
461 : }
462 :
463 : /***** get the next kml feature in the container *****/
464 : const FeaturePtr poKmlFeature =
465 3160 : m_poKmlLayer->get_feature_array_at(iFeature++);
466 1580 : if (poKmlFeature->has_id())
467 538 : id = poKmlFeature->get_id();
468 :
469 : /***** what type of kml feature in the container? *****/
470 1580 : switch (poKmlFeature->Type())
471 : {
472 1292 : case kmldom::Type_Placemark:
473 1292 : poOgrFeature = kml2feat(AsPlacemark(poKmlFeature), m_poOgrDS,
474 : this, m_poOgrFeatureDefn, m_poOgrSRS,
475 1292 : m_oMapSimpleFieldNameToOgrFieldIx);
476 1292 : break;
477 :
478 44 : case kmldom::Type_GroundOverlay:
479 44 : if (m_bReadGroundOverlay)
480 : {
481 44 : poOgrFeature = kmlgroundoverlay2feat(
482 88 : AsGroundOverlay(poKmlFeature), m_poOgrDS, this,
483 : m_poOgrFeatureDefn, m_poOgrSRS,
484 44 : m_oMapSimpleFieldNameToOgrFieldIx);
485 : }
486 44 : break;
487 :
488 244 : default:
489 244 : break;
490 : }
491 1580 : } while (!poOgrFeature);
492 :
493 : /***** set the FID on the ogr feature *****/
494 1681 : if (poOgrFeature)
495 : {
496 : // If KML id is of the form "layername.number", use number as the FID
497 1874 : if (!id.empty() && id.size() > m_osSanitizedNCName.size() &&
498 2401 : id[m_osSanitizedNCName.size()] == '.' &&
499 527 : STARTS_WITH(id.c_str(), m_osSanitizedNCName.c_str()))
500 : {
501 : auto iFID =
502 527 : CPLAtoGIntBig(id.c_str() + m_osSanitizedNCName.size() + 1);
503 527 : if (iFID > 0)
504 : {
505 526 : poOgrFeature->SetFID(iFID);
506 526 : nFID = std::max(iFID + 1, nFID);
507 : }
508 : }
509 1336 : if (poOgrFeature->GetFID() < 0)
510 810 : poOgrFeature->SetFID(nFID++);
511 :
512 1336 : if (bUpdate && !id.empty())
513 : {
514 468 : auto oIter = m_oMapKmlIdToOGRId.find(id);
515 468 : if (oIter != m_oMapKmlIdToOGRId.end())
516 : {
517 443 : poOgrFeature->SetFID(oIter->second);
518 : }
519 : else
520 : {
521 25 : m_oMapOGRIdToKmlId[poOgrFeature->GetFID()] = id;
522 25 : m_oMapKmlIdToOGRId[id] = poOgrFeature->GetFID();
523 : }
524 : }
525 : }
526 :
527 1681 : return poOgrFeature;
528 : }
529 :
530 : /******************************************************************************/
531 : /* ScanAllFeatures() */
532 : /******************************************************************************/
533 :
534 18 : void OGRLIBKMLLayer::ScanAllFeatures()
535 : {
536 18 : if (!m_bAllReadAtLeastOnce)
537 : {
538 8 : const auto iFeatureBackup = iFeature;
539 8 : const auto nFIDBackup = nFID;
540 32 : while (iFeature < nFeatures &&
541 32 : std::unique_ptr<OGRFeature>(GetNextRawFeature()))
542 : {
543 : // do nothing
544 : }
545 8 : iFeature = iFeatureBackup;
546 8 : nFID = nFIDBackup;
547 : }
548 18 : }
549 :
550 : /******************************************************************************
551 : Method to add a feature to a layer.
552 :
553 : Args: poOgrFeat pointer to the feature to add
554 :
555 : Returns: OGRERR_NONE, or OGRERR_UNSUPPORTED_OPERATION of the layer is
556 : not writable
557 :
558 : ******************************************************************************/
559 :
560 215 : OGRErr OGRLIBKMLLayer::ICreateFeature(OGRFeature *poOgrFeat)
561 : {
562 215 : if (!bUpdate)
563 0 : return OGRERR_UNSUPPORTED_OPERATION;
564 :
565 215 : if (!m_bNew)
566 : {
567 : const int idxIdField =
568 10 : m_poOgrFeatureDefn->GetFieldIndex(m_oFieldConfig.idfield);
569 10 : if (idxIdField >= 0 && poOgrFeat->IsFieldSet(idxIdField))
570 : {
571 9 : ScanAllFeatures();
572 :
573 18 : if (cpl::contains(m_oMapKmlIdToOGRId,
574 9 : poOgrFeat->GetFieldAsString(idxIdField)))
575 : {
576 0 : CPLError(CE_Failure, CPLE_AppDefined,
577 : "A feature with id %s already exists",
578 : poOgrFeat->GetFieldAsString(idxIdField));
579 0 : return OGRERR_FAILURE;
580 : }
581 : }
582 : }
583 :
584 215 : OGRGeometry *poGeomBackup = nullptr;
585 215 : if (nullptr != m_poCT)
586 : {
587 6 : poGeomBackup = poOgrFeat->StealGeometry();
588 6 : if (poGeomBackup)
589 : {
590 6 : auto poWGS84Geom = poGeomBackup->clone();
591 6 : poWGS84Geom->transform(m_poCT.get());
592 6 : poOgrFeat->SetGeometryDirectly(poWGS84Geom);
593 : }
594 : }
595 :
596 216 : if (m_bRegionBoundsAuto && poOgrFeat->GetGeometryRef() != nullptr &&
597 1 : !(poOgrFeat->GetGeometryRef()->IsEmpty()))
598 : {
599 1 : OGREnvelope sEnvelope;
600 1 : poOgrFeat->GetGeometryRef()->getEnvelope(&sEnvelope);
601 1 : m_dfRegionMinX = std::min(m_dfRegionMinX, sEnvelope.MinX);
602 1 : m_dfRegionMinY = std::min(m_dfRegionMinY, sEnvelope.MinY);
603 1 : m_dfRegionMaxX = std::max(m_dfRegionMaxX, sEnvelope.MaxX);
604 1 : m_dfRegionMaxY = std::max(m_dfRegionMaxY, sEnvelope.MaxY);
605 : }
606 :
607 : FeaturePtr poKmlFeature =
608 215 : feat2kml(m_poOgrDS, this, poOgrFeat, m_poOgrDS->GetKmlFactory(),
609 430 : m_bUseSimpleField);
610 215 : if (!poKmlFeature)
611 13 : return OGRERR_FAILURE;
612 :
613 202 : if (poGeomBackup)
614 6 : poOgrFeat->SetGeometryDirectly(poGeomBackup);
615 :
616 202 : if (m_poKmlLayer)
617 : {
618 196 : m_poKmlLayer->add_feature(poKmlFeature);
619 : }
620 : else
621 : {
622 6 : CPLAssert(m_poKmlUpdate != nullptr);
623 6 : KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory();
624 12 : CreatePtr poCreate = poKmlFactory->CreateCreate();
625 0 : ContainerPtr poContainer;
626 6 : if (m_bUpdateIsFolder)
627 0 : poContainer = poKmlFactory->CreateFolder();
628 : else
629 6 : poContainer = poKmlFactory->CreateDocument();
630 6 : poContainer->set_targetid(m_osSanitizedNCName);
631 6 : poContainer->add_feature(poKmlFeature);
632 6 : poCreate->add_container(poContainer);
633 6 : m_poKmlUpdate->add_updateoperation(poCreate);
634 : }
635 :
636 : /***** update the layer class count of features *****/
637 202 : if (m_poKmlLayer)
638 : {
639 196 : nFeatures++;
640 :
641 196 : if (poOgrFeat->GetFID() < 0)
642 : {
643 185 : poOgrFeat->SetFID(nFeatures);
644 : }
645 196 : if (!poKmlFeature->has_id())
646 : {
647 : const char *pszId =
648 185 : CPLSPrintf("%s." CPL_FRMT_GIB, m_osSanitizedNCName.c_str(),
649 : poOgrFeat->GetFID());
650 185 : poKmlFeature->set_id(pszId);
651 : }
652 196 : m_oMapOGRIdToKmlId[poOgrFeat->GetFID()] = poKmlFeature->get_id();
653 196 : m_oMapKmlIdToOGRId[poKmlFeature->get_id()] = poOgrFeat->GetFID();
654 : }
655 : else
656 : {
657 6 : if (poOgrFeat->GetFID() < 0)
658 : {
659 : static bool bAlreadyWarned = false;
660 3 : if (!bAlreadyWarned)
661 : {
662 1 : bAlreadyWarned = true;
663 1 : CPLError(CE_Warning, CPLE_AppDefined,
664 : "It is recommended to define a FID when calling "
665 : "CreateFeature() in a update document");
666 : }
667 : }
668 : else
669 : {
670 3 : if (!poKmlFeature->has_id())
671 : {
672 : const char *pszId =
673 3 : CPLSPrintf("%s." CPL_FRMT_GIB, m_osSanitizedNCName.c_str(),
674 : poOgrFeat->GetFID());
675 3 : poKmlFeature->set_id(pszId);
676 : }
677 : }
678 : }
679 :
680 : /***** mark as updated *****/
681 202 : m_poOgrDS->Updated();
682 :
683 202 : return OGRERR_NONE;
684 : }
685 :
686 : /******************************************************************************
687 : Method to update a feature to a layer.
688 :
689 : Only work on a NetworkLinkControl/Update.
690 :
691 : Args: poOgrFeat pointer to the feature to update
692 :
693 : Returns: OGRERR_NONE, or OGRERR_UNSUPPORTED_OPERATION of the layer is
694 : not writable
695 :
696 : ******************************************************************************/
697 :
698 20 : OGRErr OGRLIBKMLLayer::ISetFeature(OGRFeature *poOgrFeat)
699 : {
700 20 : if (!bUpdate)
701 8 : return OGRERR_UNSUPPORTED_OPERATION;
702 12 : if (poOgrFeat->GetFID() == OGRNullFID)
703 0 : return OGRERR_NON_EXISTING_FEATURE;
704 :
705 12 : if (m_poKmlUpdate)
706 : {
707 : FeaturePtr poKmlFeature =
708 3 : feat2kml(m_poOgrDS, this, poOgrFeat, m_poOgrDS->GetKmlFactory(),
709 6 : m_bUseSimpleField);
710 :
711 3 : const KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory();
712 3 : const ChangePtr poChange = poKmlFactory->CreateChange();
713 3 : poChange->add_object(poKmlFeature);
714 3 : m_poKmlUpdate->add_updateoperation(poChange);
715 :
716 : const char *pszId =
717 3 : CPLSPrintf("%s." CPL_FRMT_GIB, m_osSanitizedNCName.c_str(),
718 : poOgrFeat->GetFID());
719 3 : poKmlFeature->set_targetid(pszId);
720 : }
721 9 : else if (m_poKmlLayer)
722 : {
723 9 : if (DeleteFeature(poOgrFeat->GetFID()) != OGRERR_NONE)
724 1 : return OGRERR_NON_EXISTING_FEATURE;
725 8 : return ICreateFeature(poOgrFeat);
726 : }
727 :
728 : /***** mark as updated *****/
729 3 : m_poOgrDS->Updated();
730 :
731 3 : return OGRERR_NONE;
732 : }
733 :
734 : /******************************************************************************
735 : Method to delete a feature to a layer.
736 :
737 : Only work on a NetworkLinkControl/Update.
738 :
739 : Args: nFID id of the feature to delete
740 :
741 : Returns: OGRERR_NONE, or OGRERR_UNSUPPORTED_OPERATION of the layer is
742 : not writable
743 :
744 : ******************************************************************************/
745 :
746 41 : OGRErr OGRLIBKMLLayer::DeleteFeature(GIntBig nFIDIn)
747 : {
748 41 : if (!bUpdate)
749 20 : return OGRERR_UNSUPPORTED_OPERATION;
750 :
751 21 : if (m_poKmlUpdate)
752 : {
753 3 : KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory();
754 6 : DeletePtr poDelete = poKmlFactory->CreateDelete();
755 3 : m_poKmlUpdate->add_updateoperation(poDelete);
756 3 : PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark();
757 3 : poDelete->add_feature(poKmlPlacemark);
758 :
759 : const char *pszId =
760 3 : CPLSPrintf("%s." CPL_FRMT_GIB, m_osSanitizedNCName.c_str(), nFIDIn);
761 3 : poKmlPlacemark->set_targetid(pszId);
762 : }
763 18 : else if (m_poKmlLayer)
764 : {
765 18 : auto oIter = m_oMapOGRIdToKmlId.find(nFIDIn);
766 18 : if (oIter == m_oMapOGRIdToKmlId.end())
767 : {
768 9 : ScanAllFeatures();
769 :
770 9 : oIter = m_oMapOGRIdToKmlId.find(nFIDIn);
771 9 : if (oIter == m_oMapOGRIdToKmlId.end())
772 7 : return OGRERR_NON_EXISTING_FEATURE;
773 : }
774 11 : const auto &osKmlId = oIter->second;
775 11 : if (!m_poKmlLayer->DeleteFeatureById(osKmlId))
776 : {
777 0 : return OGRERR_NON_EXISTING_FEATURE;
778 : }
779 11 : nFeatures = static_cast<int>(m_poKmlLayer->get_feature_array_size());
780 11 : m_oMapKmlIdToOGRId.erase(osKmlId);
781 11 : m_oMapOGRIdToKmlId.erase(oIter);
782 : }
783 :
784 : /***** mark as updated *****/
785 14 : m_poOgrDS->Updated();
786 :
787 14 : return OGRERR_NONE;
788 : }
789 :
790 : /******************************************************************************
791 : Method to get the number of features on the layer.
792 :
793 : Args: bForce no effect as of now
794 :
795 : Returns: the number of features on the layer
796 :
797 : Note: the result can include links, folders and other items that are
798 : not supported by OGR
799 :
800 : ******************************************************************************/
801 :
802 161 : GIntBig OGRLIBKMLLayer::GetFeatureCount(int bForce)
803 : {
804 161 : if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
805 : {
806 50 : return static_cast<int>(OGRLayer::GetFeatureCount(bForce));
807 : }
808 :
809 111 : if (!m_poKmlLayer)
810 0 : return 0;
811 :
812 111 : int count = 0;
813 :
814 111 : const size_t nKmlFeatures = m_poKmlLayer->get_feature_array_size();
815 :
816 : /***** loop over the kml features in the container *****/
817 535 : for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
818 : {
819 : FeaturePtr poKmlFeature =
820 848 : m_poKmlLayer->get_feature_array_at(iKmlFeature);
821 :
822 : /***** what type of kml feature? *****/
823 424 : switch (poKmlFeature->Type())
824 : {
825 324 : case kmldom::Type_Placemark:
826 324 : count++;
827 324 : break;
828 :
829 9 : case kmldom::Type_GroundOverlay:
830 9 : if (m_bReadGroundOverlay)
831 9 : count++;
832 9 : break;
833 :
834 91 : default:
835 91 : break;
836 : }
837 : }
838 :
839 111 : return count;
840 : }
841 :
842 : /******************************************************************************
843 : IGetExtent()
844 :
845 : Args: psExtent pointer to the Envelope to store the result in
846 : bForce no effect as of now
847 :
848 : Returns: nothing
849 :
850 : ******************************************************************************/
851 :
852 22 : OGRErr OGRLIBKMLLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
853 : bool bForce)
854 : {
855 22 : Bbox oKmlBbox;
856 :
857 44 : if (m_poKmlLayer &&
858 66 : kmlengine::GetFeatureBounds(AsFeature(m_poKmlLayer), &oKmlBbox))
859 : {
860 18 : psExtent->MinX = oKmlBbox.get_west();
861 18 : psExtent->MinY = oKmlBbox.get_south();
862 18 : psExtent->MaxX = oKmlBbox.get_east();
863 18 : psExtent->MaxY = oKmlBbox.get_north();
864 :
865 18 : return OGRERR_NONE;
866 : }
867 :
868 4 : return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
869 : }
870 :
871 : /******************************************************************************
872 : Method to create a field on a layer.
873 :
874 : Args: poField pointer to the Field Definition to add
875 : bApproxOK no effect as of now
876 :
877 : Returns: OGRERR_NONE on success or OGRERR_UNSUPPORTED_OPERATION if the
878 : layer is not writable
879 :
880 : ******************************************************************************/
881 :
882 180 : OGRErr OGRLIBKMLLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
883 : {
884 180 : if (!bUpdate)
885 0 : return OGRERR_UNSUPPORTED_OPERATION;
886 :
887 180 : if (m_bUseSimpleField)
888 : {
889 358 : SimpleFieldPtr poKmlSimpleField = nullptr;
890 :
891 179 : if ((poKmlSimpleField =
892 358 : FieldDef2kml(poField, m_poOgrDS->GetKmlFactory(),
893 358 : CPL_TO_BOOL(bApproxOK), m_oFieldConfig)))
894 : {
895 119 : if (!m_poKmlSchema)
896 : {
897 : /***** Create a new schema *****/
898 41 : KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory();
899 :
900 41 : m_poKmlSchema = poKmlFactory->CreateSchema();
901 :
902 : /***** Set the id on the new schema *****/
903 82 : std::string oKmlSchemaID = m_osSanitizedNCName;
904 41 : oKmlSchemaID.append(".schema");
905 41 : m_poKmlSchema->set_id(oKmlSchemaID);
906 : }
907 :
908 119 : m_poKmlSchema->add_simplefield(poKmlSimpleField);
909 : }
910 : }
911 :
912 180 : m_poOgrFeatureDefn->AddFieldDefn(poField);
913 :
914 : /***** mark as updated *****/
915 180 : m_poOgrDS->Updated();
916 :
917 180 : return OGRERR_NONE;
918 : }
919 :
920 : /******************************************************************************
921 : Method to write the datasource to disk.
922 :
923 : Args: none
924 :
925 : Returns nothing
926 :
927 : ******************************************************************************/
928 :
929 0 : OGRErr OGRLIBKMLLayer::SyncToDisk()
930 : {
931 0 : m_poOgrDS->FlushCache(false);
932 0 : return OGRERR_NONE;
933 : }
934 :
935 : /******************************************************************************
936 : Method to get a layers style table.
937 :
938 : Args: none
939 :
940 : Returns: pointer to the layers style table, or NULL if it does
941 : not have one
942 :
943 : ******************************************************************************/
944 :
945 513 : OGRStyleTable *OGRLIBKMLLayer::GetStyleTable()
946 : {
947 513 : return m_poStyleTable;
948 : }
949 :
950 : /******************************************************************************
951 : Method to write a style table to a layer.
952 :
953 : Args: poStyleTable pointer to the style table to add
954 :
955 : Returns: nothing
956 :
957 : Note: This method assumes ownership of the style table.
958 : ******************************************************************************/
959 :
960 1 : void OGRLIBKMLLayer::SetStyleTableDirectly(OGRStyleTable *poStyleTable)
961 : {
962 1 : if (!bUpdate || !m_poKmlLayer)
963 0 : return;
964 :
965 1 : KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory();
966 :
967 1 : if (m_poStyleTable)
968 0 : delete m_poStyleTable;
969 :
970 1 : m_poStyleTable = poStyleTable;
971 :
972 1 : if (m_poKmlLayer->IsA(kmldom::Type_Document))
973 : {
974 : /***** delete all the styles *****/
975 1 : DocumentPtr poKmlDocument = AsDocument(m_poKmlLayer);
976 : const int nKmlStyles =
977 1 : static_cast<int>(poKmlDocument->get_schema_array_size());
978 :
979 1 : for (int iKmlStyle = nKmlStyles - 1; iKmlStyle >= 0; iKmlStyle--)
980 : {
981 0 : poKmlDocument->DeleteStyleSelectorAt(iKmlStyle);
982 : }
983 :
984 : /***** add the new style table to the document *****/
985 1 : styletable2kml(poStyleTable, poKmlFactory, AsContainer(poKmlDocument));
986 : }
987 :
988 : /***** mark as updated *****/
989 1 : m_poOgrDS->Updated();
990 : }
991 :
992 : /******************************************************************************
993 : Method to write a style table to a layer.
994 :
995 : Args: poStyleTable pointer to the style table to add
996 :
997 : Returns: nothing
998 :
999 : Note: This method copies the style table, and the user will still be
1000 : responsible for its destruction.
1001 : ******************************************************************************/
1002 :
1003 1 : void OGRLIBKMLLayer::SetStyleTable(OGRStyleTable *poStyleTable)
1004 : {
1005 1 : if (!bUpdate || !m_poKmlLayer)
1006 0 : return;
1007 :
1008 1 : if (poStyleTable)
1009 0 : SetStyleTableDirectly(poStyleTable->Clone());
1010 : else
1011 1 : SetStyleTableDirectly(nullptr);
1012 : }
1013 :
1014 : /******************************************************************************
1015 : Test if capability is available.
1016 :
1017 : Args: pszCap layer capability name to test
1018 :
1019 : Returns: True if the layer supports the capability, otherwise false
1020 :
1021 : ******************************************************************************/
1022 :
1023 639 : int OGRLIBKMLLayer::TestCapability(const char *pszCap) const
1024 : {
1025 639 : int result = FALSE;
1026 :
1027 : // TODO(schwehr): The false statements are weird.
1028 639 : if (EQUAL(pszCap, OLCRandomRead))
1029 2 : result = TRUE;
1030 637 : else if (EQUAL(pszCap, OLCSequentialWrite))
1031 32 : result = bUpdate;
1032 605 : else if (EQUAL(pszCap, OLCRandomWrite))
1033 27 : result =
1034 72 : bUpdate && (m_poKmlUpdate ||
1035 14 : (m_poKmlLayer &&
1036 21 : (m_poKmlLayer->get_feature_array_size() == 0 ||
1037 38 : m_poKmlLayer->get_feature_array_at(0)->has_id())));
1038 578 : else if (EQUAL(pszCap, OLCFastFeatureCount))
1039 0 : result = FALSE;
1040 578 : else if (EQUAL(pszCap, OLCFastSetNextByIndex))
1041 0 : result = FALSE;
1042 578 : else if (EQUAL(pszCap, OLCCreateField))
1043 38 : result = bUpdate;
1044 540 : else if (EQUAL(pszCap, OLCDeleteFeature))
1045 22 : result =
1046 58 : bUpdate && (m_poKmlUpdate ||
1047 12 : (m_poKmlLayer &&
1048 17 : (m_poKmlLayer->get_feature_array_size() == 0 ||
1049 29 : m_poKmlLayer->get_feature_array_at(0)->has_id())));
1050 518 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
1051 127 : result = TRUE;
1052 391 : else if (EQUAL(pszCap, OLCZGeometries))
1053 33 : result = TRUE;
1054 :
1055 639 : return result;
1056 : }
1057 :
1058 : /************************************************************************/
1059 : /* LaunderFieldNames() */
1060 : /************************************************************************/
1061 :
1062 10 : CPLString OGRLIBKMLLayer::LaunderFieldNames(CPLString osName)
1063 : {
1064 10 : CPLString osLaunderedName;
1065 64 : for (int i = 0; i < static_cast<int>(osName.size()); i++)
1066 : {
1067 54 : const char ch = osName[i];
1068 54 : if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') ||
1069 0 : (ch >= 'A' && ch <= 'Z') || (ch == '_'))
1070 54 : osLaunderedName += ch;
1071 : else
1072 0 : osLaunderedName += "_";
1073 : }
1074 10 : return osLaunderedName;
1075 : }
1076 :
1077 : /************************************************************************/
1078 : /* SetLookAt() */
1079 : /************************************************************************/
1080 :
1081 2 : void OGRLIBKMLLayer::SetLookAt(const char *pszLookatLongitude,
1082 : const char *pszLookatLatitude,
1083 : const char *pszLookatAltitude,
1084 : const char *pszLookatHeading,
1085 : const char *pszLookatTilt,
1086 : const char *pszLookatRange,
1087 : const char *pszLookatAltitudeMode)
1088 : {
1089 2 : KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory();
1090 2 : LookAtPtr lookAt = poKmlFactory->CreateLookAt();
1091 2 : lookAt->set_latitude(CPLAtof(pszLookatLatitude));
1092 2 : lookAt->set_longitude(CPLAtof(pszLookatLongitude));
1093 2 : if (pszLookatAltitude != nullptr)
1094 1 : lookAt->set_altitude(CPLAtof(pszLookatAltitude));
1095 2 : if (pszLookatHeading != nullptr)
1096 1 : lookAt->set_heading(CPLAtof(pszLookatHeading));
1097 2 : if (pszLookatTilt != nullptr)
1098 : {
1099 1 : double dfTilt = CPLAtof(pszLookatTilt);
1100 1 : if (dfTilt >= 0 && dfTilt <= 90)
1101 1 : lookAt->set_tilt(dfTilt);
1102 : else
1103 0 : CPLError(CE_Warning, CPLE_AppDefined, "Invalid value for tilt: %s",
1104 : pszLookatTilt);
1105 : }
1106 2 : lookAt->set_range(CPLAtof(pszLookatRange));
1107 2 : if (pszLookatAltitudeMode != nullptr)
1108 : {
1109 1 : int isGX = FALSE;
1110 : const int iAltitudeMode =
1111 1 : kmlAltitudeModeFromString(pszLookatAltitudeMode, isGX);
1112 1 : if (iAltitudeMode != kmldom::ALTITUDEMODE_CLAMPTOGROUND &&
1113 : pszLookatAltitude == nullptr)
1114 : {
1115 0 : CPLError(CE_Warning, CPLE_AppDefined,
1116 : "Lookat altitude should be present for altitudeMode = %s",
1117 : pszLookatAltitudeMode);
1118 : }
1119 1 : else if (isGX)
1120 : {
1121 0 : lookAt->set_gx_altitudemode(iAltitudeMode);
1122 : }
1123 : else
1124 : {
1125 1 : lookAt->set_altitudemode(iAltitudeMode);
1126 : }
1127 : }
1128 :
1129 2 : m_poKmlLayer->set_abstractview(lookAt);
1130 2 : }
1131 :
1132 : /************************************************************************/
1133 : /* SetCamera() */
1134 : /************************************************************************/
1135 :
1136 1 : void OGRLIBKMLLayer::SetCamera(const char *pszCameraLongitude,
1137 : const char *pszCameraLatitude,
1138 : const char *pszCameraAltitude,
1139 : const char *pszCameraHeading,
1140 : const char *pszCameraTilt,
1141 : const char *pszCameraRoll,
1142 : const char *pszCameraAltitudeMode)
1143 : {
1144 1 : int isGX = FALSE;
1145 1 : int iAltitudeMode = kmlAltitudeModeFromString(pszCameraAltitudeMode, isGX);
1146 1 : if (isGX == FALSE && iAltitudeMode == kmldom::ALTITUDEMODE_CLAMPTOGROUND)
1147 : {
1148 0 : CPLError(CE_Warning, CPLE_AppDefined,
1149 : "Camera altitudeMode should be different from %s",
1150 : pszCameraAltitudeMode);
1151 0 : return;
1152 : }
1153 1 : KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory();
1154 1 : CameraPtr camera = poKmlFactory->CreateCamera();
1155 1 : camera->set_latitude(CPLAtof(pszCameraLatitude));
1156 1 : camera->set_longitude(CPLAtof(pszCameraLongitude));
1157 1 : camera->set_altitude(CPLAtof(pszCameraAltitude));
1158 1 : if (pszCameraHeading != nullptr)
1159 1 : camera->set_heading(CPLAtof(pszCameraHeading));
1160 :
1161 1 : if (pszCameraTilt != nullptr)
1162 : {
1163 1 : double dfTilt = CPLAtof(pszCameraTilt);
1164 1 : if (dfTilt >= 0 && dfTilt <= 90)
1165 1 : camera->set_tilt(dfTilt);
1166 : else
1167 0 : CPLError(CE_Warning, CPLE_AppDefined, "Invalid value for tilt: %s",
1168 : pszCameraTilt);
1169 : }
1170 :
1171 1 : if (pszCameraRoll != nullptr)
1172 1 : camera->set_roll(CPLAtof(pszCameraRoll));
1173 1 : if (isGX)
1174 0 : camera->set_gx_altitudemode(iAltitudeMode);
1175 : else
1176 1 : camera->set_altitudemode(iAltitudeMode);
1177 :
1178 1 : m_poKmlLayer->set_abstractview(camera);
1179 : }
1180 :
1181 : /************************************************************************/
1182 : /* SetWriteRegion() */
1183 : /************************************************************************/
1184 :
1185 2 : void OGRLIBKMLLayer::SetWriteRegion(double dfMinLodPixels,
1186 : double dfMaxLodPixels,
1187 : double dfMinFadeExtent,
1188 : double dfMaxFadeExtent)
1189 : {
1190 2 : m_bWriteRegion = true;
1191 2 : m_bRegionBoundsAuto = true;
1192 2 : m_dfRegionMinLodPixels = dfMinLodPixels;
1193 2 : m_dfRegionMaxLodPixels = dfMaxLodPixels;
1194 2 : m_dfRegionMinFadeExtent = dfMinFadeExtent;
1195 2 : m_dfRegionMaxFadeExtent = dfMaxFadeExtent;
1196 2 : }
1197 :
1198 : /************************************************************************/
1199 : /* SetRegionBounds() */
1200 : /************************************************************************/
1201 :
1202 1 : void OGRLIBKMLLayer::SetRegionBounds(double dfMinX, double dfMinY,
1203 : double dfMaxX, double dfMaxY)
1204 : {
1205 1 : m_bRegionBoundsAuto = false;
1206 1 : m_dfRegionMinX = dfMinX;
1207 1 : m_dfRegionMinY = dfMinY;
1208 1 : m_dfRegionMaxX = dfMaxX;
1209 1 : m_dfRegionMaxY = dfMaxY;
1210 1 : }
1211 :
1212 : /************************************************************************/
1213 : /* Finalize() */
1214 : /************************************************************************/
1215 :
1216 112 : void OGRLIBKMLLayer::Finalize(DocumentPtr poKmlDocument)
1217 : {
1218 112 : KmlFactory *const poKmlFactory = m_poOgrDS->GetKmlFactory();
1219 :
1220 112 : if (m_bWriteRegion && m_dfRegionMinX < m_dfRegionMaxX)
1221 : {
1222 4 : RegionPtr region = poKmlFactory->CreateRegion();
1223 :
1224 4 : LatLonAltBoxPtr box = poKmlFactory->CreateLatLonAltBox();
1225 2 : box->set_west(m_dfRegionMinX);
1226 2 : box->set_east(m_dfRegionMaxX);
1227 2 : box->set_south(m_dfRegionMinY);
1228 2 : box->set_north(m_dfRegionMaxY);
1229 2 : region->set_latlonaltbox(box);
1230 :
1231 4 : LodPtr lod = poKmlFactory->CreateLod();
1232 2 : lod->set_minlodpixels(m_dfRegionMinLodPixels);
1233 2 : lod->set_maxlodpixels(m_dfRegionMaxLodPixels);
1234 2 : if ((m_dfRegionMinFadeExtent != 0 || m_dfRegionMaxFadeExtent != 0) &&
1235 1 : m_dfRegionMinFadeExtent + m_dfRegionMaxFadeExtent <
1236 1 : m_dfRegionMaxLodPixels - m_dfRegionMinLodPixels)
1237 : {
1238 1 : lod->set_minfadeextent(m_dfRegionMinFadeExtent);
1239 1 : lod->set_maxfadeextent(m_dfRegionMaxFadeExtent);
1240 : }
1241 :
1242 2 : region->set_lod(lod);
1243 2 : m_poKmlLayer->set_region(region);
1244 : }
1245 :
1246 112 : createkmlliststyle(poKmlFactory, GetName(), m_poKmlLayer,
1247 112 : std::move(poKmlDocument), osListStyleType,
1248 112 : osListStyleIconHref);
1249 112 : }
1250 :
1251 : /************************************************************************/
1252 : /* LIBKMLGetUnits() */
1253 : /************************************************************************/
1254 :
1255 8 : static int LIBKMLGetUnits(const char *pszUnits)
1256 : {
1257 8 : if (EQUAL(pszUnits, "fraction"))
1258 6 : return kmldom::UNITS_FRACTION;
1259 2 : if (EQUAL(pszUnits, "pixels"))
1260 2 : return kmldom::UNITS_PIXELS;
1261 0 : if (EQUAL(pszUnits, "insetPixels"))
1262 0 : return kmldom::UNITS_INSETPIXELS;
1263 0 : return kmldom::UNITS_FRACTION;
1264 : }
1265 :
1266 : /************************************************************************/
1267 : /* LIBKMLSetVec2() */
1268 : /************************************************************************/
1269 :
1270 4 : static void LIBKMLSetVec2(kmldom::Vec2Ptr vec2, const char *pszX,
1271 : const char *pszY, const char *pszXUnits,
1272 : const char *pszYUnits)
1273 : {
1274 4 : const double dfX = CPLAtof(pszX);
1275 4 : const double dfY = CPLAtof(pszY);
1276 4 : vec2->set_x(dfX);
1277 4 : vec2->set_y(dfY);
1278 4 : if (dfX <= 1 && dfY <= 1)
1279 : {
1280 2 : if (pszXUnits == nullptr)
1281 1 : pszXUnits = "fraction";
1282 2 : if (pszYUnits == nullptr)
1283 1 : pszYUnits = "fraction";
1284 : }
1285 : else
1286 : {
1287 2 : if (pszXUnits == nullptr)
1288 0 : pszXUnits = "pixels";
1289 2 : if (pszYUnits == nullptr)
1290 0 : pszYUnits = "pixels";
1291 : }
1292 4 : vec2->set_xunits(LIBKMLGetUnits(pszXUnits));
1293 4 : vec2->set_yunits(LIBKMLGetUnits(pszYUnits));
1294 4 : }
1295 :
1296 : /************************************************************************/
1297 : /* SetScreenOverlay() */
1298 : /************************************************************************/
1299 :
1300 2 : void OGRLIBKMLLayer::SetScreenOverlay(
1301 : const char *pszSOHref, const char *pszSOName, const char *pszSODescription,
1302 : const char *pszSOOverlayX, const char *pszSOOverlayY,
1303 : const char *pszSOOverlayXUnits, const char *pszSOOverlayYUnits,
1304 : const char *pszSOScreenX, const char *pszSOScreenY,
1305 : const char *pszSOScreenXUnits, const char *pszSOScreenYUnits,
1306 : const char *pszSOSizeX, const char *pszSOSizeY, const char *pszSOSizeXUnits,
1307 : const char *pszSOSizeYUnits)
1308 : {
1309 2 : KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory();
1310 4 : ScreenOverlayPtr so = poKmlFactory->CreateScreenOverlay();
1311 :
1312 2 : if (pszSOName != nullptr)
1313 1 : so->set_name(pszSOName);
1314 2 : if (pszSODescription != nullptr)
1315 1 : so->set_description(pszSODescription);
1316 :
1317 2 : IconPtr icon = poKmlFactory->CreateIcon();
1318 2 : icon->set_href(pszSOHref);
1319 2 : so->set_icon(icon);
1320 :
1321 2 : if (pszSOOverlayX != nullptr && pszSOOverlayY != nullptr)
1322 : {
1323 2 : kmldom::OverlayXYPtr overlayxy = poKmlFactory->CreateOverlayXY();
1324 1 : LIBKMLSetVec2(overlayxy, pszSOOverlayX, pszSOOverlayY,
1325 : pszSOOverlayXUnits, pszSOOverlayYUnits);
1326 1 : so->set_overlayxy(overlayxy);
1327 : }
1328 :
1329 2 : if (pszSOScreenX != nullptr && pszSOScreenY != nullptr)
1330 : {
1331 2 : kmldom::ScreenXYPtr screenxy = poKmlFactory->CreateScreenXY();
1332 1 : LIBKMLSetVec2(screenxy, pszSOScreenX, pszSOScreenY, pszSOScreenXUnits,
1333 : pszSOScreenYUnits);
1334 2 : so->set_screenxy(screenxy);
1335 : }
1336 : else
1337 : {
1338 2 : kmldom::ScreenXYPtr screenxy = poKmlFactory->CreateScreenXY();
1339 1 : LIBKMLSetVec2(screenxy, "0.05", "0.05", nullptr, nullptr);
1340 1 : so->set_screenxy(screenxy);
1341 : }
1342 :
1343 2 : if (pszSOSizeX != nullptr && pszSOSizeY != nullptr)
1344 : {
1345 2 : kmldom::SizePtr sizexy = poKmlFactory->CreateSize();
1346 1 : LIBKMLSetVec2(sizexy, pszSOSizeX, pszSOSizeY, pszSOSizeXUnits,
1347 : pszSOSizeYUnits);
1348 1 : so->set_size(sizexy);
1349 : }
1350 :
1351 2 : m_poKmlLayer->add_feature(so);
1352 2 : }
1353 :
1354 : /************************************************************************/
1355 : /* SetListStyle() */
1356 : /************************************************************************/
1357 :
1358 127 : void OGRLIBKMLLayer::SetListStyle(const char *pszListStyleType,
1359 : const char *pszListStyleIconHref)
1360 : {
1361 127 : osListStyleType = pszListStyleType ? pszListStyleType : "";
1362 127 : osListStyleIconHref = pszListStyleIconHref ? pszListStyleIconHref : "";
1363 127 : }
1364 :
1365 : /************************************************************************/
1366 : /* GetDataset() */
1367 : /************************************************************************/
1368 :
1369 17 : GDALDataset *OGRLIBKMLLayer::GetDataset()
1370 : {
1371 17 : return m_poOgrDS;
1372 : }
|