1 /******************************************************************************
2  * $Id: ogrbnadatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $
3  *
4  * Project:  BNA Translator
5  * Purpose:  Implements OGRBNADataSource class
6  * Author:   Even Rouault, even dot rouault at mines dash paris dot org
7  *
8  ******************************************************************************
9  * Copyright (c) 2007-2011, Even Rouault <even dot rouault at mines-paris dot org>
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_bna.h"
31 #include "cpl_conv.h"
32 #include "cpl_string.h"
33 #include "cpl_csv.h"
34 #include "ogrbnaparser.h"
35 
36 /************************************************************************/
37 /*                          OGRBNADataSource()                          */
38 /************************************************************************/
39 
OGRBNADataSource()40 OGRBNADataSource::OGRBNADataSource()
41 
42 {
43     papoLayers = NULL;
44     nLayers = 0;
45 
46     fpOutput = NULL;
47 
48     pszName = NULL;
49 
50     pszCoordinateSeparator = NULL;
51 
52     bUpdate = FALSE;
53 }
54 
55 /************************************************************************/
56 /*                         ~OGRBNADataSource()                          */
57 /************************************************************************/
58 
~OGRBNADataSource()59 OGRBNADataSource::~OGRBNADataSource()
60 
61 {
62     if ( fpOutput != NULL )
63     {
64         VSIFCloseL( fpOutput);
65     }
66 
67     for( int i = 0; i < nLayers; i++ )
68         delete papoLayers[i];
69     CPLFree( papoLayers );
70 
71     CPLFree( pszCoordinateSeparator );
72 
73     CPLFree( pszName );
74 }
75 
76 /************************************************************************/
77 /*                           TestCapability()                           */
78 /************************************************************************/
79 
TestCapability(const char * pszCap)80 int OGRBNADataSource::TestCapability( const char * pszCap )
81 
82 {
83     if( EQUAL(pszCap,ODsCCreateLayer) )
84         return TRUE;
85     else if( EQUAL(pszCap,ODsCDeleteLayer) )
86         return FALSE;
87     else
88         return FALSE;
89 }
90 
91 /************************************************************************/
92 /*                              GetLayer()                              */
93 /************************************************************************/
94 
GetLayer(int iLayer)95 OGRLayer *OGRBNADataSource::GetLayer( int iLayer )
96 
97 {
98     if( iLayer < 0 || iLayer >= nLayers )
99         return NULL;
100     else
101         return papoLayers[iLayer];
102 }
103 
104 /************************************************************************/
105 /*                           ICreateLayer()                             */
106 /************************************************************************/
107 
ICreateLayer(const char * pszLayerName,CPL_UNUSED OGRSpatialReference * poSRS,OGRwkbGeometryType eType,CPL_UNUSED char ** papszOptions)108 OGRLayer * OGRBNADataSource::ICreateLayer( const char * pszLayerName,
109                                            CPL_UNUSED OGRSpatialReference *poSRS,
110                                            OGRwkbGeometryType eType,
111                                            CPL_UNUSED char ** papszOptions )
112 {
113     BNAFeatureType bnaFeatureType;
114 
115     switch(eType)
116     {
117         case wkbPolygon:
118         case wkbPolygon25D:
119         case wkbMultiPolygon:
120         case wkbMultiPolygon25D:
121             bnaFeatureType = BNA_POLYGON;
122             break;
123 
124         case wkbPoint:
125         case wkbPoint25D:
126             bnaFeatureType = BNA_POINT;
127             break;
128 
129         case wkbLineString:
130         case wkbLineString25D:
131             bnaFeatureType = BNA_POLYLINE;
132             break;
133 
134         default:
135             CPLError( CE_Failure, CPLE_NotSupported,
136                     "Geometry type of `%s' not supported in BNAs.\n",
137                     OGRGeometryTypeToName(eType) );
138             return NULL;
139     }
140 
141     nLayers++;
142     papoLayers = (OGRBNALayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRBNALayer*));
143     papoLayers[nLayers-1] = new OGRBNALayer( pszName, pszLayerName, bnaFeatureType, eType, TRUE, this );
144 
145     return papoLayers[nLayers-1];
146 }
147 
148 /************************************************************************/
149 /*                                Open()                                */
150 /************************************************************************/
151 
Open(const char * pszFilename,int bUpdateIn)152 int OGRBNADataSource::Open( const char * pszFilename, int bUpdateIn)
153 
154 {
155     int ok = FALSE;
156 
157     pszName = CPLStrdup( pszFilename );
158     bUpdate = bUpdateIn;
159 
160     VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
161     if (fp)
162     {
163         BNARecord* record;
164         int curLine = 0;
165         const char* layerRadixName[] = { "points", "polygons", "lines", "ellipses"};
166         OGRwkbGeometryType wkbGeomTypes[] = { wkbPoint, wkbMultiPolygon, wkbLineString, wkbPolygon };
167         int i;
168 #if defined(BNA_FAST_DS_OPEN)
169         record = BNA_GetNextRecord(fp, &ok, &curLine, FALSE, BNA_READ_NONE);
170         BNA_FreeRecord(record);
171 
172         if (ok)
173         {
174             nLayers = 4;
175 
176             papoLayers = (OGRBNALayer **) CPLMalloc(nLayers * sizeof(OGRBNALayer*));
177             for(i=0;i<4;i++)
178                 papoLayers[i] = new OGRBNALayer( pszFilename,
179                                                  layerRadixName[i],
180                                                  (BNAFeatureType)i, wkbGeomTypes[i], FALSE, this );
181         }
182 #else
183         int nFeatures[4] = { 0, 0, 0, 0 };
184         OffsetAndLine* offsetAndLineFeaturesTable[4] = { NULL, NULL, NULL, NULL };
185         int nIDs[4] = {0, 0, 0, 0};
186         int partialIndexTable = TRUE;
187 
188         while(1)
189         {
190             int offset = (int) VSIFTellL(fp);
191             int line = curLine;
192             record =  BNA_GetNextRecord(fp, &ok, &curLine, FALSE, BNA_READ_NONE);
193             if (ok == FALSE)
194             {
195                 BNA_FreeRecord(record);
196                 if (line != 0)
197                     ok = TRUE;
198                 break;
199             }
200             if (record == NULL)
201             {
202                 /* end of file */
203                 ok = TRUE;
204 
205                 /* and we have finally build the whole index table */
206                 partialIndexTable = FALSE;
207                 break;
208             }
209 
210             if (record->nIDs > nIDs[record->featureType])
211                 nIDs[record->featureType] = record->nIDs;
212 
213             nFeatures[record->featureType]++;
214             offsetAndLineFeaturesTable[record->featureType] =
215                 (OffsetAndLine*)CPLRealloc(offsetAndLineFeaturesTable[record->featureType],
216                                            nFeatures[record->featureType] * sizeof(OffsetAndLine));
217             offsetAndLineFeaturesTable[record->featureType][nFeatures[record->featureType]-1].offset = offset;
218             offsetAndLineFeaturesTable[record->featureType][nFeatures[record->featureType]-1].line = line;
219 
220             BNA_FreeRecord(record);
221         }
222 
223         nLayers = (nFeatures[0] != 0) + (nFeatures[1] != 0) + (nFeatures[2] != 0) + (nFeatures[3] != 0);
224         papoLayers = (OGRBNALayer **) CPLMalloc(nLayers * sizeof(OGRBNALayer*));
225         int iLayer = 0;
226         for(i=0;i<4;i++)
227         {
228             if (nFeatures[i])
229             {
230                 papoLayers[iLayer] = new OGRBNALayer( pszFilename,
231                                                       layerRadixName[i],
232                                                       (BNAFeatureType)i,
233                                                       wkbGeomTypes[i],
234                                                       FALSE,
235                                                       this,
236                                                       nIDs[i]);
237                 papoLayers[iLayer]->SetFeatureIndexTable(nFeatures[i],
238                                                         offsetAndLineFeaturesTable[i],
239                                                         partialIndexTable);
240                 iLayer++;
241             }
242         }
243 #endif
244         VSIFCloseL(fp);
245     }
246 
247     return ok;
248 }
249 
250 
251 /************************************************************************/
252 /*                               Create()                               */
253 /************************************************************************/
254 
Create(const char * pszFilename,char ** papszOptions)255 int OGRBNADataSource::Create( const char *pszFilename,
256                               char **papszOptions )
257 {
258     if( fpOutput != NULL)
259     {
260         CPLAssert( FALSE );
261         return FALSE;
262     }
263 
264     if( strcmp(pszFilename,"/dev/stdout") == 0 )
265         pszFilename = "/vsistdout/";
266 
267 /* -------------------------------------------------------------------- */
268 /*     Do not override exiting file.                                    */
269 /* -------------------------------------------------------------------- */
270     VSIStatBufL sStatBuf;
271 
272     if( VSIStatL( pszFilename, &sStatBuf ) == 0 )
273         return FALSE;
274 
275 /* -------------------------------------------------------------------- */
276 /*      Create the output file.                                         */
277 /* -------------------------------------------------------------------- */
278     pszName = CPLStrdup( pszFilename );
279 
280     fpOutput = VSIFOpenL( pszFilename, "wb" );
281     if( fpOutput == NULL )
282     {
283         CPLError( CE_Failure, CPLE_OpenFailed,
284                   "Failed to create BNA file %s.",
285                   pszFilename );
286         return FALSE;
287     }
288 
289     /* EOL token */
290     const char *pszCRLFFormat = CSLFetchNameValue( papszOptions, "LINEFORMAT");
291 
292     if( pszCRLFFormat == NULL )
293     {
294 #ifdef WIN32
295         bUseCRLF = TRUE;
296 #else
297         bUseCRLF = FALSE;
298 #endif
299     }
300     else if( EQUAL(pszCRLFFormat,"CRLF") )
301         bUseCRLF = TRUE;
302     else if( EQUAL(pszCRLFFormat,"LF") )
303         bUseCRLF = FALSE;
304     else
305     {
306         CPLError( CE_Warning, CPLE_AppDefined,
307                   "LINEFORMAT=%s not understood, use one of CRLF or LF.",
308                   pszCRLFFormat );
309 #ifdef WIN32
310         bUseCRLF = TRUE;
311 #else
312         bUseCRLF = FALSE;
313 #endif
314     }
315 
316     /* Multi line or single line format ? */
317     bMultiLine = CSLFetchBoolean( papszOptions, "MULTILINE", TRUE);
318 
319     /* Number of identifiers per record */
320     const char* pszNbOutID = CSLFetchNameValue ( papszOptions, "NB_IDS");
321     if (pszNbOutID == NULL)
322     {
323         nbOutID = NB_MIN_BNA_IDS;
324     }
325     else if (EQUAL(pszNbOutID, "NB_SOURCE_FIELDS"))
326     {
327         nbOutID = -1;
328     }
329     else
330     {
331         nbOutID = atoi(pszNbOutID);
332         if (nbOutID <= 0)
333         {
334             CPLError( CE_Warning, CPLE_AppDefined,
335                   "NB_ID=%s not understood. Must be >=%d and <=%d or equal to NB_SOURCE_FIELDS",
336                   pszNbOutID, NB_MIN_BNA_IDS, NB_MAX_BNA_IDS );
337             nbOutID = NB_MIN_BNA_IDS;
338         }
339         if (nbOutID > NB_MAX_BNA_IDS)
340         {
341             CPLError( CE_Warning, CPLE_AppDefined,
342                   "NB_ID=%s not understood. Must be >=%d and <=%d or equal to NB_SOURCE_FIELDS",
343                   pszNbOutID, NB_MIN_BNA_IDS, NB_MAX_BNA_IDS );
344             nbOutID = NB_MAX_BNA_IDS;
345         }
346     }
347 
348     /* Ellipses export as ellipses or polygons ? */
349     bEllipsesAsEllipses = CSLFetchBoolean( papszOptions, "ELLIPSES_AS_ELLIPSES", TRUE);
350 
351     /* Number of coordinate pairs per line */
352     const char* pszNbPairPerLine = CSLFetchNameValue( papszOptions, "NB_PAIRS_PER_LINE");
353     if (pszNbPairPerLine)
354     {
355         nbPairPerLine = atoi(pszNbPairPerLine);
356         if (nbPairPerLine <= 0)
357             nbPairPerLine = (bMultiLine == FALSE) ? 1000000000 : 1;
358         if (bMultiLine == FALSE)
359         {
360             CPLError( CE_Warning, CPLE_AppDefined, "NB_PAIR_PER_LINE option is ignored when MULTILINE=NO");
361         }
362     }
363     else
364     {
365         nbPairPerLine = (bMultiLine == FALSE) ? 1000000000 : 1;
366     }
367 
368     /* Coordinate precision */
369     const char* pszCoordinatePrecision = CSLFetchNameValue( papszOptions, "COORDINATE_PRECISION");
370     if (pszCoordinatePrecision)
371     {
372         coordinatePrecision = atoi(pszCoordinatePrecision);
373         if (coordinatePrecision <= 0)
374             coordinatePrecision = 0;
375         else if (coordinatePrecision >= 20)
376             coordinatePrecision = 20;
377     }
378     else
379     {
380         coordinatePrecision = 10;
381     }
382 
383     pszCoordinateSeparator = (char*)CSLFetchNameValue( papszOptions, "COORDINATE_SEPARATOR");
384     if (pszCoordinateSeparator == NULL)
385         pszCoordinateSeparator = CPLStrdup(",");
386     else
387         pszCoordinateSeparator = CPLStrdup(pszCoordinateSeparator);
388 
389     return TRUE;
390 }
391