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