1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Implements OGRWalkDatasource class.
5  * Author:   Xian Chen, chenxian at walkinfo.com.cn
6  *
7  ******************************************************************************
8  * Copyright (c) 2013,  ZJU Walkinfo Technology Corp., Ltd.
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 "ogrwalk.h"
30 #include <vector>
31 
32 CPL_CVSID("$Id: ogrwalkdatasource.cpp 316de08882c4b40ed999aa6e37198a5168b8e93c 2021-08-23 11:12:04 +0200 Even Rouault $")
33 
34 /************************************************************************/
35 /*                         OGRWalkDataSource()                          */
36 /************************************************************************/
37 
OGRWalkDataSource()38 OGRWalkDataSource::OGRWalkDataSource() :
39     pszName(nullptr),
40     papoLayers(nullptr),
41     nLayers(0)
42 {}
43 
44 /************************************************************************/
45 /*                        ~OGRWalkDataSource()                          */
46 /************************************************************************/
47 
~OGRWalkDataSource()48 OGRWalkDataSource::~OGRWalkDataSource()
49 
50 {
51     CPLFree( pszName );
52 
53     for( int i = 0; i < nLayers; i++ )
54     {
55         CPLAssert( nullptr != papoLayers[i] );
56         delete papoLayers[i];
57     }
58 
59     CPLFree( papoLayers );
60 }
61 
62 /************************************************************************/
63 /*                              Open()                                  */
64 /************************************************************************/
65 
Open(const char * pszNewName,int)66 int OGRWalkDataSource::Open( const char * pszNewName, int /* bUpdate */ )
67 {
68 /* -------------------------------------------------------------------- */
69 /*      If this is the name of an MDB file, then construct the          */
70 /*      appropriate connection string.  Otherwise clip of WALK: to      */
71 /*      get the DSN.                                                    */
72 /* -------------------------------------------------------------------- */
73     if( STARTS_WITH_CI(pszNewName, "WALK:") )
74     {
75         char *pszDSN = CPLStrdup( pszNewName + 5 );
76         CPLDebug( "Walk", "EstablishSession(%s)", pszDSN );
77         if( !oSession.EstablishSession( pszDSN, nullptr, nullptr ) )
78         {
79             CPLError( CE_Failure, CPLE_AppDefined,
80                       "Unable to initialize ODBC connection to DSN for %s,\n"
81                       "%s", pszDSN, oSession.GetLastError() );
82             CPLFree( pszDSN );
83             return FALSE;
84         }
85     }
86     else
87     {
88         if ( !oSession.ConnectToMsAccess( pszNewName, nullptr ) )
89         {
90            return FALSE;
91         }
92     }
93 
94     // check for WalkLayers table
95     {
96         bool bFoundWalkLayersTable = false;
97         CPLODBCStatement oTableList( &oSession );
98         if( oTableList.GetTables() )
99         {
100             while( oTableList.Fetch() )
101             {
102                 const CPLString osTableName = CPLString( oTableList.GetColData(2, "") );
103                 const CPLString osLCTableName(CPLString(osTableName).tolower());
104                 if( osLCTableName == "walklayers" )
105                 {
106                     bFoundWalkLayersTable = true;
107                     break;
108                 }
109             }
110         }
111         if (!bFoundWalkLayersTable )
112             return FALSE;
113     }
114 
115     pszName = CPLStrdup( pszNewName );
116 
117 /* -------------------------------------------------------------------- */
118 /*      Collect list of layers and their attributes.                    */
119 /* -------------------------------------------------------------------- */
120     std::vector<char **> apapszGeomColumns;
121     CPLODBCStatement oStmt( &oSession );
122 
123     oStmt.Append( "SELECT LayerID, LayerName, minE, maxE, minN, maxN, Memo  FROM WalkLayers" );
124 
125     if( !oStmt.ExecuteSQL() )
126     {
127         CPLDebug( "Walk",
128                   "SELECT on WalkLayers fails, perhaps not a walk database?\n%s",
129                   oSession.GetLastError() );
130         return FALSE;
131     }
132 
133     while( oStmt.Fetch() )
134     {
135         int i, iNew = static_cast<int>(apapszGeomColumns.size());
136         char **papszRecord = nullptr;
137 
138         for( i = 1; i < 7; i++ )
139             papszRecord = CSLAddString( papszRecord, oStmt.GetColData(i) ); //Add LayerName, Extent and Memo
140 
141         apapszGeomColumns.resize(iNew+1);
142         apapszGeomColumns[iNew] = papszRecord;
143     }
144 
145 /* -------------------------------------------------------------------- */
146 /*      Create a layer for each spatial table.                          */
147 /* -------------------------------------------------------------------- */
148     papoLayers = (OGRWalkLayer **) CPLCalloc(apapszGeomColumns.size(),
149                                              sizeof( void * ));
150 
151     for( unsigned int iTable = 0; iTable < apapszGeomColumns.size(); iTable++ )
152     {
153         char **papszRecord = apapszGeomColumns[iTable];
154 
155         OGRWalkTableLayer  *poLayer = new OGRWalkTableLayer( this );
156 
157         if( poLayer->Initialize( papszRecord[0],        // LayerName
158                                  "Geometry",            // Geometry Column Name
159                                  CPLAtof(papszRecord[1]),  // Extent MinE
160                                  CPLAtof(papszRecord[2]),  // Extent MaxE
161                                  CPLAtof(papszRecord[3]),  // Extent MinN
162                                  CPLAtof(papszRecord[4]),  // Extent MaxN
163                                  papszRecord[5])        // Memo for SpatialRef
164             != CE_None )
165         {
166             delete poLayer;
167         }
168         else
169             papoLayers[nLayers++] = poLayer;
170 
171         CSLDestroy( papszRecord );
172     }
173 
174     return TRUE;
175 }
176 
177 /************************************************************************/
178 /*                              GetLayer()                              */
179 /************************************************************************/
180 
GetLayer(int iLayer)181 OGRLayer *OGRWalkDataSource::GetLayer( int iLayer )
182 
183 {
184     if( iLayer < 0 || iLayer >= nLayers )
185         return nullptr;
186     else
187         return papoLayers[iLayer];
188 }
189 
190 /************************************************************************/
191 /*                             ExecuteSQL()                             */
192 /************************************************************************/
193 
ExecuteSQL(const char * pszSQLCommand,OGRGeometry * poSpatialFilter,const char * pszDialect)194 OGRLayer * OGRWalkDataSource::ExecuteSQL( const char *pszSQLCommand,
195                                           OGRGeometry *poSpatialFilter,
196                                           const char *pszDialect )
197 
198 {
199 /* -------------------------------------------------------------------- */
200 /*      Use generic implementation for recognized dialects              */
201 /* -------------------------------------------------------------------- */
202     if( IsGenericSQLDialect(pszDialect) )
203         return OGRDataSource::ExecuteSQL( pszSQLCommand,
204                                           poSpatialFilter,
205                                           pszDialect );
206 
207 /* -------------------------------------------------------------------- */
208 /*      Execute normal SQL statement in Walk.                           */
209 /*      Table_name = Layer_name + Postfix                               */
210 /*      Postfix: "Features", "Annotations" or "Styles"                  */
211 /* -------------------------------------------------------------------- */
212     CPLODBCStatement *poStmt = new CPLODBCStatement( &oSession );
213 
214     CPLDebug( "Walk", "ExecuteSQL(%s) called.", pszSQLCommand );
215     poStmt->Append( pszSQLCommand );
216     if( !poStmt->ExecuteSQL() )
217     {
218         CPLError( CE_Failure, CPLE_AppDefined,
219                   "%s", oSession.GetLastError() );
220         delete poStmt;
221         return nullptr;
222     }
223 
224 /* -------------------------------------------------------------------- */
225 /*      Are there result columns for this statement?                    */
226 /* -------------------------------------------------------------------- */
227     if( poStmt->GetColCount() == 0 )
228     {
229         delete poStmt;
230         CPLErrorReset();
231         return nullptr;
232     }
233 
234 /* -------------------------------------------------------------------- */
235 /*      Create a results layer.  It will take ownership of the          */
236 /*      statement.                                                      */
237 /* -------------------------------------------------------------------- */
238     OGRWalkSelectLayer *poLayer = new OGRWalkSelectLayer( this, poStmt );
239 
240     if( poSpatialFilter != nullptr )
241         poLayer->SetSpatialFilter( poSpatialFilter );
242 
243     return poLayer;
244 }
245 
246 /************************************************************************/
247 /*                          ReleaseResultSet()                          */
248 /************************************************************************/
249 
ReleaseResultSet(OGRLayer * poLayer)250 void OGRWalkDataSource::ReleaseResultSet( OGRLayer * poLayer )
251 
252 {
253     delete poLayer;
254 }
255