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