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 : #include "ogr_libkml.h"
18 : #include "ogrlibkmlstyle.h"
19 : #include "ogr_p.h"
20 : #include "cpl_vsi_error.h"
21 :
22 : using kmlbase::Attributes;
23 : using kmldom::ContainerPtr;
24 : using kmldom::DocumentPtr;
25 : using kmldom::ElementPtr;
26 : using kmldom::FeaturePtr;
27 : using kmldom::FolderPtr;
28 : using kmldom::KmlFactory;
29 : using kmldom::KmlPtr;
30 : using kmldom::LinkPtr;
31 : using kmldom::LinkSnippetPtr;
32 : using kmldom::NetworkLinkControlPtr;
33 : using kmldom::NetworkLinkPtr;
34 : using kmldom::SchemaPtr;
35 : using kmldom::SnippetPtr;
36 : using kmldom::StyleSelectorPtr;
37 : using kmlengine::KmzFile;
38 :
39 : /************************************************************************/
40 : /* OGRLIBKMLParse() */
41 : /************************************************************************/
42 :
43 138 : static ElementPtr OGRLIBKMLParse(const std::string &oKml, std::string *posError)
44 : {
45 : try
46 : {
47 : // To allow reading files using an explicit namespace prefix like
48 : // <kml:kml> we need to use ParseNS (see #6981). But if we use ParseNS,
49 : // we have issues reading gx: elements. So use ParseNS only when we have
50 : // errors with Parse. This is not completely satisfactory...
51 276 : ElementPtr element = kmldom::Parse(oKml, posError);
52 138 : if (!element)
53 1 : element = kmldom::ParseNS(oKml, posError);
54 138 : return element;
55 : }
56 0 : catch (const std::exception &ex)
57 : {
58 0 : CPLError(CE_Failure, CPLE_AppDefined,
59 0 : "LIBKML: libstdc++ exception during Parse() : %s", ex.what());
60 0 : return nullptr;
61 : }
62 : }
63 :
64 : /******************************************************************************
65 : OGRLIBKMLDataSource Constructor
66 :
67 : Args: none
68 :
69 : Returns: nothing
70 :
71 : ******************************************************************************/
72 :
73 391 : OGRLIBKMLDataSource::OGRLIBKMLDataSource(KmlFactory *poKmlFactory)
74 : : papoLayers(nullptr), nLayers(0), nAllocated(0), bUpdate(false),
75 : bUpdated(false), m_papszOptions(nullptr), m_isKml(false),
76 : m_poKmlDSKml(nullptr), m_poKmlDSContainer(nullptr),
77 : m_poKmlUpdate(nullptr), m_isKmz(false), m_poKmlDocKml(nullptr),
78 : m_poKmlDocKmlRoot(nullptr), m_poKmlStyleKml(nullptr), m_isDir(false),
79 391 : m_poKmlFactory(poKmlFactory)
80 : {
81 391 : }
82 :
83 : /************************************************************************/
84 : /* OGRLIBKMLPreProcessInput() */
85 : /************************************************************************/
86 :
87 : // Substitute <snippet> by deprecated <Snippet> since libkml currently
88 : // only supports Snippet but ogckml22.xsd has deprecated it in favor of snippet.
89 119 : static void OGRLIBKMLPreProcessInput(std::string &oKml)
90 : {
91 119 : size_t nPos = 0;
92 : while (true)
93 : {
94 120 : nPos = oKml.find("<snippet>", nPos);
95 120 : if (nPos == std::string::npos)
96 : {
97 119 : break;
98 : }
99 1 : oKml[nPos + 1] = 'S';
100 1 : nPos = oKml.find("</snippet>", nPos);
101 1 : if (nPos == std::string::npos)
102 : {
103 0 : break;
104 : }
105 1 : oKml[nPos + 2] = 'S';
106 : }
107 :
108 : // Workaround Windows specific issue with libkml (at least the version
109 : // used by OSGeo4W at time of writing), where tabulations as
110 : // coordinate separators aren't properly handled
111 : //(see https://trac.osgeo.org/gdal/ticket/7231)
112 : // Another Windows specific issue is that if the content of
113 : // <coordinates> is non-empty and does not contain any digit,
114 : // libkml hangs (see https://trac.osgeo.org/gdal/ticket/7232)
115 119 : nPos = 0;
116 : while (true)
117 : {
118 482 : nPos = oKml.find("<coordinates>", nPos);
119 482 : if (nPos == std::string::npos)
120 : {
121 119 : break;
122 : }
123 363 : size_t nPosEnd = oKml.find("</coordinates>", nPos);
124 363 : if (nPosEnd == std::string::npos)
125 : {
126 0 : break;
127 : }
128 363 : nPos += strlen("<coordinates>");
129 363 : size_t nPosAfterCoordinates = nPos;
130 363 : bool bDigitFound = false;
131 131550 : for (; nPos < nPosEnd; nPos++)
132 : {
133 131187 : char ch = oKml[nPos];
134 131187 : if (ch >= '0' && ch <= '9')
135 78607 : bDigitFound = true;
136 52580 : else if (ch == '\t' || ch == '\n')
137 2277 : oKml[nPos] = ' ';
138 : }
139 363 : if (!bDigitFound)
140 : {
141 : oKml.replace(nPosAfterCoordinates,
142 4 : nPosEnd + strlen("</coordinates>") -
143 : nPosAfterCoordinates,
144 4 : "</coordinates>");
145 4 : nPos = nPosAfterCoordinates + strlen("</coordinates>");
146 : }
147 363 : }
148 :
149 : // Some non conformant file may contain
150 : // MultiPolygon/MultiLineString/MultiPoint See
151 : // https://github.com/OSGeo/gdal/issues/4031 Replace them by MultiGeometry.
152 119 : nPos = 0;
153 : while (true)
154 : {
155 122 : const char *pszStartTag = "<MultiPolygon>";
156 122 : const char *pszEndTag = "";
157 122 : CPL_IGNORE_RET_VAL(pszEndTag); // Make CSA happy
158 122 : auto nNewPos = oKml.find(pszStartTag, nPos);
159 122 : if (nNewPos != std::string::npos)
160 : {
161 1 : pszEndTag = "</MultiPolygon>";
162 : }
163 : else
164 : {
165 121 : pszStartTag = "<MultiLineString>";
166 121 : nNewPos = oKml.find(pszStartTag, nPos);
167 121 : if (nNewPos != std::string::npos)
168 : {
169 1 : pszEndTag = "</MultiLineString>";
170 : }
171 : else
172 : {
173 120 : pszStartTag = "<MultiPoint>";
174 120 : nNewPos = oKml.find(pszStartTag, nPos);
175 120 : if (nNewPos != std::string::npos)
176 1 : pszEndTag = "</MultiPoint>";
177 : else
178 119 : break;
179 : }
180 : }
181 3 : nPos = nNewPos;
182 3 : oKml.replace(nPos, strlen(pszStartTag), "<MultiGeometry>");
183 3 : nPos = oKml.find(pszEndTag, nPos);
184 3 : if (nPos == std::string::npos)
185 0 : break;
186 3 : oKml.replace(nPos, strlen(pszEndTag), "</MultiGeometry>");
187 3 : }
188 119 : }
189 :
190 : /************************************************************************/
191 : /* OGRLIBKMLRemoveSpaces() */
192 : /************************************************************************/
193 :
194 456 : static void OGRLIBKMLRemoveSpaces(std::string &osKml,
195 : const std::string &osNeedle)
196 : {
197 456 : size_t nPos = 0;
198 1368 : const std::string osLtNeedle(std::string("<").append(osNeedle));
199 912 : std::string osTmp;
200 912 : std::string osRet;
201 : while (true)
202 : {
203 545 : auto nPosNew = osKml.find(osLtNeedle, nPos);
204 545 : if (nPosNew == std::string::npos)
205 : {
206 456 : osRet.append(osKml, nPos);
207 456 : break;
208 : }
209 89 : const size_t nPosOri = nPosNew;
210 89 : nPosNew = osKml.find(">\n", nPosNew);
211 89 : if (nPosNew == std::string::npos || nPosNew + 2 == osKml.size())
212 : {
213 0 : osRet.append(osKml, nPos);
214 0 : break;
215 : }
216 : // Skip \n character
217 89 : osRet.append(osKml, nPos, nPosNew - nPos + 1);
218 89 : nPos = nPosNew + 2;
219 :
220 : // Remove leading spaces of " </{osNeedle}>"
221 89 : osTmp.clear();
222 1077 : for (size_t nPosTmp = nPosOri - 1; osKml[nPosTmp] == ' '; nPosTmp--)
223 : {
224 988 : osTmp += ' ';
225 : }
226 89 : osTmp += "</";
227 89 : osTmp += osNeedle;
228 89 : osTmp += '>';
229 89 : nPosNew = osKml.find(osTmp, nPos);
230 89 : if (nPosNew != std::string::npos)
231 : {
232 89 : osRet.append(osKml, nPos, nPosNew - nPos);
233 89 : osRet += "</";
234 89 : osRet += osNeedle;
235 89 : osRet += '>';
236 89 : nPos = nPosNew + osTmp.size();
237 : }
238 : else
239 : {
240 0 : osRet.append(osKml, nPos);
241 0 : break;
242 : }
243 89 : }
244 456 : osKml = std::move(osRet);
245 456 : }
246 :
247 : /************************************************************************/
248 : /* OGRLIBKMLPostProcessOutput() */
249 : /************************************************************************/
250 :
251 : // Substitute deprecated <Snippet> by <snippet> since libkml currently
252 : // only supports Snippet but ogckml22.xsd has deprecated it in favor of snippet.
253 152 : static void OGRLIBKMLPostProcessOutput(std::string &oKml)
254 : {
255 : // Manually add <?xml> node since libkml does not produce it currently
256 : // and this is useful in some circumstances (#5407).
257 152 : if (!(oKml.size() >= 2 && oKml[0] == '<' && oKml[1] == '?'))
258 152 : oKml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + oKml;
259 :
260 152 : size_t nPos = 0;
261 : while (true)
262 : {
263 155 : nPos = oKml.find("<Snippet>", nPos);
264 155 : if (nPos == std::string::npos)
265 : {
266 152 : break;
267 : }
268 3 : oKml[nPos + 1] = 's';
269 3 : nPos = oKml.find("</Snippet>", nPos);
270 3 : if (nPos == std::string::npos)
271 : {
272 0 : break;
273 : }
274 3 : oKml[nPos + 2] = 's';
275 : }
276 :
277 : // Fix indentation problems.
278 152 : OGRLIBKMLRemoveSpaces(oKml, "snippet");
279 152 : OGRLIBKMLRemoveSpaces(oKml, "linkSnippet");
280 152 : OGRLIBKMLRemoveSpaces(oKml, "SimpleData");
281 152 : }
282 :
283 : /******************************************************************************
284 : Method to write a single file ds .kml at ds destroy.
285 :
286 : Args: none
287 :
288 : Returns: nothing
289 :
290 : ******************************************************************************/
291 :
292 61 : bool OGRLIBKMLDataSource::WriteKml()
293 : {
294 122 : std::string oKmlFilename = GetDescription();
295 :
296 61 : if (m_poKmlDSContainer && m_poKmlDSContainer->IsA(kmldom::Type_Document))
297 : {
298 120 : DocumentPtr poKmlDocument = AsDocument(m_poKmlDSContainer);
299 :
300 60 : ParseDocumentOptions(m_poKmlDSKml, poKmlDocument);
301 :
302 124 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
303 : {
304 64 : SchemaPtr poKmlSchema = nullptr;
305 :
306 64 : if ((poKmlSchema = papoLayers[iLayer]->GetKmlSchema()))
307 : {
308 : const size_t nKmlSchemas =
309 4 : poKmlDocument->get_schema_array_size();
310 8 : SchemaPtr poKmlSchema2 = nullptr;
311 :
312 4 : for (size_t iKmlSchema = 0; iKmlSchema < nKmlSchemas;
313 : iKmlSchema++)
314 : {
315 : poKmlSchema2 =
316 1 : poKmlDocument->get_schema_array_at(iKmlSchema);
317 1 : if (poKmlSchema2 == poKmlSchema)
318 1 : break;
319 : }
320 :
321 4 : if (poKmlSchema2 != poKmlSchema)
322 3 : poKmlDocument->add_schema(poKmlSchema);
323 : }
324 :
325 64 : papoLayers[iLayer]->Finalize(poKmlDocument);
326 : }
327 : }
328 : else
329 : {
330 1 : ParseDocumentOptions(m_poKmlDSKml, nullptr);
331 : }
332 :
333 122 : std::string oKmlOut;
334 61 : oKmlOut = kmldom::SerializePretty(m_poKmlDSKml);
335 61 : OGRLIBKMLPostProcessOutput(oKmlOut);
336 :
337 61 : bool bRet = true;
338 61 : if (!oKmlOut.empty())
339 : {
340 61 : VSILFILE *fp = VSIFOpenExL(oKmlFilename.c_str(), "wb", true);
341 61 : if (fp == nullptr)
342 : {
343 0 : CPLError(CE_Failure, CPLE_FileIO, "Error writing %s: %s",
344 : oKmlFilename.c_str(), VSIGetLastErrorMsg());
345 0 : return false;
346 : }
347 :
348 61 : if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
349 0 : bRet = false;
350 61 : if (VSIFCloseL(fp) != 0)
351 0 : bRet = false;
352 : }
353 61 : return bRet;
354 : }
355 :
356 : /******************************************************************************/
357 : /* OGRLIBKMLCreateOGCKml22() */
358 : /******************************************************************************/
359 :
360 148 : static KmlPtr OGRLIBKMLCreateOGCKml22(KmlFactory *poFactory,
361 : char **papszOptions = nullptr)
362 : {
363 148 : const char *pszAuthorName = CSLFetchNameValue(papszOptions, "AUTHOR_NAME");
364 148 : const char *pszAuthorURI = CSLFetchNameValue(papszOptions, "AUTHOR_URI");
365 : const char *pszAuthorEmail =
366 148 : CSLFetchNameValue(papszOptions, "AUTHOR_EMAIL");
367 148 : const char *pszLink = CSLFetchNameValue(papszOptions, "LINK");
368 147 : const bool bWithAtom = pszAuthorName != nullptr ||
369 147 : pszAuthorURI != nullptr ||
370 295 : pszAuthorEmail != nullptr || pszLink != nullptr;
371 :
372 148 : KmlPtr kml = poFactory->CreateKml();
373 148 : if (bWithAtom)
374 : {
375 2 : const char *kAttrs[] = {"xmlns", "http://www.opengis.net/kml/2.2",
376 : "xmlns:atom", "http://www.w3.org/2005/Atom",
377 : nullptr};
378 2 : kml->AddUnknownAttributes(Attributes::Create(kAttrs));
379 : }
380 : else
381 : {
382 146 : const char *kAttrs[] = {"xmlns", "http://www.opengis.net/kml/2.2",
383 : nullptr};
384 146 : kml->AddUnknownAttributes(Attributes::Create(kAttrs));
385 : }
386 148 : return kml;
387 : }
388 :
389 : /******************************************************************************
390 : Method to write a ds .kmz at ds destroy.
391 :
392 : Args: none
393 :
394 : Returns: nothing
395 :
396 : ******************************************************************************/
397 :
398 8 : bool OGRLIBKMLDataSource::WriteKmz()
399 : {
400 16 : std::string osTmpFilename;
401 15 : if (!VSISupportsRandomWrite(GetDescription(), false) ||
402 7 : EQUAL(CPLGetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", ""),
403 : "FORCED"))
404 : {
405 4 : osTmpFilename = CPLGenerateTempFilenameSafe(
406 6 : CPLGetBasenameSafe(GetDescription()).c_str());
407 : }
408 :
409 10 : void *hZIP = CPLCreateZip(osTmpFilename.empty() ? GetDescription()
410 2 : : osTmpFilename.c_str(),
411 : nullptr);
412 :
413 8 : if (!hZIP)
414 : {
415 0 : CPLError(CE_Failure, CPLE_NoWriteAccess, "Error creating %s: %s",
416 0 : GetDescription(), VSIGetLastErrorMsg());
417 0 : return false;
418 : }
419 :
420 8 : bool bRet = true;
421 :
422 : /***** write out the doc.kml ****/
423 8 : const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
424 :
425 8 : if (CPLTestBool(pszUseDocKml) && (m_poKmlDocKml || m_poKmlUpdate))
426 : {
427 : // If we do not have the doc.kmlroot
428 : // make it and add the container.
429 7 : if (!m_poKmlDocKmlRoot)
430 : {
431 : m_poKmlDocKmlRoot =
432 7 : OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions);
433 :
434 7 : auto kml = AsKml(m_poKmlDocKmlRoot);
435 7 : if (m_poKmlDocKml)
436 : {
437 6 : if (kml)
438 : {
439 6 : kml->set_feature(m_poKmlDocKml);
440 : }
441 : }
442 :
443 7 : ParseDocumentOptions(std::move(kml), AsDocument(m_poKmlDocKml));
444 : }
445 :
446 14 : std::string oKmlOut = kmldom::SerializePretty(m_poKmlDocKmlRoot);
447 7 : OGRLIBKMLPostProcessOutput(oKmlOut);
448 :
449 14 : if (CPLCreateFileInZip(hZIP, "doc.kml", nullptr) != CE_None ||
450 7 : CPLWriteFileInZip(hZIP, oKmlOut.data(),
451 7 : static_cast<int>(oKmlOut.size())) != CE_None)
452 : {
453 0 : bRet = false;
454 0 : CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
455 0 : "doc.kml", GetDescription());
456 : }
457 7 : CPLCloseFileInZip(hZIP);
458 : }
459 :
460 : /***** loop though the layers and write them *****/
461 17 : for (int iLayer = 0; iLayer < nLayers && !m_poKmlUpdate; iLayer++)
462 : {
463 18 : ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer();
464 :
465 9 : if (poKmlContainer->IsA(kmldom::Type_Document))
466 : {
467 18 : DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
468 9 : SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema();
469 :
470 13 : if (!poKmlDocument->get_schema_array_size() && poKmlSchema &&
471 4 : poKmlSchema->get_simplefield_array_size())
472 : {
473 4 : poKmlDocument->add_schema(std::move(poKmlSchema));
474 : }
475 :
476 9 : papoLayers[iLayer]->Finalize(std::move(poKmlDocument));
477 : }
478 :
479 : // If we do not have the layers root
480 : // make it and add the container.
481 18 : KmlPtr poKmlKml = nullptr;
482 :
483 9 : if (!(poKmlKml = AsKml(papoLayers[iLayer]->GetKmlLayerRoot())))
484 : {
485 9 : poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
486 :
487 9 : poKmlKml->set_feature(poKmlContainer);
488 : }
489 :
490 18 : std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
491 9 : OGRLIBKMLPostProcessOutput(oKmlOut);
492 :
493 9 : if (iLayer == 0 && CPLTestBool(pszUseDocKml))
494 : {
495 5 : CPL_IGNORE_RET_VAL(CPLCreateFileInZip(hZIP, "layers/", nullptr));
496 : }
497 :
498 9 : const char *pszLayerFileName = nullptr;
499 9 : if (CPLTestBool(pszUseDocKml))
500 : pszLayerFileName =
501 8 : CPLSPrintf("layers/%s", papoLayers[iLayer]->GetFileName());
502 : else
503 1 : pszLayerFileName = papoLayers[iLayer]->GetFileName();
504 :
505 18 : if (CPLCreateFileInZip(hZIP, pszLayerFileName, nullptr) != CE_None ||
506 9 : CPLWriteFileInZip(hZIP, oKmlOut.data(),
507 9 : static_cast<int>(oKmlOut.size())) != CE_None)
508 0 : CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
509 0 : papoLayers[iLayer]->GetFileName(), GetDescription());
510 9 : CPLCloseFileInZip(hZIP);
511 : }
512 :
513 : /***** write the style table *****/
514 8 : if (m_poKmlStyleKml)
515 : {
516 2 : KmlPtr poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
517 :
518 1 : poKmlKml->set_feature(m_poKmlStyleKml);
519 2 : std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
520 1 : OGRLIBKMLPostProcessOutput(oKmlOut);
521 :
522 1 : if (CPLCreateFileInZip(hZIP, "style/", nullptr) != CE_None ||
523 2 : CPLCreateFileInZip(hZIP, "style/style.kml", nullptr) != CE_None ||
524 1 : CPLWriteFileInZip(hZIP, oKmlOut.data(),
525 1 : static_cast<int>(oKmlOut.size())) != CE_None)
526 : {
527 0 : bRet = false;
528 0 : CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
529 0 : "style/style.kml", GetDescription());
530 : }
531 1 : CPLCloseFileInZip(hZIP);
532 : }
533 :
534 8 : CPLCloseZip(hZIP);
535 :
536 8 : if (!osTmpFilename.empty())
537 : {
538 2 : if (bRet)
539 : {
540 2 : bRet = CPLCopyFile(GetDescription(), osTmpFilename.c_str()) == 0;
541 2 : if (!bRet)
542 1 : CPLError(CE_Failure, CPLE_FileIO,
543 1 : "Cannot copy temporary file to %s", GetDescription());
544 : }
545 2 : VSIUnlink(osTmpFilename.c_str());
546 : }
547 :
548 8 : return bRet;
549 : }
550 :
551 : /******************************************************************************
552 : Method to write a dir ds at ds destroy.
553 :
554 : Args: none
555 :
556 : Returns: nothing
557 :
558 : ******************************************************************************/
559 :
560 37 : bool OGRLIBKMLDataSource::WriteDir()
561 : {
562 : /***** write out the doc.kml ****/
563 37 : const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
564 :
565 37 : bool bRet = true;
566 37 : if (CPLTestBool(pszUseDocKml) && (m_poKmlDocKml || m_poKmlUpdate))
567 : {
568 : // If we don't have the doc.kml root
569 : // make it and add the container.
570 37 : if (!m_poKmlDocKmlRoot)
571 : {
572 : m_poKmlDocKmlRoot =
573 37 : OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions);
574 37 : auto kml = AsKml(m_poKmlDocKmlRoot);
575 37 : if (kml)
576 : {
577 37 : if (m_poKmlDocKml)
578 36 : kml->set_feature(m_poKmlDocKml);
579 : }
580 :
581 37 : ParseDocumentOptions(std::move(kml), AsDocument(m_poKmlDocKml));
582 : }
583 :
584 37 : std::string oKmlOut = kmldom::SerializePretty(m_poKmlDocKmlRoot);
585 37 : OGRLIBKMLPostProcessOutput(oKmlOut);
586 :
587 : const std::string osOutfile =
588 37 : CPLFormFilenameSafe(GetDescription(), "doc.kml", nullptr);
589 :
590 37 : VSILFILE *fp = VSIFOpenExL(osOutfile.c_str(), "wb", true);
591 37 : if (fp == nullptr)
592 : {
593 0 : CPLError(CE_Failure, CPLE_FileIO, "Error writing %s to %s: %s",
594 0 : "doc.kml", GetDescription(), VSIGetLastErrorMsg());
595 0 : return false;
596 : }
597 :
598 37 : if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
599 0 : bRet = false;
600 37 : if (VSIFCloseL(fp) != 0)
601 0 : bRet = false;
602 : }
603 :
604 : /***** loop though the layers and write them *****/
605 74 : for (int iLayer = 0; iLayer < nLayers && !m_poKmlUpdate; iLayer++)
606 : {
607 37 : ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer();
608 :
609 37 : if (poKmlContainer->IsA(kmldom::Type_Document))
610 : {
611 74 : DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
612 37 : SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema();
613 :
614 54 : if (!poKmlDocument->get_schema_array_size() && poKmlSchema &&
615 17 : poKmlSchema->get_simplefield_array_size())
616 : {
617 17 : poKmlDocument->add_schema(std::move(poKmlSchema));
618 : }
619 :
620 37 : papoLayers[iLayer]->Finalize(std::move(poKmlDocument));
621 : }
622 :
623 : // If we do not have the layers root
624 : // make it and add the container.
625 37 : KmlPtr poKmlKml = nullptr;
626 :
627 37 : if (!(poKmlKml = AsKml(papoLayers[iLayer]->GetKmlLayerRoot())))
628 : {
629 37 : poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
630 :
631 37 : poKmlKml->set_feature(poKmlContainer);
632 : }
633 :
634 37 : std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
635 37 : OGRLIBKMLPostProcessOutput(oKmlOut);
636 :
637 : const std::string osOutfile = CPLFormFilenameSafe(
638 37 : GetDescription(), papoLayers[iLayer]->GetFileName(), nullptr);
639 :
640 37 : VSILFILE *fp = VSIFOpenL(osOutfile.c_str(), "wb");
641 37 : if (fp == nullptr)
642 : {
643 0 : CPLError(CE_Failure, CPLE_FileIO, "ERROR Writing %s to %s",
644 0 : papoLayers[iLayer]->GetFileName(), GetDescription());
645 0 : return false;
646 : }
647 :
648 37 : if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
649 0 : bRet = false;
650 37 : if (VSIFCloseL(fp) != 0)
651 0 : bRet = false;
652 : }
653 :
654 : /***** write the style table *****/
655 37 : if (m_poKmlStyleKml)
656 : {
657 0 : KmlPtr poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
658 :
659 0 : poKmlKml->set_feature(m_poKmlStyleKml);
660 0 : std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
661 0 : OGRLIBKMLPostProcessOutput(oKmlOut);
662 :
663 : const std::string osOutfile =
664 0 : CPLFormFilenameSafe(GetDescription(), "style.kml", nullptr);
665 :
666 0 : VSILFILE *fp = VSIFOpenL(osOutfile.c_str(), "wb");
667 0 : if (fp == nullptr)
668 : {
669 0 : CPLError(CE_Failure, CPLE_FileIO, "ERROR Writing %s to %s",
670 0 : "style.kml", GetDescription());
671 0 : return false;
672 : }
673 :
674 0 : if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
675 0 : bRet = false;
676 0 : if (VSIFCloseL(fp) != 0)
677 0 : bRet = false;
678 : }
679 37 : return bRet;
680 : }
681 :
682 : /******************************************************************************
683 : Method to write the datasource to disk.
684 :
685 : Args: none
686 :
687 : Returns nothing
688 :
689 : ******************************************************************************/
690 :
691 398 : CPLErr OGRLIBKMLDataSource::FlushCache(bool /* bAtClosing */)
692 : {
693 398 : if (!bUpdated)
694 292 : return CE_None;
695 :
696 106 : CPLErr eErr = CE_None;
697 106 : if (bUpdate && IsKml())
698 : {
699 61 : if (!WriteKml())
700 0 : eErr = CE_Failure;
701 : }
702 45 : else if (bUpdate && IsKmz())
703 : {
704 8 : if (!WriteKmz())
705 1 : eErr = CE_Failure;
706 : }
707 37 : else if (bUpdate && IsDir())
708 : {
709 37 : if (!WriteDir())
710 0 : eErr = CE_Failure;
711 : }
712 :
713 106 : bUpdated = false;
714 106 : return eErr;
715 : }
716 :
717 : /******************************************************************************
718 : OGRLIBKMLDataSource Destructor
719 :
720 : Args: none
721 :
722 : Returns: nothing
723 :
724 : ******************************************************************************/
725 :
726 782 : OGRLIBKMLDataSource::~OGRLIBKMLDataSource()
727 : {
728 : /***** sync the DS to disk *****/
729 391 : OGRLIBKMLDataSource::FlushCache(true);
730 :
731 736 : for (int i = 0; i < nLayers; i++)
732 345 : delete papoLayers[i];
733 :
734 391 : CPLFree(papoLayers);
735 :
736 391 : CSLDestroy(m_papszOptions);
737 782 : }
738 :
739 : /******************************************************************************
740 : Method to parse a schemas out of a document.
741 :
742 : Args: poKmlDocument pointer to the document to parse
743 :
744 : Returns: nothing
745 :
746 : ******************************************************************************/
747 :
748 26 : SchemaPtr OGRLIBKMLDataSource::FindSchema(const char *pszSchemaUrl)
749 : {
750 26 : if (!pszSchemaUrl || !*pszSchemaUrl)
751 0 : return nullptr;
752 :
753 26 : char *pszID = nullptr;
754 26 : char *pszFile = nullptr;
755 26 : char *pszSchemaName = nullptr;
756 52 : DocumentPtr poKmlDocument = nullptr;
757 52 : SchemaPtr poKmlSchemaResult = nullptr;
758 :
759 26 : if (*pszSchemaUrl == '#')
760 : {
761 24 : pszID = CPLStrdup(pszSchemaUrl + 1);
762 :
763 : /***** kml *****/
764 24 : if (IsKml() && m_poKmlDSContainer->IsA(kmldom::Type_Document))
765 24 : poKmlDocument = AsDocument(m_poKmlDSContainer);
766 :
767 : /***** kmz *****/
768 0 : else if ((IsKmz() || IsDir()) && m_poKmlDocKml &&
769 0 : m_poKmlDocKml->IsA(kmldom::Type_Document))
770 0 : poKmlDocument = AsDocument(m_poKmlDocKml);
771 : }
772 2 : else if (const char *pszPound = strchr(pszSchemaUrl, '#'))
773 : {
774 0 : pszFile = CPLStrdup(pszSchemaUrl);
775 0 : pszID = CPLStrdup(pszPound + 1);
776 0 : pszFile[pszPound - pszSchemaUrl] = '\0';
777 : }
778 : else
779 : {
780 2 : pszSchemaName = CPLStrdup(pszSchemaUrl);
781 :
782 : /***** kml *****/
783 2 : if (IsKml() && m_poKmlDSContainer->IsA(kmldom::Type_Document))
784 2 : poKmlDocument = AsDocument(m_poKmlDSContainer);
785 :
786 : /***** kmz *****/
787 :
788 0 : else if ((IsKmz() || IsDir()) && m_poKmlDocKml &&
789 0 : m_poKmlDocKml->IsA(kmldom::Type_Document))
790 0 : poKmlDocument = AsDocument(m_poKmlDocKml);
791 : }
792 :
793 26 : if (poKmlDocument)
794 : {
795 26 : size_t nKmlSchemas = poKmlDocument->get_schema_array_size();
796 :
797 29 : for (size_t iKmlSchema = 0; iKmlSchema < nKmlSchemas; iKmlSchema++)
798 : {
799 : SchemaPtr poKmlSchema =
800 27 : poKmlDocument->get_schema_array_at(iKmlSchema);
801 27 : if (poKmlSchema->has_id() && pszID)
802 : {
803 25 : if (EQUAL(pszID, poKmlSchema->get_id().c_str()))
804 : {
805 23 : poKmlSchemaResult = std::move(poKmlSchema);
806 23 : break;
807 : }
808 : }
809 :
810 2 : else if (poKmlSchema->has_name() && pszSchemaName)
811 : {
812 2 : if (EQUAL(pszSchemaName, poKmlSchema->get_name().c_str()))
813 : {
814 1 : poKmlSchemaResult = std::move(poKmlSchema);
815 1 : break;
816 : }
817 : }
818 : }
819 : }
820 :
821 26 : CPLFree(pszFile);
822 26 : CPLFree(pszID);
823 26 : CPLFree(pszSchemaName);
824 :
825 26 : return poKmlSchemaResult;
826 : }
827 :
828 : /******************************************************************************
829 : Method to allocate memory for the layer array, create the layer,
830 : and add it to the layer array.
831 :
832 : Args: pszLayerName the name of the layer
833 : eGType the layers geometry type
834 : poOgrDS pointer to the datasource the layer is in
835 : poKmlRoot pointer to the root kml element of the layer
836 : pszFileName the filename of the layer
837 : bNew true if its a new layer
838 : bUpdate true if the layer is writable
839 : nGuess a guess at the number of additional layers
840 : we are going to need
841 :
842 : Returns: Pointer to the new layer
843 : ******************************************************************************/
844 :
845 361 : OGRLIBKMLLayer *OGRLIBKMLDataSource::AddLayer(
846 : const char *pszLayerName, OGRwkbGeometryType eGType,
847 : const OGRSpatialReference *poSRS, OGRLIBKMLDataSource *poOgrDS,
848 : ElementPtr poKmlRoot, ContainerPtr poKmlContainer, const char *pszFileName,
849 : int bNew, int bUpdateIn, int nGuess)
850 : {
851 : // Build unique layer name
852 361 : CPLString osUniqueLayername(pszLayerName);
853 361 : int nIter = 2;
854 : while (true)
855 : {
856 362 : if (GetLayerByName(osUniqueLayername) == nullptr)
857 361 : break;
858 1 : osUniqueLayername = CPLSPrintf("%s (#%d)", pszLayerName, nIter);
859 1 : nIter++;
860 : }
861 :
862 : /***** check to see if we have enough space to store the layer *****/
863 361 : if (nLayers == nAllocated)
864 : {
865 257 : nAllocated += nGuess;
866 257 : papoLayers = static_cast<OGRLIBKMLLayer **>(
867 257 : CPLRealloc(papoLayers, sizeof(OGRLIBKMLLayer *) * nAllocated));
868 : }
869 :
870 : /***** create the layer *****/
871 : OGRLIBKMLLayer *poOgrLayer = new OGRLIBKMLLayer(
872 361 : osUniqueLayername, eGType, poSRS, poOgrDS, std::move(poKmlRoot),
873 722 : std::move(poKmlContainer), m_poKmlUpdate, pszFileName, bNew, bUpdateIn);
874 :
875 : /***** add the layer to the array *****/
876 361 : const int iLayer = nLayers++;
877 361 : papoLayers[iLayer] = poOgrLayer;
878 361 : osUniqueLayername.toupper();
879 361 : m_oMapLayers[std::move(osUniqueLayername)] = poOgrLayer;
880 :
881 722 : return poOgrLayer;
882 : }
883 :
884 : /******************************************************************************
885 : Method to parse multiple layers out of a container.
886 :
887 : Args: poKmlContainer pointer to the container to parse
888 :
889 : Returns: number of features in the container that are not another
890 : container
891 :
892 : ******************************************************************************/
893 :
894 459 : int OGRLIBKMLDataSource::ParseLayers(ContainerPtr poKmlContainer, bool bRecurse)
895 : {
896 : /***** if container is null just bail now *****/
897 459 : if (!poKmlContainer)
898 0 : return 0;
899 :
900 459 : const size_t nKmlFeatures = poKmlContainer->get_feature_array_size();
901 :
902 : /***** loop over the container to separate the style, layers, etc *****/
903 :
904 459 : int nResult = 0;
905 1396 : for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
906 : {
907 : FeaturePtr poKmlFeat =
908 1874 : poKmlContainer->get_feature_array_at(iKmlFeature);
909 :
910 : /***** container *****/
911 :
912 937 : if (poKmlFeat->IsA(kmldom::Type_Container))
913 : {
914 382 : if (bRecurse)
915 : {
916 : /***** see if the container has a name *****/
917 :
918 211 : std::string oKmlFeatName;
919 211 : if (poKmlFeat->has_name())
920 : {
921 : /* Strip leading and trailing spaces */
922 210 : const char *l_pszName = poKmlFeat->get_name().c_str();
923 210 : while (*l_pszName == ' ' || *l_pszName == '\n' ||
924 420 : *l_pszName == '\r' || *l_pszName == '\t')
925 0 : l_pszName++;
926 210 : oKmlFeatName = l_pszName;
927 210 : int nSize = (int)oKmlFeatName.size();
928 420 : while (nSize > 0 && (oKmlFeatName[nSize - 1] == ' ' ||
929 210 : oKmlFeatName[nSize - 1] == '\n' ||
930 210 : oKmlFeatName[nSize - 1] == '\r' ||
931 210 : oKmlFeatName[nSize - 1] == '\t'))
932 : {
933 0 : nSize--;
934 0 : oKmlFeatName.resize(nSize);
935 : }
936 : }
937 : /***** use the feature index number as the name *****/
938 : /***** not sure i like this c++ ich *****/
939 : else
940 : {
941 1 : std::stringstream oOut;
942 1 : oOut << iKmlFeature;
943 1 : oKmlFeatName = "Layer";
944 1 : oKmlFeatName.append(oOut.str());
945 : }
946 :
947 : /***** create the layer *****/
948 :
949 211 : AddLayer(oKmlFeatName.c_str(), wkbUnknown, nullptr, this,
950 422 : nullptr, AsContainer(poKmlFeat), "", FALSE, bUpdate,
951 : static_cast<int>(nKmlFeatures));
952 :
953 : /***** check if any features are another layer *****/
954 211 : ParseLayers(AsContainer(poKmlFeat), true);
955 : }
956 : }
957 : else
958 : {
959 555 : nResult++;
960 : }
961 : }
962 :
963 459 : return nResult;
964 : }
965 :
966 : /******************************************************************************
967 : Function to get the container from the kmlroot.
968 :
969 : Args: poKmlRoot the root element
970 :
971 : Returns: root if its a container, if its a kml the container it
972 : contains, or NULL
973 :
974 : ******************************************************************************/
975 :
976 138 : static ContainerPtr GetContainerFromRoot(KmlFactory *poKmlFactory,
977 : ElementPtr poKmlRoot)
978 : {
979 138 : ContainerPtr poKmlContainer = nullptr;
980 :
981 : const bool bReadGroundOverlay =
982 138 : CPLTestBool(CPLGetConfigOption("LIBKML_READ_GROUND_OVERLAY", "YES"));
983 :
984 138 : if (poKmlRoot)
985 : {
986 : /***** skip over the <kml> we want the container *****/
987 138 : if (poKmlRoot->IsA(kmldom::Type_kml))
988 : {
989 276 : KmlPtr poKmlKml = AsKml(poKmlRoot);
990 :
991 138 : if (poKmlKml && poKmlKml->has_feature())
992 : {
993 276 : FeaturePtr poKmlFeat = poKmlKml->get_feature();
994 :
995 138 : if (poKmlFeat->IsA(kmldom::Type_Container))
996 135 : poKmlContainer = AsContainer(poKmlFeat);
997 3 : else if (poKmlFeat->IsA(kmldom::Type_Placemark) ||
998 0 : (bReadGroundOverlay &&
999 0 : poKmlFeat->IsA(kmldom::Type_GroundOverlay)))
1000 : {
1001 3 : poKmlContainer = poKmlFactory->CreateDocument();
1002 6 : poKmlContainer->add_feature(
1003 6 : kmldom::AsFeature(kmlengine::Clone(poKmlFeat)));
1004 : }
1005 : }
1006 : }
1007 0 : else if (poKmlRoot->IsA(kmldom::Type_Container))
1008 : {
1009 0 : poKmlContainer = AsContainer(std::move(poKmlRoot));
1010 : }
1011 : }
1012 :
1013 138 : return poKmlContainer;
1014 : }
1015 :
1016 : /******************************************************************************
1017 : Method to parse a kml string into the style table.
1018 : ******************************************************************************/
1019 :
1020 4 : int OGRLIBKMLDataSource::ParseIntoStyleTable(std::string *poKmlStyleKml,
1021 : const char *pszMyStylePath)
1022 : {
1023 : /***** parse the kml into the dom *****/
1024 8 : std::string oKmlErrors;
1025 8 : ElementPtr poKmlRoot = OGRLIBKMLParse(*poKmlStyleKml, &oKmlErrors);
1026 :
1027 4 : if (!poKmlRoot)
1028 : {
1029 0 : CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing style kml %s :%s",
1030 : pszMyStylePath, oKmlErrors.c_str());
1031 0 : return false;
1032 : }
1033 :
1034 8 : ContainerPtr poKmlContainer = nullptr;
1035 :
1036 4 : if (!(poKmlContainer =
1037 8 : GetContainerFromRoot(m_poKmlFactory, std::move(poKmlRoot))))
1038 : {
1039 0 : return false;
1040 : }
1041 :
1042 4 : ParseStyles(AsDocument(std::move(poKmlContainer)), &m_poStyleTable);
1043 4 : m_osStylePath = pszMyStylePath;
1044 :
1045 4 : return true;
1046 : }
1047 :
1048 : /******************************************************************************
1049 : Method to open a kml file.
1050 :
1051 : Args: pszFilename file to open
1052 : bUpdate update mode
1053 :
1054 : Returns: True on success, false on failure
1055 :
1056 : ******************************************************************************/
1057 :
1058 119 : int OGRLIBKMLDataSource::OpenKml(const char *pszFilename, int bUpdateIn)
1059 : {
1060 238 : std::string oKmlKml;
1061 238 : std::string osBuffer;
1062 119 : osBuffer.resize(4096);
1063 :
1064 119 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
1065 119 : if (fp == nullptr)
1066 : {
1067 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s", pszFilename);
1068 0 : return FALSE;
1069 : }
1070 119 : int nRead = 0;
1071 208 : while ((nRead = static_cast<int>(
1072 327 : VSIFReadL(&osBuffer[0], 1, osBuffer.size(), fp))) != 0)
1073 : {
1074 : try
1075 : {
1076 208 : oKmlKml.append(osBuffer.c_str(), nRead);
1077 : }
1078 0 : catch (const std::exception &ex)
1079 : {
1080 0 : CPLDebug("LIBKML", "libstdc++ exception during ingestion: %s",
1081 0 : ex.what());
1082 0 : VSIFCloseL(fp);
1083 0 : return FALSE;
1084 : }
1085 : }
1086 119 : OGRLIBKMLPreProcessInput(oKmlKml);
1087 119 : VSIFCloseL(fp);
1088 :
1089 238 : CPLLocaleC oLocaleForcer;
1090 :
1091 : /***** parse the kml into the DOM *****/
1092 238 : std::string oKmlErrors;
1093 :
1094 119 : m_poKmlDSKml = AsKml(OGRLIBKMLParse(oKmlKml, &oKmlErrors));
1095 :
1096 119 : if (!m_poKmlDSKml)
1097 : {
1098 0 : CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing kml %s :%s",
1099 : pszFilename, oKmlErrors.c_str());
1100 :
1101 0 : return FALSE;
1102 : }
1103 :
1104 : /***** get the container from root *****/
1105 119 : if (!(m_poKmlDSContainer =
1106 238 : GetContainerFromRoot(m_poKmlFactory, m_poKmlDSKml)))
1107 : {
1108 0 : CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing kml %s :%s %s",
1109 : pszFilename, "This file does not fit the OGR model,",
1110 : "there is no container element at the root.");
1111 :
1112 0 : return FALSE;
1113 : }
1114 :
1115 119 : m_isKml = true;
1116 :
1117 : /***** get the styles *****/
1118 119 : ParseStyles(AsDocument(m_poKmlDSContainer), &m_poStyleTable);
1119 :
1120 : /***** parse for layers *****/
1121 119 : int nPlacemarks = ParseLayers(m_poKmlDSContainer, false);
1122 :
1123 : /***** if there is placemarks in the root its a layer *****/
1124 119 : if (nPlacemarks)
1125 : {
1126 16 : std::string layername_default(CPLGetBasenameSafe(pszFilename));
1127 :
1128 16 : if (m_poKmlDSContainer->has_name())
1129 : {
1130 6 : layername_default = m_poKmlDSContainer->get_name();
1131 : }
1132 :
1133 16 : AddLayer(layername_default.c_str(), wkbUnknown, nullptr, this,
1134 16 : m_poKmlDSKml, m_poKmlDSContainer, pszFilename, FALSE,
1135 : bUpdateIn, 1);
1136 : }
1137 :
1138 119 : ParseLayers(m_poKmlDSContainer, true);
1139 :
1140 119 : return TRUE;
1141 : }
1142 :
1143 : /******************************************************************************
1144 : Method to open a kmz file.
1145 :
1146 : Args: pszFilename file to open
1147 : bUpdate update mode
1148 :
1149 : Returns: True on success, false on failure
1150 :
1151 : ******************************************************************************/
1152 :
1153 6 : int OGRLIBKMLDataSource::OpenKmz(const char *pszFilename, int bUpdateIn)
1154 : {
1155 12 : std::string oKmlKmz;
1156 6 : char szBuffer[1024 + 1] = {};
1157 :
1158 6 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
1159 6 : if (fp == nullptr)
1160 : {
1161 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s", pszFilename);
1162 0 : return FALSE;
1163 : }
1164 6 : int nRead = 0;
1165 16 : while ((nRead = static_cast<int>(VSIFReadL(szBuffer, 1, 1024, fp))) != 0)
1166 : {
1167 : try
1168 : {
1169 10 : oKmlKmz.append(szBuffer, nRead);
1170 : }
1171 0 : catch (const std::bad_alloc &)
1172 : {
1173 0 : VSIFCloseL(fp);
1174 0 : return FALSE;
1175 : }
1176 : }
1177 6 : VSIFCloseL(fp);
1178 :
1179 6 : KmzFile *poKmlKmzfile = KmzFile::OpenFromString(oKmlKmz);
1180 :
1181 6 : if (!poKmlKmzfile)
1182 : {
1183 0 : CPLError(CE_Failure, CPLE_OpenFailed, "%s is not a valid kmz file",
1184 : pszFilename);
1185 0 : return FALSE;
1186 : }
1187 :
1188 12 : CPLLocaleC oLocaleForcer;
1189 :
1190 : /***** read the doc.kml *****/
1191 12 : std::string oKmlKml;
1192 12 : std::string oKmlKmlPath;
1193 6 : if (!poKmlKmzfile->ReadKmlAndGetPath(&oKmlKml, &oKmlKmlPath))
1194 : {
1195 0 : return FALSE;
1196 : }
1197 :
1198 : /***** parse the kml into the DOM *****/
1199 12 : std::string oKmlErrors;
1200 12 : ElementPtr poKmlDocKmlRoot = OGRLIBKMLParse(oKmlKml, &oKmlErrors);
1201 :
1202 6 : if (!poKmlDocKmlRoot)
1203 : {
1204 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1205 : "ERROR parsing kml layer %s from %s :%s", oKmlKmlPath.c_str(),
1206 : pszFilename, oKmlErrors.c_str());
1207 :
1208 0 : return FALSE;
1209 : }
1210 :
1211 : /***** Get the child container from root. *****/
1212 12 : ContainerPtr poKmlContainer = nullptr;
1213 :
1214 6 : if (!(poKmlContainer =
1215 12 : GetContainerFromRoot(m_poKmlFactory, poKmlDocKmlRoot)))
1216 : {
1217 0 : CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing %s from %s :%s",
1218 : oKmlKmlPath.c_str(), pszFilename,
1219 : "kml contains no Containers");
1220 :
1221 0 : return FALSE;
1222 : }
1223 :
1224 : /***** loop over the container looking for network links *****/
1225 :
1226 6 : size_t nKmlFeatures = poKmlContainer->get_feature_array_size();
1227 6 : int nLinks = 0;
1228 :
1229 21 : for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
1230 : {
1231 : FeaturePtr poKmlFeat =
1232 15 : poKmlContainer->get_feature_array_at(iKmlFeature);
1233 :
1234 : /***** is it a network link? *****/
1235 15 : if (!poKmlFeat->IsA(kmldom::Type_NetworkLink))
1236 8 : continue;
1237 :
1238 7 : NetworkLinkPtr poKmlNetworkLink = AsNetworkLink(poKmlFeat);
1239 :
1240 : /***** does it have a link? *****/
1241 7 : if (!poKmlNetworkLink->has_link())
1242 0 : continue;
1243 :
1244 7 : LinkPtr poKmlLink = poKmlNetworkLink->get_link();
1245 :
1246 : /***** does the link have a href? *****/
1247 7 : if (!poKmlLink->has_href())
1248 0 : continue;
1249 :
1250 7 : const kmlengine::Href oKmlHref(poKmlLink->get_href());
1251 :
1252 : /***** is the link relative? *****/
1253 7 : if (oKmlHref.IsRelativePath())
1254 : {
1255 7 : nLinks++;
1256 :
1257 7 : std::string oKml;
1258 7 : if (poKmlKmzfile->ReadFile(oKmlHref.get_path().c_str(), &oKml))
1259 : {
1260 : /***** parse the kml into the DOM *****/
1261 7 : oKmlErrors.clear();
1262 7 : ElementPtr poKmlLyrRoot = OGRLIBKMLParse(oKml, &oKmlErrors);
1263 :
1264 7 : if (!poKmlLyrRoot)
1265 : {
1266 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1267 : "ERROR parsing kml layer %s from %s :%s",
1268 0 : oKmlHref.get_path().c_str(), pszFilename,
1269 : oKmlErrors.c_str());
1270 :
1271 0 : continue;
1272 : }
1273 :
1274 : /***** get the container from root *****/
1275 : ContainerPtr poKmlLyrContainer =
1276 7 : GetContainerFromRoot(m_poKmlFactory, poKmlLyrRoot);
1277 :
1278 7 : if (!poKmlLyrContainer)
1279 : {
1280 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1281 : "ERROR parsing kml layer %s from %s :%s",
1282 0 : oKmlHref.get_path().c_str(), pszFilename,
1283 : oKmlErrors.c_str());
1284 :
1285 0 : continue;
1286 : }
1287 :
1288 : /***** create the layer *****/
1289 : const std::string osLayerName =
1290 7 : poKmlNetworkLink->has_name()
1291 7 : ? poKmlNetworkLink->get_name()
1292 14 : : CPLGetBasenameSafe(oKmlHref.get_path().c_str());
1293 :
1294 14 : AddLayer(osLayerName.c_str(), wkbUnknown, nullptr, this,
1295 7 : std::move(poKmlLyrRoot), poKmlLyrContainer,
1296 7 : oKmlHref.get_path().c_str(), FALSE, bUpdateIn,
1297 : static_cast<int>(nKmlFeatures));
1298 :
1299 : /***** check if any features are another layer *****/
1300 7 : ParseLayers(std::move(poKmlLyrContainer), true);
1301 : }
1302 : }
1303 : }
1304 :
1305 : /***** if the doc.kml has links store it so if were in update mode we can
1306 : * write it *****/
1307 6 : if (nLinks)
1308 : {
1309 5 : m_poKmlDocKml = std::move(poKmlContainer);
1310 5 : m_poKmlDocKmlRoot = std::move(poKmlDocKmlRoot);
1311 : }
1312 : /***** if the doc.kml has no links treat it as a normal kml file *****/
1313 : else
1314 : {
1315 : /* TODO: There could still be a separate styles file in the KMZ
1316 : if there is this would be a layer style table IF its only a single
1317 : layer.
1318 : */
1319 :
1320 : /***** get the styles *****/
1321 1 : ParseStyles(AsDocument(poKmlContainer), &m_poStyleTable);
1322 :
1323 : /***** parse for layers *****/
1324 1 : const int nPlacemarks = ParseLayers(poKmlContainer, false);
1325 :
1326 : /***** if there is placemarks in the root its a layer *****/
1327 1 : if (nPlacemarks)
1328 : {
1329 1 : std::string layername_default(CPLGetBasenameSafe(pszFilename));
1330 :
1331 1 : if (poKmlContainer->has_name())
1332 : {
1333 1 : layername_default = poKmlContainer->get_name();
1334 : }
1335 :
1336 2 : AddLayer(layername_default.c_str(), wkbUnknown, nullptr, this,
1337 1 : std::move(poKmlDocKmlRoot), poKmlContainer, pszFilename,
1338 : FALSE, bUpdateIn, 1);
1339 : }
1340 :
1341 1 : ParseLayers(std::move(poKmlContainer), true);
1342 : }
1343 :
1344 : /***** read the style table if it has one *****/
1345 6 : std::string oKmlStyleKml;
1346 6 : if (poKmlKmzfile->ReadFile("style/style.kml", &oKmlStyleKml))
1347 2 : ParseIntoStyleTable(&oKmlStyleKml, "style/style.kml");
1348 :
1349 : /***** cleanup *****/
1350 6 : delete poKmlKmzfile;
1351 6 : m_isKmz = true;
1352 :
1353 6 : return TRUE;
1354 : }
1355 :
1356 : /******************************************************************************
1357 : Method to open a dir.
1358 :
1359 : Args: pszFilename Dir to open
1360 : bUpdate update mode
1361 :
1362 : Returns: True on success, false on failure
1363 :
1364 : ******************************************************************************/
1365 :
1366 163 : int OGRLIBKMLDataSource::OpenDir(const char *pszFilename, int bUpdateIn)
1367 : {
1368 163 : char **papszDirList = VSIReadDir(pszFilename);
1369 :
1370 163 : if (papszDirList == nullptr)
1371 6 : return FALSE;
1372 :
1373 157 : const int nFiles = CSLCount(papszDirList);
1374 :
1375 4529 : for (int iFile = 0; iFile < nFiles; iFile++)
1376 : {
1377 : /***** make sure its a .kml file *****/
1378 4372 : if (!EQUAL(CPLGetExtensionSafe(papszDirList[iFile]).c_str(), "kml"))
1379 4371 : continue;
1380 :
1381 : /***** read the file *****/
1382 2 : std::string oKmlKml;
1383 2 : char szBuffer[1024 + 1] = {};
1384 :
1385 : const CPLString osFilePath =
1386 2 : CPLFormFilenameSafe(pszFilename, papszDirList[iFile], nullptr);
1387 :
1388 2 : VSILFILE *fp = VSIFOpenL(osFilePath, "rb");
1389 2 : if (fp == nullptr)
1390 : {
1391 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s",
1392 : osFilePath.c_str());
1393 0 : continue;
1394 : }
1395 :
1396 2 : int nRead = 0;
1397 4 : while ((nRead = static_cast<int>(VSIFReadL(szBuffer, 1, 1024, fp))) !=
1398 : 0)
1399 : {
1400 : try
1401 : {
1402 2 : oKmlKml.append(szBuffer, nRead);
1403 : }
1404 0 : catch (const std::bad_alloc &)
1405 : {
1406 0 : VSIFCloseL(fp);
1407 0 : CSLDestroy(papszDirList);
1408 0 : return FALSE;
1409 : }
1410 : }
1411 2 : VSIFCloseL(fp);
1412 :
1413 2 : CPLLocaleC oLocaleForcer;
1414 :
1415 : /***** parse the kml into the DOM *****/
1416 2 : std::string oKmlErrors;
1417 2 : ElementPtr poKmlRoot = OGRLIBKMLParse(oKmlKml, &oKmlErrors);
1418 :
1419 2 : if (!poKmlRoot)
1420 : {
1421 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1422 : "ERROR parsing kml layer %s from %s :%s",
1423 : osFilePath.c_str(), pszFilename, oKmlErrors.c_str());
1424 :
1425 0 : continue;
1426 : }
1427 :
1428 : /***** Get the container from the root *****/
1429 2 : ContainerPtr poKmlContainer = nullptr;
1430 :
1431 2 : if (!(poKmlContainer = GetContainerFromRoot(m_poKmlFactory, poKmlRoot)))
1432 : {
1433 0 : CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing kml %s :%s %s",
1434 : pszFilename, "This file does not fit the OGR model,",
1435 : "there is no container element at the root.");
1436 0 : continue;
1437 : }
1438 :
1439 : /***** is it a style table? *****/
1440 2 : if (EQUAL(papszDirList[iFile], "style.kml"))
1441 : {
1442 0 : ParseStyles(AsDocument(poKmlContainer), &m_poStyleTable);
1443 0 : m_osStylePath = "style.kml";
1444 0 : continue;
1445 : }
1446 :
1447 : // Root document generated by ourselves. Contains nothing of interest
1448 2 : if (poKmlContainer->has_id() && poKmlContainer->get_id() == "root_doc")
1449 : {
1450 1 : continue;
1451 : }
1452 :
1453 : const std::string osLayerName =
1454 1 : poKmlContainer->has_name()
1455 1 : ? poKmlContainer->get_name()
1456 2 : : std::string(CPLGetBasenameSafe(osFilePath.c_str()));
1457 :
1458 : /***** create the layer *****/
1459 2 : AddLayer(osLayerName.c_str(), wkbUnknown, nullptr, this,
1460 1 : std::move(poKmlRoot), poKmlContainer, osFilePath.c_str(),
1461 : FALSE, bUpdateIn, nFiles);
1462 :
1463 : /***** check if any features are another layer *****/
1464 1 : ParseLayers(std::move(poKmlContainer), true);
1465 : }
1466 :
1467 157 : CSLDestroy(papszDirList);
1468 :
1469 157 : if (nLayers > 0)
1470 : {
1471 1 : m_isDir = true;
1472 1 : return TRUE;
1473 : }
1474 :
1475 156 : return FALSE;
1476 : }
1477 :
1478 : /******************************************************************************
1479 : Method to open a datasource.
1480 :
1481 : Args: pszFilename Darasource to open
1482 : bUpdate update mode
1483 :
1484 : Returns: True on success, false on failure
1485 :
1486 : ******************************************************************************/
1487 :
1488 0 : static bool CheckIsKMZ(const char *pszFilename)
1489 : {
1490 0 : char **papszFiles = VSIReadDir(pszFilename);
1491 0 : char **papszIter = papszFiles;
1492 0 : bool bFoundKML = false;
1493 0 : while (papszIter && *papszIter)
1494 : {
1495 0 : if (EQUAL(CPLGetExtensionSafe(*papszIter).c_str(), "kml"))
1496 : {
1497 0 : bFoundKML = true;
1498 0 : break;
1499 : }
1500 : else
1501 : {
1502 0 : CPLString osFilename(pszFilename);
1503 0 : osFilename += "/";
1504 0 : osFilename += *papszIter;
1505 0 : if (CheckIsKMZ(osFilename))
1506 : {
1507 0 : bFoundKML = true;
1508 0 : break;
1509 : }
1510 : }
1511 0 : papszIter++;
1512 : }
1513 0 : CSLDestroy(papszFiles);
1514 0 : return bFoundKML;
1515 : }
1516 :
1517 288 : int OGRLIBKMLDataSource::Open(const char *pszFilename, int bUpdateIn)
1518 : {
1519 288 : bUpdate = CPL_TO_BOOL(bUpdateIn);
1520 :
1521 : /***** dir *****/
1522 : VSIStatBufL sStatBuf;
1523 576 : if (!VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) &&
1524 288 : VSI_ISDIR(sStatBuf.st_mode))
1525 : {
1526 163 : return OpenDir(pszFilename, bUpdate);
1527 : }
1528 :
1529 : /***** kml *****/
1530 125 : if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kml"))
1531 : {
1532 119 : return OpenKml(pszFilename, bUpdate);
1533 : }
1534 :
1535 : /***** kmz *****/
1536 6 : if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kmz"))
1537 : {
1538 6 : return OpenKmz(pszFilename, bUpdate);
1539 : }
1540 :
1541 0 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
1542 0 : if (fp == nullptr)
1543 0 : return FALSE;
1544 :
1545 0 : char szBuffer[1024 + 1] = {};
1546 0 : const int nRead = static_cast<int>(VSIFReadL(szBuffer, 1, 1024, fp));
1547 0 : szBuffer[nRead] = 0;
1548 :
1549 0 : VSIFCloseL(fp);
1550 :
1551 : // Does it look like a zip file?
1552 0 : if (nRead == 1024 && szBuffer[0] == 0x50 && szBuffer[1] == 0x4B &&
1553 0 : szBuffer[2] == 0x03 && szBuffer[3] == 0x04)
1554 : {
1555 0 : CPLString osFilename("/vsizip/");
1556 0 : osFilename += pszFilename;
1557 0 : if (!CheckIsKMZ(osFilename))
1558 0 : return FALSE;
1559 :
1560 0 : return OpenKmz(pszFilename, bUpdate);
1561 : }
1562 :
1563 0 : if (strstr(szBuffer, "<kml>") || strstr(szBuffer, "<kml xmlns="))
1564 0 : return OpenKml(pszFilename, bUpdate);
1565 :
1566 0 : return FALSE;
1567 : }
1568 :
1569 : /************************************************************************/
1570 : /* IsValidPhoneNumber() */
1571 : /************************************************************************/
1572 :
1573 : // Very approximative validation of http://tools.ietf.org/html/rfc3966#page-6
1574 1 : static bool IsValidPhoneNumber(const char *pszPhoneNumber)
1575 : {
1576 1 : if (STARTS_WITH(pszPhoneNumber, "tel:"))
1577 1 : pszPhoneNumber += strlen("tel:");
1578 1 : char ch = '\0';
1579 1 : bool bDigitFound = false;
1580 1 : if (*pszPhoneNumber == '+')
1581 0 : pszPhoneNumber++;
1582 4 : while ((ch = *pszPhoneNumber) != '\0')
1583 : {
1584 3 : if (ch >= '0' && ch <= '9')
1585 3 : bDigitFound = true;
1586 0 : else if (ch == ';')
1587 0 : break;
1588 0 : else if (!(ch == '-' || ch == '.' || ch == '(' || ch == ')'))
1589 0 : return false;
1590 3 : pszPhoneNumber++;
1591 : }
1592 1 : return bDigitFound;
1593 : }
1594 :
1595 : /************************************************************************/
1596 : /* SetCommonOptions() */
1597 : /************************************************************************/
1598 :
1599 224 : void OGRLIBKMLDataSource::SetCommonOptions(ContainerPtr poKmlContainer,
1600 : CSLConstList papszOptions)
1601 : {
1602 224 : const char *l_pszName = CSLFetchNameValue(papszOptions, "NAME");
1603 224 : if (l_pszName != nullptr)
1604 5 : poKmlContainer->set_name(l_pszName);
1605 :
1606 224 : const char *pszVisibilility = CSLFetchNameValue(papszOptions, "VISIBILITY");
1607 224 : if (pszVisibilility != nullptr)
1608 2 : poKmlContainer->set_visibility(CPLTestBool(pszVisibilility));
1609 :
1610 224 : const char *pszOpen = CSLFetchNameValue(papszOptions, "OPEN");
1611 224 : if (pszOpen != nullptr)
1612 2 : poKmlContainer->set_open(CPLTestBool(pszOpen));
1613 :
1614 224 : const char *pszSnippet = CSLFetchNameValue(papszOptions, "SNIPPET");
1615 224 : if (pszSnippet != nullptr)
1616 : {
1617 4 : SnippetPtr poKmlSnippet = m_poKmlFactory->CreateSnippet();
1618 2 : poKmlSnippet->set_text(pszSnippet);
1619 2 : poKmlContainer->set_snippet(poKmlSnippet);
1620 : }
1621 :
1622 224 : const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
1623 224 : if (pszDescription != nullptr)
1624 2 : poKmlContainer->set_description(pszDescription);
1625 224 : }
1626 :
1627 : /************************************************************************/
1628 : /* ParseDocumentOptions() */
1629 : /************************************************************************/
1630 :
1631 105 : void OGRLIBKMLDataSource::ParseDocumentOptions(KmlPtr poKml,
1632 : DocumentPtr poKmlDocument)
1633 : {
1634 105 : if (poKmlDocument)
1635 : {
1636 : const char *pszDocumentId =
1637 102 : CSLFetchNameValueDef(m_papszOptions, "DOCUMENT_ID", "root_doc");
1638 102 : poKmlDocument->set_id(pszDocumentId);
1639 :
1640 : const char *pszAuthorName =
1641 102 : CSLFetchNameValue(m_papszOptions, "AUTHOR_NAME");
1642 : const char *pszAuthorURI =
1643 102 : CSLFetchNameValue(m_papszOptions, "AUTHOR_URI");
1644 : const char *pszAuthorEmail =
1645 102 : CSLFetchNameValue(m_papszOptions, "AUTHOR_EMAIL");
1646 102 : const char *pszLink = CSLFetchNameValue(m_papszOptions, "LINK");
1647 :
1648 102 : if (pszAuthorName != nullptr || pszAuthorURI != nullptr ||
1649 : pszAuthorEmail != nullptr)
1650 : {
1651 2 : kmldom::AtomAuthorPtr author = m_poKmlFactory->CreateAtomAuthor();
1652 1 : if (pszAuthorName != nullptr)
1653 1 : author->set_name(pszAuthorName);
1654 1 : if (pszAuthorURI != nullptr)
1655 : {
1656 : // Ad-hoc validation. The ABNF is horribly complicated:
1657 : // http://tools.ietf.org/search/rfc3987#page-7
1658 1 : if (STARTS_WITH(pszAuthorURI, "http://") ||
1659 0 : STARTS_WITH(pszAuthorURI, "https://"))
1660 : {
1661 1 : author->set_uri(pszAuthorURI);
1662 : }
1663 : else
1664 : {
1665 0 : CPLError(CE_Warning, CPLE_AppDefined,
1666 : "Invalid URI for AUTHOR_URI");
1667 : }
1668 : }
1669 1 : if (pszAuthorEmail != nullptr)
1670 : {
1671 1 : const char *pszArobase = strchr(pszAuthorEmail, '@');
1672 1 : if (pszArobase != nullptr &&
1673 1 : strchr(pszArobase + 1, '.') != nullptr)
1674 : {
1675 1 : author->set_email(pszAuthorEmail);
1676 : }
1677 : else
1678 : {
1679 0 : CPLError(CE_Warning, CPLE_AppDefined,
1680 : "Invalid email for AUTHOR_EMAIL");
1681 : }
1682 : }
1683 1 : poKmlDocument->set_atomauthor(author);
1684 : }
1685 :
1686 102 : if (pszLink != nullptr)
1687 : {
1688 2 : kmldom::AtomLinkPtr link = m_poKmlFactory->CreateAtomLink();
1689 1 : link->set_href(pszLink);
1690 1 : link->set_rel("related");
1691 1 : poKmlDocument->set_atomlink(link);
1692 : }
1693 :
1694 : const char *pszPhoneNumber =
1695 102 : CSLFetchNameValue(m_papszOptions, "PHONENUMBER");
1696 102 : if (pszPhoneNumber != nullptr)
1697 : {
1698 1 : if (IsValidPhoneNumber(pszPhoneNumber))
1699 : {
1700 1 : if (!STARTS_WITH(pszPhoneNumber, "tel:"))
1701 0 : poKmlDocument->set_phonenumber(
1702 : CPLSPrintf("tel:%s", pszPhoneNumber));
1703 : else
1704 1 : poKmlDocument->set_phonenumber(pszPhoneNumber);
1705 : }
1706 : else
1707 : {
1708 0 : CPLError(CE_Warning, CPLE_AppDefined, "Invalid phone number");
1709 : }
1710 : }
1711 :
1712 102 : SetCommonOptions(poKmlDocument, m_papszOptions);
1713 :
1714 : CPLString osListStyleType =
1715 204 : CSLFetchNameValueDef(m_papszOptions, "LISTSTYLE_TYPE", "");
1716 : CPLString osListStyleIconHref =
1717 102 : CSLFetchNameValueDef(m_papszOptions, "LISTSTYLE_ICON_HREF", "");
1718 102 : createkmlliststyle(m_poKmlFactory, pszDocumentId, poKmlDocument,
1719 : poKmlDocument, osListStyleType, osListStyleIconHref);
1720 : }
1721 :
1722 105 : if (poKml)
1723 : {
1724 105 : if (m_poKmlUpdate)
1725 : {
1726 : NetworkLinkControlPtr nlc =
1727 6 : m_poKmlFactory->CreateNetworkLinkControl();
1728 3 : poKml->set_networklinkcontrol(nlc);
1729 3 : if (m_poKmlUpdate->get_updateoperation_array_size() != 0)
1730 : {
1731 3 : nlc->set_update(m_poKmlUpdate);
1732 : }
1733 : }
1734 :
1735 : const char *pszNLCMinRefreshPeriod =
1736 105 : CSLFetchNameValue(m_papszOptions, "NLC_MINREFRESHPERIOD");
1737 : const char *pszNLCMaxSessionLength =
1738 105 : CSLFetchNameValue(m_papszOptions, "NLC_MAXSESSIONLENGTH");
1739 : const char *pszNLCCookie =
1740 105 : CSLFetchNameValue(m_papszOptions, "NLC_COOKIE");
1741 : const char *pszNLCMessage =
1742 105 : CSLFetchNameValue(m_papszOptions, "NLC_MESSAGE");
1743 : const char *pszNLCLinkName =
1744 105 : CSLFetchNameValue(m_papszOptions, "NLC_LINKNAME");
1745 : const char *pszNLCLinkDescription =
1746 105 : CSLFetchNameValue(m_papszOptions, "NLC_LINKDESCRIPTION");
1747 : const char *pszNLCLinkSnippet =
1748 105 : CSLFetchNameValue(m_papszOptions, "NLC_LINKSNIPPET");
1749 : const char *pszNLCExpires =
1750 105 : CSLFetchNameValue(m_papszOptions, "NLC_EXPIRES");
1751 :
1752 105 : if (pszNLCMinRefreshPeriod != nullptr ||
1753 102 : pszNLCMaxSessionLength != nullptr || pszNLCCookie != nullptr ||
1754 102 : pszNLCMessage != nullptr || pszNLCLinkName != nullptr ||
1755 102 : pszNLCLinkDescription != nullptr || pszNLCLinkSnippet != nullptr ||
1756 : pszNLCExpires != nullptr)
1757 : {
1758 6 : NetworkLinkControlPtr nlc = nullptr;
1759 3 : if (poKml->has_networklinkcontrol())
1760 : {
1761 0 : nlc = poKml->get_networklinkcontrol();
1762 : }
1763 : else
1764 : {
1765 3 : nlc = m_poKmlFactory->CreateNetworkLinkControl();
1766 3 : poKml->set_networklinkcontrol(nlc);
1767 : }
1768 3 : if (pszNLCMinRefreshPeriod != nullptr)
1769 : {
1770 3 : const double dfVal = CPLAtof(pszNLCMinRefreshPeriod);
1771 3 : if (dfVal >= 0)
1772 3 : nlc->set_minrefreshperiod(dfVal);
1773 : }
1774 3 : if (pszNLCMaxSessionLength != nullptr)
1775 : {
1776 3 : const double dfVal = CPLAtof(pszNLCMaxSessionLength);
1777 3 : nlc->set_maxsessionlength(dfVal);
1778 : }
1779 3 : if (pszNLCCookie != nullptr)
1780 : {
1781 3 : nlc->set_cookie(pszNLCCookie);
1782 : }
1783 3 : if (pszNLCMessage != nullptr)
1784 : {
1785 3 : nlc->set_message(pszNLCMessage);
1786 : }
1787 3 : if (pszNLCLinkName != nullptr)
1788 : {
1789 3 : nlc->set_linkname(pszNLCLinkName);
1790 : }
1791 3 : if (pszNLCLinkDescription != nullptr)
1792 : {
1793 3 : nlc->set_linkdescription(pszNLCLinkDescription);
1794 : }
1795 3 : if (pszNLCLinkSnippet != nullptr)
1796 : {
1797 : LinkSnippetPtr linksnippet =
1798 3 : m_poKmlFactory->CreateLinkSnippet();
1799 3 : linksnippet->set_text(pszNLCLinkSnippet);
1800 3 : nlc->set_linksnippet(std::move(linksnippet));
1801 : }
1802 3 : if (pszNLCExpires != nullptr)
1803 : {
1804 : OGRField sField;
1805 3 : if (OGRParseXMLDateTime(pszNLCExpires, &sField))
1806 : {
1807 3 : char *pszXMLDate = OGRGetXMLDateTime(&sField);
1808 3 : nlc->set_expires(pszXMLDate);
1809 3 : CPLFree(pszXMLDate);
1810 : }
1811 : }
1812 : }
1813 : }
1814 105 : }
1815 :
1816 : /******************************************************************************
1817 : Method to create a single file .kml ds.
1818 :
1819 : Args: pszFilename the datasource to create
1820 : papszOptions datasource creation options
1821 :
1822 : Returns: True on success, false on failure
1823 :
1824 : ******************************************************************************/
1825 :
1826 57 : int OGRLIBKMLDataSource::CreateKml(const char * /* pszFilename */,
1827 : char **papszOptions)
1828 : {
1829 57 : m_poKmlDSKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory, papszOptions);
1830 57 : if (osUpdateTargetHref.empty())
1831 : {
1832 112 : DocumentPtr poKmlDocument = m_poKmlFactory->CreateDocument();
1833 56 : m_poKmlDSKml->set_feature(poKmlDocument);
1834 56 : m_poKmlDSContainer = poKmlDocument;
1835 : }
1836 :
1837 57 : m_isKml = true;
1838 57 : bUpdated = true;
1839 :
1840 57 : return true;
1841 : }
1842 :
1843 : /******************************************************************************
1844 : Method to create a .kmz ds.
1845 :
1846 : Args: pszFilename the datasource to create
1847 : papszOptions datasource creation options
1848 :
1849 : Returns: True on success, false on failure
1850 :
1851 : ******************************************************************************/
1852 :
1853 8 : int OGRLIBKMLDataSource::CreateKmz(const char * /* pszFilename */,
1854 : char ** /* papszOptions */)
1855 : {
1856 : /***** create the doc.kml *****/
1857 8 : if (osUpdateTargetHref.empty())
1858 : {
1859 : const char *pszUseDocKml =
1860 7 : CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
1861 :
1862 7 : if (CPLTestBool(pszUseDocKml))
1863 : {
1864 6 : m_poKmlDocKml = m_poKmlFactory->CreateDocument();
1865 : }
1866 : }
1867 :
1868 : // Layers are written in a layers/ subdirectory, hence ../style
1869 : // to access style/style.kml
1870 8 : m_osStylePath = "../style/style.kml";
1871 :
1872 8 : m_isKmz = true;
1873 8 : bUpdated = true;
1874 :
1875 8 : return TRUE;
1876 : }
1877 :
1878 : /******************************************************************************
1879 : Method to create a dir datasource.
1880 :
1881 : Args: pszFilename the datasource to create
1882 : papszOptions datasource creation options
1883 :
1884 : Returns: True on success, false on failure
1885 :
1886 : ******************************************************************************/
1887 :
1888 38 : int OGRLIBKMLDataSource::CreateDir(const char *pszFilename,
1889 : char ** /* papszOptions */)
1890 : {
1891 38 : if (VSIMkdir(pszFilename, 0755))
1892 : {
1893 1 : CPLError(CE_Failure, CPLE_AppDefined,
1894 : "ERROR Creating dir: %s for KML datasource", pszFilename);
1895 1 : return FALSE;
1896 : }
1897 :
1898 37 : m_isDir = true;
1899 37 : bUpdated = true;
1900 :
1901 37 : if (osUpdateTargetHref.empty())
1902 : {
1903 : const char *pszUseDocKml =
1904 36 : CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
1905 :
1906 36 : if (CPLTestBool(pszUseDocKml))
1907 : {
1908 36 : m_poKmlDocKml = m_poKmlFactory->CreateDocument();
1909 : }
1910 : }
1911 :
1912 37 : m_osStylePath = "style.kml";
1913 :
1914 37 : return TRUE;
1915 : }
1916 :
1917 : /******************************************************************************
1918 : Method to create a datasource.
1919 :
1920 : Args: pszFilename the datasource to create
1921 : papszOptions datasource creation options
1922 :
1923 : Returns: True on success, false on failure
1924 :
1925 : env vars:
1926 : LIBKML_USE_DOC.KML default: yes
1927 :
1928 : ******************************************************************************/
1929 :
1930 103 : int OGRLIBKMLDataSource::Create(const char *pszFilename, char **papszOptions)
1931 : {
1932 103 : if (strcmp(pszFilename, "/dev/stdout") == 0)
1933 0 : pszFilename = "/vsistdout/";
1934 :
1935 103 : SetDescription(pszFilename);
1936 103 : bUpdate = true;
1937 :
1938 : osUpdateTargetHref =
1939 103 : CSLFetchNameValueDef(papszOptions, "UPDATE_TARGETHREF", "");
1940 103 : if (!osUpdateTargetHref.empty())
1941 : {
1942 3 : m_poKmlUpdate = m_poKmlFactory->CreateUpdate();
1943 3 : m_poKmlUpdate->set_targethref(osUpdateTargetHref.c_str());
1944 : }
1945 :
1946 103 : m_papszOptions = CSLDuplicate(papszOptions);
1947 :
1948 : /***** kml *****/
1949 309 : if (strcmp(pszFilename, "/vsistdout/") == 0 ||
1950 206 : STARTS_WITH(pszFilename, "/vsigzip/") ||
1951 206 : EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kml"))
1952 57 : return CreateKml(pszFilename, papszOptions);
1953 :
1954 : /***** kmz *****/
1955 46 : if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kmz"))
1956 8 : return CreateKmz(pszFilename, papszOptions);
1957 :
1958 : /***** dir *****/
1959 38 : return CreateDir(pszFilename, papszOptions);
1960 : }
1961 :
1962 : /******************************************************************************
1963 : Method to get a layer by index.
1964 :
1965 : Args: iLayer the index of the layer to get
1966 :
1967 : Returns: pointer to the layer, or NULL if the layer does not exist
1968 :
1969 : ******************************************************************************/
1970 :
1971 95 : OGRLayer *OGRLIBKMLDataSource::GetLayer(int iLayer)
1972 : {
1973 95 : if (iLayer < 0 || iLayer >= nLayers)
1974 4 : return nullptr;
1975 :
1976 91 : return papoLayers[iLayer];
1977 : }
1978 :
1979 : /******************************************************************************
1980 : Method to get a layer by name.
1981 :
1982 : Args: pszname name of the layer to get
1983 :
1984 : Returns: pointer to the layer, or NULL if the layer does not exist
1985 :
1986 : ******************************************************************************/
1987 :
1988 613 : OGRLayer *OGRLIBKMLDataSource::GetLayerByName(const char *pszName)
1989 : {
1990 613 : auto oIter = m_oMapLayers.find(CPLString(pszName).toupper());
1991 613 : if (oIter != m_oMapLayers.end())
1992 233 : return oIter->second;
1993 :
1994 380 : return nullptr;
1995 : }
1996 :
1997 : /******************************************************************************
1998 : Method to DeleteLayers in a .kml datasource.
1999 :
2000 : Args: iLayer index of the layer to delete
2001 :
2002 : Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure
2003 :
2004 : ******************************************************************************/
2005 :
2006 0 : OGRErr OGRLIBKMLDataSource::DeleteLayerKml(int iLayer)
2007 : {
2008 0 : OGRLIBKMLLayer *poOgrLayer = (OGRLIBKMLLayer *)papoLayers[iLayer];
2009 :
2010 : /***** loop over the features *****/
2011 :
2012 0 : const size_t nKmlFeatures = m_poKmlDSContainer->get_feature_array_size();
2013 :
2014 0 : for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
2015 : {
2016 : FeaturePtr poKmlFeat =
2017 0 : m_poKmlDSContainer->get_feature_array_at(iKmlFeature);
2018 :
2019 0 : if (poKmlFeat == poOgrLayer->GetKmlLayer())
2020 : {
2021 0 : m_poKmlDSContainer->DeleteFeatureAt(iKmlFeature);
2022 0 : break;
2023 : }
2024 : }
2025 :
2026 0 : return OGRERR_NONE;
2027 : }
2028 :
2029 : /******************************************************************************
2030 : Method to DeleteLayers in a .kmz datasource.
2031 :
2032 : Args: iLayer index of the layer to delete
2033 :
2034 : Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure
2035 :
2036 : ******************************************************************************/
2037 :
2038 16 : OGRErr OGRLIBKMLDataSource::DeleteLayerKmz(int iLayer)
2039 : {
2040 16 : OGRLIBKMLLayer *poOgrLayer = papoLayers[iLayer];
2041 :
2042 16 : const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
2043 :
2044 16 : if (CPLTestBool(pszUseDocKml) && m_poKmlDocKml)
2045 : {
2046 : /***** loop over the features *****/
2047 16 : const size_t nKmlFeatures = m_poKmlDocKml->get_feature_array_size();
2048 :
2049 16 : for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
2050 : {
2051 : FeaturePtr poKmlFeat =
2052 16 : m_poKmlDocKml->get_feature_array_at(iKmlFeature);
2053 :
2054 16 : if (poKmlFeat->IsA(kmldom::Type_NetworkLink))
2055 : {
2056 16 : NetworkLinkPtr poKmlNetworkLink = AsNetworkLink(poKmlFeat);
2057 :
2058 : /***** does it have a link? *****/
2059 16 : if (poKmlNetworkLink->has_link())
2060 : {
2061 16 : LinkPtr poKmlLink = poKmlNetworkLink->get_link();
2062 :
2063 : /***** does the link have a href? *****/
2064 16 : if (poKmlLink->has_href())
2065 : {
2066 16 : const kmlengine::Href oKmlHref(poKmlLink->get_href());
2067 :
2068 : /***** is the link relative? *****/
2069 16 : if (oKmlHref.IsRelativePath())
2070 : {
2071 16 : if (EQUAL(oKmlHref.get_path().c_str(),
2072 : poOgrLayer->GetFileName()))
2073 : {
2074 16 : m_poKmlDocKml->DeleteFeatureAt(iKmlFeature);
2075 16 : break;
2076 : }
2077 : }
2078 : }
2079 : }
2080 : }
2081 : }
2082 : }
2083 :
2084 16 : return OGRERR_NONE;
2085 : }
2086 :
2087 : /******************************************************************************
2088 : Method to delete a layer in a datasource.
2089 :
2090 : Args: iLayer index of the layer to delete
2091 :
2092 : Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure
2093 :
2094 : ******************************************************************************/
2095 :
2096 16 : OGRErr OGRLIBKMLDataSource::DeleteLayer(int iLayer)
2097 : {
2098 16 : if (!bUpdate)
2099 0 : return OGRERR_UNSUPPORTED_OPERATION;
2100 :
2101 16 : if (iLayer >= nLayers)
2102 0 : return OGRERR_FAILURE;
2103 :
2104 16 : if (IsKml())
2105 : {
2106 0 : DeleteLayerKml(iLayer);
2107 : }
2108 16 : else if (IsKmz())
2109 : {
2110 0 : DeleteLayerKmz(iLayer);
2111 : }
2112 16 : else if (IsDir())
2113 : {
2114 16 : DeleteLayerKmz(iLayer);
2115 :
2116 : /***** delete the file the layer corresponds to *****/
2117 : const std::string osFilePath = CPLFormFilenameSafe(
2118 32 : GetDescription(), papoLayers[iLayer]->GetFileName(), nullptr);
2119 : VSIStatBufL oStatBufL;
2120 16 : if (!VSIStatL(osFilePath.c_str(), &oStatBufL))
2121 : {
2122 0 : if (VSIUnlink(osFilePath.c_str()))
2123 : {
2124 0 : CPLError(CE_Failure, CPLE_AppDefined,
2125 : "ERROR Deleting Layer %s from filesystem as %s",
2126 0 : papoLayers[iLayer]->GetName(), osFilePath.c_str());
2127 : }
2128 : }
2129 : }
2130 :
2131 16 : m_oMapLayers.erase(CPLString(papoLayers[iLayer]->GetName()).toupper());
2132 16 : delete papoLayers[iLayer];
2133 16 : memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
2134 16 : sizeof(void *) * (nLayers - iLayer - 1));
2135 16 : nLayers--;
2136 16 : bUpdated = true;
2137 :
2138 16 : return OGRERR_NONE;
2139 : }
2140 :
2141 : /******************************************************************************
2142 : Method to create a layer in a single file .kml.
2143 :
2144 : Args: pszLayerName name of the layer to create
2145 : poOgrSRS the SRS of the layer
2146 : eGType the layers geometry type
2147 : papszOptions layer creation options
2148 :
2149 : Returns: return a pointer to the new layer or NULL on failure
2150 :
2151 : ******************************************************************************/
2152 :
2153 61 : OGRLIBKMLLayer *OGRLIBKMLDataSource::CreateLayerKml(
2154 : const char *pszLayerName, const OGRSpatialReference *poSRS,
2155 : OGRwkbGeometryType eGType, CSLConstList papszOptions)
2156 : {
2157 61 : ContainerPtr poKmlLayerContainer = nullptr;
2158 :
2159 61 : if (m_poKmlDSContainer)
2160 : {
2161 60 : if (CPLFetchBool(papszOptions, "FOLDER", false))
2162 2 : poKmlLayerContainer = m_poKmlFactory->CreateFolder();
2163 : else
2164 58 : poKmlLayerContainer = m_poKmlFactory->CreateDocument();
2165 120 : poKmlLayerContainer->set_id(
2166 120 : OGRLIBKMLGetSanitizedNCName(pszLayerName).c_str());
2167 :
2168 60 : m_poKmlDSContainer->add_feature(poKmlLayerContainer);
2169 : }
2170 :
2171 : /***** create the layer *****/
2172 : OGRLIBKMLLayer *poOgrLayer =
2173 61 : AddLayer(pszLayerName, eGType, poSRS, this, nullptr,
2174 61 : poKmlLayerContainer, "", TRUE, bUpdate, 1);
2175 :
2176 : /***** add the layer name as a <Name> *****/
2177 61 : if (poKmlLayerContainer)
2178 60 : poKmlLayerContainer->set_name(pszLayerName);
2179 1 : else if (CPLFetchBool(papszOptions, "FOLDER", false))
2180 : {
2181 0 : poOgrLayer->SetUpdateIsFolder(TRUE);
2182 : }
2183 :
2184 122 : return poOgrLayer;
2185 : }
2186 :
2187 : /******************************************************************************
2188 : Method to create a layer in a .kmz or dir.
2189 :
2190 : Args: pszLayerName name of the layer to create
2191 : poOgrSRS the SRS of the layer
2192 : eGType the layers geometry type
2193 : papszOptions layer creation options
2194 :
2195 : Returns: return a pointer to the new layer or NULL on failure
2196 :
2197 : ******************************************************************************/
2198 :
2199 64 : OGRLIBKMLLayer *OGRLIBKMLDataSource::CreateLayerKmz(
2200 : const char *pszLayerName, const OGRSpatialReference *poSRS,
2201 : OGRwkbGeometryType eGType, CSLConstList papszOptions)
2202 : {
2203 64 : DocumentPtr poKmlDocument = nullptr;
2204 :
2205 64 : if (!m_poKmlUpdate)
2206 : {
2207 : /***** add a network link to doc.kml *****/
2208 : const char *pszUseDocKml =
2209 62 : CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
2210 :
2211 62 : if (CPLTestBool(pszUseDocKml) && m_poKmlDocKml)
2212 : {
2213 61 : poKmlDocument = AsDocument(m_poKmlDocKml);
2214 :
2215 122 : NetworkLinkPtr poKmlNetLink = m_poKmlFactory->CreateNetworkLink();
2216 61 : poKmlNetLink->set_name(
2217 : CSLFetchNameValueDef(papszOptions, "NAME", pszLayerName));
2218 :
2219 122 : LinkPtr poKmlLink = m_poKmlFactory->CreateLink();
2220 :
2221 61 : std::string oHref;
2222 61 : if (IsKmz())
2223 8 : oHref.append("layers/");
2224 61 : oHref.append(pszLayerName);
2225 61 : oHref.append(".kml");
2226 61 : poKmlLink->set_href(oHref);
2227 :
2228 61 : poKmlNetLink->set_link(poKmlLink);
2229 61 : poKmlDocument->add_feature(poKmlNetLink);
2230 : }
2231 :
2232 : /***** create the layer *****/
2233 :
2234 62 : poKmlDocument = m_poKmlFactory->CreateDocument();
2235 186 : poKmlDocument->set_id(
2236 124 : OGRLIBKMLGetSanitizedNCName(pszLayerName).c_str());
2237 : }
2238 :
2239 : OGRLIBKMLLayer *poOgrLayer =
2240 64 : AddLayer(pszLayerName, eGType, poSRS, this, nullptr, poKmlDocument,
2241 64 : CPLFormFilenameSafe(nullptr, pszLayerName, ".kml").c_str(),
2242 64 : TRUE, bUpdate, 1);
2243 :
2244 : /***** add the layer name as a <Name> *****/
2245 64 : if (!m_poKmlUpdate)
2246 : {
2247 62 : poKmlDocument->set_name(pszLayerName);
2248 : }
2249 :
2250 128 : return poOgrLayer;
2251 : }
2252 :
2253 : /******************************************************************************
2254 : ICreateLayer()
2255 :
2256 : Args: pszLayerName name of the layer to create
2257 : poOgrSRS the SRS of the layer
2258 : eGType the layers geometry type
2259 : papszOptions layer creation options
2260 :
2261 : Returns: return a pointer to the new layer or NULL on failure
2262 :
2263 : ******************************************************************************/
2264 :
2265 : OGRLayer *
2266 125 : OGRLIBKMLDataSource::ICreateLayer(const char *pszLayerName,
2267 : const OGRGeomFieldDefn *poGeomFieldDefn,
2268 : CSLConstList papszOptions)
2269 : {
2270 125 : if (!bUpdate)
2271 0 : return nullptr;
2272 :
2273 125 : if ((IsKmz() || IsDir()) && EQUAL(pszLayerName, "doc"))
2274 : {
2275 0 : CPLError(CE_Failure, CPLE_AppDefined,
2276 : "'doc' is an invalid layer name in a KMZ file");
2277 0 : return nullptr;
2278 : }
2279 :
2280 125 : const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
2281 : const auto poOgrSRS =
2282 125 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
2283 :
2284 125 : OGRLIBKMLLayer *poOgrLayer = nullptr;
2285 :
2286 : /***** kml DS *****/
2287 125 : if (IsKml())
2288 : {
2289 : poOgrLayer =
2290 61 : CreateLayerKml(pszLayerName, poOgrSRS, eGType, papszOptions);
2291 : }
2292 64 : else if (IsKmz() || IsDir())
2293 : {
2294 : poOgrLayer =
2295 64 : CreateLayerKmz(pszLayerName, poOgrSRS, eGType, papszOptions);
2296 : }
2297 :
2298 : const char *pszLookatLongitude =
2299 125 : CSLFetchNameValue(papszOptions, "LOOKAT_LONGITUDE");
2300 : const char *pszLookatLatitude =
2301 125 : CSLFetchNameValue(papszOptions, "LOOKAT_LATITUDE");
2302 : const char *pszLookatAltitude =
2303 125 : CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDE");
2304 : const char *pszLookatHeading =
2305 125 : CSLFetchNameValue(papszOptions, "LOOKAT_HEADING");
2306 125 : const char *pszLookatTilt = CSLFetchNameValue(papszOptions, "LOOKAT_TILT");
2307 : const char *pszLookatRange =
2308 125 : CSLFetchNameValue(papszOptions, "LOOKAT_RANGE");
2309 : const char *pszLookatAltitudeMode =
2310 125 : CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDEMODE");
2311 125 : if (poOgrLayer != nullptr && pszLookatLongitude != nullptr &&
2312 2 : pszLookatLatitude != nullptr && pszLookatRange != nullptr)
2313 : {
2314 2 : poOgrLayer->SetLookAt(pszLookatLongitude, pszLookatLatitude,
2315 : pszLookatAltitude, pszLookatHeading,
2316 : pszLookatTilt, pszLookatRange,
2317 : pszLookatAltitudeMode);
2318 : }
2319 : else
2320 : {
2321 : const char *pszCameraLongitude =
2322 123 : CSLFetchNameValue(papszOptions, "CAMERA_LONGITUDE");
2323 : const char *pszCameraLatitude =
2324 123 : CSLFetchNameValue(papszOptions, "CAMERA_LATITUDE");
2325 : const char *pszCameraAltitude =
2326 123 : CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDE");
2327 : const char *pszCameraHeading =
2328 123 : CSLFetchNameValue(papszOptions, "CAMERA_HEADING");
2329 : const char *pszCameraTilt =
2330 123 : CSLFetchNameValue(papszOptions, "CAMERA_TILT");
2331 : const char *pszCameraRoll =
2332 123 : CSLFetchNameValue(papszOptions, "CAMERA_ROLL");
2333 : const char *pszCameraAltitudeMode =
2334 123 : CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDEMODE");
2335 123 : if (poOgrLayer != nullptr && pszCameraLongitude != nullptr &&
2336 1 : pszCameraLatitude != nullptr && pszCameraAltitude != nullptr &&
2337 : pszCameraAltitudeMode != nullptr)
2338 : {
2339 1 : poOgrLayer->SetCamera(pszCameraLongitude, pszCameraLatitude,
2340 : pszCameraAltitude, pszCameraHeading,
2341 : pszCameraTilt, pszCameraRoll,
2342 : pszCameraAltitudeMode);
2343 : }
2344 : }
2345 :
2346 : const char *pszRegionAdd =
2347 125 : CSLFetchNameValueDef(papszOptions, "ADD_REGION", "FALSE");
2348 125 : const char *pszRegionXMin = CSLFetchNameValue(papszOptions, "REGION_XMIN");
2349 125 : const char *pszRegionYMin = CSLFetchNameValue(papszOptions, "REGION_YMIN");
2350 125 : const char *pszRegionXMax = CSLFetchNameValue(papszOptions, "REGION_XMAX");
2351 125 : const char *pszRegionYMax = CSLFetchNameValue(papszOptions, "REGION_YMAX");
2352 : const char *pszRegionMinLodPixels =
2353 125 : CSLFetchNameValueDef(papszOptions, "REGION_MIN_LOD_PIXELS", "256");
2354 : const char *pszRegionMaxLodPixels =
2355 125 : CSLFetchNameValueDef(papszOptions, "REGION_MAX_LOD_PIXELS", "-1");
2356 : const char *pszRegionMinFadeExtent =
2357 125 : CSLFetchNameValueDef(papszOptions, "REGION_MIN_FADE_EXTENT", "0");
2358 : const char *pszRegionMaxFadeExtent =
2359 125 : CSLFetchNameValueDef(papszOptions, "REGION_MAX_FADE_EXTENT", "0");
2360 125 : if (poOgrLayer != nullptr && CPLTestBool(pszRegionAdd))
2361 : {
2362 2 : poOgrLayer->SetWriteRegion(
2363 : CPLAtof(pszRegionMinLodPixels), CPLAtof(pszRegionMaxLodPixels),
2364 : CPLAtof(pszRegionMinFadeExtent), CPLAtof(pszRegionMaxFadeExtent));
2365 2 : if (pszRegionXMin != nullptr && pszRegionYMin != nullptr &&
2366 1 : pszRegionXMax != nullptr && pszRegionYMax != nullptr)
2367 : {
2368 1 : const double xmin = CPLAtof(pszRegionXMin);
2369 1 : const double ymin = CPLAtof(pszRegionYMin);
2370 1 : const double xmax = CPLAtof(pszRegionXMax);
2371 1 : const double ymax = CPLAtof(pszRegionYMax);
2372 1 : if (xmin < xmax && ymin < ymax)
2373 1 : poOgrLayer->SetRegionBounds(xmin, ymin, xmax, ymax);
2374 : }
2375 : }
2376 :
2377 125 : const char *pszSOHref = CSLFetchNameValue(papszOptions, "SO_HREF");
2378 125 : const char *pszSOName = CSLFetchNameValue(papszOptions, "SO_NAME");
2379 : const char *pszSODescription =
2380 125 : CSLFetchNameValue(papszOptions, "SO_DESCRIPTION");
2381 125 : const char *pszSOOverlayX = CSLFetchNameValue(papszOptions, "SO_OVERLAY_X");
2382 125 : const char *pszSOOverlayY = CSLFetchNameValue(papszOptions, "SO_OVERLAY_Y");
2383 : const char *pszSOOverlayXUnits =
2384 125 : CSLFetchNameValue(papszOptions, "SO_OVERLAY_XUNITS");
2385 : const char *pszSOOverlayYUnits =
2386 125 : CSLFetchNameValue(papszOptions, "SO_OVERLAY_YUNITS");
2387 125 : const char *pszSOScreenX = CSLFetchNameValue(papszOptions, "SO_SCREEN_X");
2388 125 : const char *pszSOScreenY = CSLFetchNameValue(papszOptions, "SO_SCREEN_Y");
2389 : const char *pszSOScreenXUnits =
2390 125 : CSLFetchNameValue(papszOptions, "SO_SCREEN_XUNITS");
2391 : const char *pszSOScreenYUnits =
2392 125 : CSLFetchNameValue(papszOptions, "SO_SCREEN_YUNITS");
2393 125 : const char *pszSOSizeX = CSLFetchNameValue(papszOptions, "SO_SIZE_X");
2394 125 : const char *pszSOSizeY = CSLFetchNameValue(papszOptions, "SO_SIZE_Y");
2395 : const char *pszSOSizeXUnits =
2396 125 : CSLFetchNameValue(papszOptions, "SO_SIZE_XUNITS");
2397 : const char *pszSOSizeYUnits =
2398 125 : CSLFetchNameValue(papszOptions, "SO_SIZE_YUNITS");
2399 125 : if (poOgrLayer != nullptr && pszSOHref != nullptr)
2400 : {
2401 2 : poOgrLayer->SetScreenOverlay(
2402 : pszSOHref, pszSOName, pszSODescription, pszSOOverlayX,
2403 : pszSOOverlayY, pszSOOverlayXUnits, pszSOOverlayYUnits, pszSOScreenX,
2404 : pszSOScreenY, pszSOScreenXUnits, pszSOScreenYUnits, pszSOSizeX,
2405 : pszSOSizeY, pszSOSizeXUnits, pszSOSizeYUnits);
2406 : }
2407 :
2408 : const char *pszListStyleType =
2409 125 : CSLFetchNameValue(papszOptions, "LISTSTYLE_TYPE");
2410 : const char *pszListStyleIconHref =
2411 125 : CSLFetchNameValue(papszOptions, "LISTSTYLE_ICON_HREF");
2412 125 : if (poOgrLayer != nullptr)
2413 : {
2414 125 : poOgrLayer->SetListStyle(pszListStyleType, pszListStyleIconHref);
2415 : }
2416 :
2417 125 : if (poOgrLayer != nullptr && poOgrLayer->GetKmlLayer())
2418 : {
2419 122 : SetCommonOptions(poOgrLayer->GetKmlLayer(), papszOptions);
2420 : }
2421 :
2422 : /***** mark the dataset as updated *****/
2423 125 : if (poOgrLayer)
2424 125 : bUpdated = true;
2425 :
2426 125 : return poOgrLayer;
2427 : }
2428 :
2429 : /******************************************************************************
2430 : Method to get a datasources style table.
2431 :
2432 : Args: none
2433 :
2434 : Returns: pointer to the datasources style table, or NULL if it does
2435 : not have one
2436 :
2437 : ******************************************************************************/
2438 :
2439 7 : OGRStyleTable *OGRLIBKMLDataSource::GetStyleTable()
2440 : {
2441 7 : return m_poStyleTable;
2442 : }
2443 :
2444 : /******************************************************************************
2445 : Method to write a style table to a single file .kml ds.
2446 :
2447 : Args: poStyleTable pointer to the style table to add
2448 :
2449 : Returns: nothing
2450 :
2451 : ******************************************************************************/
2452 :
2453 4 : void OGRLIBKMLDataSource::SetStyleTable2Kml(OGRStyleTable *poStyleTable)
2454 : {
2455 4 : if (!m_poKmlDSContainer)
2456 0 : return;
2457 :
2458 : /***** delete all the styles *****/
2459 :
2460 4 : DocumentPtr poKmlDocument = AsDocument(m_poKmlDSContainer);
2461 : int nKmlStyles =
2462 4 : static_cast<int>(poKmlDocument->get_styleselector_array_size());
2463 :
2464 4 : for (int iKmlStyle = nKmlStyles - 1; iKmlStyle >= 0; iKmlStyle--)
2465 : {
2466 0 : poKmlDocument->DeleteStyleSelectorAt(iKmlStyle);
2467 : }
2468 :
2469 : /***** add the new style table to the document *****/
2470 :
2471 4 : styletable2kml(poStyleTable, m_poKmlFactory, AsContainer(poKmlDocument),
2472 : m_papszOptions);
2473 : }
2474 :
2475 : /******************************************************************************
2476 : Method to write a style table to a kmz ds.
2477 :
2478 : Args: poStyleTable pointer to the style table to add
2479 :
2480 : Returns: nothing
2481 :
2482 : ******************************************************************************/
2483 :
2484 1 : void OGRLIBKMLDataSource::SetStyleTable2Kmz(OGRStyleTable *poStyleTable)
2485 : {
2486 1 : if (m_poKmlStyleKml || poStyleTable != nullptr)
2487 : {
2488 : /***** replace the style document with a new one *****/
2489 :
2490 1 : m_poKmlStyleKml = m_poKmlFactory->CreateDocument();
2491 1 : m_poKmlStyleKml->set_id("styleId");
2492 :
2493 1 : styletable2kml(poStyleTable, m_poKmlFactory, m_poKmlStyleKml);
2494 : }
2495 1 : }
2496 :
2497 : /******************************************************************************
2498 : Method to write a style table to a datasource.
2499 :
2500 : Args: poStyleTable pointer to the style table to add
2501 :
2502 : Returns: nothing
2503 :
2504 : Note: This method assumes ownership of the style table.
2505 :
2506 : ******************************************************************************/
2507 :
2508 5 : void OGRLIBKMLDataSource::SetStyleTableDirectly(OGRStyleTable *poStyleTable)
2509 : {
2510 5 : if (!bUpdate)
2511 0 : return;
2512 :
2513 5 : if (m_poStyleTable)
2514 0 : delete m_poStyleTable;
2515 :
2516 5 : m_poStyleTable = poStyleTable;
2517 :
2518 : /***** a kml datasource? *****/
2519 5 : if (IsKml())
2520 4 : SetStyleTable2Kml(m_poStyleTable);
2521 :
2522 1 : else if (IsKmz() || IsDir())
2523 1 : SetStyleTable2Kmz(m_poStyleTable);
2524 :
2525 5 : bUpdated = true;
2526 : }
2527 :
2528 : /******************************************************************************
2529 : Method to write a style table to a datasource.
2530 :
2531 : Args: poStyleTable pointer to the style table to add
2532 :
2533 : Returns: nothing
2534 :
2535 : Note: This method copies the style table, and the user will still be
2536 : responsible for its destruction.
2537 :
2538 : ******************************************************************************/
2539 :
2540 5 : void OGRLIBKMLDataSource::SetStyleTable(OGRStyleTable *poStyleTable)
2541 : {
2542 5 : if (!bUpdate)
2543 0 : return;
2544 :
2545 5 : if (poStyleTable)
2546 4 : SetStyleTableDirectly(poStyleTable->Clone());
2547 : else
2548 1 : SetStyleTableDirectly(nullptr);
2549 : }
2550 :
2551 : /******************************************************************************
2552 : Test if capability is available.
2553 :
2554 : Args: pszCap datasource capability name to test
2555 :
2556 : Returns: TRUE or FALSE
2557 :
2558 : ODsCCreateLayer: True if this datasource can create new layers.
2559 : ODsCDeleteLayer: True if this datasource can delete existing layers.
2560 :
2561 : ******************************************************************************/
2562 :
2563 104 : int OGRLIBKMLDataSource::TestCapability(const char *pszCap)
2564 : {
2565 104 : if (EQUAL(pszCap, ODsCCreateLayer))
2566 38 : return bUpdate;
2567 66 : else if (EQUAL(pszCap, ODsCDeleteLayer))
2568 18 : return bUpdate;
2569 48 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
2570 0 : return bUpdate;
2571 48 : else if (EQUAL(pszCap, ODsCZGeometries))
2572 22 : return TRUE;
2573 :
2574 26 : return FALSE;
2575 : }
|