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 142 : 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 284 : ElementPtr element = kmldom::Parse(oKml, posError);
52 142 : if (!element)
53 1 : element = kmldom::ParseNS(oKml, posError);
54 142 : 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 445 : 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 445 : m_poKmlFactory(poKmlFactory)
80 : {
81 445 : }
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 123 : static void OGRLIBKMLPreProcessInput(std::string &oKml)
90 : {
91 123 : size_t nPos = 0;
92 : while (true)
93 : {
94 124 : nPos = oKml.find("<snippet>", nPos);
95 124 : if (nPos == std::string::npos)
96 : {
97 123 : 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 123 : nPos = 0;
116 : while (true)
117 : {
118 524 : nPos = oKml.find("<coordinates>", nPos);
119 524 : if (nPos == std::string::npos)
120 : {
121 123 : break;
122 : }
123 401 : size_t nPosEnd = oKml.find("</coordinates>", nPos);
124 401 : if (nPosEnd == std::string::npos)
125 : {
126 0 : break;
127 : }
128 401 : nPos += strlen("<coordinates>");
129 401 : size_t nPosAfterCoordinates = nPos;
130 401 : bool bDigitFound = false;
131 134474 : for (; nPos < nPosEnd; nPos++)
132 : {
133 134073 : char ch = oKml[nPos];
134 134073 : if (ch >= '0' && ch <= '9')
135 81042 : bDigitFound = true;
136 53031 : else if (ch == '\t' || ch == '\n')
137 2283 : oKml[nPos] = ' ';
138 : }
139 401 : 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 401 : }
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 123 : nPos = 0;
153 : while (true)
154 : {
155 126 : const char *pszStartTag = "<MultiPolygon>";
156 126 : const char *pszEndTag = "";
157 126 : CPL_IGNORE_RET_VAL(pszEndTag); // Make CSA happy
158 126 : auto nNewPos = oKml.find(pszStartTag, nPos);
159 126 : if (nNewPos != std::string::npos)
160 : {
161 1 : pszEndTag = "</MultiPolygon>";
162 : }
163 : else
164 : {
165 125 : pszStartTag = "<MultiLineString>";
166 125 : nNewPos = oKml.find(pszStartTag, nPos);
167 125 : if (nNewPos != std::string::npos)
168 : {
169 1 : pszEndTag = "</MultiLineString>";
170 : }
171 : else
172 : {
173 124 : pszStartTag = "<MultiPoint>";
174 124 : nNewPos = oKml.find(pszStartTag, nPos);
175 124 : if (nNewPos != std::string::npos)
176 1 : pszEndTag = "</MultiPoint>";
177 : else
178 123 : 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 123 : }
189 :
190 : /************************************************************************/
191 : /* OGRLIBKMLRemoveSpaces() */
192 : /************************************************************************/
193 :
194 465 : static void OGRLIBKMLRemoveSpaces(std::string &osKml,
195 : const std::string &osNeedle)
196 : {
197 465 : size_t nPos = 0;
198 1395 : const std::string osLtNeedle(std::string("<").append(osNeedle));
199 930 : std::string osTmp;
200 930 : std::string osRet;
201 : while (true)
202 : {
203 557 : auto nPosNew = osKml.find(osLtNeedle, nPos);
204 557 : if (nPosNew == std::string::npos)
205 : {
206 465 : osRet.append(osKml, nPos);
207 465 : break;
208 : }
209 92 : const size_t nPosOri = nPosNew;
210 92 : nPosNew = osKml.find(">\n", nPosNew);
211 92 : 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 92 : osRet.append(osKml, nPos, nPosNew - nPos + 1);
218 92 : nPos = nPosNew + 2;
219 :
220 : // Remove leading spaces of " </{osNeedle}>"
221 92 : osTmp.clear();
222 1116 : for (size_t nPosTmp = nPosOri - 1; osKml[nPosTmp] == ' '; nPosTmp--)
223 : {
224 1024 : osTmp += ' ';
225 : }
226 92 : osTmp += "</";
227 92 : osTmp += osNeedle;
228 92 : osTmp += '>';
229 92 : nPosNew = osKml.find(osTmp, nPos);
230 92 : if (nPosNew != std::string::npos)
231 : {
232 92 : osRet.append(osKml, nPos, nPosNew - nPos);
233 92 : osRet += "</";
234 92 : osRet += osNeedle;
235 92 : osRet += '>';
236 92 : nPos = nPosNew + osTmp.size();
237 : }
238 : else
239 : {
240 0 : osRet.append(osKml, nPos);
241 0 : break;
242 : }
243 92 : }
244 465 : osKml = std::move(osRet);
245 465 : }
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 155 : 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 155 : if (!(oKml.size() >= 2 && oKml[0] == '<' && oKml[1] == '?'))
258 155 : oKml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + oKml;
259 :
260 155 : size_t nPos = 0;
261 : while (true)
262 : {
263 158 : nPos = oKml.find("<Snippet>", nPos);
264 158 : if (nPos == std::string::npos)
265 : {
266 155 : 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 155 : OGRLIBKMLRemoveSpaces(oKml, "snippet");
279 155 : OGRLIBKMLRemoveSpaces(oKml, "linkSnippet");
280 155 : OGRLIBKMLRemoveSpaces(oKml, "SimpleData");
281 155 : }
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 63 : bool OGRLIBKMLDataSource::WriteKml()
293 : {
294 126 : std::string oKmlFilename = GetDescription();
295 :
296 63 : if (m_poKmlDSContainer && m_poKmlDSContainer->IsA(kmldom::Type_Document))
297 : {
298 124 : DocumentPtr poKmlDocument = AsDocument(m_poKmlDSContainer);
299 :
300 62 : ParseDocumentOptions(m_poKmlDSKml, poKmlDocument);
301 :
302 128 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
303 : {
304 66 : SchemaPtr poKmlSchema = nullptr;
305 :
306 66 : if ((poKmlSchema = papoLayers[iLayer]->GetKmlSchema()))
307 : {
308 : const size_t nKmlSchemas =
309 5 : poKmlDocument->get_schema_array_size();
310 10 : SchemaPtr poKmlSchema2 = nullptr;
311 :
312 5 : 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 5 : if (poKmlSchema2 != poKmlSchema)
322 4 : poKmlDocument->add_schema(poKmlSchema);
323 : }
324 :
325 66 : papoLayers[iLayer]->Finalize(poKmlDocument);
326 : }
327 : }
328 : else
329 : {
330 1 : ParseDocumentOptions(m_poKmlDSKml, nullptr);
331 : }
332 :
333 126 : std::string oKmlOut;
334 63 : oKmlOut = kmldom::SerializePretty(m_poKmlDSKml);
335 63 : OGRLIBKMLPostProcessOutput(oKmlOut);
336 :
337 63 : bool bRet = true;
338 63 : if (!oKmlOut.empty())
339 : {
340 63 : VSILFILE *fp = VSIFOpenExL(oKmlFilename.c_str(), "wb", true);
341 63 : 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 63 : if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
349 0 : bRet = false;
350 63 : if (VSIFCloseL(fp) != 0)
351 0 : bRet = false;
352 : }
353 63 : return bRet;
354 : }
355 :
356 : /************************************************************************/
357 : /* OGRLIBKMLCreateOGCKml22() */
358 : /************************************************************************/
359 :
360 151 : static KmlPtr OGRLIBKMLCreateOGCKml22(KmlFactory *poFactory,
361 : CSLConstList papszOptions = nullptr)
362 : {
363 151 : const char *pszAuthorName = CSLFetchNameValue(papszOptions, "AUTHOR_NAME");
364 151 : const char *pszAuthorURI = CSLFetchNameValue(papszOptions, "AUTHOR_URI");
365 : const char *pszAuthorEmail =
366 151 : CSLFetchNameValue(papszOptions, "AUTHOR_EMAIL");
367 151 : const char *pszLink = CSLFetchNameValue(papszOptions, "LINK");
368 150 : const bool bWithAtom = pszAuthorName != nullptr ||
369 150 : pszAuthorURI != nullptr ||
370 301 : pszAuthorEmail != nullptr || pszLink != nullptr;
371 :
372 151 : KmlPtr kml = poFactory->CreateKml();
373 151 : 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 149 : const char *kAttrs[] = {"xmlns", "http://www.opengis.net/kml/2.2",
383 : nullptr};
384 149 : kml->AddUnknownAttributes(Attributes::Create(kAttrs));
385 : }
386 151 : 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 38 : bool OGRLIBKMLDataSource::WriteDir()
561 : {
562 : /***** write out the doc.kml ****/
563 38 : const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
564 :
565 38 : bool bRet = true;
566 38 : 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 38 : if (!m_poKmlDocKmlRoot)
571 : {
572 : m_poKmlDocKmlRoot =
573 38 : OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions);
574 38 : auto kml = AsKml(m_poKmlDocKmlRoot);
575 38 : if (kml)
576 : {
577 38 : if (m_poKmlDocKml)
578 37 : kml->set_feature(m_poKmlDocKml);
579 : }
580 :
581 38 : ParseDocumentOptions(std::move(kml), AsDocument(m_poKmlDocKml));
582 : }
583 :
584 38 : std::string oKmlOut = kmldom::SerializePretty(m_poKmlDocKmlRoot);
585 38 : OGRLIBKMLPostProcessOutput(oKmlOut);
586 :
587 : const std::string osOutfile =
588 38 : CPLFormFilenameSafe(GetDescription(), "doc.kml", nullptr);
589 :
590 38 : VSILFILE *fp = VSIFOpenExL(osOutfile.c_str(), "wb", true);
591 38 : 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 38 : if (VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp) != oKmlOut.size())
599 0 : bRet = false;
600 38 : if (VSIFCloseL(fp) != 0)
601 0 : bRet = false;
602 : }
603 :
604 : /***** loop though the layers and write them *****/
605 75 : 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 38 : 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 38 : 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 452 : CPLErr OGRLIBKMLDataSource::FlushCache(bool /* bAtClosing */)
692 : {
693 452 : if (!bUpdated)
694 343 : return CE_None;
695 :
696 109 : CPLErr eErr = CE_None;
697 109 : if (bUpdate && IsKml())
698 : {
699 63 : if (!WriteKml())
700 0 : eErr = CE_Failure;
701 : }
702 46 : else if (bUpdate && IsKmz())
703 : {
704 8 : if (!WriteKmz())
705 1 : eErr = CE_Failure;
706 : }
707 38 : else if (bUpdate && IsDir())
708 : {
709 38 : if (!WriteDir())
710 0 : eErr = CE_Failure;
711 : }
712 :
713 109 : bUpdated = false;
714 109 : return eErr;
715 : }
716 :
717 : /******************************************************************************
718 : OGRLIBKMLDataSource Destructor
719 :
720 : Args: none
721 :
722 : Returns: nothing
723 :
724 : ******************************************************************************/
725 :
726 890 : OGRLIBKMLDataSource::~OGRLIBKMLDataSource()
727 : {
728 : /***** sync the DS to disk *****/
729 445 : OGRLIBKMLDataSource::FlushCache(true);
730 :
731 796 : for (int i = 0; i < nLayers; i++)
732 351 : delete papoLayers[i];
733 :
734 445 : CPLFree(papoLayers);
735 :
736 445 : CSLDestroy(m_papszOptions);
737 890 : }
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 29 : SchemaPtr OGRLIBKMLDataSource::FindSchema(const char *pszSchemaUrl)
749 : {
750 29 : if (!pszSchemaUrl || !*pszSchemaUrl)
751 0 : return nullptr;
752 :
753 29 : char *pszID = nullptr;
754 29 : char *pszFile = nullptr;
755 29 : char *pszSchemaName = nullptr;
756 58 : DocumentPtr poKmlDocument = nullptr;
757 58 : SchemaPtr poKmlSchemaResult = nullptr;
758 :
759 29 : if (*pszSchemaUrl == '#')
760 : {
761 27 : pszID = CPLStrdup(pszSchemaUrl + 1);
762 :
763 : /***** kml *****/
764 27 : if (IsKml() && m_poKmlDSContainer->IsA(kmldom::Type_Document))
765 27 : 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 29 : if (poKmlDocument)
794 : {
795 29 : size_t nKmlSchemas = poKmlDocument->get_schema_array_size();
796 :
797 32 : for (size_t iKmlSchema = 0; iKmlSchema < nKmlSchemas; iKmlSchema++)
798 : {
799 : SchemaPtr poKmlSchema =
800 30 : poKmlDocument->get_schema_array_at(iKmlSchema);
801 30 : if (poKmlSchema->has_id() && pszID)
802 : {
803 28 : if (EQUAL(pszID, poKmlSchema->get_id().c_str()))
804 : {
805 26 : poKmlSchemaResult = std::move(poKmlSchema);
806 26 : 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 29 : CPLFree(pszFile);
822 29 : CPLFree(pszID);
823 29 : CPLFree(pszSchemaName);
824 :
825 29 : 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 367 : OGRLIBKMLLayer *OGRLIBKMLDataSource::AddLayer(
846 : const char *pszLayerName, OGRwkbGeometryType eGType,
847 : const OGRSpatialReference *poSRS, OGRLIBKMLDataSource *poOgrDS,
848 : ElementPtr poKmlRoot, ContainerPtr poKmlContainer, const char *pszFileName,
849 : bool bNew, bool bUpdateIn, int nGuess)
850 : {
851 : // Build unique layer name
852 367 : CPLString osUniqueLayername(pszLayerName);
853 367 : int nIter = 2;
854 : while (true)
855 : {
856 368 : if (GetLayerByName(osUniqueLayername) == nullptr)
857 367 : 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 367 : if (nLayers == nAllocated)
864 : {
865 263 : nAllocated += nGuess;
866 263 : papoLayers = static_cast<OGRLIBKMLLayer **>(
867 263 : CPLRealloc(papoLayers, sizeof(OGRLIBKMLLayer *) * nAllocated));
868 : }
869 :
870 : /***** create the layer *****/
871 : OGRLIBKMLLayer *poOgrLayer = new OGRLIBKMLLayer(
872 367 : osUniqueLayername, eGType, poSRS, poOgrDS, std::move(poKmlRoot),
873 734 : std::move(poKmlContainer), m_poKmlUpdate, pszFileName, bNew, bUpdateIn);
874 :
875 : /***** add the layer to the array *****/
876 367 : const int iLayer = nLayers++;
877 367 : papoLayers[iLayer] = poOgrLayer;
878 367 : osUniqueLayername.toupper();
879 367 : m_oMapLayers[std::move(osUniqueLayername)] = poOgrLayer;
880 :
881 734 : 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 471 : int OGRLIBKMLDataSource::ParseLayers(ContainerPtr poKmlContainer, bool bRecurse)
895 : {
896 : /***** if container is null just bail now *****/
897 471 : if (!poKmlContainer)
898 0 : return 0;
899 :
900 471 : const size_t nKmlFeatures = poKmlContainer->get_feature_array_size();
901 :
902 : /***** loop over the container to separate the style, layers, etc *****/
903 :
904 471 : int nResult = 0;
905 1454 : for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
906 : {
907 : FeaturePtr poKmlFeat =
908 1966 : poKmlContainer->get_feature_array_at(iKmlFeature);
909 :
910 : /***** container *****/
911 :
912 983 : if (poKmlFeat->IsA(kmldom::Type_Container))
913 : {
914 390 : if (bRecurse)
915 : {
916 : /***** see if the container has a name *****/
917 :
918 215 : std::string oKmlFeatName;
919 215 : if (poKmlFeat->has_name())
920 : {
921 : /* Strip leading and trailing spaces */
922 214 : const char *l_pszName = poKmlFeat->get_name().c_str();
923 214 : while (*l_pszName == ' ' || *l_pszName == '\n' ||
924 428 : *l_pszName == '\r' || *l_pszName == '\t')
925 0 : l_pszName++;
926 214 : oKmlFeatName = l_pszName;
927 214 : int nSize = (int)oKmlFeatName.size();
928 427 : while (nSize > 0 && (oKmlFeatName[nSize - 1] == ' ' ||
929 213 : oKmlFeatName[nSize - 1] == '\n' ||
930 213 : oKmlFeatName[nSize - 1] == '\r' ||
931 213 : 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 215 : AddLayer(oKmlFeatName.c_str(), wkbUnknown, nullptr, this,
950 430 : nullptr, AsContainer(poKmlFeat), "", false, bUpdate,
951 : static_cast<int>(nKmlFeatures));
952 :
953 : /***** check if any features are another layer *****/
954 215 : ParseLayers(AsContainer(poKmlFeat), true);
955 : }
956 : }
957 : else
958 : {
959 593 : nResult++;
960 : }
961 : }
962 :
963 471 : 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 142 : static ContainerPtr GetContainerFromRoot(KmlFactory *poKmlFactory,
977 : ElementPtr poKmlRoot)
978 : {
979 142 : ContainerPtr poKmlContainer = nullptr;
980 :
981 : const bool bReadGroundOverlay =
982 142 : CPLTestBool(CPLGetConfigOption("LIBKML_READ_GROUND_OVERLAY", "YES"));
983 :
984 142 : if (poKmlRoot)
985 : {
986 : /***** skip over the <kml> we want the container *****/
987 142 : if (poKmlRoot->IsA(kmldom::Type_kml))
988 : {
989 284 : KmlPtr poKmlKml = AsKml(poKmlRoot);
990 :
991 142 : if (poKmlKml && poKmlKml->has_feature())
992 : {
993 284 : FeaturePtr poKmlFeat = poKmlKml->get_feature();
994 :
995 142 : if (poKmlFeat->IsA(kmldom::Type_Container))
996 139 : 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 142 : 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 123 : int OGRLIBKMLDataSource::OpenKml(const char *pszFilename, int bUpdateIn)
1059 : {
1060 246 : std::string oKmlKml;
1061 246 : std::string osBuffer;
1062 123 : osBuffer.resize(4096);
1063 :
1064 123 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
1065 123 : if (fp == nullptr)
1066 : {
1067 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s", pszFilename);
1068 0 : return FALSE;
1069 : }
1070 123 : int nRead = 0;
1071 216 : while ((nRead = static_cast<int>(
1072 339 : VSIFReadL(&osBuffer[0], 1, osBuffer.size(), fp))) != 0)
1073 : {
1074 : try
1075 : {
1076 216 : 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 123 : OGRLIBKMLPreProcessInput(oKmlKml);
1087 123 : VSIFCloseL(fp);
1088 :
1089 246 : CPLLocaleC oLocaleForcer;
1090 :
1091 : /***** parse the kml into the DOM *****/
1092 246 : std::string oKmlErrors;
1093 :
1094 123 : m_poKmlDSKml = AsKml(OGRLIBKMLParse(oKmlKml, &oKmlErrors));
1095 :
1096 123 : 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 123 : if (!(m_poKmlDSContainer =
1106 246 : 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 123 : m_isKml = true;
1116 :
1117 : /***** get the styles *****/
1118 123 : ParseStyles(AsDocument(m_poKmlDSContainer), &m_poStyleTable);
1119 :
1120 : /***** parse for layers *****/
1121 123 : int nPlacemarks = ParseLayers(m_poKmlDSContainer, false);
1122 :
1123 : /***** if there is placemarks in the root its a layer *****/
1124 123 : 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 123 : ParseLayers(m_poKmlDSContainer, true);
1139 :
1140 123 : 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 210 : int OGRLIBKMLDataSource::OpenDir(const char *pszFilename, int bUpdateIn)
1367 : {
1368 210 : char **papszDirList = VSIReadDir(pszFilename);
1369 :
1370 210 : if (papszDirList == nullptr)
1371 11 : return FALSE;
1372 :
1373 199 : const int nFiles = CSLCount(papszDirList);
1374 :
1375 3339 : for (int iFile = 0; iFile < nFiles; iFile++)
1376 : {
1377 : /***** make sure its a .kml file *****/
1378 3140 : if (!EQUAL(CPLGetExtensionSafe(papszDirList[iFile]).c_str(), "kml"))
1379 3139 : 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 199 : CSLDestroy(papszDirList);
1468 :
1469 199 : if (nLayers > 0)
1470 : {
1471 1 : m_isDir = true;
1472 1 : return TRUE;
1473 : }
1474 :
1475 198 : 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 339 : int OGRLIBKMLDataSource::Open(const char *pszFilename, bool bUpdateIn)
1518 : {
1519 339 : bUpdate = CPL_TO_BOOL(bUpdateIn);
1520 :
1521 : /***** dir *****/
1522 : VSIStatBufL sStatBuf;
1523 678 : if (!VSIStatExL(pszFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) &&
1524 339 : VSI_ISDIR(sStatBuf.st_mode))
1525 : {
1526 210 : return OpenDir(pszFilename, bUpdate);
1527 : }
1528 :
1529 : /***** kml *****/
1530 129 : if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kml"))
1531 : {
1532 123 : 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 229 : void OGRLIBKMLDataSource::SetCommonOptions(ContainerPtr poKmlContainer,
1600 : CSLConstList papszOptions)
1601 : {
1602 229 : const char *l_pszName = CSLFetchNameValue(papszOptions, "NAME");
1603 229 : if (l_pszName != nullptr)
1604 5 : poKmlContainer->set_name(l_pszName);
1605 :
1606 229 : const char *pszVisibilility = CSLFetchNameValue(papszOptions, "VISIBILITY");
1607 229 : if (pszVisibilility != nullptr)
1608 2 : poKmlContainer->set_visibility(CPLTestBool(pszVisibilility));
1609 :
1610 229 : const char *pszOpen = CSLFetchNameValue(papszOptions, "OPEN");
1611 229 : if (pszOpen != nullptr)
1612 2 : poKmlContainer->set_open(CPLTestBool(pszOpen));
1613 :
1614 229 : const char *pszSnippet = CSLFetchNameValue(papszOptions, "SNIPPET");
1615 229 : 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 229 : const char *pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
1623 229 : if (pszDescription != nullptr)
1624 2 : poKmlContainer->set_description(pszDescription);
1625 229 : }
1626 :
1627 : /************************************************************************/
1628 : /* ParseDocumentOptions() */
1629 : /************************************************************************/
1630 :
1631 108 : void OGRLIBKMLDataSource::ParseDocumentOptions(KmlPtr poKml,
1632 : DocumentPtr poKmlDocument)
1633 : {
1634 108 : if (poKmlDocument)
1635 : {
1636 : const char *pszDocumentId =
1637 105 : CSLFetchNameValueDef(m_papszOptions, "DOCUMENT_ID", "root_doc");
1638 105 : poKmlDocument->set_id(pszDocumentId);
1639 :
1640 : const char *pszAuthorName =
1641 105 : CSLFetchNameValue(m_papszOptions, "AUTHOR_NAME");
1642 : const char *pszAuthorURI =
1643 105 : CSLFetchNameValue(m_papszOptions, "AUTHOR_URI");
1644 : const char *pszAuthorEmail =
1645 105 : CSLFetchNameValue(m_papszOptions, "AUTHOR_EMAIL");
1646 105 : const char *pszLink = CSLFetchNameValue(m_papszOptions, "LINK");
1647 :
1648 105 : 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 105 : 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 105 : CSLFetchNameValue(m_papszOptions, "PHONENUMBER");
1696 105 : 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 105 : SetCommonOptions(poKmlDocument, m_papszOptions);
1713 :
1714 : CPLString osListStyleType =
1715 210 : CSLFetchNameValueDef(m_papszOptions, "LISTSTYLE_TYPE", "");
1716 : CPLString osListStyleIconHref =
1717 105 : CSLFetchNameValueDef(m_papszOptions, "LISTSTYLE_ICON_HREF", "");
1718 105 : createkmlliststyle(m_poKmlFactory, pszDocumentId, poKmlDocument,
1719 : poKmlDocument, osListStyleType, osListStyleIconHref);
1720 : }
1721 :
1722 108 : if (poKml)
1723 : {
1724 108 : 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 108 : CSLFetchNameValue(m_papszOptions, "NLC_MINREFRESHPERIOD");
1737 : const char *pszNLCMaxSessionLength =
1738 108 : CSLFetchNameValue(m_papszOptions, "NLC_MAXSESSIONLENGTH");
1739 : const char *pszNLCCookie =
1740 108 : CSLFetchNameValue(m_papszOptions, "NLC_COOKIE");
1741 : const char *pszNLCMessage =
1742 108 : CSLFetchNameValue(m_papszOptions, "NLC_MESSAGE");
1743 : const char *pszNLCLinkName =
1744 108 : CSLFetchNameValue(m_papszOptions, "NLC_LINKNAME");
1745 : const char *pszNLCLinkDescription =
1746 108 : CSLFetchNameValue(m_papszOptions, "NLC_LINKDESCRIPTION");
1747 : const char *pszNLCLinkSnippet =
1748 108 : CSLFetchNameValue(m_papszOptions, "NLC_LINKSNIPPET");
1749 : const char *pszNLCExpires =
1750 108 : CSLFetchNameValue(m_papszOptions, "NLC_EXPIRES");
1751 :
1752 108 : if (pszNLCMinRefreshPeriod != nullptr ||
1753 105 : pszNLCMaxSessionLength != nullptr || pszNLCCookie != nullptr ||
1754 105 : pszNLCMessage != nullptr || pszNLCLinkName != nullptr ||
1755 105 : 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 108 : }
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 59 : int OGRLIBKMLDataSource::CreateKml(const char * /* pszFilename */,
1827 : CSLConstList papszOptions)
1828 : {
1829 59 : m_poKmlDSKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory, papszOptions);
1830 59 : if (osUpdateTargetHref.empty())
1831 : {
1832 116 : DocumentPtr poKmlDocument = m_poKmlFactory->CreateDocument();
1833 58 : m_poKmlDSKml->set_feature(poKmlDocument);
1834 58 : m_poKmlDSContainer = poKmlDocument;
1835 : }
1836 :
1837 59 : m_isKml = true;
1838 59 : bUpdated = true;
1839 :
1840 59 : 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 : CSLConstList /* 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 39 : int OGRLIBKMLDataSource::CreateDir(const char *pszFilename,
1889 : CSLConstList /* papszOptions */)
1890 : {
1891 39 : 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 38 : m_isDir = true;
1899 38 : bUpdated = true;
1900 :
1901 38 : if (osUpdateTargetHref.empty())
1902 : {
1903 : const char *pszUseDocKml =
1904 37 : CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
1905 :
1906 37 : if (CPLTestBool(pszUseDocKml))
1907 : {
1908 37 : m_poKmlDocKml = m_poKmlFactory->CreateDocument();
1909 : }
1910 : }
1911 :
1912 38 : m_osStylePath = "style.kml";
1913 :
1914 38 : 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 106 : int OGRLIBKMLDataSource::Create(const char *pszFilename,
1931 : CSLConstList papszOptions)
1932 : {
1933 106 : if (strcmp(pszFilename, "/dev/stdout") == 0)
1934 0 : pszFilename = "/vsistdout/";
1935 :
1936 106 : SetDescription(pszFilename);
1937 106 : bUpdate = true;
1938 :
1939 : osUpdateTargetHref =
1940 106 : CSLFetchNameValueDef(papszOptions, "UPDATE_TARGETHREF", "");
1941 106 : if (!osUpdateTargetHref.empty())
1942 : {
1943 3 : m_poKmlUpdate = m_poKmlFactory->CreateUpdate();
1944 3 : m_poKmlUpdate->set_targethref(osUpdateTargetHref.c_str());
1945 : }
1946 :
1947 106 : m_papszOptions = CSLDuplicate(papszOptions);
1948 :
1949 : /***** kml *****/
1950 318 : if (strcmp(pszFilename, "/vsistdout/") == 0 ||
1951 212 : STARTS_WITH(pszFilename, "/vsigzip/") ||
1952 212 : EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kml"))
1953 59 : return CreateKml(pszFilename, papszOptions);
1954 :
1955 : /***** kmz *****/
1956 47 : if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "kmz"))
1957 8 : return CreateKmz(pszFilename, papszOptions);
1958 :
1959 : /***** dir *****/
1960 39 : return CreateDir(pszFilename, papszOptions);
1961 : }
1962 :
1963 : /******************************************************************************
1964 : Method to get a layer by index.
1965 :
1966 : Args: iLayer the index of the layer to get
1967 :
1968 : Returns: pointer to the layer, or NULL if the layer does not exist
1969 :
1970 : ******************************************************************************/
1971 :
1972 98 : const OGRLayer *OGRLIBKMLDataSource::GetLayer(int iLayer) const
1973 : {
1974 98 : if (iLayer < 0 || iLayer >= nLayers)
1975 4 : return nullptr;
1976 :
1977 94 : return papoLayers[iLayer];
1978 : }
1979 :
1980 : /******************************************************************************
1981 : Method to get a layer by name.
1982 :
1983 : Args: pszname name of the layer to get
1984 :
1985 : Returns: pointer to the layer, or NULL if the layer does not exist
1986 :
1987 : ******************************************************************************/
1988 :
1989 619 : OGRLayer *OGRLIBKMLDataSource::GetLayerByName(const char *pszName)
1990 : {
1991 619 : auto oIter = m_oMapLayers.find(CPLString(pszName).toupper());
1992 619 : if (oIter != m_oMapLayers.end())
1993 233 : return oIter->second;
1994 :
1995 386 : return nullptr;
1996 : }
1997 :
1998 : /******************************************************************************
1999 : Method to DeleteLayers in a .kml datasource.
2000 :
2001 : Args: iLayer index of the layer to delete
2002 :
2003 : Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure
2004 :
2005 : ******************************************************************************/
2006 :
2007 0 : OGRErr OGRLIBKMLDataSource::DeleteLayerKml(int iLayer)
2008 : {
2009 0 : OGRLIBKMLLayer *poOgrLayer = (OGRLIBKMLLayer *)papoLayers[iLayer];
2010 :
2011 : /***** loop over the features *****/
2012 :
2013 0 : const size_t nKmlFeatures = m_poKmlDSContainer->get_feature_array_size();
2014 :
2015 0 : for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
2016 : {
2017 : FeaturePtr poKmlFeat =
2018 0 : m_poKmlDSContainer->get_feature_array_at(iKmlFeature);
2019 :
2020 0 : if (poKmlFeat == poOgrLayer->GetKmlLayer())
2021 : {
2022 0 : m_poKmlDSContainer->DeleteFeatureAt(iKmlFeature);
2023 0 : break;
2024 : }
2025 : }
2026 :
2027 0 : return OGRERR_NONE;
2028 : }
2029 :
2030 : /******************************************************************************
2031 : Method to DeleteLayers in a .kmz datasource.
2032 :
2033 : Args: iLayer index of the layer to delete
2034 :
2035 : Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure
2036 :
2037 : ******************************************************************************/
2038 :
2039 16 : OGRErr OGRLIBKMLDataSource::DeleteLayerKmz(int iLayer)
2040 : {
2041 16 : OGRLIBKMLLayer *poOgrLayer = papoLayers[iLayer];
2042 :
2043 16 : const char *pszUseDocKml = CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
2044 :
2045 16 : if (CPLTestBool(pszUseDocKml) && m_poKmlDocKml)
2046 : {
2047 : /***** loop over the features *****/
2048 16 : const size_t nKmlFeatures = m_poKmlDocKml->get_feature_array_size();
2049 :
2050 16 : for (size_t iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++)
2051 : {
2052 : FeaturePtr poKmlFeat =
2053 16 : m_poKmlDocKml->get_feature_array_at(iKmlFeature);
2054 :
2055 16 : if (poKmlFeat->IsA(kmldom::Type_NetworkLink))
2056 : {
2057 16 : NetworkLinkPtr poKmlNetworkLink = AsNetworkLink(poKmlFeat);
2058 :
2059 : /***** does it have a link? *****/
2060 16 : if (poKmlNetworkLink->has_link())
2061 : {
2062 16 : LinkPtr poKmlLink = poKmlNetworkLink->get_link();
2063 :
2064 : /***** does the link have a href? *****/
2065 16 : if (poKmlLink->has_href())
2066 : {
2067 16 : const kmlengine::Href oKmlHref(poKmlLink->get_href());
2068 :
2069 : /***** is the link relative? *****/
2070 16 : if (oKmlHref.IsRelativePath())
2071 : {
2072 16 : if (EQUAL(oKmlHref.get_path().c_str(),
2073 : poOgrLayer->GetFileName()))
2074 : {
2075 16 : m_poKmlDocKml->DeleteFeatureAt(iKmlFeature);
2076 16 : break;
2077 : }
2078 : }
2079 : }
2080 : }
2081 : }
2082 : }
2083 : }
2084 :
2085 16 : return OGRERR_NONE;
2086 : }
2087 :
2088 : /******************************************************************************
2089 : Method to delete a layer in a datasource.
2090 :
2091 : Args: iLayer index of the layer to delete
2092 :
2093 : Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure
2094 :
2095 : ******************************************************************************/
2096 :
2097 16 : OGRErr OGRLIBKMLDataSource::DeleteLayer(int iLayer)
2098 : {
2099 16 : if (!bUpdate)
2100 0 : return OGRERR_UNSUPPORTED_OPERATION;
2101 :
2102 16 : if (iLayer >= nLayers)
2103 0 : return OGRERR_FAILURE;
2104 :
2105 16 : if (IsKml())
2106 : {
2107 0 : DeleteLayerKml(iLayer);
2108 : }
2109 16 : else if (IsKmz())
2110 : {
2111 0 : DeleteLayerKmz(iLayer);
2112 : }
2113 16 : else if (IsDir())
2114 : {
2115 16 : DeleteLayerKmz(iLayer);
2116 :
2117 : /***** delete the file the layer corresponds to *****/
2118 : const std::string osFilePath = CPLFormFilenameSafe(
2119 32 : GetDescription(), papoLayers[iLayer]->GetFileName(), nullptr);
2120 : VSIStatBufL oStatBufL;
2121 16 : if (!VSIStatL(osFilePath.c_str(), &oStatBufL))
2122 : {
2123 0 : if (VSIUnlink(osFilePath.c_str()))
2124 : {
2125 0 : CPLError(CE_Failure, CPLE_AppDefined,
2126 : "ERROR Deleting Layer %s from filesystem as %s",
2127 0 : papoLayers[iLayer]->GetName(), osFilePath.c_str());
2128 : }
2129 : }
2130 : }
2131 :
2132 16 : m_oMapLayers.erase(CPLString(papoLayers[iLayer]->GetName()).toupper());
2133 16 : delete papoLayers[iLayer];
2134 16 : memmove(papoLayers + iLayer, papoLayers + iLayer + 1,
2135 16 : sizeof(void *) * (nLayers - iLayer - 1));
2136 16 : nLayers--;
2137 16 : bUpdated = true;
2138 :
2139 16 : return OGRERR_NONE;
2140 : }
2141 :
2142 : /******************************************************************************
2143 : Method to create a layer in a single file .kml.
2144 :
2145 : Args: pszLayerName name of the layer to create
2146 : poOgrSRS the SRS of the layer
2147 : eGType the layers geometry type
2148 : papszOptions layer creation options
2149 :
2150 : Returns: return a pointer to the new layer or NULL on failure
2151 :
2152 : ******************************************************************************/
2153 :
2154 63 : OGRLIBKMLLayer *OGRLIBKMLDataSource::CreateLayerKml(
2155 : const char *pszLayerName, const OGRSpatialReference *poSRS,
2156 : OGRwkbGeometryType eGType, CSLConstList papszOptions)
2157 : {
2158 63 : ContainerPtr poKmlLayerContainer = nullptr;
2159 :
2160 63 : if (m_poKmlDSContainer)
2161 : {
2162 62 : if (CPLFetchBool(papszOptions, "FOLDER", false))
2163 2 : poKmlLayerContainer = m_poKmlFactory->CreateFolder();
2164 : else
2165 60 : poKmlLayerContainer = m_poKmlFactory->CreateDocument();
2166 124 : poKmlLayerContainer->set_id(
2167 124 : OGRLIBKMLGetSanitizedNCName(pszLayerName).c_str());
2168 :
2169 62 : m_poKmlDSContainer->add_feature(poKmlLayerContainer);
2170 : }
2171 :
2172 : /***** create the layer *****/
2173 : OGRLIBKMLLayer *poOgrLayer =
2174 63 : AddLayer(pszLayerName, eGType, poSRS, this, nullptr,
2175 63 : poKmlLayerContainer, "", true, bUpdate, 1);
2176 :
2177 : /***** add the layer name as a <Name> *****/
2178 63 : if (poKmlLayerContainer)
2179 62 : poKmlLayerContainer->set_name(pszLayerName);
2180 1 : else if (CPLFetchBool(papszOptions, "FOLDER", false))
2181 : {
2182 0 : poOgrLayer->SetUpdateIsFolder(TRUE);
2183 : }
2184 :
2185 126 : return poOgrLayer;
2186 : }
2187 :
2188 : /******************************************************************************
2189 : Method to create a layer in a .kmz or dir.
2190 :
2191 : Args: pszLayerName name of the layer to create
2192 : poOgrSRS the SRS of the layer
2193 : eGType the layers geometry type
2194 : papszOptions layer creation options
2195 :
2196 : Returns: return a pointer to the new layer or NULL on failure
2197 :
2198 : ******************************************************************************/
2199 :
2200 64 : OGRLIBKMLLayer *OGRLIBKMLDataSource::CreateLayerKmz(
2201 : const char *pszLayerName, const OGRSpatialReference *poSRS,
2202 : OGRwkbGeometryType eGType, CSLConstList papszOptions)
2203 : {
2204 64 : DocumentPtr poKmlDocument = nullptr;
2205 :
2206 64 : if (!m_poKmlUpdate)
2207 : {
2208 : /***** add a network link to doc.kml *****/
2209 : const char *pszUseDocKml =
2210 62 : CPLGetConfigOption("LIBKML_USE_DOC.KML", "yes");
2211 :
2212 62 : if (CPLTestBool(pszUseDocKml) && m_poKmlDocKml)
2213 : {
2214 61 : poKmlDocument = AsDocument(m_poKmlDocKml);
2215 :
2216 122 : NetworkLinkPtr poKmlNetLink = m_poKmlFactory->CreateNetworkLink();
2217 61 : poKmlNetLink->set_name(
2218 : CSLFetchNameValueDef(papszOptions, "NAME", pszLayerName));
2219 :
2220 122 : LinkPtr poKmlLink = m_poKmlFactory->CreateLink();
2221 :
2222 61 : std::string oHref;
2223 61 : if (IsKmz())
2224 8 : oHref.append("layers/");
2225 61 : oHref.append(pszLayerName);
2226 61 : oHref.append(".kml");
2227 61 : poKmlLink->set_href(oHref);
2228 :
2229 61 : poKmlNetLink->set_link(poKmlLink);
2230 61 : poKmlDocument->add_feature(poKmlNetLink);
2231 : }
2232 :
2233 : /***** create the layer *****/
2234 :
2235 62 : poKmlDocument = m_poKmlFactory->CreateDocument();
2236 186 : poKmlDocument->set_id(
2237 124 : OGRLIBKMLGetSanitizedNCName(pszLayerName).c_str());
2238 : }
2239 :
2240 : OGRLIBKMLLayer *poOgrLayer =
2241 64 : AddLayer(pszLayerName, eGType, poSRS, this, nullptr, poKmlDocument,
2242 64 : CPLFormFilenameSafe(nullptr, pszLayerName, ".kml").c_str(),
2243 64 : true, bUpdate, 1);
2244 :
2245 : /***** add the layer name as a <Name> *****/
2246 64 : if (!m_poKmlUpdate)
2247 : {
2248 62 : poKmlDocument->set_name(pszLayerName);
2249 : }
2250 :
2251 128 : return poOgrLayer;
2252 : }
2253 :
2254 : /******************************************************************************
2255 : ICreateLayer()
2256 :
2257 : Args: pszLayerName name of the layer to create
2258 : poOgrSRS the SRS of the layer
2259 : eGType the layers geometry type
2260 : papszOptions layer creation options
2261 :
2262 : Returns: return a pointer to the new layer or NULL on failure
2263 :
2264 : ******************************************************************************/
2265 :
2266 : OGRLayer *
2267 128 : OGRLIBKMLDataSource::ICreateLayer(const char *pszLayerName,
2268 : const OGRGeomFieldDefn *poGeomFieldDefn,
2269 : CSLConstList papszOptions)
2270 : {
2271 128 : if (!bUpdate)
2272 0 : return nullptr;
2273 :
2274 128 : if (CPLLaunderForFilenameSafe(pszLayerName, nullptr) != pszLayerName)
2275 : {
2276 1 : CPLError(CE_Failure, CPLE_AppDefined,
2277 : "Illegal characters in '%s' to form a valid filename",
2278 : pszLayerName);
2279 1 : return nullptr;
2280 : }
2281 :
2282 127 : if ((IsKmz() || IsDir()) && EQUAL(pszLayerName, "doc"))
2283 : {
2284 0 : CPLError(CE_Failure, CPLE_AppDefined,
2285 : "'doc' is an invalid layer name in a KMZ file");
2286 0 : return nullptr;
2287 : }
2288 :
2289 127 : const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
2290 : const auto poOgrSRS =
2291 127 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
2292 :
2293 127 : OGRLIBKMLLayer *poOgrLayer = nullptr;
2294 :
2295 : /***** kml DS *****/
2296 127 : if (IsKml())
2297 : {
2298 : poOgrLayer =
2299 63 : CreateLayerKml(pszLayerName, poOgrSRS, eGType, papszOptions);
2300 : }
2301 64 : else if (IsKmz() || IsDir())
2302 : {
2303 : poOgrLayer =
2304 64 : CreateLayerKmz(pszLayerName, poOgrSRS, eGType, papszOptions);
2305 : }
2306 :
2307 : const char *pszLookatLongitude =
2308 127 : CSLFetchNameValue(papszOptions, "LOOKAT_LONGITUDE");
2309 : const char *pszLookatLatitude =
2310 127 : CSLFetchNameValue(papszOptions, "LOOKAT_LATITUDE");
2311 : const char *pszLookatAltitude =
2312 127 : CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDE");
2313 : const char *pszLookatHeading =
2314 127 : CSLFetchNameValue(papszOptions, "LOOKAT_HEADING");
2315 127 : const char *pszLookatTilt = CSLFetchNameValue(papszOptions, "LOOKAT_TILT");
2316 : const char *pszLookatRange =
2317 127 : CSLFetchNameValue(papszOptions, "LOOKAT_RANGE");
2318 : const char *pszLookatAltitudeMode =
2319 127 : CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDEMODE");
2320 127 : if (poOgrLayer != nullptr && pszLookatLongitude != nullptr &&
2321 2 : pszLookatLatitude != nullptr && pszLookatRange != nullptr)
2322 : {
2323 2 : poOgrLayer->SetLookAt(pszLookatLongitude, pszLookatLatitude,
2324 : pszLookatAltitude, pszLookatHeading,
2325 : pszLookatTilt, pszLookatRange,
2326 : pszLookatAltitudeMode);
2327 : }
2328 : else
2329 : {
2330 : const char *pszCameraLongitude =
2331 125 : CSLFetchNameValue(papszOptions, "CAMERA_LONGITUDE");
2332 : const char *pszCameraLatitude =
2333 125 : CSLFetchNameValue(papszOptions, "CAMERA_LATITUDE");
2334 : const char *pszCameraAltitude =
2335 125 : CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDE");
2336 : const char *pszCameraHeading =
2337 125 : CSLFetchNameValue(papszOptions, "CAMERA_HEADING");
2338 : const char *pszCameraTilt =
2339 125 : CSLFetchNameValue(papszOptions, "CAMERA_TILT");
2340 : const char *pszCameraRoll =
2341 125 : CSLFetchNameValue(papszOptions, "CAMERA_ROLL");
2342 : const char *pszCameraAltitudeMode =
2343 125 : CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDEMODE");
2344 125 : if (poOgrLayer != nullptr && pszCameraLongitude != nullptr &&
2345 1 : pszCameraLatitude != nullptr && pszCameraAltitude != nullptr &&
2346 : pszCameraAltitudeMode != nullptr)
2347 : {
2348 1 : poOgrLayer->SetCamera(pszCameraLongitude, pszCameraLatitude,
2349 : pszCameraAltitude, pszCameraHeading,
2350 : pszCameraTilt, pszCameraRoll,
2351 : pszCameraAltitudeMode);
2352 : }
2353 : }
2354 :
2355 : const char *pszRegionAdd =
2356 127 : CSLFetchNameValueDef(papszOptions, "ADD_REGION", "FALSE");
2357 127 : const char *pszRegionXMin = CSLFetchNameValue(papszOptions, "REGION_XMIN");
2358 127 : const char *pszRegionYMin = CSLFetchNameValue(papszOptions, "REGION_YMIN");
2359 127 : const char *pszRegionXMax = CSLFetchNameValue(papszOptions, "REGION_XMAX");
2360 127 : const char *pszRegionYMax = CSLFetchNameValue(papszOptions, "REGION_YMAX");
2361 : const char *pszRegionMinLodPixels =
2362 127 : CSLFetchNameValueDef(papszOptions, "REGION_MIN_LOD_PIXELS", "256");
2363 : const char *pszRegionMaxLodPixels =
2364 127 : CSLFetchNameValueDef(papszOptions, "REGION_MAX_LOD_PIXELS", "-1");
2365 : const char *pszRegionMinFadeExtent =
2366 127 : CSLFetchNameValueDef(papszOptions, "REGION_MIN_FADE_EXTENT", "0");
2367 : const char *pszRegionMaxFadeExtent =
2368 127 : CSLFetchNameValueDef(papszOptions, "REGION_MAX_FADE_EXTENT", "0");
2369 127 : if (poOgrLayer != nullptr && CPLTestBool(pszRegionAdd))
2370 : {
2371 2 : poOgrLayer->SetWriteRegion(
2372 : CPLAtof(pszRegionMinLodPixels), CPLAtof(pszRegionMaxLodPixels),
2373 : CPLAtof(pszRegionMinFadeExtent), CPLAtof(pszRegionMaxFadeExtent));
2374 2 : if (pszRegionXMin != nullptr && pszRegionYMin != nullptr &&
2375 1 : pszRegionXMax != nullptr && pszRegionYMax != nullptr)
2376 : {
2377 1 : const double xmin = CPLAtof(pszRegionXMin);
2378 1 : const double ymin = CPLAtof(pszRegionYMin);
2379 1 : const double xmax = CPLAtof(pszRegionXMax);
2380 1 : const double ymax = CPLAtof(pszRegionYMax);
2381 1 : if (xmin < xmax && ymin < ymax)
2382 1 : poOgrLayer->SetRegionBounds(xmin, ymin, xmax, ymax);
2383 : }
2384 : }
2385 :
2386 127 : const char *pszSOHref = CSLFetchNameValue(papszOptions, "SO_HREF");
2387 127 : const char *pszSOName = CSLFetchNameValue(papszOptions, "SO_NAME");
2388 : const char *pszSODescription =
2389 127 : CSLFetchNameValue(papszOptions, "SO_DESCRIPTION");
2390 127 : const char *pszSOOverlayX = CSLFetchNameValue(papszOptions, "SO_OVERLAY_X");
2391 127 : const char *pszSOOverlayY = CSLFetchNameValue(papszOptions, "SO_OVERLAY_Y");
2392 : const char *pszSOOverlayXUnits =
2393 127 : CSLFetchNameValue(papszOptions, "SO_OVERLAY_XUNITS");
2394 : const char *pszSOOverlayYUnits =
2395 127 : CSLFetchNameValue(papszOptions, "SO_OVERLAY_YUNITS");
2396 127 : const char *pszSOScreenX = CSLFetchNameValue(papszOptions, "SO_SCREEN_X");
2397 127 : const char *pszSOScreenY = CSLFetchNameValue(papszOptions, "SO_SCREEN_Y");
2398 : const char *pszSOScreenXUnits =
2399 127 : CSLFetchNameValue(papszOptions, "SO_SCREEN_XUNITS");
2400 : const char *pszSOScreenYUnits =
2401 127 : CSLFetchNameValue(papszOptions, "SO_SCREEN_YUNITS");
2402 127 : const char *pszSOSizeX = CSLFetchNameValue(papszOptions, "SO_SIZE_X");
2403 127 : const char *pszSOSizeY = CSLFetchNameValue(papszOptions, "SO_SIZE_Y");
2404 : const char *pszSOSizeXUnits =
2405 127 : CSLFetchNameValue(papszOptions, "SO_SIZE_XUNITS");
2406 : const char *pszSOSizeYUnits =
2407 127 : CSLFetchNameValue(papszOptions, "SO_SIZE_YUNITS");
2408 127 : if (poOgrLayer != nullptr && pszSOHref != nullptr)
2409 : {
2410 2 : poOgrLayer->SetScreenOverlay(
2411 : pszSOHref, pszSOName, pszSODescription, pszSOOverlayX,
2412 : pszSOOverlayY, pszSOOverlayXUnits, pszSOOverlayYUnits, pszSOScreenX,
2413 : pszSOScreenY, pszSOScreenXUnits, pszSOScreenYUnits, pszSOSizeX,
2414 : pszSOSizeY, pszSOSizeXUnits, pszSOSizeYUnits);
2415 : }
2416 :
2417 : const char *pszListStyleType =
2418 127 : CSLFetchNameValue(papszOptions, "LISTSTYLE_TYPE");
2419 : const char *pszListStyleIconHref =
2420 127 : CSLFetchNameValue(papszOptions, "LISTSTYLE_ICON_HREF");
2421 127 : if (poOgrLayer != nullptr)
2422 : {
2423 127 : poOgrLayer->SetListStyle(pszListStyleType, pszListStyleIconHref);
2424 : }
2425 :
2426 127 : if (poOgrLayer != nullptr && poOgrLayer->GetKmlLayer())
2427 : {
2428 124 : SetCommonOptions(poOgrLayer->GetKmlLayer(), papszOptions);
2429 : }
2430 :
2431 : /***** mark the dataset as updated *****/
2432 127 : if (poOgrLayer)
2433 127 : bUpdated = true;
2434 :
2435 127 : return poOgrLayer;
2436 : }
2437 :
2438 : /******************************************************************************
2439 : Method to get a datasources style table.
2440 :
2441 : Args: none
2442 :
2443 : Returns: pointer to the datasources style table, or NULL if it does
2444 : not have one
2445 :
2446 : ******************************************************************************/
2447 :
2448 7 : OGRStyleTable *OGRLIBKMLDataSource::GetStyleTable()
2449 : {
2450 7 : return m_poStyleTable;
2451 : }
2452 :
2453 : /******************************************************************************
2454 : Method to write a style table to a single file .kml ds.
2455 :
2456 : Args: poStyleTable pointer to the style table to add
2457 :
2458 : Returns: nothing
2459 :
2460 : ******************************************************************************/
2461 :
2462 4 : void OGRLIBKMLDataSource::SetStyleTable2Kml(OGRStyleTable *poStyleTable)
2463 : {
2464 4 : if (!m_poKmlDSContainer)
2465 0 : return;
2466 :
2467 : /***** delete all the styles *****/
2468 :
2469 4 : DocumentPtr poKmlDocument = AsDocument(m_poKmlDSContainer);
2470 : int nKmlStyles =
2471 4 : static_cast<int>(poKmlDocument->get_styleselector_array_size());
2472 :
2473 4 : for (int iKmlStyle = nKmlStyles - 1; iKmlStyle >= 0; iKmlStyle--)
2474 : {
2475 0 : poKmlDocument->DeleteStyleSelectorAt(iKmlStyle);
2476 : }
2477 :
2478 : /***** add the new style table to the document *****/
2479 :
2480 4 : styletable2kml(poStyleTable, m_poKmlFactory, AsContainer(poKmlDocument),
2481 4 : m_papszOptions);
2482 : }
2483 :
2484 : /******************************************************************************
2485 : Method to write a style table to a kmz ds.
2486 :
2487 : Args: poStyleTable pointer to the style table to add
2488 :
2489 : Returns: nothing
2490 :
2491 : ******************************************************************************/
2492 :
2493 1 : void OGRLIBKMLDataSource::SetStyleTable2Kmz(OGRStyleTable *poStyleTable)
2494 : {
2495 1 : if (m_poKmlStyleKml || poStyleTable != nullptr)
2496 : {
2497 : /***** replace the style document with a new one *****/
2498 :
2499 1 : m_poKmlStyleKml = m_poKmlFactory->CreateDocument();
2500 1 : m_poKmlStyleKml->set_id("styleId");
2501 :
2502 1 : styletable2kml(poStyleTable, m_poKmlFactory, m_poKmlStyleKml);
2503 : }
2504 1 : }
2505 :
2506 : /******************************************************************************
2507 : Method to write a style table to a datasource.
2508 :
2509 : Args: poStyleTable pointer to the style table to add
2510 :
2511 : Returns: nothing
2512 :
2513 : Note: This method assumes ownership of the style table.
2514 :
2515 : ******************************************************************************/
2516 :
2517 5 : void OGRLIBKMLDataSource::SetStyleTableDirectly(OGRStyleTable *poStyleTable)
2518 : {
2519 5 : if (!bUpdate)
2520 0 : return;
2521 :
2522 5 : if (m_poStyleTable)
2523 0 : delete m_poStyleTable;
2524 :
2525 5 : m_poStyleTable = poStyleTable;
2526 :
2527 : /***** a kml datasource? *****/
2528 5 : if (IsKml())
2529 4 : SetStyleTable2Kml(m_poStyleTable);
2530 :
2531 1 : else if (IsKmz() || IsDir())
2532 1 : SetStyleTable2Kmz(m_poStyleTable);
2533 :
2534 5 : bUpdated = true;
2535 : }
2536 :
2537 : /******************************************************************************
2538 : Method to write a style table to a datasource.
2539 :
2540 : Args: poStyleTable pointer to the style table to add
2541 :
2542 : Returns: nothing
2543 :
2544 : Note: This method copies the style table, and the user will still be
2545 : responsible for its destruction.
2546 :
2547 : ******************************************************************************/
2548 :
2549 5 : void OGRLIBKMLDataSource::SetStyleTable(OGRStyleTable *poStyleTable)
2550 : {
2551 5 : if (!bUpdate)
2552 0 : return;
2553 :
2554 5 : if (poStyleTable)
2555 4 : SetStyleTableDirectly(poStyleTable->Clone());
2556 : else
2557 1 : SetStyleTableDirectly(nullptr);
2558 : }
2559 :
2560 : /******************************************************************************
2561 : Test if capability is available.
2562 :
2563 : Args: pszCap datasource capability name to test
2564 :
2565 : Returns: TRUE or FALSE
2566 :
2567 : ODsCCreateLayer: True if this datasource can create new layers.
2568 : ODsCDeleteLayer: True if this datasource can delete existing layers.
2569 :
2570 : ******************************************************************************/
2571 :
2572 104 : int OGRLIBKMLDataSource::TestCapability(const char *pszCap) const
2573 : {
2574 104 : if (EQUAL(pszCap, ODsCCreateLayer))
2575 38 : return bUpdate;
2576 66 : else if (EQUAL(pszCap, ODsCDeleteLayer))
2577 18 : return bUpdate;
2578 48 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
2579 0 : return bUpdate;
2580 48 : else if (EQUAL(pszCap, ODsCZGeometries))
2581 22 : return TRUE;
2582 :
2583 26 : return FALSE;
2584 : }
|