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