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