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