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 2360 : OGRFeatureDefn *OGRKMLLayer::GetLayerDefn()
120 : {
121 2360 : return poFeatureDefn_;
122 : }
123 :
124 : /************************************************************************/
125 : /* ResetReading() */
126 : /************************************************************************/
127 :
128 602 : void OGRKMLLayer::ResetReading()
129 : {
130 602 : iNextKMLId_ = 0;
131 602 : nLastAsked = -1;
132 602 : nLastCount = -1;
133 602 : }
134 :
135 : /************************************************************************/
136 : /* GetNextFeature() */
137 : /************************************************************************/
138 :
139 749 : 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 749 : KML *poKMLFile = poDS_->GetKMLFile();
148 749 : if (poKMLFile == nullptr)
149 16 : return nullptr;
150 :
151 733 : poKMLFile->selectLayer(nLayerNumber_);
152 :
153 : while (true)
154 : {
155 : auto poFeatureKML = std::unique_ptr<Feature>(
156 916 : poKMLFile->getFeature(iNextKMLId_++, nLastAsked, nLastCount));
157 :
158 916 : if (poFeatureKML == nullptr)
159 189 : return nullptr;
160 :
161 727 : auto poFeature = std::make_unique<OGRFeature>(poFeatureDefn_);
162 :
163 727 : if (poFeatureKML->poGeom)
164 : {
165 727 : poFeature->SetGeometry(std::move(poFeatureKML->poGeom));
166 : }
167 :
168 : // Add fields.
169 1454 : poFeature->SetField(poFeatureDefn_->GetFieldIndex("Name"),
170 727 : poFeatureKML->sName.c_str());
171 1454 : poFeature->SetField(poFeatureDefn_->GetFieldIndex("Description"),
172 727 : poFeatureKML->sDescription.c_str());
173 727 : poFeature->SetFID(iNextKMLId_ - 1);
174 :
175 727 : if (poFeature->GetGeometryRef() != nullptr && poSRS_ != nullptr)
176 : {
177 727 : poFeature->GetGeometryRef()->assignSpatialReference(poSRS_);
178 : }
179 :
180 : // Check spatial/attribute filters.
181 1663 : if ((m_poFilterGeom == nullptr ||
182 1378 : FilterGeometry(poFeature->GetGeometryRef())) &&
183 651 : (m_poAttrQuery == nullptr ||
184 215 : m_poAttrQuery->Evaluate(poFeature.get())))
185 : {
186 544 : 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 : OGRFeatureDefn *featureDefinition = GetLayerDefn();
231 231 : for (int j = 0; j < featureDefinition->GetFieldCount(); j++)
232 : {
233 191 : OGRFieldDefn *fieldDefinition = featureDefinition->GetFieldDefn(j);
234 :
235 382 : if (nullptr != poDS_->GetNameField() &&
236 191 : EQUAL(fieldDefinition->GetNameRef(), poDS_->GetNameField()))
237 40 : continue;
238 :
239 302 : if (nullptr != poDS_->GetDescriptionField() &&
240 151 : EQUAL(fieldDefinition->GetNameRef(), poDS_->GetDescriptionField()))
241 40 : continue;
242 :
243 111 : if (osRet.empty())
244 : {
245 : osRet += CPLSPrintf("<Schema name=\"%s\" id=\"%s\">\n", pszName_,
246 37 : pszName_);
247 : }
248 :
249 111 : const char *pszKMLType = nullptr;
250 111 : const char *pszKMLEltName = nullptr;
251 : // Match the OGR type to the GDAL type.
252 111 : switch (fieldDefinition->GetType())
253 : {
254 20 : case OFTInteger:
255 20 : pszKMLType = "int";
256 20 : pszKMLEltName = "SimpleField";
257 20 : break;
258 0 : case OFTIntegerList:
259 0 : pszKMLType = "int";
260 0 : pszKMLEltName = "SimpleArrayField";
261 0 : break;
262 21 : case OFTReal:
263 21 : pszKMLType = "float";
264 21 : pszKMLEltName = "SimpleField";
265 21 : break;
266 0 : case OFTRealList:
267 0 : pszKMLType = "float";
268 0 : pszKMLEltName = "SimpleArrayField";
269 0 : break;
270 38 : case OFTString:
271 38 : pszKMLType = "string";
272 38 : pszKMLEltName = "SimpleField";
273 38 : break;
274 0 : case OFTStringList:
275 0 : pszKMLType = "string";
276 0 : pszKMLEltName = "SimpleArrayField";
277 0 : break;
278 : // TODO: KML doesn't handle these data types yet...
279 32 : case OFTDate:
280 : case OFTTime:
281 : case OFTDateTime:
282 32 : pszKMLType = "string";
283 32 : pszKMLEltName = "SimpleField";
284 32 : break;
285 :
286 0 : default:
287 0 : pszKMLType = "string";
288 0 : pszKMLEltName = "SimpleField";
289 0 : break;
290 : }
291 : osRet += CPLSPrintf("\t<%s name=\"%s\" type=\"%s\"></%s>\n",
292 : pszKMLEltName, fieldDefinition->GetNameRef(),
293 111 : pszKMLType, pszKMLEltName);
294 : }
295 :
296 40 : if (!osRet.empty())
297 37 : osRet += CPLSPrintf("%s", "</Schema>\n");
298 :
299 40 : return osRet;
300 : }
301 :
302 : /************************************************************************/
303 : /* ICreateFeature() */
304 : /************************************************************************/
305 :
306 129 : OGRErr OGRKMLLayer::ICreateFeature(OGRFeature *poFeature)
307 : {
308 129 : CPLAssert(nullptr != poFeature);
309 129 : CPLAssert(nullptr != poDS_);
310 :
311 129 : if (!bWriter_)
312 0 : return OGRERR_FAILURE;
313 :
314 129 : if (bClosedForWriting)
315 : {
316 1 : CPLError(
317 : CE_Failure, CPLE_NotSupported,
318 : "Interleaved feature adding to different layers is not supported");
319 1 : return OGRERR_FAILURE;
320 : }
321 :
322 128 : VSILFILE *fp = poDS_->GetOutputFP();
323 128 : CPLAssert(nullptr != fp);
324 :
325 128 : if (poDS_->GetLayerCount() == 1 && nWroteFeatureCount_ == 0)
326 : {
327 44 : CPLString osRet = WriteSchema();
328 22 : if (!osRet.empty())
329 20 : VSIFPrintfL(fp, "%s", osRet.c_str());
330 22 : bSchemaWritten_ = true;
331 :
332 22 : VSIFPrintfL(fp, "<Folder><name>%s</name>\n", pszName_);
333 : }
334 :
335 128 : ++nWroteFeatureCount_;
336 128 : char *pszEscapedLayerName = OGRGetXML_UTF8_EscapedString(GetDescription());
337 128 : VSIFPrintfL(fp, " <Placemark id=\"%s." CPL_FRMT_GIB "\">\n",
338 : pszEscapedLayerName, nWroteFeatureCount_);
339 128 : CPLFree(pszEscapedLayerName);
340 :
341 128 : if (poFeature->GetFID() == OGRNullFID)
342 128 : poFeature->SetFID(iNextKMLId_++);
343 :
344 : // Find and write the name element
345 128 : if (nullptr != poDS_->GetNameField())
346 : {
347 834 : for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
348 : {
349 706 : OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
350 :
351 1063 : if (poFeature->IsFieldSetAndNotNull(iField) &&
352 357 : EQUAL(poField->GetNameRef(), poDS_->GetNameField()))
353 : {
354 2 : const char *pszRaw = poFeature->GetFieldAsString(iField);
355 2 : while (*pszRaw == ' ')
356 0 : pszRaw++;
357 :
358 2 : char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
359 :
360 2 : VSIFPrintfL(fp, "\t<name>%s</name>\n", pszEscaped);
361 2 : CPLFree(pszEscaped);
362 : }
363 : }
364 : }
365 :
366 128 : if (nullptr != poDS_->GetDescriptionField())
367 : {
368 834 : for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
369 : {
370 706 : OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
371 :
372 1063 : if (poFeature->IsFieldSetAndNotNull(iField) &&
373 357 : EQUAL(poField->GetNameRef(), poDS_->GetDescriptionField()))
374 : {
375 1 : const char *pszRaw = poFeature->GetFieldAsString(iField);
376 1 : while (*pszRaw == ' ')
377 0 : pszRaw++;
378 :
379 1 : char *pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
380 :
381 1 : VSIFPrintfL(fp, "\t<description>%s</description>\n",
382 : pszEscaped);
383 1 : CPLFree(pszEscaped);
384 : }
385 : }
386 : }
387 :
388 128 : OGRwkbGeometryType eGeomType = wkbNone;
389 128 : if (poFeature->GetGeometryRef() != nullptr)
390 90 : eGeomType = wkbFlatten(poFeature->GetGeometryRef()->getGeometryType());
391 :
392 128 : if (wkbPolygon == eGeomType || wkbMultiPolygon == eGeomType ||
393 72 : wkbLineString == eGeomType || wkbMultiLineString == eGeomType)
394 : {
395 63 : OGRStylePen *poPen = nullptr;
396 126 : OGRStyleMgr oSM;
397 :
398 63 : if (poFeature->GetStyleString() != nullptr)
399 : {
400 0 : oSM.InitFromFeature(poFeature);
401 :
402 0 : for (int i = 0; i < oSM.GetPartCount(); i++)
403 : {
404 0 : OGRStyleTool *poTool = oSM.GetPart(i);
405 0 : if (poTool && poTool->GetType() == OGRSTCPen)
406 : {
407 0 : poPen = cpl::down_cast<OGRStylePen *>(poTool);
408 0 : break;
409 : }
410 0 : delete poTool;
411 : }
412 : }
413 :
414 63 : VSIFPrintfL(fp, "\t<Style>");
415 63 : if (poPen != nullptr)
416 : {
417 0 : GBool bDefault = FALSE;
418 :
419 : /* Require width to be returned in pixel */
420 0 : poPen->SetUnit(OGRSTUPixel);
421 0 : double fW = poPen->Width(bDefault);
422 0 : if (bDefault)
423 0 : fW = 1;
424 0 : const char *pszColor = poPen->Color(bDefault);
425 0 : const int nColorLen = static_cast<int>(CPLStrnlen(pszColor, 10));
426 0 : if (pszColor != nullptr && pszColor[0] == '#' && !bDefault &&
427 : nColorLen >= 7)
428 : {
429 0 : char acColor[9] = {0};
430 : /* Order of KML color is aabbggrr, whereas OGR color is
431 : * #rrggbb[aa] ! */
432 0 : if (nColorLen == 9)
433 : {
434 0 : acColor[0] = pszColor[7]; /* A */
435 0 : acColor[1] = pszColor[8];
436 : }
437 : else
438 : {
439 0 : acColor[0] = 'F';
440 0 : acColor[1] = 'F';
441 : }
442 0 : acColor[2] = pszColor[5]; /* B */
443 0 : acColor[3] = pszColor[6];
444 0 : acColor[4] = pszColor[3]; /* G */
445 0 : acColor[5] = pszColor[4];
446 0 : acColor[6] = pszColor[1]; /* R */
447 0 : acColor[7] = pszColor[2];
448 0 : VSIFPrintfL(fp, "<LineStyle><color>%s</color>", acColor);
449 0 : VSIFPrintfL(fp, "<width>%g</width>", fW);
450 0 : VSIFPrintfL(fp, "</LineStyle>");
451 : }
452 : else
453 : {
454 0 : VSIFPrintfL(fp,
455 : "<LineStyle><color>ff0000ff</color></LineStyle>");
456 : }
457 : }
458 : else
459 : {
460 63 : VSIFPrintfL(fp, "<LineStyle><color>ff0000ff</color></LineStyle>");
461 : }
462 63 : delete poPen;
463 : // If we're dealing with a polygon, add a line style that will stand out
464 : // a bit.
465 63 : VSIFPrintfL(fp, "<PolyStyle><fill>0</fill></PolyStyle></Style>\n");
466 : }
467 :
468 128 : bool bHasFoundOtherField = false;
469 :
470 : // Write all fields as SchemaData
471 834 : for (int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++)
472 : {
473 706 : OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn(iField);
474 :
475 706 : if (poFeature->IsFieldSetAndNotNull(iField))
476 : {
477 714 : if (nullptr != poDS_->GetNameField() &&
478 357 : EQUAL(poField->GetNameRef(), poDS_->GetNameField()))
479 2 : continue;
480 :
481 710 : if (nullptr != poDS_->GetDescriptionField() &&
482 355 : EQUAL(poField->GetNameRef(), poDS_->GetDescriptionField()))
483 1 : continue;
484 :
485 354 : if (!bHasFoundOtherField)
486 : {
487 86 : VSIFPrintfL(fp,
488 : "\t<ExtendedData><SchemaData schemaUrl=\"#%s\">\n",
489 : pszName_);
490 86 : bHasFoundOtherField = true;
491 : }
492 354 : const char *pszRaw = poFeature->GetFieldAsString(iField);
493 :
494 354 : while (*pszRaw == ' ')
495 0 : pszRaw++;
496 :
497 354 : char *pszEscaped = nullptr;
498 354 : if (poFeatureDefn_->GetFieldDefn(iField)->GetType() == OFTReal)
499 : {
500 152 : pszEscaped = CPLStrdup(pszRaw);
501 : }
502 : else
503 : {
504 202 : pszEscaped = OGRGetXML_UTF8_EscapedString(pszRaw);
505 : }
506 :
507 354 : VSIFPrintfL(fp, "\t\t<SimpleData name=\"%s\">%s</SimpleData>\n",
508 : poField->GetNameRef(), pszEscaped);
509 :
510 354 : CPLFree(pszEscaped);
511 : }
512 : }
513 :
514 128 : if (bHasFoundOtherField)
515 : {
516 86 : VSIFPrintfL(fp, "\t</SchemaData></ExtendedData>\n");
517 : }
518 :
519 : // Write out Geometry - for now it isn't indented properly.
520 128 : if (poFeature->GetGeometryRef() != nullptr)
521 : {
522 90 : char *pszGeometry = nullptr;
523 90 : OGREnvelope sGeomBounds;
524 90 : OGRGeometry *poWGS84Geom = nullptr;
525 :
526 90 : if (nullptr != poCT_)
527 : {
528 1 : poWGS84Geom = poFeature->GetGeometryRef()->clone();
529 1 : poWGS84Geom->transform(poCT_);
530 : }
531 : else
532 : {
533 89 : poWGS84Geom = poFeature->GetGeometryRef();
534 : }
535 :
536 90 : pszGeometry = OGR_G_ExportToKML(OGRGeometry::ToHandle(poWGS84Geom),
537 90 : poDS_->GetAltitudeMode());
538 90 : if (pszGeometry != nullptr)
539 : {
540 90 : VSIFPrintfL(fp, " %s\n", pszGeometry);
541 : }
542 : else
543 : {
544 0 : CPLError(CE_Failure, CPLE_AppDefined,
545 : "Export of geometry to KML failed");
546 : }
547 90 : CPLFree(pszGeometry);
548 :
549 90 : poWGS84Geom->getEnvelope(&sGeomBounds);
550 90 : poDS_->GrowExtents(&sGeomBounds);
551 :
552 90 : if (nullptr != poCT_)
553 : {
554 1 : delete poWGS84Geom;
555 : }
556 : }
557 :
558 128 : VSIFPrintfL(fp, " </Placemark>\n");
559 128 : return OGRERR_NONE;
560 : }
561 :
562 : /************************************************************************/
563 : /* TestCapability() */
564 : /************************************************************************/
565 :
566 333 : int OGRKMLLayer::TestCapability(const char *pszCap)
567 : {
568 333 : if (EQUAL(pszCap, OLCSequentialWrite))
569 : {
570 22 : return bWriter_;
571 : }
572 311 : else if (EQUAL(pszCap, OLCCreateField))
573 : {
574 28 : return bWriter_ && iNextKMLId_ == 0;
575 : }
576 283 : else if (EQUAL(pszCap, OLCFastFeatureCount))
577 : {
578 : // if( poFClass == NULL
579 : // || m_poFilterGeom != NULL
580 : // || m_poAttrQuery != NULL )
581 0 : return FALSE;
582 :
583 : // return poFClass->GetFeatureCount() != -1;
584 : }
585 :
586 283 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
587 78 : return TRUE;
588 205 : else if (EQUAL(pszCap, OLCZGeometries))
589 18 : return TRUE;
590 :
591 187 : return FALSE;
592 : }
593 :
594 : /************************************************************************/
595 : /* CreateField() */
596 : /************************************************************************/
597 :
598 111 : OGRErr OGRKMLLayer::CreateField(const OGRFieldDefn *poField,
599 : CPL_UNUSED int bApproxOK)
600 : {
601 111 : if (!bWriter_ || iNextKMLId_ != 0)
602 0 : return OGRERR_FAILURE;
603 :
604 111 : OGRFieldDefn oCleanCopy(poField);
605 111 : poFeatureDefn_->AddFieldDefn(&oCleanCopy);
606 :
607 111 : return OGRERR_NONE;
608 : }
609 :
610 : /************************************************************************/
611 : /* SetLayerNumber() */
612 : /************************************************************************/
613 :
614 81 : void OGRKMLLayer::SetLayerNumber(int nLayer)
615 : {
616 81 : nLayerNumber_ = nLayer;
617 81 : }
618 :
619 : /************************************************************************/
620 : /* GetDataset() */
621 : /************************************************************************/
622 :
623 17 : GDALDataset *OGRKMLLayer::GetDataset()
624 : {
625 17 : return poDS_;
626 : }
|