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 : auto nNewPos = oKml.find(pszStartTag, nPos);
158 122 : if (nNewPos != std::string::npos)
159 : {
160 1 : pszEndTag = "</MultiPolygon>";
161 : }
162 : else
163 : {
164 121 : pszStartTag = "<MultiLineString>";
165 121 : nNewPos = oKml.find(pszStartTag, nPos);
166 121 : if (nNewPos != std::string::npos)
167 : {
168 1 : pszEndTag = "</MultiLineString>";
169 : }
170 : else
171 : {
172 120 : pszStartTag = "<MultiPoint>";
173 120 : nNewPos = oKml.find(pszStartTag, nPos);
174 120 : if (nNewPos != std::string::npos)
175 1 : pszEndTag = "</MultiPoint>";
176 : else
177 119 : break;
178 : }
179 : }
180 3 : nPos = nNewPos;
181 3 : oKml.replace(nPos, strlen(pszStartTag), "<MultiGeometry>");
182 3 : nPos = oKml.find(pszEndTag, nPos);
183 3 : if (nPos == std::string::npos)
184 0 : break;
185 3 : oKml.replace(nPos, strlen(pszEndTag), "</MultiGeometry>");
186 3 : }
187 119 : }
188 :
189 : /************************************************************************/
190 : /* OGRLIBKMLRemoveSpaces() */
191 : /************************************************************************/
192 :
193 456 : static void OGRLIBKMLRemoveSpaces(std::string &osKml,
194 : const std::string &osNeedle)
195 : {
196 456 : size_t nPos = 0;
197 1368 : const std::string osLtNeedle(std::string("<").append(osNeedle));
198 912 : std::string osTmp;
199 912 : std::string osRet;
200 : while (true)
201 : {
202 545 : auto nPosNew = osKml.find(osLtNeedle, nPos);
203 545 : if (nPosNew == std::string::npos)
204 : {
205 456 : osRet.append(osKml, nPos);
206 456 : break;
207 : }
208 89 : const size_t nPosOri = nPosNew;
209 89 : nPosNew = osKml.find(">\n", nPosNew);
210 89 : if (nPosNew == std::string::npos || nPosNew + 2 == osKml.size())
211 : {
212 0 : osRet.append(osKml, nPos);
213 0 : break;
214 : }
215 : // Skip \n character
216 89 : osRet.append(osKml, nPos, nPosNew - nPos + 1);
217 89 : nPos = nPosNew + 2;
218 :
219 : // Remove leading spaces of " </{osNeedle}>"
220 89 : osTmp.clear();
221 1077 : for (size_t nPosTmp = nPosOri - 1; osKml[nPosTmp] == ' '; nPosTmp--)
222 : {
223 988 : osTmp += ' ';
224 : }
225 89 : osTmp += "</";
226 89 : osTmp += osNeedle;
227 89 : osTmp += '>';
228 89 : nPosNew = osKml.find(osTmp, nPos);
229 89 : if (nPosNew != std::string::npos)
230 : {
231 89 : osRet.append(osKml, nPos, nPosNew - nPos);
232 89 : osRet += "</";
233 89 : osRet += osNeedle;
234 89 : osRet += '>';
235 89 : nPos = nPosNew + osTmp.size();
236 : }
237 : else
238 : {
239 0 : osRet.append(osKml, nPos);
240 0 : break;
241 : }
242 89 : }
243 456 : osKml = std::move(osRet);
244 456 : }
245 :
246 : /************************************************************************/
247 : /* OGRLIBKMLPostProcessOutput() */
248 : /************************************************************************/
249 :
250 : // Substitute deprecated <Snippet> by <snippet> since libkml currently
251 : // only supports Snippet but ogckml22.xsd has deprecated it in favor of snippet.
252 152 : static void OGRLIBKMLPostProcessOutput(std::string &oKml)
253 : {
254 : // Manually add <?xml> node since libkml does not produce it currently
255 : // and this is useful in some circumstances (#5407).
256 152 : if (!(oKml.size() >= 2 && oKml[0] == '<' && oKml[1] == '?'))
257 152 : oKml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + oKml;
258 :
259 152 : size_t nPos = 0;
260 : while (true)
261 : {
262 155 : nPos = oKml.find("<Snippet>", nPos);
263 155 : if (nPos == std::string::npos)
264 : {
265 152 : break;
266 : }
267 3 : oKml[nPos + 1] = 's';
268 3 : nPos = oKml.find("</Snippet>", nPos);
269 3 : if (nPos == std::string::npos)
270 : {
271 0 : break;
272 : }
273 3 : oKml[nPos + 2] = 's';
274 : }
275 :
276 : // Fix indentation problems.
277 152 : OGRLIBKMLRemoveSpaces(oKml, "snippet");
278 152 : OGRLIBKMLRemoveSpaces(oKml, "linkSnippet");
279 152 : OGRLIBKMLRemoveSpaces(oKml, "SimpleData");
280 152 : }
281 :
282 : /******************************************************************************
283 : Method to write a single file ds .kml at ds destroy.
284 :
285 : Args: none
286 :
287 : Returns: nothing
288 :
289 : ******************************************************************************/
290 :
291 61 : bool OGRLIBKMLDataSource::WriteKml()
292 : {
293 122 : std::string oKmlFilename = GetDescription();
294 :
295 61 : if (m_poKmlDSContainer && m_poKmlDSContainer->IsA(kmldom::Type_Document))
296 : {
297 120 : DocumentPtr poKmlDocument = AsDocument(m_poKmlDSContainer);
298 :
299 60 : ParseDocumentOptions(m_poKmlDSKml, poKmlDocument);
300 :
301 124 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
302 : {
303 64 : SchemaPtr poKmlSchema = nullptr;
304 :
305 64 : if ((poKmlSchema = papoLayers[iLayer]->GetKmlSchema()))
306 : {
307 : const size_t nKmlSchemas =
308 4 : poKmlDocument->get_schema_array_size();
309 8 : SchemaPtr poKmlSchema2 = nullptr;
310 :
311 4 : for (size_t iKmlSchema = 0; iKmlSchema < nKmlSchemas;
312 : iKmlSchema++)
313 : {
314 : poKmlSchema2 =
315 1 : poKmlDocument->get_schema_array_at(iKmlSchema);
316 1 : if (poKmlSchema2 == poKmlSchema)
317 1 : break;
318 : }
319 :
320 4 : if (poKmlSchema2 != poKmlSchema)
321 3 : poKmlDocument->add_schema(poKmlSchema);
322 : }
323 :
324 64 : papoLayers[iLayer]->Finalize(poKmlDocument);
325 : }
326 : }
327 : else
328 : {
329 1 : ParseDocumentOptions(m_poKmlDSKml, nullptr);
330 : }
331 :
332 122 : std::string oKmlOut;
333 61 : oKmlOut = kmldom::SerializePretty(m_poKmlDSKml);
334 61 : OGRLIBKMLPostProcessOutput(oKmlOut);
335 :
336 61 : bool bRet = true;
337 61 : if (!oKmlOut.empty())
338 : {
339 61 : VSILFILE *fp = VSIFOpenExL(oKmlFilename.c_str(), "wb", true);
340 61 : if (fp == nullptr)
341 : {
342 0 : CPLError(CE_Failure, CPLE_FileIO, "Error writing %s: %s",
343 : oKmlFilename.c_str(), VSIGetLastErrorMsg());
344 0 : return false;
345 : }
346 :
347 61 : if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
348 0 : bRet = false;
349 61 : if (VSIFCloseL(fp) != 0)
350 0 : bRet = false;
351 : }
352 61 : return bRet;
353 : }
354 :
355 : /******************************************************************************/
356 : /* OGRLIBKMLCreateOGCKml22() */
357 : /******************************************************************************/
358 :
359 148 : static KmlPtr OGRLIBKMLCreateOGCKml22(KmlFactory *poFactory,
360 : char **papszOptions = nullptr)
361 : {
362 148 : const char *pszAuthorName = CSLFetchNameValue(papszOptions, "AUTHOR_NAME");
363 148 : const char *pszAuthorURI = CSLFetchNameValue(papszOptions, "AUTHOR_URI");
364 : const char *pszAuthorEmail =
365 148 : CSLFetchNameValue(papszOptions, "AUTHOR_EMAIL");
366 148 : const char *pszLink = CSLFetchNameValue(papszOptions, "LINK");
367 147 : const bool bWithAtom = pszAuthorName != nullptr ||
368 147 : pszAuthorURI != nullptr ||
369 295 : pszAuthorEmail != nullptr || pszLink != nullptr;
370 :
371 148 : KmlPtr kml = poFactory->CreateKml();
372 148 : if (bWithAtom)
373 : {
374 2 : const char *kAttrs[] = {"xmlns", "http://www.opengis.net/kml/2.2",
375 : "xmlns:atom", "http://www.w3.org/2005/Atom",
376 : nullptr};
377 2 : kml->AddUnknownAttributes(Attributes::Create(kAttrs));
378 : }
379 : else
380 : {
381 146 : const char *kAttrs[] = {"xmlns", "http://www.opengis.net/kml/2.2",
382 : nullptr};
383 146 : kml->AddUnknownAttributes(Attributes::Create(kAttrs));
384 : }
385 148 : return kml;
386 : }
387 :
388 : /******************************************************************************
389 : Method to write a ds .kmz at ds destroy.
390 :
391 : Args: none
392 :
393 : Returns: nothing
394 :
395 : ******************************************************************************/
396 :
397 8 : bool OGRLIBKMLDataSource::WriteKmz()
398 : {
399 16 : std::string osTmpFilename;
400 15 : if (!VSISupportsRandomWrite(GetDescription(), false) ||
401 7 : EQUAL(CPLGetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", ""),
402 : "FORCED"))
403 : {
404 : osTmpFilename =
405 2 : CPLGenerateTempFilename(CPLGetBasename(GetDescription()));
406 : }
407 :
408 10 : void *hZIP = CPLCreateZip(osTmpFilename.empty() ? GetDescription()
409 2 : : osTmpFilename.c_str(),
410 : nullptr);
411 :
412 8 : if (!hZIP)
413 : {
414 0 : CPLError(CE_Failure, CPLE_NoWriteAccess, "Error creating %s: %s",
415 0 : GetDescription(), VSIGetLastErrorMsg());
416 0 : return false;
417 : }
418 :
419 8 : bool bRet = true;
420 :
421 : /***** write out the doc.kml ****/
422 8 : const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
423 :
424 8 : if (CPLTestBool(pszUseDocKml) && (m_poKmlDocKml || m_poKmlUpdate))
425 : {
426 : // If we do not have the doc.kmlroot
427 : // make it and add the container.
428 7 : if (!m_poKmlDocKmlRoot)
429 : {
430 : m_poKmlDocKmlRoot =
431 7 : OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions);
432 :
433 7 : auto kml = AsKml(m_poKmlDocKmlRoot);
434 7 : if (m_poKmlDocKml)
435 : {
436 6 : if (kml)
437 : {
438 6 : kml->set_feature(m_poKmlDocKml);
439 : }
440 : }
441 :
442 7 : ParseDocumentOptions(std::move(kml), AsDocument(m_poKmlDocKml));
443 : }
444 :
445 14 : std::string oKmlOut = kmldom::SerializePretty(m_poKmlDocKmlRoot);
446 7 : OGRLIBKMLPostProcessOutput(oKmlOut);
447 :
448 14 : if (CPLCreateFileInZip(hZIP, "doc.kml", nullptr) != CE_None ||
449 7 : CPLWriteFileInZip(hZIP, oKmlOut.data(),
450 7 : static_cast<int>(oKmlOut.size())) != CE_None)
451 : {
452 0 : bRet = false;
453 0 : CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
454 0 : "doc.kml", GetDescription());
455 : }
456 7 : CPLCloseFileInZip(hZIP);
457 : }
458 :
459 : /***** loop though the layers and write them *****/
460 17 : for (int iLayer = 0; iLayer < nLayers && !m_poKmlUpdate; iLayer++)
461 : {
462 18 : ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer();
463 :
464 9 : if (poKmlContainer->IsA(kmldom::Type_Document))
465 : {
466 18 : DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
467 9 : SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema();
468 :
469 13 : if (!poKmlDocument->get_schema_array_size() && poKmlSchema &&
470 4 : poKmlSchema->get_simplefield_array_size())
471 : {
472 4 : poKmlDocument->add_schema(std::move(poKmlSchema));
473 : }
474 :
475 9 : papoLayers[iLayer]->Finalize(std::move(poKmlDocument));
476 : }
477 :
478 : // If we do not have the layers root
479 : // make it and add the container.
480 18 : KmlPtr poKmlKml = nullptr;
481 :
482 9 : if (!(poKmlKml = AsKml(papoLayers[iLayer]->GetKmlLayerRoot())))
483 : {
484 9 : poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
485 :
486 9 : poKmlKml->set_feature(poKmlContainer);
487 : }
488 :
489 18 : std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
490 9 : OGRLIBKMLPostProcessOutput(oKmlOut);
491 :
492 9 : if (iLayer == 0 && CPLTestBool(pszUseDocKml))
493 : {
494 5 : CPL_IGNORE_RET_VAL(CPLCreateFileInZip(hZIP, "layers/", nullptr));
495 : }
496 :
497 9 : const char *pszLayerFileName = nullptr;
498 9 : if (CPLTestBool(pszUseDocKml))
499 : pszLayerFileName =
500 8 : CPLSPrintf("layers/%s", papoLayers[iLayer]->GetFileName());
501 : else
502 1 : pszLayerFileName = papoLayers[iLayer]->GetFileName();
503 :
504 18 : if (CPLCreateFileInZip(hZIP, pszLayerFileName, nullptr) != CE_None ||
505 9 : CPLWriteFileInZip(hZIP, oKmlOut.data(),
506 9 : static_cast<int>(oKmlOut.size())) != CE_None)
507 0 : CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
508 0 : papoLayers[iLayer]->GetFileName(), GetDescription());
509 9 : CPLCloseFileInZip(hZIP);
510 : }
511 :
512 : /***** write the style table *****/
513 8 : if (m_poKmlStyleKml)
514 : {
515 2 : KmlPtr poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
516 :
517 1 : poKmlKml->set_feature(m_poKmlStyleKml);
518 2 : std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
519 1 : OGRLIBKMLPostProcessOutput(oKmlOut);
520 :
521 1 : if (CPLCreateFileInZip(hZIP, "style/", nullptr) != CE_None ||
522 2 : CPLCreateFileInZip(hZIP, "style/style.kml", nullptr) != CE_None ||
523 1 : CPLWriteFileInZip(hZIP, oKmlOut.data(),
524 1 : static_cast<int>(oKmlOut.size())) != CE_None)
525 : {
526 0 : bRet = false;
527 0 : CPLError(CE_Failure, CPLE_FileIO, "ERROR adding %s to %s",
528 0 : "style/style.kml", GetDescription());
529 : }
530 1 : CPLCloseFileInZip(hZIP);
531 : }
532 :
533 8 : CPLCloseZip(hZIP);
534 :
535 8 : if (!osTmpFilename.empty())
536 : {
537 2 : if (bRet)
538 : {
539 2 : bRet = CPLCopyFile(GetDescription(), osTmpFilename.c_str()) == 0;
540 2 : if (!bRet)
541 1 : CPLError(CE_Failure, CPLE_FileIO,
542 1 : "Cannot copy temporary file to %s", GetDescription());
543 : }
544 2 : VSIUnlink(osTmpFilename.c_str());
545 : }
546 :
547 8 : return bRet;
548 : }
549 :
550 : /******************************************************************************
551 : Method to write a dir ds at ds destroy.
552 :
553 : Args: none
554 :
555 : Returns: nothing
556 :
557 : ******************************************************************************/
558 :
559 37 : bool OGRLIBKMLDataSource::WriteDir()
560 : {
561 : /***** write out the doc.kml ****/
562 37 : const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
563 :
564 37 : bool bRet = true;
565 37 : if (CPLTestBool(pszUseDocKml) && (m_poKmlDocKml || m_poKmlUpdate))
566 : {
567 : // If we don't have the doc.kml root
568 : // make it and add the container.
569 37 : if (!m_poKmlDocKmlRoot)
570 : {
571 : m_poKmlDocKmlRoot =
572 37 : OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions);
573 37 : auto kml = AsKml(m_poKmlDocKmlRoot);
574 37 : if (kml)
575 : {
576 37 : if (m_poKmlDocKml)
577 36 : kml->set_feature(m_poKmlDocKml);
578 : }
579 :
580 37 : ParseDocumentOptions(std::move(kml), AsDocument(m_poKmlDocKml));
581 : }
582 :
583 37 : std::string oKmlOut = kmldom::SerializePretty(m_poKmlDocKmlRoot);
584 37 : OGRLIBKMLPostProcessOutput(oKmlOut);
585 :
586 : const char *pszOutfile =
587 37 : CPLFormFilename(GetDescription(), "doc.kml", nullptr);
588 :
589 37 : VSILFILE *fp = VSIFOpenExL(pszOutfile, "wb", true);
590 37 : if (fp == nullptr)
591 : {
592 0 : CPLError(CE_Failure, CPLE_FileIO, "Error writing %s to %s: %s",
593 0 : "doc.kml", GetDescription(), VSIGetLastErrorMsg());
594 0 : return false;
595 : }
596 :
597 37 : if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
598 0 : bRet = false;
599 37 : if (VSIFCloseL(fp) != 0)
600 0 : bRet = false;
601 : }
602 :
603 : /***** loop though the layers and write them *****/
604 74 : for (int iLayer = 0; iLayer < nLayers && !m_poKmlUpdate; iLayer++)
605 : {
606 37 : ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer();
607 :
608 37 : if (poKmlContainer->IsA(kmldom::Type_Document))
609 : {
610 74 : DocumentPtr poKmlDocument = AsDocument(poKmlContainer);
611 37 : SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema();
612 :
613 54 : if (!poKmlDocument->get_schema_array_size() && poKmlSchema &&
614 17 : poKmlSchema->get_simplefield_array_size())
615 : {
616 17 : poKmlDocument->add_schema(std::move(poKmlSchema));
617 : }
618 :
619 37 : papoLayers[iLayer]->Finalize(std::move(poKmlDocument));
620 : }
621 :
622 : // If we do not have the layers root
623 : // make it and add the container.
624 37 : KmlPtr poKmlKml = nullptr;
625 :
626 37 : if (!(poKmlKml = AsKml(papoLayers[iLayer]->GetKmlLayerRoot())))
627 : {
628 37 : poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
629 :
630 37 : poKmlKml->set_feature(poKmlContainer);
631 : }
632 :
633 37 : std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
634 37 : OGRLIBKMLPostProcessOutput(oKmlOut);
635 :
636 37 : const char *pszOutfile = CPLFormFilename(
637 37 : GetDescription(), papoLayers[iLayer]->GetFileName(), nullptr);
638 :
639 37 : VSILFILE *fp = VSIFOpenL(pszOutfile, "wb");
640 37 : if (fp == nullptr)
641 : {
642 0 : CPLError(CE_Failure, CPLE_FileIO, "ERROR Writing %s to %s",
643 0 : papoLayers[iLayer]->GetFileName(), GetDescription());
644 0 : return false;
645 : }
646 :
647 37 : if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
648 0 : bRet = false;
649 37 : if (VSIFCloseL(fp) != 0)
650 0 : bRet = false;
651 : }
652 :
653 : /***** write the style table *****/
654 37 : if (m_poKmlStyleKml)
655 : {
656 0 : KmlPtr poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory);
657 :
658 0 : poKmlKml->set_feature(m_poKmlStyleKml);
659 0 : std::string oKmlOut = kmldom::SerializePretty(poKmlKml);
660 0 : OGRLIBKMLPostProcessOutput(oKmlOut);
661 :
662 : const char *pszOutfile =
663 0 : CPLFormFilename(GetDescription(), "style.kml", nullptr);
664 :
665 0 : VSILFILE *fp = VSIFOpenL(pszOutfile, "wb");
666 0 : if (fp == nullptr)
667 : {
668 0 : CPLError(CE_Failure, CPLE_FileIO, "ERROR Writing %s to %s",
669 0 : "style.kml", GetDescription());
670 0 : return false;
671 : }
672 :
673 0 : if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
674 0 : bRet = false;
675 0 : if (VSIFCloseL(fp) != 0)
676 0 : bRet = false;
677 : }
678 37 : return bRet;
679 : }
680 :
681 : /******************************************************************************
682 : Method to write the datasource to disk.
683 :
684 : Args: none
685 :
686 : Returns nothing
687 :
688 : ******************************************************************************/
689 :
690 398 : CPLErr OGRLIBKMLDataSource::FlushCache(bool /* bAtClosing */)
691 : {
692 398 : if (!bUpdated)
693 292 : return CE_None;
694 :
695 106 : CPLErr eErr = CE_None;
696 106 : if (bUpdate && IsKml())
697 : {
698 61 : if (!WriteKml())
699 0 : eErr = CE_Failure;
700 : }
701 45 : else if (bUpdate && IsKmz())
702 : {
703 8 : if (!WriteKmz())
704 1 : eErr = CE_Failure;
705 : }
706 37 : else if (bUpdate && IsDir())
707 : {
708 37 : if (!WriteDir())
709 0 : eErr = CE_Failure;
710 : }
711 :
712 106 : bUpdated = false;
713 106 : return eErr;
714 : }
715 :
716 : /******************************************************************************
717 : OGRLIBKMLDataSource Destructor
718 :
719 : Args: none
720 :
721 : Returns: nothing
722 :
723 : ******************************************************************************/
724 :
725 782 : OGRLIBKMLDataSource::~OGRLIBKMLDataSource()
726 : {
727 : /***** sync the DS to disk *****/
728 391 : OGRLIBKMLDataSource::FlushCache(true);
729 :
730 736 : for (int i = 0; i < nLayers; i++)
731 345 : delete papoLayers[i];
732 :
733 391 : CPLFree(papoLayers);
734 :
735 391 : CSLDestroy(m_papszOptions);
736 782 : }
737 :
738 : /******************************************************************************
739 : Method to parse a schemas out of a document.
740 :
741 : Args: poKmlDocument pointer to the document to parse
742 :
743 : Returns: nothing
744 :
745 : ******************************************************************************/
746 :
747 26 : SchemaPtr OGRLIBKMLDataSource::FindSchema(const char *pszSchemaUrl)
748 : {
749 26 : if (!pszSchemaUrl || !*pszSchemaUrl)
750 0 : return nullptr;
751 :
752 26 : char *pszID = nullptr;
753 26 : char *pszFile = nullptr;
754 26 : char *pszSchemaName = nullptr;
755 52 : DocumentPtr poKmlDocument = nullptr;
756 52 : SchemaPtr poKmlSchemaResult = nullptr;
757 :
758 26 : if (*pszSchemaUrl == '#')
759 : {
760 24 : pszID = CPLStrdup(pszSchemaUrl + 1);
761 :
762 : /***** kml *****/
763 24 : if (IsKml() && m_poKmlDSContainer->IsA(kmldom::Type_Document))
764 24 : poKmlDocument = AsDocument(m_poKmlDSContainer);
765 :
766 : /***** kmz *****/
767 0 : else if ((IsKmz() || IsDir()) && m_poKmlDocKml &&
768 0 : m_poKmlDocKml->IsA(kmldom::Type_Document))
769 0 : poKmlDocument = AsDocument(m_poKmlDocKml);
770 : }
771 2 : else if (const char *pszPound = strchr(pszSchemaUrl, '#'))
772 : {
773 0 : pszFile = CPLStrdup(pszSchemaUrl);
774 0 : pszID = CPLStrdup(pszPound + 1);
775 0 : pszFile[pszPound - pszSchemaUrl] = '\0';
776 : }
777 : else
778 : {
779 2 : pszSchemaName = CPLStrdup(pszSchemaUrl);
780 :
781 : /***** kml *****/
782 2 : if (IsKml() && m_poKmlDSContainer->IsA(kmldom::Type_Document))
783 2 : poKmlDocument = AsDocument(m_poKmlDSContainer);
784 :
785 : /***** kmz *****/
786 :
787 0 : else if ((IsKmz() || IsDir()) && m_poKmlDocKml &&
788 0 : m_poKmlDocKml->IsA(kmldom::Type_Document))
789 0 : poKmlDocument = AsDocument(m_poKmlDocKml);
790 : }
791 :
792 26 : if (poKmlDocument)
793 : {
794 26 : size_t nKmlSchemas = poKmlDocument->get_schema_array_size();
795 :
796 29 : for (size_t iKmlSchema = 0; iKmlSchema < nKmlSchemas; iKmlSchema++)
797 : {
798 : SchemaPtr poKmlSchema =
799 27 : poKmlDocument->get_schema_array_at(iKmlSchema);
800 27 : if (poKmlSchema->has_id() && pszID)
801 : {
802 25 : if (EQUAL(pszID, poKmlSchema->get_id().c_str()))
803 : {
804 23 : poKmlSchemaResult = std::move(poKmlSchema);
805 23 : break;
806 : }
807 : }
808 :
809 2 : else if (poKmlSchema->has_name() && pszSchemaName)
810 : {
811 2 : if (EQUAL(pszSchemaName, poKmlSchema->get_name().c_str()))
812 : {
813 1 : poKmlSchemaResult = std::move(poKmlSchema);
814 1 : break;
815 : }
816 : }
817 : }
818 : }
819 :
820 26 : CPLFree(pszFile);
821 26 : CPLFree(pszID);
822 26 : CPLFree(pszSchemaName);
823 :
824 26 : return poKmlSchemaResult;
825 : }
826 :
827 : /******************************************************************************
828 : Method to allocate memory for the layer array, create the layer,
829 : and add it to the layer array.
830 :
831 : Args: pszLayerName the name of the layer
832 : eGType the layers geometry type
833 : poOgrDS pointer to the datasource the layer is in
834 : poKmlRoot pointer to the root kml element of the layer
835 : pszFileName the filename of the layer
836 : bNew true if its a new layer
837 : bUpdate true if the layer is writable
838 : nGuess a guess at the number of additional layers
839 : we are going to need
840 :
841 : Returns: Pointer to the new layer
842 : ******************************************************************************/
843 :
844 361 : OGRLIBKMLLayer *OGRLIBKMLDataSource::AddLayer(
845 : const char *pszLayerName, OGRwkbGeometryType eGType,
846 : const OGRSpatialReference *poSRS, OGRLIBKMLDataSource *poOgrDS,
847 : ElementPtr poKmlRoot, ContainerPtr poKmlContainer, const char *pszFileName,
848 : int bNew, int bUpdateIn, int nGuess)
849 : {
850 : // Build unique layer name
851 361 : CPLString osUniqueLayername(pszLayerName);
852 361 : int nIter = 2;
853 : while (true)
854 : {
855 362 : if (GetLayerByName(osUniqueLayername) == nullptr)
856 361 : break;
857 1 : osUniqueLayername = CPLSPrintf("%s (#%d)", pszLayerName, nIter);
858 1 : nIter++;
859 : }
860 :
861 : /***** check to see if we have enough space to store the layer *****/
862 361 : if (nLayers == nAllocated)
863 : {
864 257 : nAllocated += nGuess;
865 257 : papoLayers = static_cast<OGRLIBKMLLayer **>(
866 257 : CPLRealloc(papoLayers, sizeof(OGRLIBKMLLayer *) * nAllocated));
867 : }
868 :
869 : /***** create the layer *****/
870 : OGRLIBKMLLayer *poOgrLayer = new OGRLIBKMLLayer(
871 361 : osUniqueLayername, eGType, poSRS, poOgrDS, std::move(poKmlRoot),
872 722 : std::move(poKmlContainer), m_poKmlUpdate, pszFileName, bNew, bUpdateIn);
873 :
874 : /***** add the layer to the array *****/
875 361 : const int iLayer = nLayers++;
876 361 : papoLayers[iLayer] = poOgrLayer;
877 361 : osUniqueLayername.toupper();
878 361 : m_oMapLayers[std::move(osUniqueLayername)] = poOgrLayer;
879 :
880 722 : return poOgrLayer;
881 : }
882 :
883 : /******************************************************************************
884 : Method to parse multiple layers out of a container.
885 :
886 : Args: poKmlContainer pointer to the container to parse
887 :
888 : Returns: number of features in the container that are not another
889 : container
890 :
891 : ******************************************************************************/
892 :
893 459 : int OGRLIBKMLDataSource::ParseLayers(ContainerPtr poKmlContainer, bool bRecurse)
894 : {
895 : /***** if container is null just bail now *****/
896 459 : if (!poKmlContainer)
897 0 : return 0;
898 :
899 459 : const size_t nKmlFeatures = poKmlContainer->get_feature_array_size();
900 :
901 : /***** loop over the container to separate the style, layers, etc *****/
902 :
903 459 : int nResult = 0;
904 1396 : for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
905 : {
906 : FeaturePtr poKmlFeat =
907 1874 : poKmlContainer->get_feature_array_at(iKmlFeature);
908 :
909 : /***** container *****/
910 :
911 937 : if (poKmlFeat->IsA(kmldom::Type_Container))
912 : {
913 382 : if (bRecurse)
914 : {
915 : /***** see if the container has a name *****/
916 :
917 211 : std::string oKmlFeatName;
918 211 : if (poKmlFeat->has_name())
919 : {
920 : /* Strip leading and trailing spaces */
921 210 : const char *l_pszName = poKmlFeat->get_name().c_str();
922 210 : while (*l_pszName == ' ' || *l_pszName == '\n' ||
923 420 : *l_pszName == '\r' || *l_pszName == '\t')
924 0 : l_pszName++;
925 210 : oKmlFeatName = l_pszName;
926 210 : int nSize = (int)oKmlFeatName.size();
927 420 : while (nSize > 0 && (oKmlFeatName[nSize - 1] == ' ' ||
928 210 : oKmlFeatName[nSize - 1] == '\n' ||
929 210 : oKmlFeatName[nSize - 1] == '\r' ||
930 210 : oKmlFeatName[nSize - 1] == '\t'))
931 : {
932 0 : nSize--;
933 0 : oKmlFeatName.resize(nSize);
934 : }
935 : }
936 : /***** use the feature index number as the name *****/
937 : /***** not sure i like this c++ ich *****/
938 : else
939 : {
940 1 : std::stringstream oOut;
941 1 : oOut << iKmlFeature;
942 1 : oKmlFeatName = "Layer";
943 1 : oKmlFeatName.append(oOut.str());
944 : }
945 :
946 : /***** create the layer *****/
947 :
948 211 : AddLayer(oKmlFeatName.c_str(), wkbUnknown, nullptr, this,
949 422 : nullptr, AsContainer(poKmlFeat), "", FALSE, bUpdate,
950 : static_cast<int>(nKmlFeatures));
951 :
952 : /***** check if any features are another layer *****/
953 211 : ParseLayers(AsContainer(poKmlFeat), true);
954 : }
955 : }
956 : else
957 : {
958 555 : nResult++;
959 : }
960 : }
961 :
962 459 : return nResult;
963 : }
964 :
965 : /******************************************************************************
966 : Function to get the container from the kmlroot.
967 :
968 : Args: poKmlRoot the root element
969 :
970 : Returns: root if its a container, if its a kml the container it
971 : contains, or NULL
972 :
973 : ******************************************************************************/
974 :
975 138 : static ContainerPtr GetContainerFromRoot(KmlFactory *poKmlFactory,
976 : ElementPtr poKmlRoot)
977 : {
978 138 : ContainerPtr poKmlContainer = nullptr;
979 :
980 : const bool bReadGroundOverlay =
981 138 : CPLTestBool(CPLGetConfigOption("LIBKML_READ_GROUND_OVERLAY", "YES"));
982 :
983 138 : if (poKmlRoot)
984 : {
985 : /***** skip over the <kml> we want the container *****/
986 138 : if (poKmlRoot->IsA(kmldom::Type_kml))
987 : {
988 276 : KmlPtr poKmlKml = AsKml(poKmlRoot);
989 :
990 138 : if (poKmlKml && poKmlKml->has_feature())
991 : {
992 276 : FeaturePtr poKmlFeat = poKmlKml->get_feature();
993 :
994 138 : if (poKmlFeat->IsA(kmldom::Type_Container))
995 135 : poKmlContainer = AsContainer(poKmlFeat);
996 3 : else if (poKmlFeat->IsA(kmldom::Type_Placemark) ||
997 0 : (bReadGroundOverlay &&
998 0 : poKmlFeat->IsA(kmldom::Type_GroundOverlay)))
999 : {
1000 3 : poKmlContainer = poKmlFactory->CreateDocument();
1001 6 : poKmlContainer->add_feature(
1002 6 : kmldom::AsFeature(kmlengine::Clone(poKmlFeat)));
1003 : }
1004 : }
1005 : }
1006 0 : else if (poKmlRoot->IsA(kmldom::Type_Container))
1007 : {
1008 0 : poKmlContainer = AsContainer(std::move(poKmlRoot));
1009 : }
1010 : }
1011 :
1012 138 : return poKmlContainer;
1013 : }
1014 :
1015 : /******************************************************************************
1016 : Method to parse a kml string into the style table.
1017 : ******************************************************************************/
1018 :
1019 4 : int OGRLIBKMLDataSource::ParseIntoStyleTable(std::string *poKmlStyleKml,
1020 : const char *pszMyStylePath)
1021 : {
1022 : /***** parse the kml into the dom *****/
1023 8 : std::string oKmlErrors;
1024 8 : ElementPtr poKmlRoot = OGRLIBKMLParse(*poKmlStyleKml, &oKmlErrors);
1025 :
1026 4 : if (!poKmlRoot)
1027 : {
1028 0 : CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing style kml %s :%s",
1029 : pszMyStylePath, oKmlErrors.c_str());
1030 0 : return false;
1031 : }
1032 :
1033 8 : ContainerPtr poKmlContainer = nullptr;
1034 :
1035 4 : if (!(poKmlContainer =
1036 8 : GetContainerFromRoot(m_poKmlFactory, std::move(poKmlRoot))))
1037 : {
1038 0 : return false;
1039 : }
1040 :
1041 4 : ParseStyles(AsDocument(std::move(poKmlContainer)), &m_poStyleTable);
1042 4 : m_osStylePath = pszMyStylePath;
1043 :
1044 4 : return true;
1045 : }
1046 :
1047 : /******************************************************************************
1048 : Method to open a kml file.
1049 :
1050 : Args: pszFilename file to open
1051 : bUpdate update mode
1052 :
1053 : Returns: True on success, false on failure
1054 :
1055 : ******************************************************************************/
1056 :
1057 119 : int OGRLIBKMLDataSource::OpenKml(const char *pszFilename, int bUpdateIn)
1058 : {
1059 238 : std::string oKmlKml;
1060 238 : std::string osBuffer;
1061 119 : osBuffer.resize(4096);
1062 :
1063 119 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
1064 119 : if (fp == nullptr)
1065 : {
1066 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s", pszFilename);
1067 0 : return FALSE;
1068 : }
1069 119 : int nRead = 0;
1070 208 : while ((nRead = static_cast<int>(
1071 327 : VSIFReadL(&osBuffer[0], 1, osBuffer.size(), fp))) != 0)
1072 : {
1073 : try
1074 : {
1075 208 : oKmlKml.append(osBuffer.c_str(), nRead);
1076 : }
1077 0 : catch (const std::exception &ex)
1078 : {
1079 0 : CPLDebug("LIBKML", "libstdc++ exception during ingestion: %s",
1080 0 : ex.what());
1081 0 : VSIFCloseL(fp);
1082 0 : return FALSE;
1083 : }
1084 : }
1085 119 : OGRLIBKMLPreProcessInput(oKmlKml);
1086 119 : VSIFCloseL(fp);
1087 :
1088 238 : CPLLocaleC oLocaleForcer;
1089 :
1090 : /***** parse the kml into the DOM *****/
1091 238 : std::string oKmlErrors;
1092 :
1093 119 : m_poKmlDSKml = AsKml(OGRLIBKMLParse(oKmlKml, &oKmlErrors));
1094 :
1095 119 : if (!m_poKmlDSKml)
1096 : {
1097 0 : CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing kml %s :%s",
1098 : pszFilename, oKmlErrors.c_str());
1099 :
1100 0 : return FALSE;
1101 : }
1102 :
1103 : /***** get the container from root *****/
1104 119 : if (!(m_poKmlDSContainer =
1105 238 : GetContainerFromRoot(m_poKmlFactory, m_poKmlDSKml)))
1106 : {
1107 0 : CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing kml %s :%s %s",
1108 : pszFilename, "This file does not fit the OGR model,",
1109 : "there is no container element at the root.");
1110 :
1111 0 : return FALSE;
1112 : }
1113 :
1114 119 : m_isKml = true;
1115 :
1116 : /***** get the styles *****/
1117 119 : ParseStyles(AsDocument(m_poKmlDSContainer), &m_poStyleTable);
1118 :
1119 : /***** parse for layers *****/
1120 119 : int nPlacemarks = ParseLayers(m_poKmlDSContainer, false);
1121 :
1122 : /***** if there is placemarks in the root its a layer *****/
1123 119 : if (nPlacemarks)
1124 : {
1125 16 : std::string layername_default(CPLGetBasename(pszFilename));
1126 :
1127 16 : if (m_poKmlDSContainer->has_name())
1128 : {
1129 6 : layername_default = m_poKmlDSContainer->get_name();
1130 : }
1131 :
1132 16 : AddLayer(layername_default.c_str(), wkbUnknown, nullptr, this,
1133 16 : m_poKmlDSKml, m_poKmlDSContainer, pszFilename, FALSE,
1134 : bUpdateIn, 1);
1135 : }
1136 :
1137 119 : ParseLayers(m_poKmlDSContainer, true);
1138 :
1139 119 : return TRUE;
1140 : }
1141 :
1142 : /******************************************************************************
1143 : Method to open a kmz file.
1144 :
1145 : Args: pszFilename file to open
1146 : bUpdate update mode
1147 :
1148 : Returns: True on success, false on failure
1149 :
1150 : ******************************************************************************/
1151 :
1152 6 : int OGRLIBKMLDataSource::OpenKmz(const char *pszFilename, int bUpdateIn)
1153 : {
1154 12 : std::string oKmlKmz;
1155 6 : char szBuffer[1024 + 1] = {};
1156 :
1157 6 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
1158 6 : if (fp == nullptr)
1159 : {
1160 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s", pszFilename);
1161 0 : return FALSE;
1162 : }
1163 6 : int nRead = 0;
1164 16 : while ((nRead = static_cast<int>(VSIFReadL(szBuffer, 1, 1024, fp))) != 0)
1165 : {
1166 : try
1167 : {
1168 10 : oKmlKmz.append(szBuffer, nRead);
1169 : }
1170 0 : catch (const std::bad_alloc &)
1171 : {
1172 0 : VSIFCloseL(fp);
1173 0 : return FALSE;
1174 : }
1175 : }
1176 6 : VSIFCloseL(fp);
1177 :
1178 6 : KmzFile *poKmlKmzfile = KmzFile::OpenFromString(oKmlKmz);
1179 :
1180 6 : if (!poKmlKmzfile)
1181 : {
1182 0 : CPLError(CE_Failure, CPLE_OpenFailed, "%s is not a valid kmz file",
1183 : pszFilename);
1184 0 : return FALSE;
1185 : }
1186 :
1187 12 : CPLLocaleC oLocaleForcer;
1188 :
1189 : /***** read the doc.kml *****/
1190 12 : std::string oKmlKml;
1191 12 : std::string oKmlKmlPath;
1192 6 : if (!poKmlKmzfile->ReadKmlAndGetPath(&oKmlKml, &oKmlKmlPath))
1193 : {
1194 0 : return FALSE;
1195 : }
1196 :
1197 : /***** parse the kml into the DOM *****/
1198 12 : std::string oKmlErrors;
1199 12 : ElementPtr poKmlDocKmlRoot = OGRLIBKMLParse(oKmlKml, &oKmlErrors);
1200 :
1201 6 : if (!poKmlDocKmlRoot)
1202 : {
1203 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1204 : "ERROR parsing kml layer %s from %s :%s", oKmlKmlPath.c_str(),
1205 : pszFilename, oKmlErrors.c_str());
1206 :
1207 0 : return FALSE;
1208 : }
1209 :
1210 : /***** Get the child container from root. *****/
1211 12 : ContainerPtr poKmlContainer = nullptr;
1212 :
1213 6 : if (!(poKmlContainer =
1214 12 : GetContainerFromRoot(m_poKmlFactory, poKmlDocKmlRoot)))
1215 : {
1216 0 : CPLError(CE_Failure, CPLE_OpenFailed, "ERROR parsing %s from %s :%s",
1217 : oKmlKmlPath.c_str(), pszFilename,
1218 : "kml contains no Containers");
1219 :
1220 0 : return FALSE;
1221 : }
1222 :
1223 : /***** loop over the container looking for network links *****/
1224 :
1225 6 : size_t nKmlFeatures = poKmlContainer->get_feature_array_size();
1226 6 : int nLinks = 0;
1227 :
1228 21 : for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
1229 : {
1230 : FeaturePtr poKmlFeat =
1231 15 : poKmlContainer->get_feature_array_at(iKmlFeature);
1232 :
1233 : /***** is it a network link? *****/
1234 15 : if (!poKmlFeat->IsA(kmldom::Type_NetworkLink))
1235 8 : continue;
1236 :
1237 7 : NetworkLinkPtr poKmlNetworkLink = AsNetworkLink(poKmlFeat);
1238 :
1239 : /***** does it have a link? *****/
1240 7 : if (!poKmlNetworkLink->has_link())
1241 0 : continue;
1242 :
1243 7 : LinkPtr poKmlLink = poKmlNetworkLink->get_link();
1244 :
1245 : /***** does the link have a href? *****/
1246 7 : if (!poKmlLink->has_href())
1247 0 : continue;
1248 :
1249 7 : const kmlengine::Href oKmlHref(poKmlLink->get_href());
1250 :
1251 : /***** is the link relative? *****/
1252 7 : if (oKmlHref.IsRelativePath())
1253 : {
1254 7 : nLinks++;
1255 :
1256 7 : std::string oKml;
1257 7 : if (poKmlKmzfile->ReadFile(oKmlHref.get_path().c_str(), &oKml))
1258 : {
1259 : /***** parse the kml into the DOM *****/
1260 7 : oKmlErrors.clear();
1261 7 : ElementPtr poKmlLyrRoot = OGRLIBKMLParse(oKml, &oKmlErrors);
1262 :
1263 7 : if (!poKmlLyrRoot)
1264 : {
1265 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1266 : "ERROR parsing kml layer %s from %s :%s",
1267 0 : oKmlHref.get_path().c_str(), pszFilename,
1268 : oKmlErrors.c_str());
1269 :
1270 0 : continue;
1271 : }
1272 :
1273 : /***** get the container from root *****/
1274 : ContainerPtr poKmlLyrContainer =
1275 7 : GetContainerFromRoot(m_poKmlFactory, poKmlLyrRoot);
1276 :
1277 7 : if (!poKmlLyrContainer)
1278 : {
1279 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1280 : "ERROR parsing kml layer %s from %s :%s",
1281 0 : oKmlHref.get_path().c_str(), pszFilename,
1282 : oKmlErrors.c_str());
1283 :
1284 0 : continue;
1285 : }
1286 :
1287 : /***** create the layer *****/
1288 : const std::string osLayerName =
1289 7 : poKmlNetworkLink->has_name()
1290 7 : ? poKmlNetworkLink->get_name()
1291 : : std::string(
1292 14 : CPLGetBasename(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(CPLGetBasename(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(CPLGetExtension(papszDirList[iFile]), "kml"))
1379 4371 : continue;
1380 :
1381 : /***** read the file *****/
1382 2 : std::string oKmlKml;
1383 2 : char szBuffer[1024 + 1] = {};
1384 :
1385 : CPLString osFilePath =
1386 2 : CPLFormFilename(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(CPLGetBasename(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(CPLGetExtension(*papszIter), "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(CPLGetExtension(pszFilename), "kml"))
1531 : {
1532 119 : return OpenKml(pszFilename, bUpdate);
1533 : }
1534 :
1535 : /***** kmz *****/
1536 6 : if (EQUAL(CPLGetExtension(pszFilename), "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 103 : EQUAL(CPLGetExtension(pszFilename), "kml"))
1952 57 : return CreateKml(pszFilename, papszOptions);
1953 :
1954 : /***** kmz *****/
1955 46 : if (EQUAL(CPLGetExtension(pszFilename), "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 16 : const char *pszFilePath = CPLFormFilename(
2118 16 : GetDescription(), papoLayers[iLayer]->GetFileName(), nullptr);
2119 : VSIStatBufL oStatBufL;
2120 16 : if (!VSIStatL(pszFilePath, &oStatBufL))
2121 : {
2122 0 : if (VSIUnlink(pszFilePath))
2123 : {
2124 0 : CPLError(CE_Failure, CPLE_AppDefined,
2125 : "ERROR Deleting Layer %s from filesystem as %s",
2126 0 : papoLayers[iLayer]->GetName(), pszFilePath);
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 64 : OGRLIBKMLLayer *poOgrLayer = AddLayer(
2240 : pszLayerName, eGType, poSRS, this, nullptr, poKmlDocument,
2241 64 : CPLFormFilename(nullptr, pszLayerName, ".kml"), TRUE, bUpdate, 1);
2242 :
2243 : /***** add the layer name as a <Name> *****/
2244 64 : if (!m_poKmlUpdate)
2245 : {
2246 62 : poKmlDocument->set_name(pszLayerName);
2247 : }
2248 :
2249 128 : return poOgrLayer;
2250 : }
2251 :
2252 : /******************************************************************************
2253 : ICreateLayer()
2254 :
2255 : Args: pszLayerName name of the layer to create
2256 : poOgrSRS the SRS of the layer
2257 : eGType the layers geometry type
2258 : papszOptions layer creation options
2259 :
2260 : Returns: return a pointer to the new layer or NULL on failure
2261 :
2262 : ******************************************************************************/
2263 :
2264 : OGRLayer *
2265 125 : OGRLIBKMLDataSource::ICreateLayer(const char *pszLayerName,
2266 : const OGRGeomFieldDefn *poGeomFieldDefn,
2267 : CSLConstList papszOptions)
2268 : {
2269 125 : if (!bUpdate)
2270 0 : return nullptr;
2271 :
2272 125 : if ((IsKmz() || IsDir()) && EQUAL(pszLayerName, "doc"))
2273 : {
2274 0 : CPLError(CE_Failure, CPLE_AppDefined,
2275 : "'doc' is an invalid layer name in a KMZ file");
2276 0 : return nullptr;
2277 : }
2278 :
2279 125 : const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
2280 : const auto poOgrSRS =
2281 125 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
2282 :
2283 125 : OGRLIBKMLLayer *poOgrLayer = nullptr;
2284 :
2285 : /***** kml DS *****/
2286 125 : if (IsKml())
2287 : {
2288 : poOgrLayer =
2289 61 : CreateLayerKml(pszLayerName, poOgrSRS, eGType, papszOptions);
2290 : }
2291 64 : else if (IsKmz() || IsDir())
2292 : {
2293 : poOgrLayer =
2294 64 : CreateLayerKmz(pszLayerName, poOgrSRS, eGType, papszOptions);
2295 : }
2296 :
2297 : const char *pszLookatLongitude =
2298 125 : CSLFetchNameValue(papszOptions, "LOOKAT_LONGITUDE");
2299 : const char *pszLookatLatitude =
2300 125 : CSLFetchNameValue(papszOptions, "LOOKAT_LATITUDE");
2301 : const char *pszLookatAltitude =
2302 125 : CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDE");
2303 : const char *pszLookatHeading =
2304 125 : CSLFetchNameValue(papszOptions, "LOOKAT_HEADING");
2305 125 : const char *pszLookatTilt = CSLFetchNameValue(papszOptions, "LOOKAT_TILT");
2306 : const char *pszLookatRange =
2307 125 : CSLFetchNameValue(papszOptions, "LOOKAT_RANGE");
2308 : const char *pszLookatAltitudeMode =
2309 125 : CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDEMODE");
2310 125 : if (poOgrLayer != nullptr && pszLookatLongitude != nullptr &&
2311 2 : pszLookatLatitude != nullptr && pszLookatRange != nullptr)
2312 : {
2313 2 : poOgrLayer->SetLookAt(pszLookatLongitude, pszLookatLatitude,
2314 : pszLookatAltitude, pszLookatHeading,
2315 : pszLookatTilt, pszLookatRange,
2316 : pszLookatAltitudeMode);
2317 : }
2318 : else
2319 : {
2320 : const char *pszCameraLongitude =
2321 123 : CSLFetchNameValue(papszOptions, "CAMERA_LONGITUDE");
2322 : const char *pszCameraLatitude =
2323 123 : CSLFetchNameValue(papszOptions, "CAMERA_LATITUDE");
2324 : const char *pszCameraAltitude =
2325 123 : CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDE");
2326 : const char *pszCameraHeading =
2327 123 : CSLFetchNameValue(papszOptions, "CAMERA_HEADING");
2328 : const char *pszCameraTilt =
2329 123 : CSLFetchNameValue(papszOptions, "CAMERA_TILT");
2330 : const char *pszCameraRoll =
2331 123 : CSLFetchNameValue(papszOptions, "CAMERA_ROLL");
2332 : const char *pszCameraAltitudeMode =
2333 123 : CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDEMODE");
2334 123 : if (poOgrLayer != nullptr && pszCameraLongitude != nullptr &&
2335 1 : pszCameraLatitude != nullptr && pszCameraAltitude != nullptr &&
2336 : pszCameraAltitudeMode != nullptr)
2337 : {
2338 1 : poOgrLayer->SetCamera(pszCameraLongitude, pszCameraLatitude,
2339 : pszCameraAltitude, pszCameraHeading,
2340 : pszCameraTilt, pszCameraRoll,
2341 : pszCameraAltitudeMode);
2342 : }
2343 : }
2344 :
2345 : const char *pszRegionAdd =
2346 125 : CSLFetchNameValueDef(papszOptions, "ADD_REGION", "FALSE");
2347 125 : const char *pszRegionXMin = CSLFetchNameValue(papszOptions, "REGION_XMIN");
2348 125 : const char *pszRegionYMin = CSLFetchNameValue(papszOptions, "REGION_YMIN");
2349 125 : const char *pszRegionXMax = CSLFetchNameValue(papszOptions, "REGION_XMAX");
2350 125 : const char *pszRegionYMax = CSLFetchNameValue(papszOptions, "REGION_YMAX");
2351 : const char *pszRegionMinLodPixels =
2352 125 : CSLFetchNameValueDef(papszOptions, "REGION_MIN_LOD_PIXELS", "256");
2353 : const char *pszRegionMaxLodPixels =
2354 125 : CSLFetchNameValueDef(papszOptions, "REGION_MAX_LOD_PIXELS", "-1");
2355 : const char *pszRegionMinFadeExtent =
2356 125 : CSLFetchNameValueDef(papszOptions, "REGION_MIN_FADE_EXTENT", "0");
2357 : const char *pszRegionMaxFadeExtent =
2358 125 : CSLFetchNameValueDef(papszOptions, "REGION_MAX_FADE_EXTENT", "0");
2359 125 : if (poOgrLayer != nullptr && CPLTestBool(pszRegionAdd))
2360 : {
2361 2 : poOgrLayer->SetWriteRegion(
2362 : CPLAtof(pszRegionMinLodPixels), CPLAtof(pszRegionMaxLodPixels),
2363 : CPLAtof(pszRegionMinFadeExtent), CPLAtof(pszRegionMaxFadeExtent));
2364 2 : if (pszRegionXMin != nullptr && pszRegionYMin != nullptr &&
2365 1 : pszRegionXMax != nullptr && pszRegionYMax != nullptr)
2366 : {
2367 1 : const double xmin = CPLAtof(pszRegionXMin);
2368 1 : const double ymin = CPLAtof(pszRegionYMin);
2369 1 : const double xmax = CPLAtof(pszRegionXMax);
2370 1 : const double ymax = CPLAtof(pszRegionYMax);
2371 1 : if (xmin < xmax && ymin < ymax)
2372 1 : poOgrLayer->SetRegionBounds(xmin, ymin, xmax, ymax);
2373 : }
2374 : }
2375 :
2376 125 : const char *pszSOHref = CSLFetchNameValue(papszOptions, "SO_HREF");
2377 125 : const char *pszSOName = CSLFetchNameValue(papszOptions, "SO_NAME");
2378 : const char *pszSODescription =
2379 125 : CSLFetchNameValue(papszOptions, "SO_DESCRIPTION");
2380 125 : const char *pszSOOverlayX = CSLFetchNameValue(papszOptions, "SO_OVERLAY_X");
2381 125 : const char *pszSOOverlayY = CSLFetchNameValue(papszOptions, "SO_OVERLAY_Y");
2382 : const char *pszSOOverlayXUnits =
2383 125 : CSLFetchNameValue(papszOptions, "SO_OVERLAY_XUNITS");
2384 : const char *pszSOOverlayYUnits =
2385 125 : CSLFetchNameValue(papszOptions, "SO_OVERLAY_YUNITS");
2386 125 : const char *pszSOScreenX = CSLFetchNameValue(papszOptions, "SO_SCREEN_X");
2387 125 : const char *pszSOScreenY = CSLFetchNameValue(papszOptions, "SO_SCREEN_Y");
2388 : const char *pszSOScreenXUnits =
2389 125 : CSLFetchNameValue(papszOptions, "SO_SCREEN_XUNITS");
2390 : const char *pszSOScreenYUnits =
2391 125 : CSLFetchNameValue(papszOptions, "SO_SCREEN_YUNITS");
2392 125 : const char *pszSOSizeX = CSLFetchNameValue(papszOptions, "SO_SIZE_X");
2393 125 : const char *pszSOSizeY = CSLFetchNameValue(papszOptions, "SO_SIZE_Y");
2394 : const char *pszSOSizeXUnits =
2395 125 : CSLFetchNameValue(papszOptions, "SO_SIZE_XUNITS");
2396 : const char *pszSOSizeYUnits =
2397 125 : CSLFetchNameValue(papszOptions, "SO_SIZE_YUNITS");
2398 125 : if (poOgrLayer != nullptr && pszSOHref != nullptr)
2399 : {
2400 2 : poOgrLayer->SetScreenOverlay(
2401 : pszSOHref, pszSOName, pszSODescription, pszSOOverlayX,
2402 : pszSOOverlayY, pszSOOverlayXUnits, pszSOOverlayYUnits, pszSOScreenX,
2403 : pszSOScreenY, pszSOScreenXUnits, pszSOScreenYUnits, pszSOSizeX,
2404 : pszSOSizeY, pszSOSizeXUnits, pszSOSizeYUnits);
2405 : }
2406 :
2407 : const char *pszListStyleType =
2408 125 : CSLFetchNameValue(papszOptions, "LISTSTYLE_TYPE");
2409 : const char *pszListStyleIconHref =
2410 125 : CSLFetchNameValue(papszOptions, "LISTSTYLE_ICON_HREF");
2411 125 : if (poOgrLayer != nullptr)
2412 : {
2413 125 : poOgrLayer->SetListStyle(pszListStyleType, pszListStyleIconHref);
2414 : }
2415 :
2416 125 : if (poOgrLayer != nullptr && poOgrLayer->GetKmlLayer())
2417 : {
2418 122 : SetCommonOptions(poOgrLayer->GetKmlLayer(), papszOptions);
2419 : }
2420 :
2421 : /***** mark the dataset as updated *****/
2422 125 : if (poOgrLayer)
2423 125 : bUpdated = true;
2424 :
2425 125 : return poOgrLayer;
2426 : }
2427 :
2428 : /******************************************************************************
2429 : Method to get a datasources style table.
2430 :
2431 : Args: none
2432 :
2433 : Returns: pointer to the datasources style table, or NULL if it does
2434 : not have one
2435 :
2436 : ******************************************************************************/
2437 :
2438 7 : OGRStyleTable *OGRLIBKMLDataSource::GetStyleTable()
2439 : {
2440 7 : return m_poStyleTable;
2441 : }
2442 :
2443 : /******************************************************************************
2444 : Method to write a style table to a single file .kml ds.
2445 :
2446 : Args: poStyleTable pointer to the style table to add
2447 :
2448 : Returns: nothing
2449 :
2450 : ******************************************************************************/
2451 :
2452 4 : void OGRLIBKMLDataSource::SetStyleTable2Kml(OGRStyleTable *poStyleTable)
2453 : {
2454 4 : if (!m_poKmlDSContainer)
2455 0 : return;
2456 :
2457 : /***** delete all the styles *****/
2458 :
2459 4 : DocumentPtr poKmlDocument = AsDocument(m_poKmlDSContainer);
2460 : int nKmlStyles =
2461 4 : static_cast<int>(poKmlDocument->get_styleselector_array_size());
2462 :
2463 4 : for (int iKmlStyle = nKmlStyles - 1; iKmlStyle >= 0; iKmlStyle--)
2464 : {
2465 0 : poKmlDocument->DeleteStyleSelectorAt(iKmlStyle);
2466 : }
2467 :
2468 : /***** add the new style table to the document *****/
2469 :
2470 4 : styletable2kml(poStyleTable, m_poKmlFactory, AsContainer(poKmlDocument),
2471 : m_papszOptions);
2472 : }
2473 :
2474 : /******************************************************************************
2475 : Method to write a style table to a kmz ds.
2476 :
2477 : Args: poStyleTable pointer to the style table to add
2478 :
2479 : Returns: nothing
2480 :
2481 : ******************************************************************************/
2482 :
2483 1 : void OGRLIBKMLDataSource::SetStyleTable2Kmz(OGRStyleTable *poStyleTable)
2484 : {
2485 1 : if (m_poKmlStyleKml || poStyleTable != nullptr)
2486 : {
2487 : /***** replace the style document with a new one *****/
2488 :
2489 1 : m_poKmlStyleKml = m_poKmlFactory->CreateDocument();
2490 1 : m_poKmlStyleKml->set_id("styleId");
2491 :
2492 1 : styletable2kml(poStyleTable, m_poKmlFactory, m_poKmlStyleKml);
2493 : }
2494 1 : }
2495 :
2496 : /******************************************************************************
2497 : Method to write a style table to a datasource.
2498 :
2499 : Args: poStyleTable pointer to the style table to add
2500 :
2501 : Returns: nothing
2502 :
2503 : Note: This method assumes ownership of the style table.
2504 :
2505 : ******************************************************************************/
2506 :
2507 5 : void OGRLIBKMLDataSource::SetStyleTableDirectly(OGRStyleTable *poStyleTable)
2508 : {
2509 5 : if (!bUpdate)
2510 0 : return;
2511 :
2512 5 : if (m_poStyleTable)
2513 0 : delete m_poStyleTable;
2514 :
2515 5 : m_poStyleTable = poStyleTable;
2516 :
2517 : /***** a kml datasource? *****/
2518 5 : if (IsKml())
2519 4 : SetStyleTable2Kml(m_poStyleTable);
2520 :
2521 1 : else if (IsKmz() || IsDir())
2522 1 : SetStyleTable2Kmz(m_poStyleTable);
2523 :
2524 5 : bUpdated = true;
2525 : }
2526 :
2527 : /******************************************************************************
2528 : Method to write a style table to a datasource.
2529 :
2530 : Args: poStyleTable pointer to the style table to add
2531 :
2532 : Returns: nothing
2533 :
2534 : Note: This method copies the style table, and the user will still be
2535 : responsible for its destruction.
2536 :
2537 : ******************************************************************************/
2538 :
2539 5 : void OGRLIBKMLDataSource::SetStyleTable(OGRStyleTable *poStyleTable)
2540 : {
2541 5 : if (!bUpdate)
2542 0 : return;
2543 :
2544 5 : if (poStyleTable)
2545 4 : SetStyleTableDirectly(poStyleTable->Clone());
2546 : else
2547 1 : SetStyleTableDirectly(nullptr);
2548 : }
2549 :
2550 : /******************************************************************************
2551 : Test if capability is available.
2552 :
2553 : Args: pszCap datasource capability name to test
2554 :
2555 : Returns: TRUE or FALSE
2556 :
2557 : ODsCCreateLayer: True if this datasource can create new layers.
2558 : ODsCDeleteLayer: True if this datasource can delete existing layers.
2559 :
2560 : ******************************************************************************/
2561 :
2562 104 : int OGRLIBKMLDataSource::TestCapability(const char *pszCap)
2563 : {
2564 104 : if (EQUAL(pszCap, ODsCCreateLayer))
2565 38 : return bUpdate;
2566 66 : else if (EQUAL(pszCap, ODsCDeleteLayer))
2567 18 : return bUpdate;
2568 48 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
2569 0 : return bUpdate;
2570 48 : else if (EQUAL(pszCap, ODsCZGeometries))
2571 22 : return TRUE;
2572 :
2573 26 : return FALSE;
2574 : }
|