1 /******************************************************************************
2  *
3  * Project:  WAsP Translator
4  * Purpose:  Implements OGRWAsPDataSource class
5  * Author:   Vincent Mora, vincent dot mora at oslandia dot com
6  *
7  ******************************************************************************
8  * Copyright (c) 2014, Oslandia <info at oslandia dot 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 "ogrwasp.h"
30 #include "cpl_conv.h"
31 #include "cpl_string.h"
32 
33 #include <cassert>
34 #include <sstream>
35 
36 CPL_CVSID("$Id: ogrwaspdatasource.cpp 8e5eeb35bf76390e3134a4ea7076dab7d478ea0e 2018-11-14 22:55:13 +0100 Even Rouault $")
37 
38 /************************************************************************/
39 /*                          OGRWAsPDataSource()                          */
40 /************************************************************************/
41 
OGRWAsPDataSource(const char * pszName,VSILFILE * hFileHandle)42 OGRWAsPDataSource::OGRWAsPDataSource( const char * pszName,
43                                       VSILFILE * hFileHandle ) :
44     sFilename(pszName),
45     hFile(hFileHandle)
46 {}
47 
48 /************************************************************************/
49 /*                         ~OGRWAsPDataSource()                          */
50 /************************************************************************/
51 
~OGRWAsPDataSource()52 OGRWAsPDataSource::~OGRWAsPDataSource()
53 
54 {
55     oLayer.reset(); /* we write to file int layer dtor */
56     VSIFCloseL( hFile ); /* nothing smart can be done here in case of error */
57 }
58 
59 /************************************************************************/
60 /*                           TestCapability()                           */
61 /************************************************************************/
62 
TestCapability(const char * pszCap)63 int OGRWAsPDataSource::TestCapability( const char * pszCap )
64 
65 {
66     return EQUAL(pszCap,ODsCCreateLayer) && oLayer.get() == nullptr;
67 }
68 
69 /************************************************************************/
70 /*                              GetLayerByName()                        */
71 /************************************************************************/
72 
GetLayerByName(const char * pszName)73 OGRLayer *OGRWAsPDataSource::GetLayerByName( const char * pszName )
74 
75 {
76     return ( oLayer.get() && EQUAL( pszName, oLayer->GetName() ) )
77         ? oLayer.get()
78         : nullptr;
79 }
80 
81 /************************************************************************/
82 /*                              Load()                                  */
83 /************************************************************************/
84 
Load(bool bSilent)85 OGRErr OGRWAsPDataSource::Load(bool bSilent)
86 
87 {
88     /* if we don't have a layer, we read from file */
89     if ( oLayer.get() )
90     {
91         if (!bSilent) CPLError( CE_Failure, CPLE_NotSupported, "layer already loaded");
92         return OGRERR_FAILURE;
93     }
94     /* Parse the first line of the file in case it is a spatial ref*/
95     const char * pszLine = CPLReadLine2L( hFile, 1024, nullptr );
96     if ( !pszLine )
97     {
98         if (!bSilent) CPLError( CE_Failure, CPLE_FileIO, "empty file");
99         return OGRERR_FAILURE;
100     }
101     CPLString sLine( pszLine );
102     sLine = sLine.substr(0, sLine.find("|"));
103     OGRSpatialReference * poSpatialRef = new OGRSpatialReference;
104     poSpatialRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
105     if ( poSpatialRef->importFromProj4( sLine.c_str() ) != OGRERR_NONE )
106     {
107         if (!bSilent) CPLError( CE_Warning, CPLE_FileIO, "cannot find spatial reference");
108         delete poSpatialRef;
109         poSpatialRef = nullptr;
110     }
111 
112     /* TODO Parse those line since they define a coordinate transformation */
113     CPLReadLineL( hFile );
114     CPLReadLineL( hFile );
115     CPLReadLineL( hFile );
116 
117     oLayer.reset( new OGRWAsPLayer( CPLGetBasename(sFilename.c_str()),
118                                     hFile,
119                                     poSpatialRef ) );
120     if (poSpatialRef) poSpatialRef->Release();
121 
122     const vsi_l_offset iOffset = VSIFTellL( hFile );
123     pszLine = CPLReadLineL( hFile );
124     if ( !pszLine )
125     {
126         if (!bSilent) CPLError( CE_Failure, CPLE_FileIO, "no feature in file");
127         oLayer.reset();
128         return OGRERR_FAILURE;
129     }
130 
131     double dfValues[4];
132     int iNumValues = 0;
133     {
134         std::istringstream iss(pszLine);
135         while ( iNumValues < 4 && (iss >> dfValues[iNumValues] ) ){ ++iNumValues; }
136 
137         if ( iNumValues < 2 )
138         {
139             if (!bSilent && iNumValues)
140                 CPLError(CE_Failure, CPLE_FileIO, "no enough values" );
141             else if (!bSilent)
142                 CPLError(CE_Failure, CPLE_FileIO, "no feature in file" );
143 
144             oLayer.reset();
145             return OGRERR_FAILURE;
146         }
147     }
148 
149     if ( iNumValues == 3 || iNumValues == 4 )
150     {
151         OGRFieldDefn left("z_left", OFTReal);
152         OGRFieldDefn right("z_right", OFTReal);
153         oLayer->CreateField( &left );
154         oLayer->CreateField( &right );
155     }
156     if ( iNumValues == 2 || iNumValues == 4 )
157     {
158         OGRFieldDefn height("elevation", OFTReal);
159         oLayer->CreateField( &height );
160     }
161 
162     VSIFSeekL( hFile, iOffset, SEEK_SET );
163     return OGRERR_NONE;
164 }
165 
166 /************************************************************************/
167 /*                              GetLayer()                              */
168 /************************************************************************/
169 
GetLayer(int iLayer)170 OGRLayer *OGRWAsPDataSource::GetLayer( int iLayer )
171 
172 {
173     return ( iLayer == 0 ) ? oLayer.get() : nullptr;
174 }
175 
176 /************************************************************************/
177 /*                             ICreateLayer()                           */
178 /************************************************************************/
179 
ICreateLayer(const char * pszName,OGRSpatialReference * poSpatialRef,OGRwkbGeometryType eGType,char ** papszOptions)180 OGRLayer *OGRWAsPDataSource::ICreateLayer(const char *pszName,
181                                      OGRSpatialReference *poSpatialRef,
182                                      OGRwkbGeometryType eGType,
183                                      char ** papszOptions)
184 
185 {
186 
187     if ( eGType != wkbLineString
188       && eGType != wkbLineString25D
189       && eGType != wkbMultiLineString
190       && eGType != wkbMultiLineString25D
191       && eGType != wkbPolygon
192       && eGType != wkbPolygon25D
193       && eGType != wkbMultiPolygon
194       && eGType != wkbMultiPolygon25D )
195     {
196         CPLError( CE_Failure,
197                 CPLE_NotSupported,
198                 "unsupported geometry type %s", OGRGeometryTypeToName( eGType ) );
199         return nullptr;
200     }
201 
202     if ( !OGRGeometryFactory::haveGEOS()
203             && ( eGType == wkbPolygon
204               || eGType == wkbPolygon25D
205               || eGType == wkbMultiPolygon
206               || eGType == wkbMultiPolygon25D ))
207     {
208         CPLError( CE_Failure,
209                 CPLE_NotSupported,
210                 "unsupported geometry type %s without GEOS support", OGRGeometryTypeToName( eGType ) );
211         return nullptr;
212     }
213 
214     if ( oLayer.get() )
215     {
216         CPLError( CE_Failure,
217                 CPLE_NotSupported,
218                 "this data source does not support more than one layer" );
219         return nullptr;
220     }
221 
222     CPLString sFirstField, sSecondField, sGeomField;
223 
224     const char *pszFields = CSLFetchNameValue( papszOptions, "WASP_FIELDS" );
225     const CPLString sFields( pszFields ? pszFields : "" );
226     if ( ! sFields.empty() )
227     {
228         /* parse the comma separated list of fields */
229         const size_t iComma = sFields.find(',');
230         if ( std::string::npos != iComma )
231         {
232             sFirstField = sFields.substr(0, iComma);
233             sSecondField = sFields.substr( iComma + 1 );
234         }
235         else
236         {
237             sFirstField = sFields;
238         }
239     }
240 
241     const char *pszGeomField = CSLFetchNameValue( papszOptions, "WASP_GEOM_FIELD" );
242     sGeomField = CPLString( pszGeomField ? pszGeomField : "" );
243 
244     const bool bMerge = CPLTestBool(CSLFetchNameValueDef( papszOptions, "WASP_MERGE", "YES" ));
245 
246     std::unique_ptr<double> pdfTolerance;
247     {
248         const char *pszToler = CSLFetchNameValue( papszOptions, "WASP_TOLERANCE" );
249 
250         if (pszToler)
251         {
252             if ( !OGRGeometryFactory::haveGEOS() )
253             {
254                 CPLError( CE_Warning,
255                         CPLE_IllegalArg,
256                         "GEOS support not enabled, ignoring option WASP_TOLERANCE" );
257             }
258             else
259             {
260                 pdfTolerance.reset( new double );
261                 if (!(std::istringstream( pszToler ) >> *pdfTolerance ))
262                 {
263                     CPLError( CE_Failure,
264                             CPLE_IllegalArg,
265                             "cannot set tolerance from %s", pszToler );
266                     return nullptr;
267                 }
268             }
269         }
270     }
271 
272     std::unique_ptr<double> pdfAdjacentPointTolerance;
273     {
274         const char *pszAdjToler = CSLFetchNameValue( papszOptions, "WASP_ADJ_TOLER" );
275         if ( pszAdjToler )
276         {
277             pdfAdjacentPointTolerance.reset( new double );
278             if (!(std::istringstream( pszAdjToler ) >> *pdfAdjacentPointTolerance ))
279             {
280                 CPLError( CE_Failure,
281                         CPLE_IllegalArg,
282                         "cannot set tolerance from %s", pszAdjToler );
283                 return nullptr;
284             }
285         }
286     }
287 
288     std::unique_ptr<double> pdfPointToCircleRadius;
289     {
290         const char *pszPtToCircRad = CSLFetchNameValue( papszOptions, "WASP_POINT_TO_CIRCLE_RADIUS" );
291         if ( pszPtToCircRad )
292         {
293             pdfPointToCircleRadius.reset( new double );
294             if (!(std::istringstream( pszPtToCircRad ) >> *pdfPointToCircleRadius ))
295             {
296                 CPLError( CE_Failure,
297                         CPLE_IllegalArg,
298                         "cannot set tolerance from %s", pszPtToCircRad );
299                 return nullptr;
300             }
301         }
302     }
303 
304     auto poSRSClone = poSpatialRef;
305     if( poSRSClone )
306     {
307         poSRSClone = poSRSClone->Clone();
308         poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
309     }
310     oLayer.reset( new OGRWAsPLayer( CPLGetBasename(pszName),
311                                     hFile,
312                                     poSRSClone,
313                                     sFirstField,
314                                     sSecondField,
315                                     sGeomField,
316                                     bMerge,
317                                     pdfTolerance.release(),
318                                     pdfAdjacentPointTolerance.release(),
319                                     pdfPointToCircleRadius.release() ) );
320     if( poSRSClone )
321         poSRSClone->Release();
322 
323     char * ppszWktSpatialRef = nullptr ;
324     if ( poSpatialRef
325          && poSpatialRef->exportToProj4( &ppszWktSpatialRef ) == OGRERR_NONE )
326     {
327         VSIFPrintfL( hFile, "%s\n", ppszWktSpatialRef );
328     }
329     else
330     {
331         VSIFPrintfL( hFile, "no spatial ref sys\n" );
332     }
333     CPLFree( ppszWktSpatialRef );
334 
335     VSIFPrintfL( hFile, "  0.0 0.0 0.0 0.0\n" );
336     VSIFPrintfL( hFile, "  1.0 0.0 1.0 0.0\n" );
337     VSIFPrintfL( hFile, "  1.0 0.0\n" );
338     return oLayer.get();
339 }
340