/src/gdal/ogr/ogrsf_frmts/kml/ogrkmldatasource.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: KML Driver |
4 | | * Purpose: Implementation of OGRKMLDataSource class. |
5 | | * Author: Christopher Condit, condit@sdsc.edu; |
6 | | * Jens Oberender, j.obi@troja.net |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2006, Christopher Condit |
10 | | * 2007, Jens Oberender |
11 | | * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com> |
12 | | * |
13 | | * SPDX-License-Identifier: MIT |
14 | | ****************************************************************************/ |
15 | | #include "cpl_port.h" |
16 | | #include "ogr_kml.h" |
17 | | |
18 | | #include <cstring> |
19 | | #include <string> |
20 | | |
21 | | #include "cpl_conv.h" |
22 | | #include "cpl_error.h" |
23 | | #include "cpl_minixml.h" |
24 | | #include "cpl_string.h" |
25 | | #include "cpl_vsi.h" |
26 | | #include "cpl_vsi_error.h" |
27 | | #include "ogr_core.h" |
28 | | #include "ogr_spatialref.h" |
29 | | #include "kml.h" |
30 | | #include "kmlutility.h" |
31 | | #include "kmlvector.h" |
32 | | #include "ogrsf_frmts.h" |
33 | | |
34 | | /************************************************************************/ |
35 | | /* OGRKMLDataSource() */ |
36 | | /************************************************************************/ |
37 | | |
38 | 3.04k | OGRKMLDataSource::OGRKMLDataSource() = default; |
39 | | |
40 | | /************************************************************************/ |
41 | | /* ~OGRKMLDataSource() */ |
42 | | /************************************************************************/ |
43 | | |
44 | | OGRKMLDataSource::~OGRKMLDataSource() |
45 | 3.04k | { |
46 | 3.04k | if (fpOutput_ != nullptr) |
47 | 151 | { |
48 | 151 | if (nLayers_ > 0) |
49 | 150 | { |
50 | 150 | if (nLayers_ == 1 && papoLayers_[0]->nWroteFeatureCount_ == 0) |
51 | 0 | { |
52 | 0 | VSIFPrintfL(fpOutput_, "<Folder><name>%s</name>\n", |
53 | 0 | papoLayers_[0]->GetName()); |
54 | 0 | } |
55 | | |
56 | 150 | VSIFPrintfL(fpOutput_, "%s", "</Folder>\n"); |
57 | | |
58 | 769 | for (int i = 0; i < nLayers_; i++) |
59 | 619 | { |
60 | 619 | if (!(papoLayers_[i]->bSchemaWritten_) && |
61 | 515 | papoLayers_[i]->nWroteFeatureCount_ != 0) |
62 | 357 | { |
63 | 357 | CPLString osRet = papoLayers_[i]->WriteSchema(); |
64 | 357 | if (!osRet.empty()) |
65 | 339 | VSIFPrintfL(fpOutput_, "%s", osRet.c_str()); |
66 | 357 | } |
67 | 619 | } |
68 | 150 | } |
69 | 151 | VSIFPrintfL(fpOutput_, "%s", "</Document></kml>\n"); |
70 | | |
71 | 151 | VSIFCloseL(fpOutput_); |
72 | 151 | } |
73 | | |
74 | 3.04k | CSLDestroy(papszCreateOptions_); |
75 | 3.04k | CPLFree(pszNameField_); |
76 | 3.04k | CPLFree(pszDescriptionField_); |
77 | 3.04k | CPLFree(pszAltitudeMode_); |
78 | | |
79 | 3.87k | for (int i = 0; i < nLayers_; i++) |
80 | 822 | { |
81 | 822 | delete papoLayers_[i]; |
82 | 822 | } |
83 | | |
84 | 3.04k | CPLFree(papoLayers_); |
85 | | |
86 | 3.04k | #ifdef HAVE_EXPAT |
87 | 3.04k | delete poKMLFile_; |
88 | 3.04k | #endif |
89 | 3.04k | } |
90 | | |
91 | | /************************************************************************/ |
92 | | /* Open() */ |
93 | | /************************************************************************/ |
94 | | |
95 | | #ifdef HAVE_EXPAT |
96 | | int OGRKMLDataSource::Open(const char *pszNewName, int bTestOpen) |
97 | 2.89k | { |
98 | 2.89k | CPLAssert(nullptr != pszNewName); |
99 | | |
100 | | /* -------------------------------------------------------------------- */ |
101 | | /* Create a KML object and open the source file. */ |
102 | | /* -------------------------------------------------------------------- */ |
103 | 2.89k | poKMLFile_ = new KMLVector(); |
104 | | |
105 | 2.89k | if (!poKMLFile_->open(pszNewName)) |
106 | 0 | { |
107 | 0 | delete poKMLFile_; |
108 | 0 | poKMLFile_ = nullptr; |
109 | 0 | return FALSE; |
110 | 0 | } |
111 | | |
112 | | /* -------------------------------------------------------------------- */ |
113 | | /* If we aren't sure it is KML, validate it by start parsing */ |
114 | | /* -------------------------------------------------------------------- */ |
115 | 2.89k | if (bTestOpen && !poKMLFile_->isValid()) |
116 | 2.58k | { |
117 | 2.58k | delete poKMLFile_; |
118 | 2.58k | poKMLFile_ = nullptr; |
119 | 2.58k | return FALSE; |
120 | 2.58k | } |
121 | | |
122 | | /* -------------------------------------------------------------------- */ |
123 | | /* Prescan the KML file so we can later work with the structure */ |
124 | | /* -------------------------------------------------------------------- */ |
125 | 313 | if (!poKMLFile_->parse()) |
126 | 111 | { |
127 | 111 | delete poKMLFile_; |
128 | 111 | poKMLFile_ = nullptr; |
129 | 111 | return FALSE; |
130 | 111 | } |
131 | | |
132 | | /* -------------------------------------------------------------------- */ |
133 | | /* Classify the nodes */ |
134 | | /* -------------------------------------------------------------------- */ |
135 | 202 | if (!poKMLFile_->classifyNodes()) |
136 | 3 | { |
137 | 3 | delete poKMLFile_; |
138 | 3 | poKMLFile_ = nullptr; |
139 | 3 | return FALSE; |
140 | 3 | } |
141 | | |
142 | | /* -------------------------------------------------------------------- */ |
143 | | /* Eliminate the empty containers (if there is at least one */ |
144 | | /* valid container !) */ |
145 | | /* -------------------------------------------------------------------- */ |
146 | 199 | const bool bHasOnlyEmpty = poKMLFile_->hasOnlyEmpty(); |
147 | 199 | if (bHasOnlyEmpty) |
148 | 31 | CPLDebug("KML", "Has only empty containers"); |
149 | 168 | else |
150 | 168 | poKMLFile_->eliminateEmpty(); |
151 | | |
152 | | /* -------------------------------------------------------------------- */ |
153 | | /* Find layers to use in the KML structure */ |
154 | | /* -------------------------------------------------------------------- */ |
155 | 199 | poKMLFile_->findLayers(nullptr, bHasOnlyEmpty); |
156 | | |
157 | | /* -------------------------------------------------------------------- */ |
158 | | /* Print the structure */ |
159 | | /* -------------------------------------------------------------------- */ |
160 | 199 | if (CPLGetConfigOption("KML_DEBUG", nullptr) != nullptr) |
161 | 0 | poKMLFile_->print(3); |
162 | | |
163 | 199 | const int nLayers = poKMLFile_->getNumLayers(); |
164 | | |
165 | | /* -------------------------------------------------------------------- */ |
166 | | /* Allocate memory for the Layers */ |
167 | | /* -------------------------------------------------------------------- */ |
168 | 199 | papoLayers_ = |
169 | 199 | static_cast<OGRKMLLayer **>(CPLMalloc(sizeof(OGRKMLLayer *) * nLayers)); |
170 | | |
171 | 199 | OGRSpatialReference *poSRS = |
172 | 199 | new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG); |
173 | 199 | poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
174 | | |
175 | | /* -------------------------------------------------------------------- */ |
176 | | /* Create the Layers and fill them */ |
177 | | /* -------------------------------------------------------------------- */ |
178 | 402 | for (int nCount = 0; nCount < nLayers; nCount++) |
179 | 203 | { |
180 | 203 | if (!poKMLFile_->selectLayer(nCount)) |
181 | 0 | { |
182 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
183 | 0 | "There are no layers or a layer can not be found!"); |
184 | 0 | break; |
185 | 0 | } |
186 | | |
187 | 203 | OGRwkbGeometryType poGeotype = wkbUnknown; |
188 | 203 | if (poKMLFile_->getCurrentType() == Point) |
189 | 28 | poGeotype = wkbPoint; |
190 | 175 | else if (poKMLFile_->getCurrentType() == LineString) |
191 | 5 | poGeotype = wkbLineString; |
192 | 170 | else if (poKMLFile_->getCurrentType() == Polygon) |
193 | 13 | poGeotype = wkbPolygon; |
194 | 157 | else if (poKMLFile_->getCurrentType() == MultiPoint) |
195 | 0 | poGeotype = wkbMultiPoint; |
196 | 157 | else if (poKMLFile_->getCurrentType() == MultiLineString) |
197 | 11 | poGeotype = wkbMultiLineString; |
198 | 146 | else if (poKMLFile_->getCurrentType() == MultiPolygon) |
199 | 0 | poGeotype = wkbMultiPolygon; |
200 | 146 | else if (poKMLFile_->getCurrentType() == MultiGeometry) |
201 | 2 | poGeotype = wkbGeometryCollection; |
202 | | |
203 | 203 | if (poGeotype != wkbUnknown && poKMLFile_->is25D()) |
204 | 37 | poGeotype = wkbSetZ(poGeotype); |
205 | | |
206 | | /* -------------------------------------------------------------------- |
207 | | */ |
208 | | /* Create the layer object. */ |
209 | | /* -------------------------------------------------------------------- |
210 | | */ |
211 | 203 | CPLString sName(poKMLFile_->getCurrentName()); |
212 | | |
213 | 203 | if (sName.empty()) |
214 | 7 | { |
215 | 7 | sName.Printf("Layer #%d", nCount); |
216 | 7 | } |
217 | 196 | else |
218 | 196 | { |
219 | | // Build unique layer name |
220 | 196 | int nIter = 2; |
221 | 200 | while (true) |
222 | 200 | { |
223 | 200 | if (GetLayerByName(sName) == nullptr) |
224 | 196 | break; |
225 | 4 | sName = CPLSPrintf("%s (#%d)", |
226 | 4 | poKMLFile_->getCurrentName().c_str(), nIter); |
227 | 4 | nIter++; |
228 | 4 | } |
229 | 196 | } |
230 | | |
231 | 203 | OGRKMLLayer *poLayer = |
232 | 203 | new OGRKMLLayer(sName.c_str(), poSRS, false, poGeotype, this); |
233 | | |
234 | 203 | poLayer->SetLayerNumber(nCount); |
235 | | |
236 | | /* -------------------------------------------------------------------- |
237 | | */ |
238 | | /* Add layer to data source layer list. */ |
239 | | /* -------------------------------------------------------------------- |
240 | | */ |
241 | 203 | papoLayers_[nCount] = poLayer; |
242 | | |
243 | 203 | nLayers_ = nCount + 1; |
244 | 203 | } |
245 | | |
246 | 199 | poSRS->Release(); |
247 | | |
248 | 199 | return TRUE; |
249 | 202 | } |
250 | | #endif /* HAVE_EXPAT */ |
251 | | |
252 | | /************************************************************************/ |
253 | | /* Create() */ |
254 | | /************************************************************************/ |
255 | | |
256 | | int OGRKMLDataSource::Create(const char *pszName, char **papszOptions) |
257 | 151 | { |
258 | 151 | CPLAssert(nullptr != pszName); |
259 | | |
260 | 151 | if (fpOutput_ != nullptr) |
261 | 0 | { |
262 | 0 | CPLAssert(false); |
263 | 0 | return FALSE; |
264 | 0 | } |
265 | | |
266 | 151 | if (CSLFetchNameValue(papszOptions, "NameField")) |
267 | 0 | pszNameField_ = CPLStrdup(CSLFetchNameValue(papszOptions, "NameField")); |
268 | 151 | else |
269 | 151 | pszNameField_ = CPLStrdup("Name"); |
270 | | |
271 | 151 | if (CSLFetchNameValue(papszOptions, "DescriptionField")) |
272 | 0 | pszDescriptionField_ = |
273 | 0 | CPLStrdup(CSLFetchNameValue(papszOptions, "DescriptionField")); |
274 | 151 | else |
275 | 151 | pszDescriptionField_ = CPLStrdup("Description"); |
276 | | |
277 | 151 | pszAltitudeMode_ = |
278 | 151 | CPLStrdup(CSLFetchNameValue(papszOptions, "AltitudeMode")); |
279 | 151 | if ((nullptr != pszAltitudeMode_) && strlen(pszAltitudeMode_) > 0) |
280 | 0 | { |
281 | | // Check to see that the specified AltitudeMode is valid |
282 | 0 | if (EQUAL(pszAltitudeMode_, "clampToGround") || |
283 | 0 | EQUAL(pszAltitudeMode_, "relativeToGround") || |
284 | 0 | EQUAL(pszAltitudeMode_, "absolute")) |
285 | 0 | { |
286 | 0 | CPLDebug("KML", "Using '%s' for AltitudeMode", pszAltitudeMode_); |
287 | 0 | } |
288 | 0 | else |
289 | 0 | { |
290 | 0 | CPLFree(pszAltitudeMode_); |
291 | 0 | pszAltitudeMode_ = nullptr; |
292 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
293 | 0 | "Invalid AltitudeMode specified, ignoring"); |
294 | 0 | } |
295 | 0 | } |
296 | 151 | else |
297 | 151 | { |
298 | 151 | CPLFree(pszAltitudeMode_); |
299 | 151 | pszAltitudeMode_ = nullptr; |
300 | 151 | } |
301 | | |
302 | | /* -------------------------------------------------------------------- */ |
303 | | /* Create the output file. */ |
304 | | /* -------------------------------------------------------------------- */ |
305 | | |
306 | 151 | if (strcmp(pszName, "/dev/stdout") == 0) |
307 | 0 | pszName = "/vsistdout/"; |
308 | | |
309 | 151 | fpOutput_ = VSIFOpenExL(pszName, "wb", true); |
310 | 151 | if (fpOutput_ == nullptr) |
311 | 0 | { |
312 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
313 | 0 | "Failed to create KML file %s: %s", pszName, |
314 | 0 | VSIGetLastErrorMsg()); |
315 | 0 | return FALSE; |
316 | 0 | } |
317 | | |
318 | | /* -------------------------------------------------------------------- */ |
319 | | /* Write out "standard" header. */ |
320 | | /* -------------------------------------------------------------------- */ |
321 | 151 | VSIFPrintfL(fpOutput_, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"); |
322 | | |
323 | 151 | VSIFPrintfL(fpOutput_, |
324 | 151 | "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n" |
325 | 151 | "<Document id=\"%s\">\n", |
326 | 151 | CSLFetchNameValueDef(papszOptions, "DOCUMENT_ID", "root_doc")); |
327 | | |
328 | 151 | return TRUE; |
329 | 151 | } |
330 | | |
331 | | /************************************************************************/ |
332 | | /* ICreateLayer() */ |
333 | | /************************************************************************/ |
334 | | |
335 | | OGRLayer * |
336 | | OGRKMLDataSource::ICreateLayer(const char *pszLayerName, |
337 | | const OGRGeomFieldDefn *poGeomFieldDefn, |
338 | | CSLConstList /* papszOptions*/) |
339 | 619 | { |
340 | 619 | CPLAssert(nullptr != pszLayerName); |
341 | | |
342 | | /* -------------------------------------------------------------------- */ |
343 | | /* Verify we are in update mode. */ |
344 | | /* -------------------------------------------------------------------- */ |
345 | 619 | if (fpOutput_ == nullptr) |
346 | 0 | { |
347 | 0 | CPLError(CE_Failure, CPLE_NoWriteAccess, |
348 | 0 | "Data source %s opened for read access. " |
349 | 0 | "New layer %s cannot be created.", |
350 | 0 | GetDescription(), pszLayerName); |
351 | |
|
352 | 0 | return nullptr; |
353 | 0 | } |
354 | | |
355 | 619 | const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone; |
356 | 619 | const auto poSRS = |
357 | 619 | poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr; |
358 | | |
359 | | /* -------------------------------------------------------------------- */ |
360 | | /* Close the previous layer (if there is one open) */ |
361 | | /* -------------------------------------------------------------------- */ |
362 | 619 | if (GetLayerCount() > 0) |
363 | 469 | { |
364 | 469 | if (nLayers_ == 1 && papoLayers_[0]->nWroteFeatureCount_ == 0) |
365 | 46 | { |
366 | 46 | VSIFPrintfL(fpOutput_, "<Folder><name>%s</name>\n", |
367 | 46 | papoLayers_[0]->GetName()); |
368 | 46 | } |
369 | | |
370 | 469 | VSIFPrintfL(fpOutput_, "</Folder>\n"); |
371 | 469 | papoLayers_[GetLayerCount() - 1]->SetClosedForWriting(); |
372 | 469 | } |
373 | | |
374 | | /* -------------------------------------------------------------------- */ |
375 | | /* Ensure name is safe as an element name. */ |
376 | | /* -------------------------------------------------------------------- */ |
377 | 619 | char *pszCleanLayerName = CPLStrdup(pszLayerName); |
378 | | |
379 | 619 | CPLCleanXMLElementName(pszCleanLayerName); |
380 | 619 | if (strcmp(pszCleanLayerName, pszLayerName) != 0) |
381 | 244 | { |
382 | 244 | CPLError(CE_Warning, CPLE_AppDefined, |
383 | 244 | "Layer name '%s' adjusted to '%s' for XML validity.", |
384 | 244 | pszLayerName, pszCleanLayerName); |
385 | 244 | } |
386 | | |
387 | 619 | if (GetLayerCount() > 0) |
388 | 469 | { |
389 | 469 | VSIFPrintfL(fpOutput_, "<Folder><name>%s</name>\n", pszCleanLayerName); |
390 | 469 | } |
391 | | |
392 | | /* -------------------------------------------------------------------- */ |
393 | | /* Create the layer object. */ |
394 | | /* -------------------------------------------------------------------- */ |
395 | 619 | OGRKMLLayer *poLayer = |
396 | 619 | new OGRKMLLayer(pszCleanLayerName, poSRS, true, eType, this); |
397 | | |
398 | 619 | CPLFree(pszCleanLayerName); |
399 | | |
400 | | /* -------------------------------------------------------------------- */ |
401 | | /* Add layer to data source layer list. */ |
402 | | /* -------------------------------------------------------------------- */ |
403 | 619 | papoLayers_ = static_cast<OGRKMLLayer **>( |
404 | 619 | CPLRealloc(papoLayers_, sizeof(OGRKMLLayer *) * (nLayers_ + 1))); |
405 | | |
406 | 619 | papoLayers_[nLayers_++] = poLayer; |
407 | | |
408 | 619 | return poLayer; |
409 | 619 | } |
410 | | |
411 | | /************************************************************************/ |
412 | | /* TestCapability() */ |
413 | | /************************************************************************/ |
414 | | |
415 | | int OGRKMLDataSource::TestCapability(const char *pszCap) const |
416 | | |
417 | 1.49k | { |
418 | 1.49k | if (EQUAL(pszCap, ODsCCreateLayer)) |
419 | 619 | return TRUE; |
420 | 872 | else if (EQUAL(pszCap, ODsCZGeometries)) |
421 | 0 | return TRUE; |
422 | | |
423 | 872 | return FALSE; |
424 | 1.49k | } |
425 | | |
426 | | /************************************************************************/ |
427 | | /* GetLayer() */ |
428 | | /************************************************************************/ |
429 | | |
430 | | const OGRLayer *OGRKMLDataSource::GetLayer(int iLayer) const |
431 | 8.91k | { |
432 | 8.91k | if (iLayer < 0 || iLayer >= nLayers_) |
433 | 0 | return nullptr; |
434 | | |
435 | 8.91k | return papoLayers_[iLayer]; |
436 | 8.91k | } |
437 | | |
438 | | /************************************************************************/ |
439 | | /* GrowExtents() */ |
440 | | /************************************************************************/ |
441 | | |
442 | | void OGRKMLDataSource::GrowExtents(OGREnvelope *psGeomBounds) |
443 | 34.8k | { |
444 | 34.8k | CPLAssert(nullptr != psGeomBounds); |
445 | | |
446 | 34.8k | oEnvelope_.Merge(*psGeomBounds); |
447 | 34.8k | } |