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