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