1 /******************************************************************************
2  *
3  * Project:  OGDI Bridge
4  * Purpose:  Implements OGROGDIDataSource class.
5  * Author:   Daniel Morissette, danmo@videotron.ca
6  *           (Based on some code contributed by Frank Warmerdam :)
7  *
8  ******************************************************************************
9  * Copyright (c) 2000, Daniel Morissette
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 "ogrogdi.h"
31 
32 #include "cpl_conv.h"
33 #include "cpl_string.h"
34 
35 CPL_CVSID("$Id: ogrogdidatasource.cpp b000bcc06dc07ed1b24f5102eda21dc5bd725620 2019-02-28 18:02:02 +0100 Even Rouault $")
36 
37 /************************************************************************/
38 /*                         OGROGDIDataSource()                          */
39 /************************************************************************/
40 
OGROGDIDataSource()41 OGROGDIDataSource::OGROGDIDataSource() :
42     m_papoLayers(nullptr),
43     m_nLayers(0),
44     m_nClientID(-1),
45     m_poSpatialRef(nullptr),
46     m_poCurrentLayer(nullptr),
47     m_pszFullName(nullptr),
48     m_bLaunderLayerNames(
49         CPLTestBool(CPLGetConfigOption("OGR_OGDI_LAUNDER_LAYER_NAMES", "NO")))
50 {
51     m_sGlobalBounds.north = 0.0;
52     m_sGlobalBounds.south = 0.0;
53     m_sGlobalBounds.east = 0.0;
54     m_sGlobalBounds.west = 0.0;
55     m_sGlobalBounds.ns_res = 0.0;
56     m_sGlobalBounds.ew_res = 0.0;
57 }
58 
59 /************************************************************************/
60 /*                         ~OGROGDIDataSource()                         */
61 /************************************************************************/
62 
~OGROGDIDataSource()63 OGROGDIDataSource::~OGROGDIDataSource()
64 
65 {
66     CPLFree(m_pszFullName );
67 
68     for( int i = 0; i < m_nLayers; i++ )
69         delete m_papoLayers[i];
70     CPLFree( m_papoLayers );
71 
72     if (m_nClientID != -1)
73     {
74         ecs_Result *psResult = cln_DestroyClient( m_nClientID );
75         ecs_CleanUp( psResult );
76     }
77 
78     if (m_poSpatialRef)
79         m_poSpatialRef->Release();
80 }
81 
82 /************************************************************************/
83 /*                                Open()                                */
84 /************************************************************************/
85 
Open(const char * pszNewName)86 int OGROGDIDataSource::Open( const char * pszNewName )
87 
88 {
89     CPLAssert( m_nLayers == 0 );
90 
91 /* -------------------------------------------------------------------- */
92 /*      Parse the dataset name.                                         */
93 /*      i.e.                                                            */
94 /* gltp://<hostname>/<format>/<path_to_dataset>[:<layer_name>:<Family>] */
95 /*                                                                      */
96 /*      Where <Family> is one of: Line, Area, Point, and Text           */
97 /* -------------------------------------------------------------------- */
98     if( !STARTS_WITH_CI(pszNewName, "gltp:") )
99         return FALSE;
100 
101     char *pszWorkingName = CPLStrdup( pszNewName );
102 
103     char *pszFamily = strrchr(pszWorkingName, ':');
104 
105     // Don't treat drive name colon as family separator.  It is assumed
106     // that drive names are on character long, and preceded by a
107     // forward or backward slash.
108     if( pszFamily < pszWorkingName+2
109         || pszFamily[-2] == '/'
110         || pszFamily[-2] == '\\' )
111         pszFamily = nullptr;
112 
113     char *pszLyrName = nullptr;
114     if (pszFamily && pszFamily != pszWorkingName + 4)
115     {
116         *pszFamily = '\0';
117         pszFamily++;
118 
119         pszLyrName = strrchr(pszWorkingName, ':');
120         if (pszLyrName == pszWorkingName + 4)
121             pszLyrName = nullptr;
122 
123         if( pszLyrName != nullptr )
124         {
125             *pszLyrName = '\0';
126             pszLyrName++;
127         }
128     }
129 
130 /* -------------------------------------------------------------------- */
131 /*      Open the client interface.                                      */
132 /* -------------------------------------------------------------------- */
133     ecs_Result *psResult = cln_CreateClient(&m_nClientID, pszWorkingName);
134 
135     if( ECSERROR( psResult ) )
136     {
137         CPLError( CE_Failure, CPLE_AppDefined,
138                   "OGDI DataSource Open Failed: %s\n",
139                   psResult->message ? psResult->message : "(no message string)");
140         CPLFree( pszWorkingName );
141         return FALSE;
142     }
143 
144     m_pszFullName = CPLStrdup(pszNewName);
145 
146 /* -------------------------------------------------------------------- */
147 /*      Capture some information from the file.                         */
148 /* -------------------------------------------------------------------- */
149     psResult = cln_GetGlobalBound( m_nClientID );
150     if( ECSERROR(psResult) )
151     {
152         CPLError( CE_Failure, CPLE_AppDefined,
153                   "GetGlobalBound failed: %s",
154                   psResult->message ? psResult->message : "(no message string)");
155         CPLFree( pszWorkingName );
156         return FALSE;
157     }
158 
159     m_sGlobalBounds = ECSREGION(psResult);
160 
161     psResult = cln_GetServerProjection(m_nClientID);
162     if( ECSERROR(psResult) )
163     {
164         CPLError( CE_Failure, CPLE_AppDefined,
165                   "GetServerProjection failed: %s",
166                   psResult->message ? psResult->message : "(no message string)");
167         CPLFree( pszWorkingName );
168         return FALSE;
169     }
170 
171     m_poSpatialRef = new OGRSpatialReference;
172     m_poSpatialRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
173 
174     CPLString osProjString( ECSTEXT(psResult) );
175     osProjString.replaceAll("datum=wgs84", "datum=WGS84");
176     if( m_poSpatialRef->importFromProj4(osProjString) != OGRERR_NONE )
177     {
178         CPLError( CE_Warning, CPLE_NotSupported,
179                   "untranslatable PROJ.4 projection: %s\n",
180                   ECSTEXT(psResult) ? ECSTEXT(psResult): "(no message string)" );
181         delete m_poSpatialRef;
182         m_poSpatialRef = nullptr;
183     }
184 
185 /* -------------------------------------------------------------------- */
186 /*      Select the global region.                                       */
187 /* -------------------------------------------------------------------- */
188     psResult = cln_SelectRegion( m_nClientID, &m_sGlobalBounds );
189     if( ECSERROR(psResult) )
190     {
191         CPLError( CE_Failure, CPLE_AppDefined,
192                   "SelectRegion failed: %s",
193                   psResult->message ? psResult->message : "(no message string)");
194         CPLFree( pszWorkingName );
195         return FALSE;
196     }
197 
198 /* -------------------------------------------------------------------- */
199 /*      If an explicit layer was selected, just create that layer.      */
200 /* -------------------------------------------------------------------- */
201     m_poCurrentLayer = nullptr;
202 
203     if( pszLyrName != nullptr )
204     {
205         ecs_Family  eFamily;
206 
207         if (EQUAL(pszFamily, "Line"))
208             eFamily = Line;
209         else if (EQUAL(pszFamily, "Area"))
210             eFamily = Area;
211         else if (EQUAL(pszFamily, "Point"))
212             eFamily = Point;
213         else if (EQUAL(pszFamily, "Text"))
214             eFamily = Text;
215         else
216         {
217             CPLError( CE_Failure, CPLE_AppDefined,
218                       "Invalid or unsupported family name (%s) in URL %s\n",
219                       pszFamily, m_pszFullName);
220             CPLFree( pszWorkingName );
221             return FALSE;
222         }
223 
224         IAddLayer( pszLyrName, eFamily );
225     }
226 
227 /* -------------------------------------------------------------------- */
228 /*      Otherwise create a layer for every layer in the capabilities.   */
229 /* -------------------------------------------------------------------- */
230     else
231     {
232         // Call cln_UpdateDictionary so as to be able to report errors
233         // since cln_GetLayerCapabilities() cannot do that
234         // Help in the case of DNC17/COA17A that has a missing env/fcs file
235         char* szEmpty = CPLStrdup("");
236         psResult = cln_UpdateDictionary( m_nClientID, szEmpty );
237         CPLFree(szEmpty);
238         if( ECSERROR(psResult) )
239         {
240             CPLError( CE_Failure, CPLE_AppDefined,
241                       "UpdateDictionary failed: %s",
242                       psResult->message ? psResult->message : "(no message string)");
243             CPLFree( pszWorkingName );
244             return FALSE;
245         }
246 
247         const ecs_LayerCapabilities *psLayerCap = nullptr;
248         for( int i = 0;
249              (psLayerCap = cln_GetLayerCapabilities(m_nClientID,i)) != nullptr;
250              i++ )
251         {
252             if( psLayerCap->families[Point] )
253                 IAddLayer( psLayerCap->name, Point );
254             if( psLayerCap->families[Line] )
255                 IAddLayer( psLayerCap->name, Line );
256             if( psLayerCap->families[Area] )
257                 IAddLayer( psLayerCap->name, Area );
258             if( psLayerCap->families[Text] )
259                 IAddLayer( psLayerCap->name, Text );
260         }
261     }
262 
263     CPLFree( pszWorkingName );
264 
265     return TRUE;
266 }
267 
268 /************************************************************************/
269 /*                             IAddLayer()                              */
270 /*                                                                      */
271 /*      Internal helper function for adding one existing layer to       */
272 /*      the datasource.                                                 */
273 /************************************************************************/
274 
IAddLayer(const char * pszLayerName,ecs_Family eFamily)275 void OGROGDIDataSource::IAddLayer( const char *pszLayerName,
276                                    ecs_Family eFamily )
277 
278 {
279     m_papoLayers = (OGROGDILayer**)
280         CPLRealloc( m_papoLayers, (m_nLayers+1) * sizeof(OGROGDILayer*));
281 
282     m_papoLayers[m_nLayers++] = new OGROGDILayer(this, pszLayerName, eFamily);
283 }
284 
285 /************************************************************************/
286 /*                           TestCapability()                           */
287 /************************************************************************/
288 
TestCapability(CPL_UNUSED const char * pszCap)289 int OGROGDIDataSource::TestCapability( CPL_UNUSED const char * pszCap )
290 
291 {
292     return FALSE;
293 }
294 
295 /************************************************************************/
296 /*                              GetLayer()                              */
297 /************************************************************************/
298 
GetLayer(int iLayer)299 OGRLayer *OGROGDIDataSource::GetLayer( int iLayer )
300 
301 {
302     if( iLayer < 0 || iLayer >= m_nLayers )
303         return nullptr;
304     else
305         return m_papoLayers[iLayer];
306 }
307