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 108 : OGRGmtLayer::OGRGmtLayer(GDALDataset *poDS, const char *pszFilename,
24 : VSILFILE *fp, const OGRSpatialReference *poSRS,
25 108 : bool bUpdateIn)
26 : : m_poDS(poDS), poFeatureDefn(nullptr), iNextFID(0), bUpdate(bUpdateIn),
27 : // Assume header complete in readonly mode.
28 108 : bHeaderComplete(!bUpdate), bRegionComplete(false), nRegionOffset(0),
29 108 : m_fp(fp ? fp : VSIFOpenL(pszFilename, (bUpdateIn ? "r+" : "r"))),
30 216 : papszKeyedValues(nullptr), bValidFile(false)
31 : {
32 108 : if (m_fp == nullptr)
33 35 : return;
34 :
35 : /* -------------------------------------------------------------------- */
36 : /* Create the feature definition */
37 : /* -------------------------------------------------------------------- */
38 73 : poFeatureDefn = new OGRFeatureDefn(CPLGetBasenameSafe(pszFilename).c_str());
39 73 : SetDescription(poFeatureDefn->GetName());
40 73 : poFeatureDefn->Reference();
41 :
42 : /* -------------------------------------------------------------------- */
43 : /* Read the header. */
44 : /* -------------------------------------------------------------------- */
45 73 : if (!STARTS_WITH(pszFilename, "/vsistdout"))
46 : {
47 144 : CPLString osFieldNames;
48 144 : CPLString osFieldTypes;
49 144 : CPLString osGeometryType;
50 144 : CPLString osRegion;
51 144 : CPLString osWKT;
52 144 : CPLString osProj4;
53 144 : CPLString osEPSG;
54 72 : vsi_l_offset nStartOfLine = 0;
55 :
56 72 : VSIFSeekL(m_fp, 0, SEEK_SET);
57 :
58 260 : while (ReadLine() && osLine[0] == '#')
59 : {
60 208 : if (strstr(osLine, "FEATURE_DATA"))
61 : {
62 20 : bHeaderComplete = true;
63 20 : ReadLine();
64 20 : break;
65 : }
66 :
67 188 : if (STARTS_WITH_CI(osLine, "# REGION_STUB "))
68 34 : nRegionOffset = nStartOfLine;
69 :
70 393 : for (int iKey = 0; papszKeyedValues != nullptr &&
71 359 : papszKeyedValues[iKey] != nullptr;
72 : iKey++)
73 : {
74 205 : if (papszKeyedValues[iKey][0] == 'N')
75 20 : osFieldNames = papszKeyedValues[iKey] + 1;
76 205 : if (papszKeyedValues[iKey][0] == 'T')
77 20 : osFieldTypes = papszKeyedValues[iKey] + 1;
78 205 : if (papszKeyedValues[iKey][0] == 'G')
79 53 : osGeometryType = papszKeyedValues[iKey] + 1;
80 205 : if (papszKeyedValues[iKey][0] == 'R')
81 35 : osRegion = papszKeyedValues[iKey] + 1;
82 205 : 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 188 : nStartOfLine = VSIFTellL(m_fp);
107 : }
108 :
109 : /* --------------------------------------------------------------------
110 : */
111 : /* Handle coordinate system. */
112 : /* --------------------------------------------------------------------
113 : */
114 72 : 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 70 : 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 70 : 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 72 : if (osGeometryType == "POINT")
146 8 : poFeatureDefn->SetGeomType(wkbPoint);
147 64 : else if (osGeometryType == "MULTIPOINT")
148 9 : 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 9 : poFeatureDefn->SetGeomType(wkbPolygon);
155 29 : else if (osGeometryType == "MULTIPOLYGON")
156 10 : poFeatureDefn->SetGeomType(wkbMultiPolygon);
157 :
158 : /* --------------------------------------------------------------------
159 : */
160 : /* Process a region line. */
161 : /* --------------------------------------------------------------------
162 : */
163 72 : 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 72 : 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 73 : poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS);
232 :
233 73 : bValidFile = true;
234 : }
235 :
236 : /************************************************************************/
237 : /* ~OGRGmtLayer() */
238 : /************************************************************************/
239 :
240 216 : OGRGmtLayer::~OGRGmtLayer()
241 :
242 : {
243 108 : if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
244 : {
245 12 : CPLDebug("Gmt", "%d features read on layer '%s'.",
246 6 : 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 108 : 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 108 : CSLDestroy(papszKeyedValues);
264 :
265 108 : if (poFeatureDefn)
266 73 : poFeatureDefn->Release();
267 :
268 108 : if (m_poSRS)
269 3 : m_poSRS->Release();
270 :
271 108 : if (m_fp != nullptr)
272 73 : VSIFCloseL(m_fp);
273 216 : }
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 1257 : bool OGRGmtLayer::ReadLine()
284 :
285 : {
286 : /* -------------------------------------------------------------------- */
287 : /* Clear last line. */
288 : /* -------------------------------------------------------------------- */
289 1257 : osLine.erase();
290 1257 : if (papszKeyedValues)
291 : {
292 296 : CSLDestroy(papszKeyedValues);
293 296 : papszKeyedValues = nullptr;
294 : }
295 :
296 : /* -------------------------------------------------------------------- */
297 : /* Read newline. */
298 : /* -------------------------------------------------------------------- */
299 1257 : const char *pszLine = CPLReadLineL(m_fp);
300 1257 : if (pszLine == nullptr)
301 57 : return false; // end of file.
302 :
303 1200 : osLine = pszLine;
304 :
305 : /* -------------------------------------------------------------------- */
306 : /* If this is a comment line with keyed values, parse them. */
307 : /* -------------------------------------------------------------------- */
308 :
309 1200 : if (osLine[0] != '#' || osLine.find_first_of('@') == std::string::npos)
310 900 : return true;
311 :
312 300 : CPLStringList aosKeyedValues;
313 3204 : for (size_t i = 0; i < osLine.length(); i++)
314 : {
315 2904 : if (osLine[i] == '@' && i + 2 <= osLine.size())
316 : {
317 352 : bool bInQuotes = false;
318 :
319 352 : size_t iValEnd = i + 2; // Used after for.
320 5903 : for (; iValEnd < osLine.length(); iValEnd++)
321 : {
322 10076 : if (!bInQuotes &&
323 4437 : isspace(static_cast<unsigned char>(osLine[iValEnd])))
324 88 : break;
325 :
326 6743 : if (bInQuotes && iValEnd < osLine.length() - 1 &&
327 1192 : osLine[iValEnd] == '\\')
328 : {
329 88 : iValEnd++;
330 : }
331 5463 : else if (osLine[iValEnd] == '"')
332 26 : bInQuotes = !bInQuotes;
333 : }
334 :
335 704 : const CPLString osValue = osLine.substr(i + 2, iValEnd - i - 2);
336 :
337 : // Unecape contents
338 : char *pszUEValue =
339 352 : CPLUnescapeString(osValue, nullptr, CPLES_BackslashQuotable);
340 :
341 352 : CPLString osKeyValue = osLine.substr(i + 1, 1);
342 352 : osKeyValue += pszUEValue;
343 352 : CPLFree(pszUEValue);
344 352 : aosKeyedValues.AddString(osKeyValue);
345 :
346 352 : i = iValEnd;
347 : }
348 : }
349 300 : papszKeyedValues = aosKeyedValues.StealList();
350 :
351 300 : 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 14 : bool OGRGmtLayer::NextIsFeature()
408 :
409 : {
410 14 : CPLString osSavedLine = osLine;
411 14 : const vsi_l_offset nSavedLocation = VSIFTellL(m_fp);
412 14 : bool bReturn = false;
413 :
414 14 : ReadLine();
415 :
416 14 : if (osLine[0] == '#' && strstr(osLine, "@D") != nullptr)
417 11 : bReturn = true;
418 :
419 14 : VSIFSeekL(m_fp, nSavedLocation, SEEK_SET);
420 14 : 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 28 : return bReturn;
427 : }
428 :
429 : /************************************************************************/
430 : /* GetNextRawFeature() */
431 : /************************************************************************/
432 :
433 56 : OGRFeature *OGRGmtLayer::GetNextRawFeature()
434 :
435 : {
436 : #if 0
437 : bool bMultiVertex =
438 : poFeatureDefn->GetGeomType() != wkbPoint
439 : && poFeatureDefn->GetGeomType() != wkbUnknown;
440 : #endif
441 112 : CPLString osFieldData;
442 56 : OGRGeometry *poGeom = nullptr;
443 :
444 : /* -------------------------------------------------------------------- */
445 : /* Read lines associated with this feature. */
446 : /* -------------------------------------------------------------------- */
447 873 : for (; true; ReadLine())
448 : {
449 929 : if (osLine.length() == 0)
450 26 : break;
451 :
452 903 : 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 12 : OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
460 12 : 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 11 : 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 10 : break; /* done geometry */
476 : }
477 55 : else if (eType == wkbPolygon)
478 : {
479 18 : if (ScanAheadForHole())
480 0 : poGeom->toPolygon()->addRingDirectly(new OGRLinearRing());
481 : else
482 18 : 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 836 : 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 763 : double dfX = 0.0;
513 763 : double dfY = 0.0;
514 763 : double dfZ = 0.0;
515 763 : const int nDim = CPLsscanf(osLine, "%lf %lf %lf", &dfX, &dfY, &dfZ);
516 :
517 763 : if (nDim >= 2)
518 : {
519 763 : if (poGeom == nullptr)
520 : {
521 36 : switch (poFeatureDefn->GetGeomType())
522 : {
523 0 : case wkbLineString:
524 0 : poGeom = new OGRLineString();
525 0 : break;
526 :
527 20 : case wkbPolygon:
528 : {
529 20 : OGRPolygon *poPoly = new OGRPolygon();
530 20 : poGeom = poPoly;
531 20 : poPoly->addRingDirectly(new OGRLinearRing());
532 20 : break;
533 : }
534 :
535 12 : case wkbMultiPolygon:
536 : {
537 12 : OGRPolygon *poPoly = new OGRPolygon();
538 12 : poPoly->addRingDirectly(new OGRLinearRing());
539 :
540 12 : OGRMultiPolygon *poMP = new OGRMultiPolygon();
541 12 : poGeom = poMP;
542 12 : poMP->addGeometryDirectly(poPoly);
543 : }
544 12 : break;
545 :
546 1 : case wkbMultiPoint:
547 1 : poGeom = new OGRMultiPoint();
548 1 : 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 763 : CPLAssert(poGeom != nullptr);
568 : // cppcheck-suppress nullPointerRedundantCheck
569 763 : 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 262 : OGRMultiPolygon *poMP = poGeom->toMultiPolygon();
600 262 : poPoly = poMP->getGeometryRef(
601 262 : poMP->getNumGeometries() - 1);
602 : }
603 : else
604 490 : 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 2 : case wkbMultiPoint:
634 : {
635 2 : OGRMultiPoint *poMP = poGeom->toMultiPoint();
636 4 : OGRPoint oPoint(dfX, dfY);
637 2 : if (nDim == 3)
638 2 : oPoint.setZ(dfZ);
639 2 : poMP->addGeometry(&oPoint);
640 : }
641 2 : break;
642 :
643 0 : default:
644 0 : CPLAssert(false);
645 : }
646 : }
647 : }
648 :
649 874 : if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
650 : {
651 1 : ReadLine();
652 1 : break;
653 : }
654 873 : }
655 :
656 56 : if (poGeom == nullptr)
657 20 : return nullptr;
658 :
659 : /* -------------------------------------------------------------------- */
660 : /* Create feature. */
661 : /* -------------------------------------------------------------------- */
662 36 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
663 36 : poGeom->assignSpatialReference(m_poSRS);
664 36 : poFeature->SetGeometryDirectly(poGeom);
665 36 : poFeature->SetFID(iNextFID++);
666 :
667 : /* -------------------------------------------------------------------- */
668 : /* Process field values. */
669 : /* -------------------------------------------------------------------- */
670 36 : char **papszFD = CSLTokenizeStringComplex(osFieldData, "|", TRUE, TRUE);
671 :
672 134 : for (int iField = 0; papszFD != nullptr && papszFD[iField] != nullptr;
673 : iField++)
674 : {
675 98 : if (iField >= poFeatureDefn->GetFieldCount())
676 0 : break;
677 :
678 98 : poFeature->SetField(iField, papszFD[iField]);
679 : }
680 :
681 36 : CSLDestroy(papszFD);
682 :
683 36 : m_nFeaturesRead++;
684 :
685 36 : return poFeature;
686 : }
687 :
688 : /************************************************************************/
689 : /* CompleteHeader() */
690 : /* */
691 : /* Finish writing out the header with field definitions and the */
692 : /* layer geometry type. */
693 : /************************************************************************/
694 :
695 19 : OGRErr OGRGmtLayer::CompleteHeader(OGRGeometry *poThisGeom)
696 :
697 : {
698 : /* -------------------------------------------------------------------- */
699 : /* If we do not already have a geometry type, try to work one */
700 : /* out and write it now. */
701 : /* -------------------------------------------------------------------- */
702 19 : if (poFeatureDefn->GetGeomType() == wkbUnknown && poThisGeom != nullptr)
703 : {
704 2 : poFeatureDefn->SetGeomType(wkbFlatten(poThisGeom->getGeometryType()));
705 :
706 2 : const char *pszGeom = nullptr;
707 2 : switch (wkbFlatten(poFeatureDefn->GetGeomType()))
708 : {
709 0 : case wkbPoint:
710 0 : pszGeom = " @GPOINT";
711 0 : break;
712 0 : case wkbLineString:
713 0 : pszGeom = " @GLINESTRING";
714 0 : break;
715 1 : case wkbPolygon:
716 1 : pszGeom = " @GPOLYGON";
717 1 : break;
718 0 : case wkbMultiPoint:
719 0 : pszGeom = " @GMULTIPOINT";
720 0 : break;
721 0 : case wkbMultiLineString:
722 0 : pszGeom = " @GMULTILINESTRING";
723 0 : break;
724 1 : case wkbMultiPolygon:
725 1 : pszGeom = " @GMULTIPOLYGON";
726 1 : break;
727 0 : default:
728 0 : pszGeom = "";
729 0 : break;
730 : }
731 :
732 2 : VSIFPrintfL(m_fp, "#%s\n", pszGeom);
733 : }
734 :
735 : /* -------------------------------------------------------------------- */
736 : /* Prepare and write the field names and types. */
737 : /* -------------------------------------------------------------------- */
738 38 : CPLString osFieldNames;
739 19 : CPLString osFieldTypes;
740 :
741 106 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
742 : {
743 87 : if (iField > 0)
744 : {
745 68 : osFieldNames += "|";
746 68 : osFieldTypes += "|";
747 : }
748 :
749 87 : osFieldNames += poFeatureDefn->GetFieldDefn(iField)->GetNameRef();
750 87 : switch (poFeatureDefn->GetFieldDefn(iField)->GetType())
751 : {
752 18 : case OFTInteger:
753 18 : osFieldTypes += "integer";
754 18 : break;
755 :
756 19 : case OFTReal:
757 19 : osFieldTypes += "double";
758 19 : break;
759 :
760 16 : case OFTDateTime:
761 16 : osFieldTypes += "datetime";
762 16 : break;
763 :
764 34 : default:
765 34 : osFieldTypes += "string";
766 34 : break;
767 : }
768 : }
769 :
770 19 : if (poFeatureDefn->GetFieldCount() > 0)
771 : {
772 19 : VSIFPrintfL(m_fp, "# @N%s\n", osFieldNames.c_str());
773 19 : VSIFPrintfL(m_fp, "# @T%s\n", osFieldTypes.c_str());
774 : }
775 :
776 : /* -------------------------------------------------------------------- */
777 : /* Mark the end of the header, and start of feature data. */
778 : /* -------------------------------------------------------------------- */
779 19 : VSIFPrintfL(m_fp, "# FEATURE_DATA\n");
780 :
781 19 : bHeaderComplete = true;
782 19 : bRegionComplete = true; // no feature written, so we know them all!
783 :
784 38 : return OGRERR_NONE;
785 : }
786 :
787 : /************************************************************************/
788 : /* ICreateFeature() */
789 : /************************************************************************/
790 :
791 86 : OGRErr OGRGmtLayer::ICreateFeature(OGRFeature *poFeature)
792 :
793 : {
794 86 : if (!bUpdate)
795 : {
796 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
797 : "Cannot create features on read-only dataset.");
798 0 : return OGRERR_FAILURE;
799 : }
800 :
801 : /* -------------------------------------------------------------------- */
802 : /* Do we need to write the header describing the fields? */
803 : /* -------------------------------------------------------------------- */
804 86 : if (!bHeaderComplete)
805 : {
806 19 : OGRErr eErr = CompleteHeader(poFeature->GetGeometryRef());
807 :
808 19 : if (eErr != OGRERR_NONE)
809 0 : return eErr;
810 : }
811 :
812 : /* -------------------------------------------------------------------- */
813 : /* Write out the feature */
814 : /* -------------------------------------------------------------------- */
815 86 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
816 :
817 86 : if (poGeom == nullptr)
818 : {
819 33 : CPLError(CE_Failure, CPLE_AppDefined,
820 : "Features without geometry not supported by GMT writer.");
821 33 : return OGRERR_FAILURE;
822 : }
823 :
824 53 : if (poFeatureDefn->GetGeomType() == wkbUnknown)
825 4 : poFeatureDefn->SetGeomType(wkbFlatten(poGeom->getGeometryType()));
826 :
827 : // Do we need a vertex collection marker grouping vertices.
828 53 : if (poFeatureDefn->GetGeomType() != wkbPoint)
829 47 : VSIFPrintfL(m_fp, ">\n");
830 :
831 : /* -------------------------------------------------------------------- */
832 : /* Write feature properties() */
833 : /* -------------------------------------------------------------------- */
834 53 : if (poFeatureDefn->GetFieldCount() > 0)
835 : {
836 106 : CPLString osFieldData;
837 :
838 270 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
839 : {
840 : OGRFieldType eFType =
841 217 : poFeatureDefn->GetFieldDefn(iField)->GetType();
842 217 : const char *pszRawValue = poFeature->GetFieldAsString(iField);
843 :
844 217 : if (iField > 0)
845 164 : osFieldData += "|";
846 :
847 : // We do not want prefix spaces for numeric values.
848 217 : if (eFType == OFTInteger || eFType == OFTReal)
849 104 : while (*pszRawValue == ' ')
850 0 : pszRawValue++;
851 :
852 217 : if (strchr(pszRawValue, ' ') || strchr(pszRawValue, '|') ||
853 186 : strchr(pszRawValue, '\t') || strchr(pszRawValue, '\n'))
854 : {
855 31 : osFieldData += "\"";
856 :
857 : char *pszEscapedVal =
858 31 : CPLEscapeString(pszRawValue, -1, CPLES_BackslashQuotable);
859 31 : osFieldData += pszEscapedVal;
860 31 : CPLFree(pszEscapedVal);
861 :
862 31 : osFieldData += "\"";
863 : }
864 : else
865 186 : osFieldData += pszRawValue;
866 : }
867 :
868 53 : VSIFPrintfL(m_fp, "# @D%s\n", osFieldData.c_str());
869 : }
870 :
871 : /* -------------------------------------------------------------------- */
872 : /* Write Geometry */
873 : /* -------------------------------------------------------------------- */
874 53 : return WriteGeometry(OGRGeometry::ToHandle(poGeom), true);
875 : }
876 :
877 : /************************************************************************/
878 : /* WriteGeometry() */
879 : /* */
880 : /* Write a geometry to the file. If bHaveAngle is true it */
881 : /* means the angle bracket preceding the point stream has */
882 : /* already been written out. */
883 : /* */
884 : /* We use the C API for geometry access because of its */
885 : /* simplified access to vertices and children geometries. */
886 : /************************************************************************/
887 :
888 126 : OGRErr OGRGmtLayer::WriteGeometry(OGRGeometryH hGeom, bool bHaveAngle)
889 :
890 : {
891 : /* -------------------------------------------------------------------- */
892 : /* This is a geometry with sub-geometries. */
893 : /* -------------------------------------------------------------------- */
894 126 : if (OGR_G_GetGeometryCount(hGeom) > 0)
895 : {
896 63 : OGRErr eErr = OGRERR_NONE;
897 :
898 63 : for (int iGeom = 0;
899 136 : iGeom < OGR_G_GetGeometryCount(hGeom) && eErr == OGRERR_NONE;
900 : iGeom++)
901 : {
902 : // We need to emit polygon @P and @H items while we still
903 : // know this is a polygon and which is the outer and inner
904 : // ring.
905 73 : if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon)
906 : {
907 36 : if (!bHaveAngle)
908 : {
909 6 : VSIFPrintfL(m_fp, ">\n");
910 6 : bHaveAngle = true;
911 : }
912 36 : if (iGeom == 0)
913 35 : VSIFPrintfL(m_fp, "# @P\n");
914 : else
915 1 : VSIFPrintfL(m_fp, "# @H\n");
916 : }
917 :
918 : eErr =
919 73 : WriteGeometry(OGR_G_GetGeometryRef(hGeom, iGeom), bHaveAngle);
920 73 : bHaveAngle = false;
921 : }
922 63 : return eErr;
923 : }
924 :
925 : /* -------------------------------------------------------------------- */
926 : /* If this is not a point we need to have an angle bracket to */
927 : /* mark the vertex list. */
928 : /* -------------------------------------------------------------------- */
929 63 : if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) != wkbPoint && !bHaveAngle)
930 4 : VSIFPrintfL(m_fp, ">\n");
931 :
932 : /* -------------------------------------------------------------------- */
933 : /* Dump vertices. */
934 : /* -------------------------------------------------------------------- */
935 63 : const int nPointCount = OGR_G_GetPointCount(hGeom);
936 63 : const int nDim = OGR_G_GetCoordinateDimension(hGeom);
937 : // For testing only. Ticket #6453
938 : const bool bUseTab =
939 63 : CPLTestBool(CPLGetConfigOption("GMT_USE_TAB", "FALSE"));
940 :
941 671 : for (int iPoint = 0; iPoint < nPointCount; iPoint++)
942 : {
943 608 : const double dfX = OGR_G_GetX(hGeom, iPoint);
944 608 : const double dfY = OGR_G_GetY(hGeom, iPoint);
945 608 : const double dfZ = OGR_G_GetZ(hGeom, iPoint);
946 :
947 608 : sRegion.Merge(dfX, dfY);
948 : char szLine[128];
949 608 : OGRMakeWktCoordinate(szLine, dfX, dfY, dfZ, nDim);
950 608 : if (bUseTab)
951 : {
952 60 : for (char *szPtr = szLine; *szPtr != '\0'; ++szPtr)
953 : {
954 47 : if (*szPtr == ' ')
955 13 : *szPtr = '\t';
956 : }
957 : }
958 608 : if (VSIFPrintfL(m_fp, "%s\n", szLine) < 1)
959 : {
960 0 : CPLError(CE_Failure, CPLE_FileIO, "Gmt write failure: %s",
961 0 : VSIStrerror(errno));
962 0 : return OGRERR_FAILURE;
963 : }
964 : }
965 :
966 63 : return OGRERR_NONE;
967 : }
968 :
969 : /************************************************************************/
970 : /* IGetExtent() */
971 : /* */
972 : /* Fetch extent of the data currently stored in the dataset. */
973 : /* The bForce flag has no effect on SHO files since that value */
974 : /* is always in the header. */
975 : /* */
976 : /* Returns OGRERR_NONE/OGRRERR_FAILURE. */
977 : /************************************************************************/
978 :
979 0 : OGRErr OGRGmtLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
980 : bool bForce)
981 :
982 : {
983 0 : if (bRegionComplete && sRegion.IsInit())
984 : {
985 0 : *psExtent = sRegion;
986 0 : return OGRERR_NONE;
987 : }
988 :
989 0 : return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
990 : }
991 :
992 : /************************************************************************/
993 : /* TestCapability() */
994 : /************************************************************************/
995 :
996 74 : bool OGRGmtLayer::TestCapability(const char *pszCap) const
997 :
998 : {
999 74 : if (EQUAL(pszCap, OLCRandomRead))
1000 0 : return FALSE;
1001 :
1002 74 : if (EQUAL(pszCap, OLCSequentialWrite))
1003 16 : return TRUE;
1004 :
1005 58 : if (EQUAL(pszCap, OLCFastSpatialFilter))
1006 0 : return FALSE;
1007 :
1008 58 : if (EQUAL(pszCap, OLCFastGetExtent))
1009 0 : return bRegionComplete;
1010 :
1011 58 : if (EQUAL(pszCap, OLCCreateField))
1012 16 : return TRUE;
1013 :
1014 42 : if (EQUAL(pszCap, OLCZGeometries))
1015 1 : return TRUE;
1016 :
1017 41 : return FALSE;
1018 : }
1019 :
1020 : /************************************************************************/
1021 : /* CreateField() */
1022 : /************************************************************************/
1023 :
1024 87 : OGRErr OGRGmtLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
1025 :
1026 : {
1027 87 : if (!bUpdate)
1028 : {
1029 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
1030 : "Cannot create fields on read-only dataset.");
1031 0 : return OGRERR_FAILURE;
1032 : }
1033 :
1034 87 : if (bHeaderComplete)
1035 : {
1036 0 : CPLError(CE_Failure, CPLE_AppDefined,
1037 : "Unable to create fields after features have been created.");
1038 0 : return OGRERR_FAILURE;
1039 : }
1040 :
1041 87 : switch (poField->GetType())
1042 : {
1043 71 : case OFTInteger:
1044 : case OFTReal:
1045 : case OFTString:
1046 : case OFTDateTime:
1047 71 : poFeatureDefn->AddFieldDefn(poField);
1048 71 : return OGRERR_NONE;
1049 :
1050 16 : default:
1051 16 : if (!bApproxOK)
1052 : {
1053 0 : CPLError(CE_Failure, CPLE_AppDefined,
1054 : "Field %s is of unsupported type %s.",
1055 : poField->GetNameRef(),
1056 : poField->GetFieldTypeName(poField->GetType()));
1057 0 : return OGRERR_FAILURE;
1058 : }
1059 16 : else if (poField->GetType() == OFTDate ||
1060 0 : poField->GetType() == OFTTime)
1061 : {
1062 16 : OGRFieldDefn oModDef(poField);
1063 16 : oModDef.SetType(OFTDateTime);
1064 16 : poFeatureDefn->AddFieldDefn(poField);
1065 16 : return OGRERR_NONE;
1066 : }
1067 : else
1068 : {
1069 0 : OGRFieldDefn oModDef(poField);
1070 0 : oModDef.SetType(OFTString);
1071 0 : poFeatureDefn->AddFieldDefn(poField);
1072 0 : return OGRERR_NONE;
1073 : }
1074 : }
1075 : }
|