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