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