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