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