/src/gdal/ogr/ogrsf_frmts/kml/kml.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: KML Driver |
4 | | * Purpose: Class for reading, parsing and handling a kmlfile. |
5 | | * Author: Jens Oberender, j.obi@troja.net |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2007, Jens Oberender |
9 | | * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | #include "kmlnode.h" |
14 | | #include "kml.h" |
15 | | |
16 | | #include <cstring> |
17 | | #include <cstdio> |
18 | | #include <exception> |
19 | | #include <iostream> |
20 | | #include <string> |
21 | | |
22 | | #include "cpl_conv.h" |
23 | | #include "cpl_error.h" |
24 | | #ifdef HAVE_EXPAT |
25 | | #include "expat.h" |
26 | | #endif |
27 | | |
28 | | constexpr int PARSER_BUF_SIZE = 8192; |
29 | | |
30 | 3.55k | KML::KML() = default; |
31 | | |
32 | | KML::~KML() |
33 | 3.55k | { |
34 | 3.55k | if (nullptr != pKMLFile_) |
35 | 3.55k | VSIFCloseL(pKMLFile_); |
36 | 3.55k | CPLFree(papoLayers_); |
37 | | |
38 | 3.55k | delete poTrunk_; |
39 | 3.55k | } |
40 | | |
41 | | bool KML::open(const char *pszFilename) |
42 | 3.55k | { |
43 | 3.55k | if (nullptr != pKMLFile_) |
44 | 0 | VSIFCloseL(pKMLFile_); |
45 | | |
46 | 3.55k | pKMLFile_ = VSIFOpenL(pszFilename, "r"); |
47 | 3.55k | return pKMLFile_ != nullptr; |
48 | 3.55k | } |
49 | | |
50 | | bool KML::parse() |
51 | 744 | { |
52 | 744 | if (nullptr == pKMLFile_) |
53 | 0 | { |
54 | 0 | sError_ = "No file given"; |
55 | 0 | return false; |
56 | 0 | } |
57 | | |
58 | 744 | if (poTrunk_ != nullptr) |
59 | 0 | { |
60 | 0 | delete poTrunk_; |
61 | 0 | poTrunk_ = nullptr; |
62 | 0 | } |
63 | | |
64 | 744 | if (poCurrent_ != nullptr) |
65 | 0 | { |
66 | 0 | delete poCurrent_; |
67 | 0 | poCurrent_ = nullptr; |
68 | 0 | } |
69 | | |
70 | 744 | XML_Parser oParser = OGRCreateExpatXMLParser(); |
71 | 744 | XML_SetUserData(oParser, this); |
72 | 744 | XML_SetElementHandler(oParser, startElement, endElement); |
73 | 744 | XML_SetCharacterDataHandler(oParser, dataHandler); |
74 | 744 | oCurrentParser = oParser; |
75 | 744 | nWithoutEventCounter = 0; |
76 | | |
77 | 744 | int nDone = 0; |
78 | 744 | unsigned nLen = 0; |
79 | 744 | std::vector<char> aBuf(PARSER_BUF_SIZE); |
80 | 744 | bool bError = false; |
81 | | |
82 | 744 | do |
83 | 16.7k | { |
84 | 16.7k | nDataHandlerCounter = 0; |
85 | 16.7k | nLen = static_cast<unsigned>( |
86 | 16.7k | VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_)); |
87 | 16.7k | nDone = nLen < aBuf.size(); |
88 | 16.7k | if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR) |
89 | 261 | { |
90 | 261 | CPLError(CE_Failure, CPLE_AppDefined, |
91 | 261 | "XML parsing of KML file failed : %s at line %d, " |
92 | 261 | "column %d", |
93 | 261 | XML_ErrorString(XML_GetErrorCode(oParser)), |
94 | 261 | static_cast<int>(XML_GetCurrentLineNumber(oParser)), |
95 | 261 | static_cast<int>(XML_GetCurrentColumnNumber(oParser))); |
96 | 261 | bError = true; |
97 | 261 | break; |
98 | 261 | } |
99 | 16.4k | nWithoutEventCounter++; |
100 | 16.4k | } while (!nDone && nLen > 0 && nWithoutEventCounter < 10); |
101 | | |
102 | 744 | XML_ParserFree(oParser); |
103 | 744 | VSIRewindL(pKMLFile_); |
104 | | |
105 | 744 | if (nWithoutEventCounter == 10) |
106 | 0 | { |
107 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
108 | 0 | "Too much data inside one element. File probably corrupted"); |
109 | 0 | bError = true; |
110 | 0 | } |
111 | | |
112 | 744 | if (bError) |
113 | 261 | { |
114 | 261 | if (poCurrent_ != nullptr) |
115 | 259 | { |
116 | 3.75k | while (poCurrent_) |
117 | 3.49k | { |
118 | 3.49k | KMLNode *poTemp = poCurrent_->getParent(); |
119 | 3.49k | delete poCurrent_; |
120 | 3.49k | poCurrent_ = poTemp; |
121 | 3.49k | } |
122 | | // No need to destroy poTrunk_ : it has been destroyed in |
123 | | // the last iteration |
124 | 259 | } |
125 | 2 | else |
126 | 2 | { |
127 | | // Case of invalid content after closing element matching |
128 | | // first <kml> element |
129 | 2 | delete poTrunk_; |
130 | 2 | } |
131 | 261 | poTrunk_ = nullptr; |
132 | 261 | return false; |
133 | 261 | } |
134 | | |
135 | 483 | poCurrent_ = nullptr; |
136 | 483 | return true; |
137 | 744 | } |
138 | | |
139 | | void KML::checkValidity() |
140 | 3.55k | { |
141 | 3.55k | if (poTrunk_ != nullptr) |
142 | 0 | { |
143 | 0 | delete poTrunk_; |
144 | 0 | poTrunk_ = nullptr; |
145 | 0 | } |
146 | | |
147 | 3.55k | if (poCurrent_ != nullptr) |
148 | 0 | { |
149 | 0 | delete poCurrent_; |
150 | 0 | poCurrent_ = nullptr; |
151 | 0 | } |
152 | | |
153 | 3.55k | if (pKMLFile_ == nullptr) |
154 | 0 | { |
155 | 0 | sError_ = "No file given"; |
156 | 0 | return; |
157 | 0 | } |
158 | | |
159 | 3.55k | XML_Parser oParser = OGRCreateExpatXMLParser(); |
160 | 3.55k | XML_SetUserData(oParser, this); |
161 | 3.55k | XML_SetElementHandler(oParser, startElementValidate, nullptr); |
162 | 3.55k | XML_SetCharacterDataHandler(oParser, dataHandlerValidate); |
163 | 3.55k | int nCount = 0; |
164 | | |
165 | 3.55k | oCurrentParser = oParser; |
166 | | |
167 | 3.55k | int nDone = 0; |
168 | 3.55k | unsigned nLen = 0; |
169 | 3.55k | std::vector<char> aBuf(PARSER_BUF_SIZE); |
170 | | |
171 | | // Parses the file until we find the first element. |
172 | 3.55k | do |
173 | 3.56k | { |
174 | 3.56k | nDataHandlerCounter = 0; |
175 | 3.56k | nLen = static_cast<unsigned>( |
176 | 3.56k | VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_)); |
177 | 3.56k | nDone = nLen < aBuf.size(); |
178 | 3.56k | if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR) |
179 | 2.78k | { |
180 | 2.78k | if (nLen <= PARSER_BUF_SIZE - 1) |
181 | 1.24k | aBuf[nLen] = 0; |
182 | 1.53k | else |
183 | 1.53k | aBuf[PARSER_BUF_SIZE - 1] = 0; |
184 | 2.78k | if (strstr(aBuf.data(), "<?xml") && |
185 | 716 | (strstr(aBuf.data(), "<kml") || |
186 | 1 | (strstr(aBuf.data(), "<Document") && |
187 | 0 | strstr(aBuf.data(), "/kml/2.")))) |
188 | 715 | { |
189 | 715 | CPLError( |
190 | 715 | CE_Failure, CPLE_AppDefined, |
191 | 715 | "XML parsing of KML file failed : %s at line %d, column %d", |
192 | 715 | XML_ErrorString(XML_GetErrorCode(oParser)), |
193 | 715 | static_cast<int>(XML_GetCurrentLineNumber(oParser)), |
194 | 715 | static_cast<int>(XML_GetCurrentColumnNumber(oParser))); |
195 | 715 | } |
196 | | |
197 | 2.78k | validity = KML_VALIDITY_INVALID; |
198 | 2.78k | XML_ParserFree(oParser); |
199 | 2.78k | VSIRewindL(pKMLFile_); |
200 | 2.78k | return; |
201 | 2.78k | } |
202 | | |
203 | 786 | nCount++; |
204 | | /* After reading 50 * PARSER_BUF_SIZE bytes, and not finding whether the file */ |
205 | | /* is KML or not, we give up and fail silently */ |
206 | 786 | } while (!nDone && nLen > 0 && validity == KML_VALIDITY_UNKNOWN && |
207 | 16 | nCount < 50); |
208 | | |
209 | 770 | XML_ParserFree(oParser); |
210 | 770 | VSIRewindL(pKMLFile_); |
211 | 770 | poCurrent_ = nullptr; |
212 | 770 | } |
213 | | |
214 | | void XMLCALL KML::startElement(void *pUserData, const char *pszName, |
215 | | const char **ppszAttr) |
216 | 2.27M | { |
217 | 2.27M | KML *poKML = static_cast<KML *>(pUserData); |
218 | 2.27M | try |
219 | 2.27M | { |
220 | 2.27M | poKML->nWithoutEventCounter = 0; |
221 | | |
222 | 2.27M | const char *pszColumn = strchr(pszName, ':'); |
223 | 2.27M | if (pszColumn) |
224 | 366 | pszName = pszColumn + 1; |
225 | | |
226 | 2.27M | if (poKML->poTrunk_ == nullptr || |
227 | 2.27M | (poKML->poCurrent_ != nullptr && |
228 | 2.27M | poKML->poCurrent_->getName().compare("description") != 0)) |
229 | 2.27M | { |
230 | 2.27M | if (poKML->nDepth_ == 1024) |
231 | 0 | { |
232 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
233 | 0 | "Too big depth level (%d) while parsing KML.", |
234 | 0 | poKML->nDepth_); |
235 | 0 | XML_StopParser(poKML->oCurrentParser, XML_FALSE); |
236 | 0 | return; |
237 | 0 | } |
238 | | |
239 | 2.27M | KMLNode *poMynew = new KMLNode(); |
240 | 2.27M | poMynew->setName(pszName); |
241 | 2.27M | poMynew->setLevel(poKML->nDepth_); |
242 | | |
243 | 3.96M | for (int i = 0; ppszAttr[i]; i += 2) |
244 | 1.68M | { |
245 | 1.68M | Attribute *poAtt = new Attribute(); |
246 | 1.68M | poAtt->sName = ppszAttr[i]; |
247 | 1.68M | poAtt->sValue = ppszAttr[i + 1]; |
248 | 1.68M | poMynew->addAttribute(poAtt); |
249 | 1.68M | } |
250 | | |
251 | 2.27M | if (poKML->poTrunk_ == nullptr) |
252 | 744 | poKML->poTrunk_ = poMynew; |
253 | 2.27M | if (poKML->poCurrent_ != nullptr) |
254 | 2.27M | poMynew->setParent(poKML->poCurrent_); |
255 | 2.27M | poKML->poCurrent_ = poMynew; |
256 | | |
257 | 2.27M | poKML->nDepth_++; |
258 | 2.27M | } |
259 | 588 | else if (poKML->poCurrent_ != nullptr) |
260 | 588 | { |
261 | 588 | std::string sNewContent = "<"; |
262 | 588 | sNewContent += pszName; |
263 | 826 | for (int i = 0; ppszAttr[i]; i += 2) |
264 | 238 | { |
265 | 238 | sNewContent += " "; |
266 | 238 | sNewContent += ppszAttr[i]; |
267 | 238 | sNewContent += "=\""; |
268 | 238 | sNewContent += ppszAttr[i + 1]; |
269 | 238 | sNewContent += "\""; |
270 | 238 | } |
271 | 588 | sNewContent += ">"; |
272 | 588 | if (poKML->poCurrent_->numContent() == 0) |
273 | 0 | poKML->poCurrent_->addContent(sNewContent); |
274 | 588 | else |
275 | 588 | poKML->poCurrent_->appendContent(sNewContent); |
276 | 588 | } |
277 | 2.27M | } |
278 | 2.27M | catch (const std::exception &ex) |
279 | 2.27M | { |
280 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s", |
281 | 0 | ex.what()); |
282 | 0 | XML_StopParser(poKML->oCurrentParser, XML_FALSE); |
283 | 0 | } |
284 | 2.27M | } |
285 | | |
286 | | void XMLCALL KML::startElementValidate(void *pUserData, const char *pszName, |
287 | | const char **ppszAttr) |
288 | 99.5k | { |
289 | 99.5k | KML *poKML = static_cast<KML *>(pUserData); |
290 | | |
291 | 99.5k | if (poKML->validity != KML_VALIDITY_UNKNOWN) |
292 | 98.1k | return; |
293 | | |
294 | 1.40k | poKML->validity = KML_VALIDITY_INVALID; |
295 | | |
296 | 1.40k | const char *pszColumn = strchr(pszName, ':'); |
297 | 1.40k | if (pszColumn) |
298 | 2 | pszName = pszColumn + 1; |
299 | | |
300 | 1.40k | if (strcmp(pszName, "kml") == 0 || strcmp(pszName, "Document") == 0) |
301 | 1.30k | { |
302 | | // Check all Attributes |
303 | 2.59k | for (int i = 0; ppszAttr[i]; i += 2) |
304 | 1.29k | { |
305 | | // Find the namespace and determine the KML version |
306 | 1.29k | if (strcmp(ppszAttr[i], "xmlns") == 0) |
307 | 1.28k | { |
308 | | // Is it KML 2.2? |
309 | 1.28k | if ((strcmp(ppszAttr[i + 1], |
310 | 1.28k | "http://earth.google.com/kml/2.2") == 0) || |
311 | 1.28k | (strcmp(ppszAttr[i + 1], |
312 | 1.28k | "http://www.opengis.net/kml/2.2") == 0)) |
313 | 1.23k | { |
314 | 1.23k | poKML->validity = KML_VALIDITY_VALID; |
315 | 1.23k | poKML->sVersion_ = "2.2"; |
316 | 1.23k | } |
317 | 50 | else if (strcmp(ppszAttr[i + 1], |
318 | 50 | "http://earth.google.com/kml/2.1") == 0) |
319 | 26 | { |
320 | 26 | poKML->validity = KML_VALIDITY_VALID; |
321 | 26 | poKML->sVersion_ = "2.1"; |
322 | 26 | } |
323 | 24 | else if (strcmp(ppszAttr[i + 1], |
324 | 24 | "http://earth.google.com/kml/2.0") == 0) |
325 | 0 | { |
326 | 0 | poKML->validity = KML_VALIDITY_VALID; |
327 | 0 | poKML->sVersion_ = "2.0"; |
328 | 0 | } |
329 | 24 | else |
330 | 24 | { |
331 | 24 | CPLDebug("KML", |
332 | 24 | "Unhandled xmlns value : %s. Going on though...", |
333 | 24 | ppszAttr[i]); |
334 | 24 | poKML->validity = KML_VALIDITY_VALID; |
335 | 24 | poKML->sVersion_ = "?"; |
336 | 24 | } |
337 | 1.28k | } |
338 | 1.29k | } |
339 | | |
340 | 1.30k | if (poKML->validity == KML_VALIDITY_INVALID) |
341 | 25 | { |
342 | 25 | CPLDebug("KML", "Did not find xmlns attribute in <kml> element. " |
343 | 25 | "Going on though..."); |
344 | 25 | poKML->validity = KML_VALIDITY_VALID; |
345 | 25 | poKML->sVersion_ = "?"; |
346 | 25 | } |
347 | 1.30k | } |
348 | 1.40k | } |
349 | | |
350 | | void XMLCALL KML::dataHandlerValidate(void *pUserData, |
351 | | const char * /* pszData */, |
352 | | int /* nLen */) |
353 | 290k | { |
354 | 290k | KML *poKML = static_cast<KML *>(pUserData); |
355 | | |
356 | 290k | poKML->nDataHandlerCounter++; |
357 | 290k | if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE) |
358 | 0 | { |
359 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
360 | 0 | "File probably corrupted (million laugh pattern)"); |
361 | 0 | XML_StopParser(poKML->oCurrentParser, XML_FALSE); |
362 | 0 | } |
363 | 290k | } |
364 | | |
365 | | void XMLCALL KML::endElement(void *pUserData, const char *pszName) |
366 | 2.26M | { |
367 | 2.26M | KML *poKML = static_cast<KML *>(pUserData); |
368 | | |
369 | 2.26M | try |
370 | 2.26M | { |
371 | 2.26M | poKML->nWithoutEventCounter = 0; |
372 | | |
373 | 2.26M | const char *pszColumn = strchr(pszName, ':'); |
374 | 2.26M | if (pszColumn) |
375 | 352 | pszName = pszColumn + 1; |
376 | | |
377 | 2.26M | if (poKML->poCurrent_ != nullptr && |
378 | 2.26M | poKML->poCurrent_->getName().compare(pszName) == 0) |
379 | 2.26M | { |
380 | 2.26M | poKML->nDepth_--; |
381 | 2.26M | KMLNode *poTmp = poKML->poCurrent_; |
382 | | // Split the coordinates |
383 | 2.26M | if (poKML->poCurrent_->getName().compare("coordinates") == 0 && |
384 | 20.5k | poKML->poCurrent_->numContent() == 1) |
385 | 15.1k | { |
386 | 15.1k | const std::string sData = poKML->poCurrent_->getContent(0); |
387 | 15.1k | std::size_t nPos = 0; |
388 | 15.1k | const std::size_t nLength = sData.length(); |
389 | 15.1k | const char *pszData = sData.c_str(); |
390 | 416k | while (true) |
391 | 416k | { |
392 | | // Cut off whitespaces |
393 | 837k | while (nPos < nLength && |
394 | 821k | (pszData[nPos] == ' ' || pszData[nPos] == '\n' || |
395 | 401k | pszData[nPos] == '\r' || pszData[nPos] == '\t')) |
396 | 420k | nPos++; |
397 | | |
398 | 416k | if (nPos == nLength) |
399 | 15.1k | break; |
400 | | |
401 | 401k | const std::size_t nPosBegin = nPos; |
402 | 401k | size_t nContentSize = 0; |
403 | | |
404 | | // Get content |
405 | 3.73M | while (nPos < nLength && pszData[nPos] != ' ' && |
406 | 3.39M | pszData[nPos] != '\n' && pszData[nPos] != '\r' && |
407 | 3.33M | pszData[nPos] != '\t') |
408 | 3.33M | { |
409 | 3.33M | nContentSize++; |
410 | 3.33M | nPos++; |
411 | 3.33M | } |
412 | | |
413 | 401k | if (nContentSize > 0) |
414 | 401k | { |
415 | 401k | std::string sTmp(pszData + nPosBegin, nContentSize); |
416 | 401k | poKML->poCurrent_->addContent(sTmp); |
417 | 401k | } |
418 | 401k | } |
419 | 15.1k | if (poKML->poCurrent_->numContent() > 1) |
420 | 15.1k | poKML->poCurrent_->deleteContent(0); |
421 | 15.1k | } |
422 | 2.25M | else if (poKML->poCurrent_->numContent() == 1) |
423 | 1.63M | { |
424 | 1.63M | const std::string sData = poKML->poCurrent_->getContent(0); |
425 | 1.63M | std::string sDataWithoutNL; |
426 | 1.63M | std::size_t nPos = 0; |
427 | 1.63M | const std::size_t nLength = sData.length(); |
428 | 1.63M | const char *pszData = sData.c_str(); |
429 | 1.63M | std::size_t nLineStartPos = 0; |
430 | 1.63M | bool bLineStart = true; |
431 | | |
432 | | // Re-assemble multi-line content by removing leading spaces for |
433 | | // each line. I am not sure why we do that. Should we preserve |
434 | | // content as such? |
435 | 34.6M | while (nPos < nLength) |
436 | 33.0M | { |
437 | 33.0M | const char ch = pszData[nPos]; |
438 | 33.0M | if (bLineStart && |
439 | 8.29M | (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) |
440 | 7.48M | nLineStartPos++; |
441 | 25.5M | else if (ch == '\n' || ch == '\r') |
442 | 217k | { |
443 | 217k | if (!bLineStart) |
444 | 217k | { |
445 | 217k | std::string sTmp(pszData + nLineStartPos, |
446 | 217k | nPos - nLineStartPos); |
447 | 217k | if (!sDataWithoutNL.empty()) |
448 | 174k | sDataWithoutNL += '\n'; |
449 | 217k | sDataWithoutNL += sTmp; |
450 | 217k | bLineStart = true; |
451 | 217k | } |
452 | 217k | nLineStartPos = nPos + 1; |
453 | 217k | } |
454 | 25.3M | else |
455 | 25.3M | { |
456 | 25.3M | bLineStart = false; |
457 | 25.3M | } |
458 | 33.0M | nPos++; |
459 | 33.0M | } |
460 | | |
461 | 1.63M | if (nLineStartPos > 0) |
462 | 1.07M | { |
463 | 1.07M | if (nLineStartPos < nPos) |
464 | 44.1k | { |
465 | 44.1k | std::string sTmp(pszData + nLineStartPos, |
466 | 44.1k | nPos - nLineStartPos); |
467 | 44.1k | if (!sDataWithoutNL.empty()) |
468 | 42.9k | sDataWithoutNL += '\n'; |
469 | 44.1k | sDataWithoutNL += sTmp; |
470 | 44.1k | } |
471 | | |
472 | 1.07M | poKML->poCurrent_->deleteContent(0); |
473 | 1.07M | poKML->poCurrent_->addContent(sDataWithoutNL); |
474 | 1.07M | } |
475 | 1.63M | } |
476 | | |
477 | 2.26M | if (poKML->poCurrent_->getParent() != nullptr) |
478 | 2.26M | poKML->poCurrent_ = poKML->poCurrent_->getParent(); |
479 | 485 | else |
480 | 485 | poKML->poCurrent_ = nullptr; |
481 | | |
482 | 2.26M | if (!poKML->isHandled(pszName)) |
483 | 1.69M | { |
484 | 1.69M | CPLDebug("KML", "Not handled: %s", pszName); |
485 | 1.69M | delete poTmp; |
486 | 1.69M | if (poKML->poCurrent_ == poTmp) |
487 | 0 | poKML->poCurrent_ = nullptr; |
488 | 1.69M | if (poKML->poTrunk_ == poTmp) |
489 | 0 | poKML->poTrunk_ = nullptr; |
490 | 1.69M | } |
491 | 576k | else |
492 | 576k | { |
493 | 576k | if (poKML->poCurrent_ != nullptr) |
494 | 576k | poKML->poCurrent_->addChildren(poTmp); |
495 | 576k | } |
496 | 2.26M | } |
497 | 523 | else if (poKML->poCurrent_ != nullptr) |
498 | 523 | { |
499 | 523 | std::string sNewContent = "</"; |
500 | 523 | sNewContent += pszName; |
501 | 523 | sNewContent += ">"; |
502 | 523 | if (poKML->poCurrent_->numContent() == 0) |
503 | 0 | poKML->poCurrent_->addContent(sNewContent); |
504 | 523 | else |
505 | 523 | poKML->poCurrent_->appendContent(sNewContent); |
506 | 523 | } |
507 | 2.26M | } |
508 | 2.26M | catch (const std::exception &ex) |
509 | 2.26M | { |
510 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s", |
511 | 0 | ex.what()); |
512 | 0 | XML_StopParser(poKML->oCurrentParser, XML_FALSE); |
513 | 0 | } |
514 | 2.26M | } |
515 | | |
516 | | void XMLCALL KML::dataHandler(void *pUserData, const char *pszData, int nLen) |
517 | 6.98M | { |
518 | 6.98M | KML *poKML = static_cast<KML *>(pUserData); |
519 | | |
520 | 6.98M | poKML->nWithoutEventCounter = 0; |
521 | | |
522 | 6.98M | if (nLen < 1 || poKML->poCurrent_ == nullptr) |
523 | 0 | return; |
524 | | |
525 | 6.98M | poKML->nDataHandlerCounter++; |
526 | 6.98M | if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE) |
527 | 0 | { |
528 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
529 | 0 | "File probably corrupted (million laugh pattern)"); |
530 | 0 | XML_StopParser(poKML->oCurrentParser, XML_FALSE); |
531 | 0 | } |
532 | | |
533 | 6.98M | try |
534 | 6.98M | { |
535 | 6.98M | std::string sData(pszData, nLen); |
536 | | |
537 | 6.98M | if (poKML->poCurrent_->numContent() == 0) |
538 | 1.64M | poKML->poCurrent_->addContent(sData); |
539 | 5.33M | else |
540 | 5.33M | poKML->poCurrent_->appendContent(sData); |
541 | 6.98M | } |
542 | 6.98M | catch (const std::exception &ex) |
543 | 6.98M | { |
544 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s", |
545 | 0 | ex.what()); |
546 | 0 | XML_StopParser(poKML->oCurrentParser, XML_FALSE); |
547 | 0 | } |
548 | 6.98M | } |
549 | | |
550 | | bool KML::isValid() |
551 | 3.55k | { |
552 | 3.55k | checkValidity(); |
553 | | |
554 | 3.55k | if (validity == KML_VALIDITY_VALID) |
555 | 744 | CPLDebug("KML", "Valid: 1 Version: %s", sVersion_.c_str()); |
556 | | |
557 | 3.55k | return validity == KML_VALIDITY_VALID; |
558 | 3.55k | } |
559 | | |
560 | | std::string KML::getError() const |
561 | 0 | { |
562 | 0 | return sError_; |
563 | 0 | } |
564 | | |
565 | | int KML::classifyNodes() |
566 | 483 | { |
567 | 483 | if (poTrunk_ == nullptr) |
568 | 0 | return false; |
569 | 483 | return poTrunk_->classify(this); |
570 | 483 | } |
571 | | |
572 | | void KML::eliminateEmpty() |
573 | 186 | { |
574 | 186 | if (poTrunk_ != nullptr) |
575 | 186 | poTrunk_->eliminateEmpty(this); |
576 | 186 | } |
577 | | |
578 | | void KML::print(unsigned short nNum) |
579 | 0 | { |
580 | 0 | if (poTrunk_ != nullptr) |
581 | 0 | poTrunk_->print(nNum); |
582 | 0 | } |
583 | | |
584 | | bool KML::isHandled(std::string const &elem) const |
585 | 2.26M | { |
586 | 2.26M | return isLeaf(elem) || isFeature(elem) || isFeatureContainer(elem) || |
587 | 1.69M | isContainer(elem) || isRest(elem); |
588 | 2.26M | } |
589 | | |
590 | | bool KML::isLeaf(std::string const & /* elem */) const |
591 | 0 | { |
592 | 0 | return false; |
593 | 0 | } |
594 | | |
595 | | bool KML::isFeature(std::string const & /* elem */) const |
596 | 0 | { |
597 | 0 | return false; |
598 | 0 | } |
599 | | |
600 | | bool KML::isFeatureContainer(std::string const & /* elem */) const |
601 | 0 | { |
602 | 0 | return false; |
603 | 0 | } |
604 | | |
605 | | bool KML::isContainer(std::string const & /* elem */) const |
606 | 0 | { |
607 | 0 | return false; |
608 | 0 | } |
609 | | |
610 | | bool KML::isRest(std::string const & /* elem */) const |
611 | 0 | { |
612 | 0 | return false; |
613 | 0 | } |
614 | | |
615 | | void KML::findLayers(KMLNode * /* poNode */, int /* bKeepEmptyContainers */) |
616 | 0 | { |
617 | | // idle |
618 | 0 | } |
619 | | |
620 | | bool KML::hasOnlyEmpty() const |
621 | 480 | { |
622 | 480 | return poTrunk_->hasOnlyEmpty(); |
623 | 480 | } |
624 | | |
625 | | int KML::getNumLayers() const |
626 | 480 | { |
627 | 480 | return nNumLayers_; |
628 | 480 | } |
629 | | |
630 | | bool KML::selectLayer(int nNum) |
631 | 1.29k | { |
632 | 1.29k | if (nNumLayers_ < 1 || nNum >= nNumLayers_) |
633 | 0 | return false; |
634 | 1.29k | poCurrent_ = papoLayers_[nNum]; |
635 | 1.29k | return true; |
636 | 1.29k | } |
637 | | |
638 | | std::string KML::getCurrentName() const |
639 | 681 | { |
640 | 681 | std::string tmp; |
641 | 681 | if (poCurrent_ != nullptr) |
642 | 681 | { |
643 | 681 | tmp = poCurrent_->getNameElement(); |
644 | 681 | } |
645 | 681 | return tmp; |
646 | 681 | } |
647 | | |
648 | | Nodetype KML::getCurrentType() const |
649 | 4.21k | { |
650 | 4.21k | if (poCurrent_ != nullptr) |
651 | 4.21k | return poCurrent_->getType(); |
652 | | |
653 | 0 | return Unknown; |
654 | 4.21k | } |
655 | | |
656 | | int KML::is25D() const |
657 | 103 | { |
658 | 103 | if (poCurrent_ != nullptr) |
659 | 103 | return poCurrent_->is25D(); |
660 | | |
661 | 0 | return Unknown; |
662 | 103 | } |
663 | | |
664 | | int KML::getNumFeatures() |
665 | 0 | { |
666 | 0 | if (poCurrent_ == nullptr) |
667 | 0 | return -1; |
668 | | |
669 | 0 | return static_cast<int>(poCurrent_->getNumFeatures()); |
670 | 0 | } |
671 | | |
672 | | Feature *KML::getFeature(std::size_t nNum, int &nLastAsked, int &nLastCount) |
673 | 626 | { |
674 | 626 | if (poCurrent_ == nullptr) |
675 | 0 | return nullptr; |
676 | | |
677 | 626 | return poCurrent_->getFeature(nNum, nLastAsked, nLastCount); |
678 | 626 | } |
679 | | |
680 | | void KML::unregisterLayerIfMatchingThisNode(KMLNode *poNode) |
681 | 485k | { |
682 | 489k | for (int i = 0; i < nNumLayers_;) |
683 | 3.62k | { |
684 | 3.62k | if (papoLayers_[i] == poNode) |
685 | 0 | { |
686 | 0 | if (i < nNumLayers_ - 1) |
687 | 0 | { |
688 | 0 | memmove(papoLayers_ + i, papoLayers_ + i + 1, |
689 | 0 | (nNumLayers_ - 1 - i) * sizeof(KMLNode *)); |
690 | 0 | } |
691 | 0 | nNumLayers_--; |
692 | 0 | break; |
693 | 0 | } |
694 | 3.62k | i++; |
695 | 3.62k | } |
696 | 485k | } |