Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRGmtLayer class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_gmt.h"
14 : #include "cpl_conv.h"
15 : #include "ogr_p.h"
16 :
17 : #include <algorithm>
18 :
19 : /************************************************************************/
20 : /* OGRGmtLayer() */
21 : /************************************************************************/
22 :
23 107 : OGRGmtLayer::OGRGmtLayer(GDALDataset *poDS, const char *pszFilename,
24 : VSILFILE *fp, const OGRSpatialReference *poSRS,
25 107 : int bUpdateIn)
26 : : m_poDS(poDS), poFeatureDefn(nullptr), iNextFID(0),
27 107 : bUpdate(CPL_TO_BOOL(bUpdateIn)),
28 : // Assume header complete in readonly mode.
29 107 : bHeaderComplete(CPL_TO_BOOL(!bUpdate)), bRegionComplete(false),
30 : nRegionOffset(0),
31 107 : m_fp(fp ? fp : VSIFOpenL(pszFilename, (bUpdateIn ? "r+" : "r"))),
32 214 : papszKeyedValues(nullptr), bValidFile(false)
33 : {
34 107 : if (m_fp == nullptr)
35 35 : return;
36 :
37 : /* -------------------------------------------------------------------- */
38 : /* Create the feature definition */
39 : /* -------------------------------------------------------------------- */
40 72 : poFeatureDefn = new OGRFeatureDefn(CPLGetBasename(pszFilename));
41 72 : SetDescription(poFeatureDefn->GetName());
42 72 : poFeatureDefn->Reference();
43 :
44 : /* -------------------------------------------------------------------- */
45 : /* Read the header. */
46 : /* -------------------------------------------------------------------- */
47 72 : if (!STARTS_WITH(pszFilename, "/vsistdout"))
48 : {
49 142 : CPLString osFieldNames;
50 142 : CPLString osFieldTypes;
51 142 : CPLString osGeometryType;
52 142 : CPLString osRegion;
53 142 : CPLString osWKT;
54 142 : CPLString osProj4;
55 142 : CPLString osEPSG;
56 71 : vsi_l_offset nStartOfLine = 0;
57 :
58 71 : VSIFSeekL(m_fp, 0, SEEK_SET);
59 :
60 258 : while (ReadLine() && osLine[0] == '#')
61 : {
62 207 : if (strstr(osLine, "FEATURE_DATA"))
63 : {
64 20 : bHeaderComplete = true;
65 20 : ReadLine();
66 20 : break;
67 : }
68 :
69 187 : if (STARTS_WITH_CI(osLine, "# REGION_STUB "))
70 34 : nRegionOffset = nStartOfLine;
71 :
72 390 : for (int iKey = 0; papszKeyedValues != nullptr &&
73 356 : papszKeyedValues[iKey] != nullptr;
74 : iKey++)
75 : {
76 203 : if (papszKeyedValues[iKey][0] == 'N')
77 20 : osFieldNames = papszKeyedValues[iKey] + 1;
78 203 : if (papszKeyedValues[iKey][0] == 'T')
79 20 : osFieldTypes = papszKeyedValues[iKey] + 1;
80 203 : if (papszKeyedValues[iKey][0] == 'G')
81 52 : osGeometryType = papszKeyedValues[iKey] + 1;
82 203 : if (papszKeyedValues[iKey][0] == 'R')
83 35 : osRegion = papszKeyedValues[iKey] + 1;
84 203 : if (papszKeyedValues[iKey][0] == 'J' &&
85 6 : papszKeyedValues[iKey][1] != 0 &&
86 6 : papszKeyedValues[iKey][2] != 0)
87 : {
88 12 : std::string osArg = papszKeyedValues[iKey] + 2;
89 10 : if (osArg[0] == '"' && osArg.size() >= 2 &&
90 4 : osArg.back() == '"')
91 : {
92 4 : osArg = osArg.substr(1, osArg.length() - 2);
93 4 : char *pszArg = CPLUnescapeString(
94 : osArg.c_str(), nullptr, CPLES_BackslashQuotable);
95 4 : osArg = pszArg;
96 4 : CPLFree(pszArg);
97 : }
98 :
99 6 : if (papszKeyedValues[iKey][1] == 'e')
100 2 : osEPSG = std::move(osArg);
101 6 : if (papszKeyedValues[iKey][1] == 'p')
102 2 : osProj4 = std::move(osArg);
103 6 : if (papszKeyedValues[iKey][1] == 'w')
104 2 : osWKT = std::move(osArg);
105 : }
106 : }
107 :
108 187 : nStartOfLine = VSIFTellL(m_fp);
109 : }
110 :
111 : /* --------------------------------------------------------------------
112 : */
113 : /* Handle coordinate system. */
114 : /* --------------------------------------------------------------------
115 : */
116 71 : if (osWKT.length())
117 : {
118 2 : m_poSRS = new OGRSpatialReference();
119 2 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
120 2 : if (m_poSRS->importFromWkt(osWKT.c_str()) != OGRERR_NONE)
121 : {
122 0 : delete m_poSRS;
123 0 : m_poSRS = nullptr;
124 : }
125 : }
126 69 : else if (osEPSG.length())
127 : {
128 0 : m_poSRS = new OGRSpatialReference();
129 0 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
130 0 : if (m_poSRS->importFromEPSG(atoi(osEPSG)) != OGRERR_NONE)
131 : {
132 0 : delete m_poSRS;
133 0 : m_poSRS = nullptr;
134 : }
135 : }
136 69 : else if (osProj4.length())
137 : {
138 0 : m_poSRS = new OGRSpatialReference();
139 0 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
140 0 : if (m_poSRS->importFromProj4(osProj4) != OGRERR_NONE)
141 : {
142 0 : delete m_poSRS;
143 0 : m_poSRS = nullptr;
144 : }
145 : }
146 :
147 71 : if (osGeometryType == "POINT")
148 8 : poFeatureDefn->SetGeomType(wkbPoint);
149 63 : else if (osGeometryType == "MULTIPOINT")
150 8 : poFeatureDefn->SetGeomType(wkbMultiPoint);
151 55 : else if (osGeometryType == "LINESTRING")
152 8 : poFeatureDefn->SetGeomType(wkbLineString);
153 47 : else if (osGeometryType == "MULTILINESTRING")
154 9 : poFeatureDefn->SetGeomType(wkbMultiLineString);
155 38 : else if (osGeometryType == "POLYGON")
156 10 : poFeatureDefn->SetGeomType(wkbPolygon);
157 28 : else if (osGeometryType == "MULTIPOLYGON")
158 9 : poFeatureDefn->SetGeomType(wkbMultiPolygon);
159 :
160 : /* --------------------------------------------------------------------
161 : */
162 : /* Process a region line. */
163 : /* --------------------------------------------------------------------
164 : */
165 71 : if (osRegion.length() > 0)
166 : {
167 : char **papszTokens =
168 35 : CSLTokenizeStringComplex(osRegion.c_str(), "/", FALSE, FALSE);
169 :
170 35 : if (CSLCount(papszTokens) == 4)
171 : {
172 35 : sRegion.MinX = CPLAtofM(papszTokens[0]);
173 35 : sRegion.MaxX = CPLAtofM(papszTokens[1]);
174 35 : sRegion.MinY = CPLAtofM(papszTokens[2]);
175 35 : sRegion.MaxY = CPLAtofM(papszTokens[3]);
176 : }
177 :
178 35 : bRegionComplete = true;
179 :
180 35 : CSLDestroy(papszTokens);
181 : }
182 :
183 : /* --------------------------------------------------------------------
184 : */
185 : /* Process fields. */
186 : /* --------------------------------------------------------------------
187 : */
188 71 : if (osFieldNames.length() || osFieldTypes.length())
189 : {
190 : char **papszFN =
191 20 : CSLTokenizeStringComplex(osFieldNames, "|", TRUE, TRUE);
192 : char **papszFT =
193 20 : CSLTokenizeStringComplex(osFieldTypes, "|", TRUE, TRUE);
194 20 : const int nFNCount = CSLCount(papszFN);
195 20 : const int nFTCount = CSLCount(papszFT);
196 20 : const int nFieldCount = std::max(nFNCount, nFTCount);
197 :
198 110 : for (int iField = 0; iField < nFieldCount; iField++)
199 : {
200 180 : OGRFieldDefn oField("", OFTString);
201 :
202 90 : if (iField < nFNCount)
203 90 : oField.SetName(papszFN[iField]);
204 : else
205 0 : oField.SetName(CPLString().Printf("Field_%d", iField + 1));
206 :
207 90 : if (iField < nFTCount)
208 : {
209 90 : if (EQUAL(papszFT[iField], "integer"))
210 19 : oField.SetType(OFTInteger);
211 71 : else if (EQUAL(papszFT[iField], "double"))
212 19 : oField.SetType(OFTReal);
213 52 : else if (EQUAL(papszFT[iField], "datetime"))
214 17 : oField.SetType(OFTDateTime);
215 : }
216 :
217 90 : poFeatureDefn->AddFieldDefn(&oField);
218 : }
219 :
220 20 : CSLDestroy(papszFN);
221 20 : CSLDestroy(papszFT);
222 : }
223 : }
224 : else
225 : {
226 1 : if (poSRS)
227 : {
228 1 : m_poSRS = poSRS->Clone();
229 1 : m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
230 : }
231 : }
232 :
233 72 : poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
234 :
235 72 : bValidFile = true;
236 : }
237 :
238 : /************************************************************************/
239 : /* ~OGRGmtLayer() */
240 : /************************************************************************/
241 :
242 214 : OGRGmtLayer::~OGRGmtLayer()
243 :
244 : {
245 107 : if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
246 : {
247 10 : CPLDebug("Gmt", "%d features read on layer '%s'.",
248 5 : static_cast<int>(m_nFeaturesRead), poFeatureDefn->GetName());
249 : }
250 :
251 : /* -------------------------------------------------------------------- */
252 : /* Write out the region bounds if we know where they go, and we */
253 : /* are in update mode. */
254 : /* -------------------------------------------------------------------- */
255 107 : if (nRegionOffset != 0 && bUpdate)
256 : {
257 34 : VSIFSeekL(m_fp, nRegionOffset, SEEK_SET);
258 34 : VSIFPrintfL(m_fp, "# @R%.12g/%.12g/%.12g/%.12g", sRegion.MinX,
259 : sRegion.MaxX, sRegion.MinY, sRegion.MaxY);
260 : }
261 :
262 : /* -------------------------------------------------------------------- */
263 : /* Clean up. */
264 : /* -------------------------------------------------------------------- */
265 107 : CSLDestroy(papszKeyedValues);
266 :
267 107 : if (poFeatureDefn)
268 72 : poFeatureDefn->Release();
269 :
270 107 : if (m_poSRS)
271 3 : m_poSRS->Release();
272 :
273 107 : if (m_fp != nullptr)
274 72 : VSIFCloseL(m_fp);
275 214 : }
276 :
277 : /************************************************************************/
278 : /* ReadLine() */
279 : /* */
280 : /* Read a line into osLine. If it is a comment line with @ */
281 : /* keyed values, parse out the keyed values into */
282 : /* papszKeyedValues. */
283 : /************************************************************************/
284 :
285 1244 : bool OGRGmtLayer::ReadLine()
286 :
287 : {
288 : /* -------------------------------------------------------------------- */
289 : /* Clear last line. */
290 : /* -------------------------------------------------------------------- */
291 1244 : osLine.erase();
292 1244 : if (papszKeyedValues)
293 : {
294 286 : CSLDestroy(papszKeyedValues);
295 286 : papszKeyedValues = nullptr;
296 : }
297 :
298 : /* -------------------------------------------------------------------- */
299 : /* Read newline. */
300 : /* -------------------------------------------------------------------- */
301 1244 : const char *pszLine = CPLReadLineL(m_fp);
302 1244 : if (pszLine == nullptr)
303 56 : return false; // end of file.
304 :
305 1188 : osLine = pszLine;
306 :
307 : /* -------------------------------------------------------------------- */
308 : /* If this is a comment line with keyed values, parse them. */
309 : /* -------------------------------------------------------------------- */
310 :
311 1188 : if (osLine[0] != '#' || osLine.find_first_of('@') == std::string::npos)
312 898 : return true;
313 :
314 290 : CPLStringList aosKeyedValues;
315 3163 : for (size_t i = 0; i < osLine.length(); i++)
316 : {
317 2873 : if (osLine[i] == '@' && i + 2 <= osLine.size())
318 : {
319 341 : bool bInQuotes = false;
320 :
321 341 : size_t iValEnd = i + 2; // Used after for.
322 5660 : for (; iValEnd < osLine.length(); iValEnd++)
323 : {
324 5406 : if (!bInQuotes && isspace((unsigned char)osLine[iValEnd]))
325 87 : break;
326 :
327 6511 : if (bInQuotes && iValEnd < osLine.length() - 1 &&
328 1192 : osLine[iValEnd] == '\\')
329 : {
330 88 : iValEnd++;
331 : }
332 5231 : else if (osLine[iValEnd] == '"')
333 26 : bInQuotes = !bInQuotes;
334 : }
335 :
336 682 : const CPLString osValue = osLine.substr(i + 2, iValEnd - i - 2);
337 :
338 : // Unecape contents
339 : char *pszUEValue =
340 341 : CPLUnescapeString(osValue, nullptr, CPLES_BackslashQuotable);
341 :
342 341 : CPLString osKeyValue = osLine.substr(i + 1, 1);
343 341 : osKeyValue += pszUEValue;
344 341 : CPLFree(pszUEValue);
345 341 : aosKeyedValues.AddString(osKeyValue);
346 :
347 341 : i = iValEnd;
348 : }
349 : }
350 290 : papszKeyedValues = aosKeyedValues.StealList();
351 :
352 290 : return true;
353 : }
354 :
355 : /************************************************************************/
356 : /* ResetReading() */
357 : /************************************************************************/
358 :
359 20 : void OGRGmtLayer::ResetReading()
360 :
361 : {
362 20 : if (iNextFID == 0)
363 18 : return;
364 :
365 2 : iNextFID = 0;
366 2 : VSIFSeekL(m_fp, 0, SEEK_SET);
367 2 : ReadLine();
368 : }
369 :
370 : /************************************************************************/
371 : /* ScanAheadForHole() */
372 : /* */
373 : /* Scan ahead to see if the next geometry is a hole. If so */
374 : /* return true, otherwise seek back to where we were and return */
375 : /* false. */
376 : /************************************************************************/
377 :
378 30 : bool OGRGmtLayer::ScanAheadForHole()
379 :
380 : {
381 60 : const CPLString osSavedLine = osLine;
382 30 : const vsi_l_offset nSavedLocation = VSIFTellL(m_fp);
383 :
384 87 : while (ReadLine() && osLine[0] == '#')
385 : {
386 58 : if (papszKeyedValues != nullptr && papszKeyedValues[0][0] == 'H')
387 1 : return true;
388 : }
389 :
390 29 : VSIFSeekL(m_fp, nSavedLocation, SEEK_SET);
391 29 : osLine = osSavedLine;
392 :
393 : // We do not actually restore papszKeyedValues, but we
394 : // assume it does not matter since this method is only called
395 : // when processing the '>' line.
396 :
397 29 : return false;
398 : }
399 :
400 : /************************************************************************/
401 : /* NextIsFeature() */
402 : /* */
403 : /* Returns true if the next line is a feature attribute line. */
404 : /* This generally indicates the end of a multilinestring or */
405 : /* multipolygon feature. */
406 : /************************************************************************/
407 :
408 5 : bool OGRGmtLayer::NextIsFeature()
409 :
410 : {
411 5 : const CPLString osSavedLine = osLine;
412 5 : const vsi_l_offset nSavedLocation = VSIFTellL(m_fp);
413 5 : bool bReturn = false;
414 :
415 5 : ReadLine();
416 :
417 5 : if (osLine[0] == '#' && strstr(osLine, "@D") != nullptr)
418 2 : bReturn = true;
419 :
420 5 : VSIFSeekL(m_fp, nSavedLocation, SEEK_SET);
421 5 : osLine = osSavedLine;
422 :
423 : // We do not actually restore papszKeyedValues, but we
424 : // assume it does not matter since this method is only called
425 : // when processing the '>' line.
426 :
427 10 : return bReturn;
428 : }
429 :
430 : /************************************************************************/
431 : /* GetNextRawFeature() */
432 : /************************************************************************/
433 :
434 55 : OGRFeature *OGRGmtLayer::GetNextRawFeature()
435 :
436 : {
437 : #if 0
438 : bool bMultiVertex =
439 : poFeatureDefn->GetGeomType() != wkbPoint
440 : && poFeatureDefn->GetGeomType() != wkbUnknown;
441 : #endif
442 110 : CPLString osFieldData;
443 55 : OGRGeometry *poGeom = nullptr;
444 :
445 : /* -------------------------------------------------------------------- */
446 : /* Read lines associated with this feature. */
447 : /* -------------------------------------------------------------------- */
448 871 : for (; true; ReadLine())
449 : {
450 926 : if (osLine.length() == 0)
451 25 : break;
452 :
453 901 : if (osLine[0] == '>')
454 : {
455 67 : OGRwkbGeometryType eType = wkbUnknown;
456 67 : if (poGeom)
457 33 : eType = wkbFlatten(poGeom->getGeometryType());
458 67 : if (eType == wkbMultiPolygon)
459 : {
460 3 : OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
461 3 : if (ScanAheadForHole())
462 : {
463 : // Add a hole to the current polygon.
464 1 : poMP->getGeometryRef(poMP->getNumGeometries() - 1)
465 1 : ->addRingDirectly(new OGRLinearRing());
466 : }
467 2 : else if (!NextIsFeature())
468 : {
469 1 : OGRPolygon *poPoly = new OGRPolygon();
470 :
471 1 : poPoly->addRingDirectly(new OGRLinearRing());
472 :
473 1 : poMP->addGeometryDirectly(poPoly);
474 : }
475 : else
476 1 : break; /* done geometry */
477 : }
478 64 : else if (eType == wkbPolygon)
479 : {
480 27 : if (ScanAheadForHole())
481 0 : poGeom->toPolygon()->addRingDirectly(new OGRLinearRing());
482 : else
483 27 : break; /* done geometry */
484 : }
485 37 : else if (eType == wkbMultiLineString && !NextIsFeature())
486 : {
487 4 : poGeom->toMultiLineString()->addGeometryDirectly(
488 2 : new OGRLineString());
489 : }
490 35 : else if (poGeom != nullptr)
491 : {
492 1 : break;
493 : }
494 34 : else if (poFeatureDefn->GetGeomType() == wkbUnknown)
495 : {
496 0 : poFeatureDefn->SetGeomType(wkbLineString);
497 : // bMultiVertex = true;
498 : }
499 : }
500 834 : else if (osLine[0] == '#')
501 : {
502 145 : for (int i = 0;
503 145 : papszKeyedValues != nullptr && papszKeyedValues[i] != nullptr;
504 : i++)
505 : {
506 72 : if (papszKeyedValues[i][0] == 'D')
507 34 : osFieldData = papszKeyedValues[i] + 1;
508 : }
509 : }
510 : else
511 : {
512 : // Parse point line.
513 761 : double dfX = 0.0;
514 761 : double dfY = 0.0;
515 761 : double dfZ = 0.0;
516 761 : const int nDim = CPLsscanf(osLine, "%lf %lf %lf", &dfX, &dfY, &dfZ);
517 :
518 761 : if (nDim >= 2)
519 : {
520 761 : if (poGeom == nullptr)
521 : {
522 35 : switch (poFeatureDefn->GetGeomType())
523 : {
524 0 : case wkbLineString:
525 0 : poGeom = new OGRLineString();
526 0 : break;
527 :
528 30 : case wkbPolygon:
529 : {
530 30 : OGRPolygon *poPoly = new OGRPolygon();
531 30 : poGeom = poPoly;
532 30 : poPoly->addRingDirectly(new OGRLinearRing());
533 30 : break;
534 : }
535 :
536 2 : case wkbMultiPolygon:
537 : {
538 2 : OGRPolygon *poPoly = new OGRPolygon();
539 2 : poPoly->addRingDirectly(new OGRLinearRing());
540 :
541 2 : OGRMultiPolygon *poMP = new OGRMultiPolygon();
542 2 : poGeom = poMP;
543 2 : poMP->addGeometryDirectly(poPoly);
544 : }
545 2 : break;
546 :
547 0 : case wkbMultiPoint:
548 0 : poGeom = new OGRMultiPoint();
549 0 : break;
550 :
551 2 : case wkbMultiLineString:
552 : {
553 : OGRMultiLineString *poMLS =
554 2 : new OGRMultiLineString();
555 2 : poGeom = poMLS;
556 2 : poMLS->addGeometryDirectly(new OGRLineString());
557 2 : break;
558 : }
559 :
560 1 : case wkbPoint:
561 : case wkbUnknown:
562 : default:
563 1 : poGeom = new OGRPoint();
564 1 : break;
565 : }
566 : }
567 :
568 761 : CPLAssert(poGeom != nullptr);
569 : // cppcheck-suppress nullPointerRedundantCheck
570 761 : switch (wkbFlatten(poGeom->getGeometryType()))
571 : {
572 1 : case wkbPoint:
573 : {
574 1 : OGRPoint *poPoint = poGeom->toPoint();
575 1 : poPoint->setX(dfX);
576 1 : poPoint->setY(dfY);
577 1 : if (nDim == 3)
578 1 : poPoint->setZ(dfZ);
579 1 : break;
580 : }
581 :
582 0 : case wkbLineString:
583 : {
584 0 : OGRLineString *poLS = poGeom->toLineString();
585 0 : if (nDim == 3)
586 0 : poLS->addPoint(dfX, dfY, dfZ);
587 : else
588 0 : poLS->addPoint(dfX, dfY);
589 0 : break;
590 : }
591 :
592 752 : case wkbPolygon:
593 : case wkbMultiPolygon:
594 : {
595 752 : OGRPolygon *poPoly = nullptr;
596 :
597 752 : if (wkbFlatten(poGeom->getGeometryType()) ==
598 : wkbMultiPolygon)
599 : {
600 17 : OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
601 17 : poPoly = poMP->getGeometryRef(
602 17 : poMP->getNumGeometries() - 1);
603 : }
604 : else
605 735 : poPoly = poGeom->toPolygon();
606 :
607 752 : OGRLinearRing *poRing = nullptr;
608 752 : if (poPoly->getNumInteriorRings() == 0)
609 748 : poRing = poPoly->getExteriorRing();
610 : else
611 4 : poRing = poPoly->getInteriorRing(
612 4 : poPoly->getNumInteriorRings() - 1);
613 :
614 752 : if (nDim == 3)
615 0 : poRing->addPoint(dfX, dfY, dfZ);
616 : else
617 752 : poRing->addPoint(dfX, dfY);
618 : }
619 752 : break;
620 :
621 8 : case wkbMultiLineString:
622 : {
623 8 : OGRMultiLineString *poML = poGeom->toMultiLineString();
624 : OGRLineString *poLine =
625 8 : poML->getGeometryRef(poML->getNumGeometries() - 1);
626 :
627 8 : if (nDim == 3)
628 0 : poLine->addPoint(dfX, dfY, dfZ);
629 : else
630 8 : poLine->addPoint(dfX, dfY);
631 : }
632 8 : break;
633 :
634 0 : default:
635 0 : CPLAssert(false);
636 : }
637 : }
638 : }
639 :
640 872 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
641 : {
642 1 : ReadLine();
643 1 : break;
644 : }
645 871 : }
646 :
647 55 : if (poGeom == nullptr)
648 20 : return nullptr;
649 :
650 : /* -------------------------------------------------------------------- */
651 : /* Create feature. */
652 : /* -------------------------------------------------------------------- */
653 35 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
654 35 : poGeom->assignSpatialReference(m_poSRS);
655 35 : poFeature->SetGeometryDirectly(poGeom);
656 35 : poFeature->SetFID(iNextFID++);
657 :
658 : /* -------------------------------------------------------------------- */
659 : /* Process field values. */
660 : /* -------------------------------------------------------------------- */
661 35 : char **papszFD = CSLTokenizeStringComplex(osFieldData, "|", TRUE, TRUE);
662 :
663 133 : for (int iField = 0; papszFD != nullptr && papszFD[iField] != nullptr;
664 : iField++)
665 : {
666 98 : if (iField >= poFeatureDefn->GetFieldCount())
667 0 : break;
668 :
669 98 : poFeature->SetField(iField, papszFD[iField]);
670 : }
671 :
672 35 : CSLDestroy(papszFD);
673 :
674 35 : m_nFeaturesRead++;
675 :
676 35 : return poFeature;
677 : }
678 :
679 : /************************************************************************/
680 : /* CompleteHeader() */
681 : /* */
682 : /* Finish writing out the header with field definitions and the */
683 : /* layer geometry type. */
684 : /************************************************************************/
685 :
686 19 : OGRErr OGRGmtLayer::CompleteHeader(OGRGeometry *poThisGeom)
687 :
688 : {
689 : /* -------------------------------------------------------------------- */
690 : /* If we do not already have a geometry type, try to work one */
691 : /* out and write it now. */
692 : /* -------------------------------------------------------------------- */
693 19 : if (poFeatureDefn->GetGeomType() == wkbUnknown && poThisGeom != nullptr)
694 : {
695 2 : poFeatureDefn->SetGeomType(wkbFlatten(poThisGeom->getGeometryType()));
696 :
697 2 : const char *pszGeom = nullptr;
698 2 : switch (wkbFlatten(poFeatureDefn->GetGeomType()))
699 : {
700 0 : case wkbPoint:
701 0 : pszGeom = " @GPOINT";
702 0 : break;
703 0 : case wkbLineString:
704 0 : pszGeom = " @GLINESTRING";
705 0 : break;
706 1 : case wkbPolygon:
707 1 : pszGeom = " @GPOLYGON";
708 1 : break;
709 0 : case wkbMultiPoint:
710 0 : pszGeom = " @GMULTIPOINT";
711 0 : break;
712 0 : case wkbMultiLineString:
713 0 : pszGeom = " @GMULTILINESTRING";
714 0 : break;
715 1 : case wkbMultiPolygon:
716 1 : pszGeom = " @GMULTIPOLYGON";
717 1 : break;
718 0 : default:
719 0 : pszGeom = "";
720 0 : break;
721 : }
722 :
723 2 : VSIFPrintfL(m_fp, "#%s\n", pszGeom);
724 : }
725 :
726 : /* -------------------------------------------------------------------- */
727 : /* Prepare and write the field names and types. */
728 : /* -------------------------------------------------------------------- */
729 38 : CPLString osFieldNames;
730 19 : CPLString osFieldTypes;
731 :
732 106 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
733 : {
734 87 : if (iField > 0)
735 : {
736 68 : osFieldNames += "|";
737 68 : osFieldTypes += "|";
738 : }
739 :
740 87 : osFieldNames += poFeatureDefn->GetFieldDefn(iField)->GetNameRef();
741 87 : switch (poFeatureDefn->GetFieldDefn(iField)->GetType())
742 : {
743 18 : case OFTInteger:
744 18 : osFieldTypes += "integer";
745 18 : break;
746 :
747 19 : case OFTReal:
748 19 : osFieldTypes += "double";
749 19 : break;
750 :
751 16 : case OFTDateTime:
752 16 : osFieldTypes += "datetime";
753 16 : break;
754 :
755 34 : default:
756 34 : osFieldTypes += "string";
757 34 : break;
758 : }
759 : }
760 :
761 19 : if (poFeatureDefn->GetFieldCount() > 0)
762 : {
763 19 : VSIFPrintfL(m_fp, "# @N%s\n", osFieldNames.c_str());
764 19 : VSIFPrintfL(m_fp, "# @T%s\n", osFieldTypes.c_str());
765 : }
766 :
767 : /* -------------------------------------------------------------------- */
768 : /* Mark the end of the header, and start of feature data. */
769 : /* -------------------------------------------------------------------- */
770 19 : VSIFPrintfL(m_fp, "# FEATURE_DATA\n");
771 :
772 19 : bHeaderComplete = true;
773 19 : bRegionComplete = true; // no feature written, so we know them all!
774 :
775 38 : return OGRERR_NONE;
776 : }
777 :
778 : /************************************************************************/
779 : /* ICreateFeature() */
780 : /************************************************************************/
781 :
782 86 : OGRErr OGRGmtLayer::ICreateFeature(OGRFeature *poFeature)
783 :
784 : {
785 86 : if (!bUpdate)
786 : {
787 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
788 : "Cannot create features on read-only dataset.");
789 0 : return OGRERR_FAILURE;
790 : }
791 :
792 : /* -------------------------------------------------------------------- */
793 : /* Do we need to write the header describing the fields? */
794 : /* -------------------------------------------------------------------- */
795 86 : if (!bHeaderComplete)
796 : {
797 19 : OGRErr eErr = CompleteHeader(poFeature->GetGeometryRef());
798 :
799 19 : if (eErr != OGRERR_NONE)
800 0 : return eErr;
801 : }
802 :
803 : /* -------------------------------------------------------------------- */
804 : /* Write out the feature */
805 : /* -------------------------------------------------------------------- */
806 86 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
807 :
808 86 : if (poGeom == nullptr)
809 : {
810 33 : CPLError(CE_Failure, CPLE_AppDefined,
811 : "Features without geometry not supported by GMT writer.");
812 33 : return OGRERR_FAILURE;
813 : }
814 :
815 53 : if (poFeatureDefn->GetGeomType() == wkbUnknown)
816 4 : poFeatureDefn->SetGeomType(wkbFlatten(poGeom->getGeometryType()));
817 :
818 : // Do we need a vertex collection marker grouping vertices.
819 53 : if (poFeatureDefn->GetGeomType() != wkbPoint)
820 47 : VSIFPrintfL(m_fp, ">\n");
821 :
822 : /* -------------------------------------------------------------------- */
823 : /* Write feature properties() */
824 : /* -------------------------------------------------------------------- */
825 53 : if (poFeatureDefn->GetFieldCount() > 0)
826 : {
827 106 : CPLString osFieldData;
828 :
829 270 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
830 : {
831 : OGRFieldType eFType =
832 217 : poFeatureDefn->GetFieldDefn(iField)->GetType();
833 217 : const char *pszRawValue = poFeature->GetFieldAsString(iField);
834 :
835 217 : if (iField > 0)
836 164 : osFieldData += "|";
837 :
838 : // We do not want prefix spaces for numeric values.
839 217 : if (eFType == OFTInteger || eFType == OFTReal)
840 104 : while (*pszRawValue == ' ')
841 0 : pszRawValue++;
842 :
843 217 : if (strchr(pszRawValue, ' ') || strchr(pszRawValue, '|') ||
844 186 : strchr(pszRawValue, '\t') || strchr(pszRawValue, '\n'))
845 : {
846 31 : osFieldData += "\"";
847 :
848 : char *pszEscapedVal =
849 31 : CPLEscapeString(pszRawValue, -1, CPLES_BackslashQuotable);
850 31 : osFieldData += pszEscapedVal;
851 31 : CPLFree(pszEscapedVal);
852 :
853 31 : osFieldData += "\"";
854 : }
855 : else
856 186 : osFieldData += pszRawValue;
857 : }
858 :
859 53 : VSIFPrintfL(m_fp, "# @D%s\n", osFieldData.c_str());
860 : }
861 :
862 : /* -------------------------------------------------------------------- */
863 : /* Write Geometry */
864 : /* -------------------------------------------------------------------- */
865 53 : return WriteGeometry(OGRGeometry::ToHandle(poGeom), true);
866 : }
867 :
868 : /************************************************************************/
869 : /* WriteGeometry() */
870 : /* */
871 : /* Write a geometry to the file. If bHaveAngle is true it */
872 : /* means the angle bracket preceding the point stream has */
873 : /* already been written out. */
874 : /* */
875 : /* We use the C API for geometry access because of its */
876 : /* simplified access to vertices and children geometries. */
877 : /************************************************************************/
878 :
879 116 : OGRErr OGRGmtLayer::WriteGeometry(OGRGeometryH hGeom, bool bHaveAngle)
880 :
881 : {
882 : /* -------------------------------------------------------------------- */
883 : /* This is a geometry with sub-geometries. */
884 : /* -------------------------------------------------------------------- */
885 116 : if (OGR_G_GetGeometryCount(hGeom) > 0)
886 : {
887 53 : OGRErr eErr = OGRERR_NONE;
888 :
889 53 : for (int iGeom = 0;
890 116 : iGeom < OGR_G_GetGeometryCount(hGeom) && eErr == OGRERR_NONE;
891 : iGeom++)
892 : {
893 : // We need to emit polygon @P and @H items while we still
894 : // know this is a polygon and which is the outer and inner
895 : // ring.
896 63 : if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon)
897 : {
898 36 : if (!bHaveAngle)
899 : {
900 6 : VSIFPrintfL(m_fp, ">\n");
901 6 : bHaveAngle = true;
902 : }
903 36 : if (iGeom == 0)
904 35 : VSIFPrintfL(m_fp, "# @P\n");
905 : else
906 1 : VSIFPrintfL(m_fp, "# @H\n");
907 : }
908 :
909 : eErr =
910 63 : WriteGeometry(OGR_G_GetGeometryRef(hGeom, iGeom), bHaveAngle);
911 63 : bHaveAngle = false;
912 : }
913 53 : return eErr;
914 : }
915 :
916 : /* -------------------------------------------------------------------- */
917 : /* If this is not a point we need to have an angle bracket to */
918 : /* mark the vertex list. */
919 : /* -------------------------------------------------------------------- */
920 63 : if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) != wkbPoint && !bHaveAngle)
921 4 : VSIFPrintfL(m_fp, ">\n");
922 :
923 : /* -------------------------------------------------------------------- */
924 : /* Dump vertices. */
925 : /* -------------------------------------------------------------------- */
926 63 : const int nPointCount = OGR_G_GetPointCount(hGeom);
927 63 : const int nDim = OGR_G_GetCoordinateDimension(hGeom);
928 : // For testing only. Ticket #6453
929 : const bool bUseTab =
930 63 : CPLTestBool(CPLGetConfigOption("GMT_USE_TAB", "FALSE"));
931 :
932 671 : for (int iPoint = 0; iPoint < nPointCount; iPoint++)
933 : {
934 608 : const double dfX = OGR_G_GetX(hGeom, iPoint);
935 608 : const double dfY = OGR_G_GetY(hGeom, iPoint);
936 608 : const double dfZ = OGR_G_GetZ(hGeom, iPoint);
937 :
938 608 : sRegion.Merge(dfX, dfY);
939 : char szLine[128];
940 608 : OGRMakeWktCoordinate(szLine, dfX, dfY, dfZ, nDim);
941 608 : if (bUseTab)
942 : {
943 60 : for (char *szPtr = szLine; *szPtr != '\0'; ++szPtr)
944 : {
945 47 : if (*szPtr == ' ')
946 13 : *szPtr = '\t';
947 : }
948 : }
949 608 : if (VSIFPrintfL(m_fp, "%s\n", szLine) < 1)
950 : {
951 0 : CPLError(CE_Failure, CPLE_FileIO, "Gmt write failure: %s",
952 0 : VSIStrerror(errno));
953 0 : return OGRERR_FAILURE;
954 : }
955 : }
956 :
957 63 : return OGRERR_NONE;
958 : }
959 :
960 : /************************************************************************/
961 : /* GetExtent() */
962 : /* */
963 : /* Fetch extent of the data currently stored in the dataset. */
964 : /* The bForce flag has no effect on SHO files since that value */
965 : /* is always in the header. */
966 : /* */
967 : /* Returns OGRERR_NONE/OGRRERR_FAILURE. */
968 : /************************************************************************/
969 :
970 0 : OGRErr OGRGmtLayer::GetExtent(OGREnvelope *psExtent, int bForce)
971 :
972 : {
973 0 : if (bRegionComplete && sRegion.IsInit())
974 : {
975 0 : *psExtent = sRegion;
976 0 : return OGRERR_NONE;
977 : }
978 :
979 0 : return OGRLayer::GetExtent(psExtent, bForce);
980 : }
981 :
982 : /************************************************************************/
983 : /* TestCapability() */
984 : /************************************************************************/
985 :
986 72 : int OGRGmtLayer::TestCapability(const char *pszCap)
987 :
988 : {
989 72 : if (EQUAL(pszCap, OLCRandomRead))
990 0 : return FALSE;
991 :
992 72 : if (EQUAL(pszCap, OLCSequentialWrite))
993 16 : return TRUE;
994 :
995 56 : if (EQUAL(pszCap, OLCFastSpatialFilter))
996 0 : return FALSE;
997 :
998 56 : if (EQUAL(pszCap, OLCFastGetExtent))
999 0 : return bRegionComplete;
1000 :
1001 56 : if (EQUAL(pszCap, OLCCreateField))
1002 16 : return TRUE;
1003 :
1004 40 : if (EQUAL(pszCap, OLCZGeometries))
1005 0 : return TRUE;
1006 :
1007 40 : return FALSE;
1008 : }
1009 :
1010 : /************************************************************************/
1011 : /* CreateField() */
1012 : /************************************************************************/
1013 :
1014 87 : OGRErr OGRGmtLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
1015 :
1016 : {
1017 87 : if (!bUpdate)
1018 : {
1019 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
1020 : "Cannot create fields on read-only dataset.");
1021 0 : return OGRERR_FAILURE;
1022 : }
1023 :
1024 87 : if (bHeaderComplete)
1025 : {
1026 0 : CPLError(CE_Failure, CPLE_AppDefined,
1027 : "Unable to create fields after features have been created.");
1028 0 : return OGRERR_FAILURE;
1029 : }
1030 :
1031 87 : switch (poField->GetType())
1032 : {
1033 71 : case OFTInteger:
1034 : case OFTReal:
1035 : case OFTString:
1036 : case OFTDateTime:
1037 71 : poFeatureDefn->AddFieldDefn(poField);
1038 71 : return OGRERR_NONE;
1039 : break;
1040 :
1041 : break;
1042 :
1043 16 : default:
1044 16 : if (!bApproxOK)
1045 : {
1046 0 : CPLError(CE_Failure, CPLE_AppDefined,
1047 : "Field %s is of unsupported type %s.",
1048 : poField->GetNameRef(),
1049 : poField->GetFieldTypeName(poField->GetType()));
1050 0 : return OGRERR_FAILURE;
1051 : }
1052 16 : else if (poField->GetType() == OFTDate ||
1053 0 : poField->GetType() == OFTTime)
1054 : {
1055 16 : OGRFieldDefn oModDef(poField);
1056 16 : oModDef.SetType(OFTDateTime);
1057 16 : poFeatureDefn->AddFieldDefn(poField);
1058 16 : return OGRERR_NONE;
1059 : }
1060 : else
1061 : {
1062 0 : OGRFieldDefn oModDef(poField);
1063 0 : oModDef.SetType(OFTString);
1064 0 : poFeatureDefn->AddFieldDefn(poField);
1065 0 : return OGRERR_NONE;
1066 : }
1067 : }
1068 : }
|