Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRPGeoLayer class, code shared between
5 : * the direct table access, and the generic SQL results.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
10 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "cpl_conv.h"
32 : #include "ogr_pgeo.h"
33 : #include "cpl_string.h"
34 : #include "ogrpgeogeometry.h"
35 :
36 : #include <algorithm>
37 :
38 : /************************************************************************/
39 : /* OGRPGeoLayer() */
40 : /************************************************************************/
41 :
42 0 : OGRPGeoLayer::OGRPGeoLayer()
43 : : poFeatureDefn(nullptr), poStmt(nullptr), poSRS(nullptr),
44 : nSRSId(-2), // we haven't even queried the database for it yet.
45 : iNextShapeId(0), poDS(nullptr), pszGeomColumn(nullptr),
46 0 : pszFIDColumn(nullptr), panFieldOrdinals(nullptr)
47 : {
48 0 : }
49 :
50 : /************************************************************************/
51 : /* ~OGRPGeoLayer() */
52 : /************************************************************************/
53 :
54 0 : OGRPGeoLayer::~OGRPGeoLayer()
55 :
56 : {
57 0 : if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
58 : {
59 0 : CPLDebug("PGeo", "%d features read on layer '%s'.",
60 0 : static_cast<int>(m_nFeaturesRead), poFeatureDefn->GetName());
61 : }
62 :
63 0 : if (poStmt != nullptr)
64 : {
65 0 : delete poStmt;
66 0 : poStmt = nullptr;
67 : }
68 :
69 0 : if (poFeatureDefn != nullptr)
70 : {
71 0 : poFeatureDefn->Release();
72 0 : poFeatureDefn = nullptr;
73 : }
74 :
75 0 : CPLFree(pszGeomColumn);
76 0 : CPLFree(panFieldOrdinals);
77 0 : CPLFree(pszFIDColumn);
78 :
79 0 : if (poSRS != nullptr)
80 : {
81 0 : poSRS->Release();
82 0 : poSRS = nullptr;
83 : }
84 0 : }
85 :
86 : /************************************************************************/
87 : /* BuildFeatureDefn() */
88 : /* */
89 : /* Build feature definition from a set of column definitions */
90 : /* set on a statement. Sift out geometry and FID fields. */
91 : /************************************************************************/
92 :
93 0 : CPLErr OGRPGeoLayer::BuildFeatureDefn(const char *pszLayerName,
94 : CPLODBCStatement *poStmtIn)
95 :
96 : {
97 0 : poFeatureDefn = new OGRFeatureDefn(pszLayerName);
98 0 : SetDescription(poFeatureDefn->GetName());
99 0 : int nRawColumns = poStmtIn->GetColCount();
100 :
101 0 : poFeatureDefn->Reference();
102 0 : poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
103 :
104 0 : panFieldOrdinals = (int *)CPLMalloc(sizeof(int) * nRawColumns);
105 :
106 0 : for (int iCol = 0; iCol < nRawColumns; iCol++)
107 : {
108 0 : OGRFieldDefn oField(poStmtIn->GetColName(iCol), OFTString);
109 :
110 0 : oField.SetWidth(
111 0 : std::max(static_cast<short>(0), poStmtIn->GetColSize(iCol)));
112 :
113 0 : if (pszGeomColumn != nullptr &&
114 0 : EQUAL(poStmtIn->GetColName(iCol), pszGeomColumn))
115 0 : continue;
116 :
117 0 : if (pszFIDColumn == nullptr &&
118 0 : EQUAL(poStmtIn->GetColName(iCol), "OBJECTID"))
119 : {
120 0 : pszFIDColumn = CPLStrdup(poStmtIn->GetColName(iCol));
121 : }
122 :
123 0 : if (pszGeomColumn == nullptr &&
124 0 : EQUAL(poStmtIn->GetColName(iCol), "Shape"))
125 : {
126 0 : pszGeomColumn = CPLStrdup(poStmtIn->GetColName(iCol));
127 0 : continue;
128 : }
129 :
130 0 : switch (poStmtIn->GetColType(iCol))
131 : {
132 0 : case SQL_INTEGER:
133 : case SQL_SMALLINT:
134 0 : oField.SetType(OFTInteger);
135 0 : break;
136 :
137 0 : case SQL_BINARY:
138 : case SQL_VARBINARY:
139 : case SQL_LONGVARBINARY:
140 0 : oField.SetType(OFTBinary);
141 0 : break;
142 :
143 0 : case SQL_DECIMAL:
144 0 : oField.SetType(OFTReal);
145 0 : oField.SetPrecision(poStmtIn->GetColPrecision(iCol));
146 0 : break;
147 :
148 0 : case SQL_FLOAT:
149 : case SQL_REAL:
150 : case SQL_DOUBLE:
151 0 : oField.SetType(OFTReal);
152 0 : oField.SetWidth(0);
153 0 : break;
154 :
155 0 : case SQL_C_DATE:
156 0 : oField.SetType(OFTDate);
157 0 : break;
158 :
159 0 : case SQL_C_TIME:
160 0 : oField.SetType(OFTTime);
161 0 : break;
162 :
163 0 : case SQL_C_TIMESTAMP:
164 : case SQL_C_TYPE_TIMESTAMP:
165 0 : oField.SetType(OFTDateTime);
166 0 : break;
167 :
168 0 : default:
169 : /* leave it as OFTString */;
170 : }
171 :
172 0 : poFeatureDefn->AddFieldDefn(&oField);
173 0 : panFieldOrdinals[poFeatureDefn->GetFieldCount() - 1] = iCol + 1;
174 : }
175 :
176 0 : if (pszGeomColumn != nullptr)
177 0 : poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColumn);
178 : else
179 0 : poFeatureDefn->SetGeomType(wkbNone);
180 :
181 0 : return CE_None;
182 : }
183 :
184 : /************************************************************************/
185 : /* ResetReading() */
186 : /************************************************************************/
187 :
188 0 : void OGRPGeoLayer::ResetReading()
189 :
190 : {
191 0 : iNextShapeId = 0;
192 0 : m_bEOF = false;
193 0 : }
194 :
195 : /************************************************************************/
196 : /* GetNextFeature() */
197 : /************************************************************************/
198 :
199 0 : OGRFeature *OGRPGeoLayer::GetNextFeature()
200 :
201 : {
202 : while (true)
203 : {
204 0 : OGRFeature *poFeature = GetNextRawFeature();
205 0 : if (poFeature == nullptr)
206 0 : return nullptr;
207 :
208 0 : if ((m_poFilterGeom == nullptr ||
209 0 : FilterGeometry(poFeature->GetGeometryRef())) &&
210 0 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
211 0 : return poFeature;
212 :
213 0 : delete poFeature;
214 0 : }
215 : }
216 :
217 : /************************************************************************/
218 : /* GetNextRawFeature() */
219 : /************************************************************************/
220 :
221 0 : OGRFeature *OGRPGeoLayer::GetNextRawFeature()
222 :
223 : {
224 0 : OGRErr err = OGRERR_NONE;
225 :
226 0 : if (m_bEOF || GetStatement() == nullptr)
227 0 : return nullptr;
228 :
229 : /* -------------------------------------------------------------------- */
230 : /* If we are marked to restart then do so, and fetch a record. */
231 : /* -------------------------------------------------------------------- */
232 0 : if (!poStmt->Fetch())
233 : {
234 0 : delete poStmt;
235 0 : poStmt = nullptr;
236 0 : m_bEOF = true;
237 0 : return nullptr;
238 : }
239 :
240 : /* -------------------------------------------------------------------- */
241 : /* Create a feature from the current result. */
242 : /* -------------------------------------------------------------------- */
243 0 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
244 :
245 0 : if (pszFIDColumn != nullptr && poStmt->GetColId(pszFIDColumn) > -1)
246 0 : poFeature->SetFID(
247 0 : atoi(poStmt->GetColData(poStmt->GetColId(pszFIDColumn))));
248 : else
249 0 : poFeature->SetFID(iNextShapeId);
250 :
251 0 : iNextShapeId++;
252 0 : m_nFeaturesRead++;
253 :
254 : /* -------------------------------------------------------------------- */
255 : /* Set the fields. */
256 : /* -------------------------------------------------------------------- */
257 0 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++)
258 : {
259 : const OGRFieldType eType =
260 0 : poFeatureDefn->GetFieldDefn(iField)->GetType();
261 0 : int iSrcField = panFieldOrdinals[iField] - 1;
262 :
263 0 : if (eType == OFTReal &&
264 0 : (poStmt->Flags() &
265 : CPLODBCStatement::Flag::RetrieveNumericColumnsAsDouble))
266 : {
267 : // for OFTReal fields we retrieve the value directly as a double
268 : // to avoid loss of precision associated with double/float->string
269 : // conversion
270 0 : const double dfValue = poStmt->GetColDataAsDouble(iSrcField);
271 0 : if (std::isnan(dfValue))
272 : {
273 0 : poFeature->SetFieldNull(iField);
274 : }
275 : else
276 : {
277 0 : poFeature->SetField(iField, dfValue);
278 : }
279 : }
280 : else
281 : {
282 0 : const char *pszValue = poStmt->GetColData(iSrcField);
283 :
284 0 : if (pszValue == nullptr)
285 0 : poFeature->SetFieldNull(iField);
286 0 : else if (poFeature->GetFieldDefnRef(iField)->GetType() == OFTBinary)
287 0 : poFeature->SetField(iField, poStmt->GetColDataLength(iSrcField),
288 : (GByte *)pszValue);
289 : else
290 0 : poFeature->SetField(iField, pszValue);
291 : }
292 : }
293 :
294 : /* -------------------------------------------------------------------- */
295 : /* Try to extract a geometry. */
296 : /* -------------------------------------------------------------------- */
297 0 : if (pszGeomColumn != nullptr)
298 : {
299 0 : int iField = poStmt->GetColId(pszGeomColumn);
300 0 : GByte *pabyShape = (GByte *)poStmt->GetColData(iField);
301 0 : int nBytes = poStmt->GetColDataLength(iField);
302 0 : OGRGeometry *poGeom = nullptr;
303 :
304 0 : if (pabyShape != nullptr)
305 : {
306 0 : err = OGRCreateFromShapeBin(pabyShape, &poGeom, nBytes);
307 0 : if (OGRERR_NONE != err)
308 : {
309 0 : CPLDebug(
310 : "PGeo",
311 : "Translation shape binary to OGR geometry failed (FID=%ld)",
312 0 : (long)poFeature->GetFID());
313 : }
314 : }
315 :
316 0 : if (poGeom != nullptr && OGRERR_NONE == err)
317 : {
318 : // always promote polygon/linestring geometries to
319 : // multipolygon/multilinestring, so that the geometry types returned
320 : // for the layer are predictable and match the advertised layer
321 : // geometry type. See more details in OGRPGeoTableLayer::Initialize
322 : const OGRwkbGeometryType eFlattenType =
323 0 : wkbFlatten(poGeom->getGeometryType());
324 0 : if (eFlattenType == wkbPolygon || eFlattenType == wkbLineString)
325 0 : poGeom = OGRGeometryFactory::forceTo(
326 0 : poGeom, OGR_GT_GetCollection(poGeom->getGeometryType()));
327 :
328 0 : poGeom->assignSpatialReference(poSRS);
329 0 : poFeature->SetGeometryDirectly(poGeom);
330 : }
331 : }
332 :
333 0 : return poFeature;
334 : }
335 :
336 : /************************************************************************/
337 : /* GetFeature() */
338 : /************************************************************************/
339 :
340 0 : OGRFeature *OGRPGeoLayer::GetFeature(GIntBig nFeatureId)
341 :
342 : {
343 : /* This should be implemented directly! */
344 :
345 0 : return OGRLayer::GetFeature(nFeatureId);
346 : }
347 :
348 : /************************************************************************/
349 : /* TestCapability() */
350 : /************************************************************************/
351 :
352 0 : int OGRPGeoLayer::TestCapability(CPL_UNUSED const char *pszCap)
353 : {
354 0 : if (EQUAL(pszCap, OLCZGeometries))
355 0 : return true;
356 0 : if (EQUAL(pszCap, OLCMeasuredGeometries))
357 0 : return true;
358 :
359 0 : return false;
360 : }
361 :
362 : /************************************************************************/
363 : /* TestCapability() */
364 : /************************************************************************/
365 :
366 0 : void OGRPGeoLayer::LookupSRID(int nSRID)
367 :
368 : {
369 : /* -------------------------------------------------------------------- */
370 : /* Fetch the corresponding WKT from the SpatialRef table. */
371 : /* -------------------------------------------------------------------- */
372 0 : CPLODBCStatement oStmt(poDS->GetSession());
373 :
374 0 : oStmt.Appendf("SELECT srtext FROM GDB_SpatialRefs WHERE srid = %d", nSRID);
375 :
376 0 : if (!oStmt.ExecuteSQL())
377 : {
378 0 : CPLError(CE_Failure, CPLE_AppDefined, "'%s' failed.\n%s",
379 0 : oStmt.GetCommand(), poDS->GetSession()->GetLastError());
380 0 : return;
381 : }
382 :
383 0 : if (!oStmt.Fetch())
384 : {
385 0 : CPLError(CE_Warning, CPLE_AppDefined, "SRID %d lookup failed.\n%s",
386 0 : nSRID, poDS->GetSession()->GetLastError());
387 0 : return;
388 : }
389 :
390 : /* -------------------------------------------------------------------- */
391 : /* Check that it isn't just a GUID. We don't know how to */
392 : /* translate those. */
393 : /* -------------------------------------------------------------------- */
394 0 : const char *pszSRText = oStmt.GetColData(0);
395 :
396 0 : if (pszSRText[0] == '{')
397 : {
398 0 : CPLDebug("PGEO", "Ignoring GUID SRTEXT: %s", pszSRText);
399 0 : return;
400 : }
401 :
402 : /* -------------------------------------------------------------------- */
403 : /* Turn it into an OGRSpatialReference. */
404 : /* -------------------------------------------------------------------- */
405 0 : poSRS = new OGRSpatialReference();
406 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
407 :
408 0 : if (poSRS->importFromWkt(pszSRText) != OGRERR_NONE)
409 : {
410 0 : CPLError(CE_Failure, CPLE_AppDefined,
411 : "importFromWKT() failed on SRS '%s'.", pszSRText);
412 0 : delete poSRS;
413 0 : poSRS = nullptr;
414 : }
415 : else
416 0 : nSRSId = nSRID;
417 : }
418 :
419 : /************************************************************************/
420 : /* GetFIDColumn() */
421 : /************************************************************************/
422 :
423 0 : const char *OGRPGeoLayer::GetFIDColumn()
424 :
425 : {
426 0 : if (pszFIDColumn != nullptr)
427 0 : return pszFIDColumn;
428 : else
429 0 : return "";
430 : }
431 :
432 : /************************************************************************/
433 : /* GetGeometryColumn() */
434 : /************************************************************************/
435 :
436 0 : const char *OGRPGeoLayer::GetGeometryColumn()
437 :
438 : {
439 0 : if (pszGeomColumn != nullptr)
440 0 : return pszGeomColumn;
441 : else
442 0 : return "";
443 : }
|