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