Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRPGeoTableLayer class, access to an existing table.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2005, Frank Warmerdam
9 : * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.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 "cpl_conv.h"
31 : #include "ogr_pgeo.h"
32 : #include "ogrpgeogeometry.h"
33 :
34 : /************************************************************************/
35 : /* OGRPGeoTableLayer() */
36 : /************************************************************************/
37 :
38 0 : OGRPGeoTableLayer::OGRPGeoTableLayer(OGRPGeoDataSource *poDSIn,
39 0 : int nODBCStatementFlags)
40 0 : : pszQuery(nullptr)
41 : {
42 0 : m_nStatementFlags = nODBCStatementFlags;
43 0 : poDS = poDSIn;
44 0 : iNextShapeId = 0;
45 0 : nSRSId = -1;
46 0 : poFeatureDefn = nullptr;
47 0 : }
48 :
49 : /************************************************************************/
50 : /* ~OGRPGeoTableLayer() */
51 : /************************************************************************/
52 :
53 0 : OGRPGeoTableLayer::~OGRPGeoTableLayer()
54 :
55 : {
56 0 : CPLFree(pszQuery);
57 0 : ClearStatement();
58 0 : }
59 :
60 : /************************************************************************/
61 : /* Initialize() */
62 : /************************************************************************/
63 :
64 0 : CPLErr OGRPGeoTableLayer::Initialize(const char *pszTableName,
65 : const char *pszGeomCol, int nShapeType,
66 : double dfExtentLeft, double dfExtentRight,
67 : double dfExtentBottom, double dfExtentTop,
68 : int nSRID, int bHasZ, int bHasM)
69 :
70 : {
71 0 : CPLODBCSession *poSession = poDS->GetSession();
72 :
73 0 : SetDescription(pszTableName);
74 :
75 0 : CPLFree(pszGeomColumn);
76 0 : if (pszGeomCol == nullptr)
77 0 : pszGeomColumn = nullptr;
78 : else
79 0 : pszGeomColumn = CPLStrdup(pszGeomCol);
80 :
81 0 : CPLFree(pszFIDColumn);
82 0 : pszFIDColumn = nullptr;
83 :
84 0 : sExtent.MinX = dfExtentLeft;
85 0 : sExtent.MaxX = dfExtentRight;
86 0 : sExtent.MinY = dfExtentBottom;
87 0 : sExtent.MaxY = dfExtentTop;
88 :
89 0 : if (pszGeomCol)
90 0 : LookupSRID(nSRID);
91 :
92 : // Setup geometry type.
93 :
94 : // The PGeo format has a similar approach to multi-part handling as
95 : // Shapefiles, where polygon and multipolygon geometries or line and
96 : // multiline geometries will co-exist in a layer reported as just polygon or
97 : // line type respectively. To handle this in a predictable way for clients
98 : // we always promote the polygon/line types to multitypes, and
99 : // correspondingly ALWAYS return multi polygon/line geometry objects for
100 : // features (even if strictly speaking the original feature had a
101 : // polygon/line geometry object)
102 : OGRwkbGeometryType eOGRType;
103 :
104 0 : switch (nShapeType)
105 : {
106 0 : case ESRI_LAYERGEOMTYPE_NULL:
107 0 : eOGRType = wkbNone;
108 0 : break;
109 :
110 0 : case ESRI_LAYERGEOMTYPE_POINT:
111 0 : eOGRType = wkbPoint;
112 0 : break;
113 :
114 0 : case ESRI_LAYERGEOMTYPE_MULTIPOINT:
115 0 : eOGRType = wkbMultiPoint;
116 0 : break;
117 :
118 0 : case ESRI_LAYERGEOMTYPE_POLYLINE:
119 0 : eOGRType = wkbMultiLineString; // see comment above
120 0 : break;
121 :
122 0 : case ESRI_LAYERGEOMTYPE_POLYGON:
123 : case ESRI_LAYERGEOMTYPE_MULTIPATCH:
124 0 : eOGRType = wkbMultiPolygon; // see comment above
125 0 : break;
126 :
127 0 : default:
128 0 : CPLDebug("PGeo", "Unexpected value for shape type : %d",
129 : nShapeType);
130 0 : eOGRType = wkbUnknown;
131 0 : break;
132 : }
133 :
134 0 : if (eOGRType != wkbUnknown && eOGRType != wkbNone)
135 : {
136 0 : if (bHasZ)
137 0 : eOGRType = wkbSetZ(eOGRType);
138 0 : if (bHasM)
139 0 : eOGRType = wkbSetM(eOGRType);
140 : }
141 0 : CPL_IGNORE_RET_VAL(eOGRType);
142 :
143 : /* -------------------------------------------------------------------- */
144 : /* Do we have a simple primary key? */
145 : /* -------------------------------------------------------------------- */
146 0 : CPLODBCStatement oGetKey(poSession);
147 :
148 0 : if (oGetKey.GetPrimaryKeys(pszTableName) && oGetKey.Fetch())
149 : {
150 0 : pszFIDColumn = CPLStrdup(oGetKey.GetColData(3));
151 :
152 0 : if (oGetKey.Fetch()) // more than one field in key!
153 : {
154 0 : CPLFree(pszFIDColumn);
155 0 : pszFIDColumn = nullptr;
156 0 : CPLDebug("PGeo", "%s: Compound primary key, ignoring.",
157 : pszTableName);
158 : }
159 : else
160 0 : CPLDebug("PGeo", "%s: Got primary key %s.", pszTableName,
161 : pszFIDColumn);
162 : }
163 : else
164 0 : CPLDebug("PGeo", "%s: no primary key", pszTableName);
165 :
166 : /* -------------------------------------------------------------------- */
167 : /* Get the column definitions for this table. */
168 : /* -------------------------------------------------------------------- */
169 0 : CPLODBCStatement oGetCol(poSession);
170 : CPLErr eErr;
171 :
172 0 : if (!oGetCol.GetColumns(pszTableName))
173 : {
174 0 : CPLError(CE_Failure, CPLE_AppDefined, "GetColumns() failed on %s.\n%s",
175 : pszTableName, poSession->GetLastError());
176 0 : return CE_Failure;
177 : }
178 :
179 0 : eErr = BuildFeatureDefn(pszTableName, &oGetCol);
180 0 : if (eErr != CE_None)
181 0 : return eErr;
182 :
183 0 : if (poFeatureDefn->GetFieldCount() == 0)
184 : {
185 0 : CPLError(
186 : CE_Failure, CPLE_AppDefined,
187 : "No column definitions found for table '%s', layer not usable.",
188 : pszTableName);
189 0 : return CE_Failure;
190 : }
191 :
192 0 : poFeatureDefn->SetGeomType(eOGRType);
193 :
194 : // Where possible, retrieve useful metadata information from the GDB_Items
195 : // table
196 0 : if (poDS->HasGdbItemsTable())
197 : {
198 0 : CPLODBCStatement oItemsStmt(poSession);
199 0 : oItemsStmt.Append(
200 : "SELECT Definition, Documentation FROM GDB_Items WHERE Name='");
201 0 : oItemsStmt.Append(pszTableName);
202 0 : oItemsStmt.Append("'");
203 0 : if (oItemsStmt.ExecuteSQL())
204 : {
205 0 : while (oItemsStmt.Fetch())
206 : {
207 : const CPLString osDefinition =
208 0 : CPLString(oItemsStmt.GetColData(0, ""));
209 0 : if (strstr(osDefinition, "DEFeatureClassInfo") != nullptr)
210 : {
211 0 : m_osDefinition = osDefinition;
212 :
213 : // try to retrieve field domains
214 : CPLXMLTreeCloser oTree(
215 0 : CPLParseXMLString(osDefinition.c_str()));
216 0 : if (oTree.get())
217 : {
218 0 : if (const CPLXMLNode *psFieldInfoExs = CPLGetXMLNode(
219 : oTree.get(),
220 : "=DEFeatureClassInfo.GPFieldInfoExs"))
221 : {
222 0 : for (const CPLXMLNode *psFieldInfoEx =
223 0 : CPLGetXMLNode(psFieldInfoExs,
224 : "GPFieldInfoEx");
225 0 : psFieldInfoEx != nullptr;
226 0 : psFieldInfoEx = psFieldInfoEx->psNext)
227 : {
228 : const CPLString osName =
229 0 : CPLGetXMLValue(psFieldInfoEx, "Name", "");
230 : const CPLString osDomainName = CPLGetXMLValue(
231 0 : psFieldInfoEx, "DomainName", "");
232 0 : if (!osDomainName.empty())
233 : {
234 : const int fieldIndex =
235 0 : poFeatureDefn->GetFieldIndex(osName);
236 0 : if (fieldIndex != -1)
237 0 : poFeatureDefn->GetFieldDefn(fieldIndex)
238 0 : ->SetDomainName(osDomainName);
239 : }
240 : }
241 : }
242 : }
243 :
244 : // try to retrieve layer medata
245 0 : m_osDocumentation = CPLString(oItemsStmt.GetColData(1, ""));
246 0 : break;
247 : }
248 : }
249 : }
250 : }
251 :
252 0 : return CE_None;
253 : }
254 :
255 : /************************************************************************/
256 : /* ClearStatement() */
257 : /************************************************************************/
258 :
259 0 : void OGRPGeoTableLayer::ClearStatement()
260 :
261 : {
262 0 : if (poStmt != nullptr)
263 : {
264 0 : delete poStmt;
265 0 : poStmt = nullptr;
266 : }
267 0 : }
268 :
269 : /************************************************************************/
270 : /* GetStatement() */
271 : /************************************************************************/
272 :
273 0 : CPLODBCStatement *OGRPGeoTableLayer::GetStatement()
274 :
275 : {
276 0 : if (poStmt == nullptr)
277 0 : ResetStatement();
278 :
279 0 : return poStmt;
280 : }
281 :
282 : /************************************************************************/
283 : /* ResetStatement() */
284 : /************************************************************************/
285 :
286 0 : OGRErr OGRPGeoTableLayer::ResetStatement()
287 :
288 : {
289 0 : ClearStatement();
290 :
291 0 : iNextShapeId = 0;
292 :
293 0 : poStmt = new CPLODBCStatement(poDS->GetSession(), m_nStatementFlags);
294 0 : poStmt->Append("SELECT * FROM ");
295 0 : poStmt->Append(poFeatureDefn->GetName());
296 0 : if (pszQuery != nullptr)
297 0 : poStmt->Appendf(" WHERE %s", pszQuery);
298 :
299 0 : if (poStmt->ExecuteSQL())
300 0 : return OGRERR_NONE;
301 : else
302 : {
303 0 : delete poStmt;
304 0 : poStmt = nullptr;
305 0 : return OGRERR_FAILURE;
306 : }
307 : }
308 :
309 : /************************************************************************/
310 : /* ResetReading() */
311 : /************************************************************************/
312 :
313 0 : void OGRPGeoTableLayer::ResetReading()
314 :
315 : {
316 0 : ClearStatement();
317 0 : OGRPGeoLayer::ResetReading();
318 0 : }
319 :
320 : /************************************************************************/
321 : /* GetFeature() */
322 : /************************************************************************/
323 :
324 0 : OGRFeature *OGRPGeoTableLayer::GetFeature(GIntBig nFeatureId)
325 :
326 : {
327 0 : if (pszFIDColumn == nullptr)
328 0 : return OGRPGeoLayer::GetFeature(nFeatureId);
329 :
330 0 : ClearStatement();
331 :
332 0 : iNextShapeId = nFeatureId;
333 :
334 0 : poStmt = new CPLODBCStatement(poDS->GetSession(), m_nStatementFlags);
335 0 : poStmt->Append("SELECT * FROM ");
336 0 : poStmt->Append(poFeatureDefn->GetName());
337 0 : poStmt->Appendf(" WHERE %s = " CPL_FRMT_GIB, pszFIDColumn, nFeatureId);
338 :
339 0 : if (!poStmt->ExecuteSQL())
340 : {
341 0 : delete poStmt;
342 0 : poStmt = nullptr;
343 0 : return nullptr;
344 : }
345 :
346 0 : return GetNextRawFeature();
347 : }
348 :
349 : /************************************************************************/
350 : /* SetAttributeFilter() */
351 : /************************************************************************/
352 :
353 0 : OGRErr OGRPGeoTableLayer::SetAttributeFilter(const char *pszQueryIn)
354 :
355 : {
356 0 : CPLFree(m_pszAttrQueryString);
357 0 : m_pszAttrQueryString = (pszQueryIn) ? CPLStrdup(pszQueryIn) : nullptr;
358 :
359 0 : if ((pszQueryIn == nullptr && pszQuery == nullptr) ||
360 0 : (pszQueryIn != nullptr && pszQuery != nullptr &&
361 0 : EQUAL(pszQueryIn, pszQuery)))
362 0 : return OGRERR_NONE;
363 :
364 0 : CPLFree(pszQuery);
365 0 : pszQuery = pszQueryIn ? CPLStrdup(pszQueryIn) : nullptr;
366 :
367 0 : ClearStatement();
368 :
369 0 : return OGRERR_NONE;
370 : }
371 :
372 : /************************************************************************/
373 : /* TestCapability() */
374 : /************************************************************************/
375 :
376 0 : int OGRPGeoTableLayer::TestCapability(const char *pszCap)
377 :
378 : {
379 0 : if (EQUAL(pszCap, OLCRandomRead))
380 0 : return TRUE;
381 :
382 0 : else if (EQUAL(pszCap, OLCFastFeatureCount))
383 0 : return m_poFilterGeom == nullptr && poDS->CountStarWorking();
384 :
385 0 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
386 0 : return FALSE;
387 :
388 : else
389 0 : return OGRPGeoLayer::TestCapability(pszCap);
390 : }
391 :
392 : /************************************************************************/
393 : /* GetFeatureCount() */
394 : /* */
395 : /* If a spatial filter is in effect, we turn control over to */
396 : /* the generic counter. Otherwise we return the total count. */
397 : /* Eventually we should consider implementing a more efficient */
398 : /* way of counting features matching a spatial query. */
399 : /************************************************************************/
400 :
401 0 : GIntBig OGRPGeoTableLayer::GetFeatureCount(int bForce)
402 :
403 : {
404 0 : if (m_poFilterGeom != nullptr || !poDS->CountStarWorking())
405 0 : return OGRPGeoLayer::GetFeatureCount(bForce);
406 :
407 0 : CPLODBCStatement oStmt(poDS->GetSession());
408 0 : oStmt.Append("SELECT COUNT(*) FROM ");
409 0 : oStmt.Append(poFeatureDefn->GetName());
410 :
411 0 : if (pszQuery != nullptr)
412 0 : oStmt.Appendf(" WHERE %s", pszQuery);
413 :
414 0 : if (!oStmt.ExecuteSQL() || !oStmt.Fetch())
415 : {
416 0 : CPLError(CE_Failure, CPLE_AppDefined,
417 : "GetFeatureCount() failed on query %s.\n%s",
418 0 : oStmt.GetCommand(), poDS->GetSession()->GetLastError());
419 0 : return OGRPGeoLayer::GetFeatureCount(bForce);
420 : }
421 :
422 0 : return CPLAtoGIntBig(oStmt.GetColData(0));
423 : }
424 :
425 : /************************************************************************/
426 : /* GetExtent() */
427 : /************************************************************************/
428 :
429 0 : OGRErr OGRPGeoTableLayer::GetExtent(OGREnvelope *psExtent,
430 : CPL_UNUSED int bForce)
431 : {
432 0 : if (pszGeomColumn == nullptr)
433 : {
434 0 : return OGRERR_FAILURE;
435 : }
436 :
437 0 : *psExtent = sExtent;
438 0 : return OGRERR_NONE;
439 : }
|