Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: KML Driver
4 : * Purpose: Implementation of OGRKMLLayer class.
5 : * Author: Christopher Condit, condit@sdsc.edu
6 : * Jens Oberender, j.obi@troja.net
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2006, Christopher Condit
10 : * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_port.h"
16 : #include "ogr_kml.h"
17 :
18 : #include <string>
19 :
20 : #include "cpl_conv.h"
21 : #include "cpl_error.h"
22 : #include "cpl_string.h"
23 : #include "cpl_vsi.h"
24 : #include "kml.h"
25 : #include "kmlutility.h"
26 : #include "ogr_api.h"
27 : #include "ogr_core.h"
28 : #include "ogr_feature.h"
29 : #include "ogr_featurestyle.h"
30 : #include "ogr_geometry.h"
31 : #include "ogr_p.h"
32 : #include "ogr_spatialref.h"
33 : #include "ogrsf_frmts.h"
34 :
35 : /* Function utility to dump OGRGeometry to KML text. */
36 : char *OGR_G_ExportToKML(OGRGeometryH hGeometry, const char *pszAltitudeMode);
37 :
38 : /************************************************************************/
39 : /* OGRKMLLayer() */
40 : /************************************************************************/
41 :
42 141 : OGRKMLLayer::OGRKMLLayer(const char *pszName,
43 : const OGRSpatialReference *poSRSIn, bool bWriterIn,
44 141 : OGRwkbGeometryType eReqType, OGRKMLDataSource *poDSIn)
45 : : poDS_(poDSIn),
46 141 : poSRS_(poSRSIn ? new OGRSpatialReference(nullptr) : nullptr),
47 141 : poFeatureDefn_(new OGRFeatureDefn(pszName)), bWriter_(bWriterIn),
48 423 : pszName_(CPLStrdup(pszName))
49 : {
50 : // KML should be created as WGS84.
51 141 : if (poSRSIn != nullptr)
52 : {
53 83 : poSRS_->SetWellKnownGeogCS("WGS84");
54 83 : poSRS_->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
55 83 : if (!poSRS_->IsSame(poSRSIn))
56 : {
57 1 : poCT_ = OGRCreateCoordinateTransformation(poSRSIn, poSRS_);
58 1 : if (poCT_ == nullptr && poDSIn->IsFirstCTError())
59 : {
60 : // If we can't create a transformation, issue a warning - but
61 : // continue the transformation.
62 0 : char *pszWKT = nullptr;
63 :
64 0 : poSRSIn->exportToPrettyWkt(&pszWKT, FALSE);
65 :
66 0 : CPLError(
67 : CE_Warning, CPLE_AppDefined,
68 : "Failed to create coordinate transformation between the "
69 : "input coordinate system and WGS84. This may be because "
70 : "they are not transformable. "
71 : "KML geometries may not render correctly. "
72 : "This message will not be issued any more."
73 : "\nSource:\n%s\n",
74 : pszWKT);
75 :
76 0 : CPLFree(pszWKT);
77 0 : poDSIn->IssuedFirstCTError();
78 : }
79 : }
80 : }
81 :
82 141 : SetDescription(poFeatureDefn_->GetName());
83 141 : poFeatureDefn_->Reference();
84 141 : poFeatureDefn_->SetGeomType(eReqType);
85 141 : if (poFeatureDefn_->GetGeomFieldCount() != 0)
86 137 : poFeatureDefn_->GetGeomFieldDefn(0)->SetSpatialRef(poSRS_);
87 :
88 282 : OGRFieldDefn oFieldName("Name", OFTString);
89 141 : poFeatureDefn_->AddFieldDefn(&oFieldName);
90 :
91 141 : OGRFieldDefn oFieldDesc("Description", OFTString);
92 141 : poFeatureDefn_->AddFieldDefn(&oFieldDesc);
93 :
94 141 : bClosedForWriting = !bWriterIn;
95 141 : }
96 :
97 : /************************************************************************/
98 : /* ~OGRKMLLayer() */
99 : /************************************************************************/
100 :
101 282 : OGRKMLLayer::~OGRKMLLayer()
102 : {
103 141 : if (nullptr != poFeatureDefn_)
104 141 : poFeatureDefn_->Release();
105 :
106 141 : if (nullptr != poSRS_)
107 83 : poSRS_->Release();
108 :
109 141 : if (nullptr != poCT_)
110 1 : delete poCT_;
111 :
112 141 : CPLFree(pszName_);
113 282 : }
114 :
115 : /************************************************************************/
116 : /* GetLayerDefn() */
117 : /************************************************************************/
118 :
119 2342 : const OGRFeatureDefn *OGRKMLLayer::GetLayerDefn() const
120 : {
121 2342 : return poFeatureDefn_;
122 : }
123 :
124 : /************************************************************************/
125 : /* ResetReading() */
126 : /************************************************************************/
127 :
128 614 : void OGRKMLLayer::ResetReading()
129 : {
130 614 : iNextKMLId_ = 0;
131 614 : nLastAsked = -1;
132 614 : nLastCount = -1;
133 614 : }
134 :
135 : /************************************************************************/
136 : /* GetNextFeature() */
137 : /************************************************************************/
138 :
139 792 : OGRFeature *OGRKMLLayer::GetNextFeature()
140 : {
141 : #ifndef HAVE_EXPAT
142 : return nullptr;
143 : #else
144 : /* -------------------------------------------------------------------- */
145 : /* Loop till we find a feature matching our criteria. */
146 : /* -------------------------------------------------------------------- */
147 792 : KML *poKMLFile = poDS_->GetKMLFile();
148 792 : if (poKMLFile == nullptr)
149 16 : return nullptr;
150 :
151 776 : poKMLFile->selectLayer(nLayerNumber_);
152 :
153 : while (true)
154 : {
155 : auto poFeatureKML = std::unique_ptr<Feature>(
156 959 : poKMLFile->getFeature(iNextKMLId_++, nLastAsked, nLastCount));
157 :
158 959 : if (poFeatureKML == nullptr)
159 207 : return nullptr;
160 :
161 752 : auto poFeature = std::make_unique<OGRFeature>(poFeatureDefn_);
162 :
163 752 : if (poFeatureKML->poGeom)
164 : {
165 752 : poFeature->SetGeometry(std::move(poFeatureKML->poGeom));
166 : }
167 :
168 : // Add fields.
169 1504 : poFeature->SetField(poFeatureDefn_->GetFieldIndex("Name"),
170 752 : poFeatureKML->sName.c_str());
171 1504 : poFeature->SetField(poFeatureDefn_->GetFieldIndex("Description"),
172 752 : poFeatureKML->sDescription.c_str());
173 752 : poFeature->SetFID(iNextKMLId_ - 1);
174 :
175 752 : if (poFeature->GetGeometryRef() != nullptr && poSRS_ != nullptr)
176 : {
177 752 : poFeature->GetGeometryRef()->assignSpatialReference(poSRS_);
178 : }
179 :
180 : // Check spatial/attribute filters.
181 1713 : if ((m_poFilterGeom == nullptr ||
182 1428 : FilterGeometry(poFeature->GetGeometryRef())) &&
183 676 : (m_poAttrQuery == nullptr ||
184 215 : m_poAttrQuery->Evaluate(poFeature.get())))
185 : {
186 569 : return poFeature.release();
187 : }
188 183 : }
189 :
190 : #endif /* HAVE_EXPAT */
191 : }
192 :
193 : /************************************************************************/
194 : /* GetFeatureCount() */
195 : /************************************************************************/
196 :
197 : #ifndef HAVE_EXPAT
198 : GIntBig OGRKMLLayer::GetFeatureCount(int /* bForce */)
199 : {
200 : return 0;
201 : }
202 : #else
203 :
204 97 : GIntBig OGRKMLLayer::GetFeatureCount(int bForce)
205 : {
206 97 : if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
207 36 : return OGRLayer::GetFeatureCount(bForce);
208 :
209 61 : KML *poKMLFile = poDS_->GetKMLFile();
210 61 : if (nullptr == poKMLFile)
211 0 : return 0;
212 :
213 61 : poKMLFile->selectLayer(nLayerNumber_);
214 :
215 61 : return poKMLFile->getNumFeatures();
216 : }
217 : #endif
218 :
219 : /************************************************************************/
220 : /* WriteSchema() */
221 : /************************************************************************/
222 :
223 40 : CPLString OGRKMLLayer::WriteSchema()
224 : {
225 40 : if (bSchemaWritten_)
226 0 : return "";
227 :
228 80 : CPLString osRet;
229 :
230 40 : const OGRFeatureDefn *featureDefinition = GetLayerDefn();
231 231 : for (int j = 0; j < featureDefinition->GetFieldCount(); j++)
232 : {
233 : const OGRFieldDefn *fieldDefinition =
234 191 : featureDefinition->GetFieldDefn(j);
235 :
236 382 : if (nullptr != poDS_->GetNameField() &&
237 191 : EQUAL(fieldDefinition->GetNameRef(), poDS_->GetNameField()))
238 40 : continue;
239 :
240 302 : if (nullptr != poDS_->GetDescriptionField() &&
241 151 : EQUAL(fieldDefinition->GetNameRef(), poDS_->GetDescriptionField()))
242 40 : continue;
243 :
244 111 : if (osRet.empty())
245 : {
246 : osRet += CPLSPrintf("<Schema name=\"%s\" id=\"%s\">\n", pszName_,
247 37 : pszName_);
248 : }
249 :
250 111 : const char *pszKMLType = nullptr;
251 111 : const char *pszKMLEltName = nullptr;
252 : // Match the OGR type to the GDAL type.
253 111 : switch (fieldDefinition->GetType())
254 : {
255 20 : case OFTInteger:
256 20 : pszKMLType = "int";
257 20 : pszKMLEltName = "SimpleField";
258 20 : break;
259 0 : case OFTIntegerList:
260 0 : pszKMLType = "int";
261 0 : pszKMLEltName = "SimpleArrayField";
262 0 : break;
263 21 : case OFTReal:
264 21 : pszKMLType = "float";
265 21 : pszKMLEltName = "SimpleField";
266 21 : break;
267 0 : case OFTRealList:
268 0 : pszKMLType = "float";
269 0 : pszKMLEltName = "SimpleArrayField";
270 0 : break;
271 38 : case OFTString:
272 38 : pszKMLType = "string";
273 38 : pszKMLEltName = "SimpleField";
274 38 : break;
275 0 : case OFTStringList:
276 0 : pszKMLType = "string";
277 0 : pszKMLEltName = "SimpleArrayField";
278 0 : break;
279 : // TODO: KML doesn't handle these data types yet...
280 32 : case OFTDate:
281 : case OFTTime:
282 : case OFTDateTime:
283 32 : pszKMLType = "string";
284 32 : pszKMLEltName = "SimpleField";
285 32 : break;
286 :
287 0 : default:
288 0 : pszKMLType = "string";
289 0 : pszKMLEltName = "SimpleField";
290 0 : break;
291 : }
292 : osRet += CPLSPrintf("\t<%s name=\"%s\" type=\"%s\"></%s>\n",
293 : pszKMLEltName, fieldDefinition->GetNameRef(),
294 111 : pszKMLType, pszKMLEltName);
295 : }
296 :
297 40 : if (!osRet.empty())
298 37 : osRet += CPLSPrintf("%s", "</Schema>\n");
299 :
300 40 : return osRet;
301 : }
302 :
303 : /************************************************************************/
304 : /* ICreateFeature() */
305 : /************************************************************************/
306 :
307 129 : OGRErr OGRKMLLayer::ICreateFeature(OGRFeature *poFeature)
308 : {
309 129 : CPLAssert(nullptr != poFeature);
310 129 : CPLAssert(nullptr != poDS_);
311 :
312 129 : if (!bWriter_)
313 0 : return OGRERR_FAILURE;
314 :
315 129 : if (bClosedForWriting)
316 : {
317 1 : CPLError(
318 : CE_Failure, CPLE_NotSupported,
319 : "Interleaved feature adding to different layers is not supported");
320 1 : return OGRERR_FAILURE;
321 : }
322 :
323 128 : VSILFILE *fp = poDS_->GetOutputFP();
324 128 : CPLAssert(nullptr != fp);
325 :
326 128 : if (poDS_->GetLayerCount() == 1 && nWroteFeatureCount_ == 0)
327 : {
328 44 : CPLString osRet = WriteSchema();
329 22 : if (!osRet.empty())
330 20 : VSIFPrintfL(fp, "%s", osRet.c_str());
331 22 : bSchemaWritten_ = true;
332 :
333 22 : VSIFPrintfL(fp, "<Folder><name>%s</name>\n", pszName_);
334 : }
335 :
336 128 : ++nWroteFeatureCount_;
337 128 : char *pszEscapedLayerName = OGRGetXML_UTF8_EscapedString(GetDescription());
338 128 : VSIFPrintfL(fp, " <Placemark id=\"%s." CPL_FRMT_GIB "\">\n",
339 : pszEscapedLayerName, nWroteFeatureCount_);
340 128 : CPLFree(pszEscapedLayerName);
341 :
342 128 : if (poFeature->GetFID() == OGRNullFID)
343 128 : poFeature->SetFID(iNextKMLId_++);
344 :
345 : // Find and write the name element
346 128 : if (nullptr != poDS_->GetNameField())
347 : {
348 834 : for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
349 : {
350 706 : OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
351 :
352 1063 : if (poFeature->IsFieldSetAndNotNull(iField) &&
353 357 : EQUAL(poField->GetNameRef(), poDS_->GetNameField()))
354 : {
355 2 : const char *pszRaw = poFeature->GetFieldAsString(iField);
356 2 : while (*pszRaw == ' ')
357 0 : pszRaw++;
358 :
359 2 : char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
360 :
361 2 : VSIFPrintfL(fp, "\t<name>%s</name>\n", pszEscaped);
362 2 : CPLFree(pszEscaped);
363 : }
364 : }
365 : }
366 :
367 128 : if (nullptr != poDS_->GetDescriptionField())
368 : {
369 834 : for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
370 : {
371 706 : OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
372 :
373 1063 : if (poFeature->IsFieldSetAndNotNull(iField) &&
374 357 : EQUAL(poField->GetNameRef(), poDS_->GetDescriptionField()))
375 : {
376 1 : const char *pszRaw = poFeature->GetFieldAsString(iField);
377 1 : while (*pszRaw == ' ')
378 0 : pszRaw++;
379 :
380 1 : char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
381 :
382 1 : VSIFPrintfL(fp, "\t<description>%s</description>\n",
383 : pszEscaped);
384 1 : CPLFree(pszEscaped);
385 : }
386 : }
387 : }
388 :
389 128 : OGRwkbGeometryType eGeomType = wkbNone;
390 128 : if (poFeature->GetGeometryRef() != nullptr)
391 90 : eGeomType = wkbFlatten(poFeature->GetGeometryRef()->getGeometryType());
392 :
393 128 : if (wkbPolygon == eGeomType || wkbMultiPolygon == eGeomType ||
394 72 : wkbLineString == eGeomType || wkbMultiLineString == eGeomType)
395 : {
396 63 : OGRStylePen *poPen = nullptr;
397 126 : OGRStyleMgr oSM;
398 :
399 63 : if (poFeature->GetStyleString() != nullptr)
400 : {
401 0 : oSM.InitFromFeature(poFeature);
402 :
403 0 : for (int i = 0; i < oSM.GetPartCount(); i++)
404 : {
405 0 : OGRStyleTool *poTool = oSM.GetPart(i);
406 0 : if (poTool && poTool->GetType() == OGRSTCPen)
407 : {
408 0 : poPen = cpl::down_cast<OGRStylePen *>(poTool);
409 0 : break;
410 : }
411 0 : delete poTool;
412 : }
413 : }
414 :
415 63 : VSIFPrintfL(fp, "\t<Style>");
416 63 : if (poPen != nullptr)
417 : {
418 0 : GBool bDefault = FALSE;
419 :
420 : /* Require width to be returned in pixel */
421 0 : poPen->SetUnit(OGRSTUPixel);
422 0 : double fW = poPen->Width(bDefault);
423 0 : if (bDefault)
424 0 : fW = 1;
425 0 : const char *pszColor = poPen->Color(bDefault);
426 0 : const int nColorLen = static_cast<int>(CPLStrnlen(pszColor, 10));
427 0 : if (pszColor != nullptr && pszColor[0] == '#' && !bDefault &&
428 : nColorLen >= 7)
429 : {
430 0 : char acColor[9] = {0};
431 : /* Order of KML color is aabbggrr, whereas OGR color is
432 : * #rrggbb[aa] ! */
433 0 : if (nColorLen == 9)
434 : {
435 0 : acColor[0] = pszColor[7]; /* A */
436 0 : acColor[1] = pszColor[8];
437 : }
438 : else
439 : {
440 0 : acColor[0] = 'F';
441 0 : acColor[1] = 'F';
442 : }
443 0 : acColor[2] = pszColor[5]; /* B */
444 0 : acColor[3] = pszColor[6];
445 0 : acColor[4] = pszColor[3]; /* G */
446 0 : acColor[5] = pszColor[4];
447 0 : acColor[6] = pszColor[1]; /* R */
448 0 : acColor[7] = pszColor[2];
449 0 : VSIFPrintfL(fp, "<LineStyle><color>%s</color>", acColor);
450 0 : VSIFPrintfL(fp, "<width>%g</width>", fW);
451 0 : VSIFPrintfL(fp, "</LineStyle>");
452 : }
453 : else
454 : {
455 0 : VSIFPrintfL(fp,
456 : "<LineStyle><color>ff0000ff</color></LineStyle>");
457 : }
458 : }
459 : else
460 : {
461 63 : VSIFPrintfL(fp, "<LineStyle><color>ff0000ff</color></LineStyle>");
462 : }
463 63 : delete poPen;
464 : // If we're dealing with a polygon, add a line style that will stand out
465 : // a bit.
466 63 : VSIFPrintfL(fp, "<PolyStyle><fill>0</fill></PolyStyle></Style>\n");
467 : }
468 :
469 128 : bool bHasFoundOtherField = false;
470 :
471 : // Write all fields as SchemaData
472 834 : for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
473 : {
474 706 : OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
475 :
476 706 : if (poFeature->IsFieldSetAndNotNull(iField))
477 : {
478 714 : if (nullptr != poDS_->GetNameField() &&
479 357 : EQUAL(poField->GetNameRef(), poDS_->GetNameField()))
480 2 : continue;
481 :
482 710 : if (nullptr != poDS_->GetDescriptionField() &&
483 355 : EQUAL(poField->GetNameRef(), poDS_->GetDescriptionField()))
484 1 : continue;
485 :
486 354 : if (!bHasFoundOtherField)
487 : {
488 86 : VSIFPrintfL(fp,
489 : "\t<ExtendedData><SchemaData schemaUrl=\"#%s\">\n",
490 : pszName_);
491 86 : bHasFoundOtherField = true;
492 : }
493 354 : const char *pszRaw = poFeature->GetFieldAsString(iField);
494 :
495 354 : while (*pszRaw == ' ')
496 0 : pszRaw++;
497 :
498 354 : char *pszEscaped = nullptr;
499 354 : if (poFeatureDefn_->GetFieldDefn(iField)->GetType() == OFTReal)
500 : {
501 152 : pszEscaped = CPLStrdup(pszRaw);
502 : }
503 : else
504 : {
505 202 : pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
506 : }
507 :
508 354 : VSIFPrintfL(fp, "\t\t<SimpleData name=\"%s\">%s</SimpleData>\n",
509 : poField->GetNameRef(), pszEscaped);
510 :
511 354 : CPLFree(pszEscaped);
512 : }
513 : }
514 :
515 128 : if (bHasFoundOtherField)
516 : {
517 86 : VSIFPrintfL(fp, "\t</SchemaData></ExtendedData>\n");
518 : }
519 :
520 : // Write out Geometry - for now it isn't indented properly.
521 128 : if (poFeature->GetGeometryRef() != nullptr)
522 : {
523 90 : char *pszGeometry = nullptr;
524 90 : OGREnvelope sGeomBounds;
525 90 : OGRGeometry *poWGS84Geom = nullptr;
526 :
527 90 : if (nullptr != poCT_)
528 : {
529 1 : poWGS84Geom = poFeature->GetGeometryRef()->clone();
530 1 : poWGS84Geom->transform(poCT_);
531 : }
532 : else
533 : {
534 89 : poWGS84Geom = poFeature->GetGeometryRef();
535 : }
536 :
537 90 : pszGeometry = OGR_G_ExportToKML(OGRGeometry::ToHandle(poWGS84Geom),
538 90 : poDS_->GetAltitudeMode());
539 90 : if (pszGeometry != nullptr)
540 : {
541 90 : VSIFPrintfL(fp, " %s\n", pszGeometry);
542 : }
543 : else
544 : {
545 0 : CPLError(CE_Failure, CPLE_AppDefined,
546 : "Export of geometry to KML failed");
547 : }
548 90 : CPLFree(pszGeometry);
549 :
550 90 : poWGS84Geom->getEnvelope(&sGeomBounds);
551 90 : poDS_->GrowExtents(&sGeomBounds);
552 :
553 90 : if (nullptr != poCT_)
554 : {
555 1 : delete poWGS84Geom;
556 : }
557 : }
558 :
559 128 : VSIFPrintfL(fp, " </Placemark>\n");
560 128 : return OGRERR_NONE;
561 : }
562 :
563 : /************************************************************************/
564 : /* TestCapability() */
565 : /************************************************************************/
566 :
567 339 : int OGRKMLLayer::TestCapability(const char *pszCap) const
568 : {
569 339 : if (EQUAL(pszCap, OLCSequentialWrite))
570 : {
571 22 : return bWriter_;
572 : }
573 317 : else if (EQUAL(pszCap, OLCCreateField))
574 : {
575 28 : return bWriter_ && iNextKMLId_ == 0;
576 : }
577 289 : else if (EQUAL(pszCap, OLCFastFeatureCount))
578 : {
579 : // if( poFClass == NULL
580 : // || m_poFilterGeom != NULL
581 : // || m_poAttrQuery != NULL )
582 0 : return FALSE;
583 :
584 : // return poFClass->GetFeatureCount() != -1;
585 : }
586 :
587 289 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
588 78 : return TRUE;
589 211 : else if (EQUAL(pszCap, OLCZGeometries))
590 18 : return TRUE;
591 :
592 193 : return FALSE;
593 : }
594 :
595 : /************************************************************************/
596 : /* CreateField() */
597 : /************************************************************************/
598 :
599 111 : OGRErr OGRKMLLayer::CreateField(const OGRFieldDefn *poField,
600 : CPL_UNUSED int bApproxOK)
601 : {
602 111 : if (!bWriter_ || iNextKMLId_ != 0)
603 0 : return OGRERR_FAILURE;
604 :
605 111 : OGRFieldDefn oCleanCopy(poField);
606 111 : poFeatureDefn_->AddFieldDefn(&oCleanCopy);
607 :
608 111 : return OGRERR_NONE;
609 : }
610 :
611 : /************************************************************************/
612 : /* SetLayerNumber() */
613 : /************************************************************************/
614 :
615 81 : void OGRKMLLayer::SetLayerNumber(int nLayer)
616 : {
617 81 : nLayerNumber_ = nLayer;
618 81 : }
619 :
620 : /************************************************************************/
621 : /* GetDataset() */
622 : /************************************************************************/
623 :
624 17 : GDALDataset *OGRKMLLayer::GetDataset()
625 : {
626 17 : return poDS_;
627 : }
|