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