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