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 : std::unique_ptr<OGRGeometry> poGeom;
288 :
289 0 : if (pabyShape != nullptr)
290 : {
291 0 : OGRGeometry *poGeomRaw = nullptr;
292 0 : err = OGRCreateFromShapeBin(pabyShape, &poGeomRaw, nBytes);
293 0 : poGeom.reset(poGeomRaw);
294 0 : if (OGRERR_NONE != err)
295 : {
296 0 : CPLDebug(
297 : "PGeo",
298 : "Translation shape binary to OGR geometry failed (FID=%ld)",
299 0 : (long)poFeature->GetFID());
300 : }
301 : }
302 :
303 0 : if (poGeom != nullptr && OGRERR_NONE == err)
304 : {
305 0 : const auto eGeomType = poGeom->getGeometryType();
306 : // always promote polygon/linestring geometries to
307 : // multipolygon/multilinestring, so that the geometry types returned
308 : // for the layer are predictable and match the advertised layer
309 : // geometry type. See more details in OGRPGeoTableLayer::Initialize
310 0 : const OGRwkbGeometryType eFlattenType = wkbFlatten(eGeomType);
311 0 : if (eFlattenType == wkbPolygon || eFlattenType == wkbLineString)
312 : {
313 0 : poGeom = OGRGeometryFactory::forceTo(
314 0 : std::move(poGeom), OGR_GT_GetCollection(eGeomType));
315 : }
316 :
317 0 : poGeom->assignSpatialReference(poSRS);
318 0 : poFeature->SetGeometry(std::move(poGeom));
319 : }
320 : }
321 :
322 0 : return poFeature;
323 : }
324 :
325 : /************************************************************************/
326 : /* GetFeature() */
327 : /************************************************************************/
328 :
329 0 : OGRFeature *OGRPGeoLayer::GetFeature(GIntBig nFeatureId)
330 :
331 : {
332 : /* This should be implemented directly! */
333 :
334 0 : return OGRLayer::GetFeature(nFeatureId);
335 : }
336 :
337 : /************************************************************************/
338 : /* TestCapability() */
339 : /************************************************************************/
340 :
341 0 : int OGRPGeoLayer::TestCapability(const char *pszCap) const
342 : {
343 0 : if (EQUAL(pszCap, OLCZGeometries))
344 0 : return true;
345 0 : if (EQUAL(pszCap, OLCMeasuredGeometries))
346 0 : return true;
347 :
348 0 : return false;
349 : }
350 :
351 : /************************************************************************/
352 : /* TestCapability() */
353 : /************************************************************************/
354 :
355 0 : void OGRPGeoLayer::LookupSRID(int nSRID)
356 :
357 : {
358 : /* -------------------------------------------------------------------- */
359 : /* Fetch the corresponding WKT from the SpatialRef table. */
360 : /* -------------------------------------------------------------------- */
361 0 : CPLODBCStatement oStmt(poDS->GetSession());
362 :
363 0 : oStmt.Appendf("SELECT srtext FROM GDB_SpatialRefs WHERE srid = %d", nSRID);
364 :
365 0 : if (!oStmt.ExecuteSQL())
366 : {
367 0 : CPLError(CE_Failure, CPLE_AppDefined, "'%s' failed.\n%s",
368 0 : oStmt.GetCommand(), poDS->GetSession()->GetLastError());
369 0 : return;
370 : }
371 :
372 0 : if (!oStmt.Fetch())
373 : {
374 0 : CPLError(CE_Warning, CPLE_AppDefined, "SRID %d lookup failed.\n%s",
375 0 : nSRID, poDS->GetSession()->GetLastError());
376 0 : return;
377 : }
378 :
379 : /* -------------------------------------------------------------------- */
380 : /* Check that it isn't just a GUID. We don't know how to */
381 : /* translate those. */
382 : /* -------------------------------------------------------------------- */
383 0 : const char *pszSRText = oStmt.GetColData(0);
384 :
385 0 : if (pszSRText[0] == '{')
386 : {
387 0 : CPLDebug("PGEO", "Ignoring GUID SRTEXT: %s", pszSRText);
388 0 : return;
389 : }
390 :
391 : /* -------------------------------------------------------------------- */
392 : /* Turn it into an OGRSpatialReference. */
393 : /* -------------------------------------------------------------------- */
394 0 : poSRS = new OGRSpatialReference();
395 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
396 :
397 0 : if (poSRS->importFromWkt(pszSRText) != OGRERR_NONE)
398 : {
399 0 : CPLError(CE_Failure, CPLE_AppDefined,
400 : "importFromWKT() failed on SRS '%s'.", pszSRText);
401 0 : delete poSRS;
402 0 : poSRS = nullptr;
403 : }
404 : else
405 0 : nSRSId = nSRID;
406 : }
407 :
408 : /************************************************************************/
409 : /* GetFIDColumn() */
410 : /************************************************************************/
411 :
412 0 : const char *OGRPGeoLayer::GetFIDColumn() const
413 :
414 : {
415 0 : if (pszFIDColumn != nullptr)
416 0 : return pszFIDColumn;
417 : else
418 0 : return "";
419 : }
420 :
421 : /************************************************************************/
422 : /* GetGeometryColumn() */
423 : /************************************************************************/
424 :
425 0 : const char *OGRPGeoLayer::GetGeometryColumn() const
426 :
427 : {
428 0 : if (pszGeomColumn != nullptr)
429 0 : return pszGeomColumn;
430 : else
431 0 : return "";
432 : }
|