Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OGDI Bridge
4 : * Purpose: Implements OGROGDILayer class.
5 : * Author: Daniel Morissette, danmo@videotron.ca
6 : * (Based on some code contributed by Frank Warmerdam :)
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2000, Daniel Morissette
10 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ******************************************************************************
14 : */
15 :
16 : #include "ogrogdi.h"
17 : #include "cpl_conv.h"
18 : #include "cpl_string.h"
19 :
20 : /************************************************************************/
21 : /* OGROGDILayer() */
22 : /************************************************************************/
23 :
24 19 : OGROGDILayer::OGROGDILayer(OGROGDIDataSource *poODS, const char *pszName,
25 19 : ecs_Family eFamily)
26 19 : : m_poODS(poODS), m_nClientID(poODS->GetClientID()),
27 38 : m_pszOGDILayerName(CPLStrdup(pszName)), m_eFamily(eFamily),
28 : m_poFeatureDefn(nullptr),
29 : // Keep a reference on the SpatialRef (owned by the dataset).
30 19 : m_poSpatialRef(m_poODS->DSGetSpatialRef()),
31 19 : m_sFilterBounds(*(m_poODS->GetGlobalBounds())), m_iNextShapeId(0),
32 19 : m_nTotalShapeCount(-1), m_nFilteredOutShapes(0)
33 : {
34 :
35 : // Select layer and feature family.
36 19 : OGROGDILayer::ResetReading();
37 :
38 19 : BuildFeatureDefn();
39 19 : }
40 :
41 : /************************************************************************/
42 : /* ~OGROGDILayer() */
43 : /************************************************************************/
44 :
45 38 : OGROGDILayer::~OGROGDILayer()
46 :
47 : {
48 19 : if (m_nFeaturesRead > 0 && m_poFeatureDefn != nullptr)
49 : {
50 16 : CPLDebug("OGDI", "%d features read on layer '%s'.",
51 8 : (int)m_nFeaturesRead, m_poFeatureDefn->GetName());
52 : }
53 :
54 19 : if (m_poFeatureDefn)
55 19 : m_poFeatureDefn->Release();
56 :
57 19 : CPLFree(m_pszOGDILayerName);
58 :
59 : // Note: we do not delete m_poSpatialRef since it is owned by the dataset
60 38 : }
61 :
62 : /************************************************************************/
63 : /* SetSpatialFilter() */
64 : /************************************************************************/
65 :
66 156 : void OGROGDILayer::SetSpatialFilter(OGRGeometry *poGeomIn)
67 :
68 : {
69 156 : if (!InstallFilter(poGeomIn))
70 84 : return;
71 :
72 72 : ResetReading();
73 :
74 72 : m_nTotalShapeCount = -1;
75 : }
76 :
77 : /************************************************************************/
78 : /* SetAttributeFilter() */
79 : /************************************************************************/
80 :
81 186 : OGRErr OGROGDILayer::SetAttributeFilter(const char *pszQuery)
82 : {
83 186 : OGRErr eErr = OGRLayer::SetAttributeFilter(pszQuery);
84 :
85 186 : ResetReading();
86 :
87 186 : m_nTotalShapeCount = -1;
88 :
89 186 : return eErr;
90 : }
91 :
92 : /************************************************************************/
93 : /* ResetReading() */
94 : /************************************************************************/
95 :
96 787 : void OGROGDILayer::ResetReading()
97 :
98 : {
99 : ecs_LayerSelection sSelectionLayer;
100 :
101 787 : sSelectionLayer.Select = m_pszOGDILayerName;
102 787 : sSelectionLayer.F = m_eFamily;
103 :
104 787 : ecs_Result *psResult = cln_SelectLayer(m_nClientID, &sSelectionLayer);
105 787 : if (ECSERROR(psResult))
106 : {
107 0 : CPLError(CE_Failure, CPLE_AppDefined,
108 : "Access to layer '%s' Failed: %s\n", m_pszOGDILayerName,
109 0 : psResult->message ? psResult->message : "(no message string)");
110 0 : return;
111 : }
112 :
113 : /* Reset spatial filter */
114 787 : if (m_poFilterGeom != nullptr)
115 : {
116 198 : OGREnvelope oEnv;
117 :
118 198 : m_poFilterGeom->getEnvelope(&oEnv);
119 :
120 198 : m_sFilterBounds.north = oEnv.MaxY;
121 198 : m_sFilterBounds.south = oEnv.MinY;
122 198 : m_sFilterBounds.west = oEnv.MinX;
123 198 : m_sFilterBounds.east = oEnv.MaxX;
124 :
125 198 : psResult = cln_SelectRegion(m_nClientID, &m_sFilterBounds);
126 198 : if (ECSERROR(psResult))
127 : {
128 0 : CPLError(CE_Failure, CPLE_AppDefined, "SelectRegion failed: %s",
129 0 : psResult->message ? psResult->message
130 : : "(no message string)");
131 0 : return;
132 : }
133 : }
134 : else
135 : {
136 : /* Reset to global bounds */
137 589 : psResult = cln_SelectRegion(m_nClientID, m_poODS->GetGlobalBounds());
138 589 : if (ECSERROR(psResult))
139 : {
140 0 : CPLError(CE_Failure, CPLE_AppDefined, "SelectRegion failed: %s",
141 0 : psResult->message ? psResult->message
142 : : "(no message string)");
143 0 : return;
144 : }
145 : }
146 :
147 787 : m_iNextShapeId = 0;
148 787 : m_nFilteredOutShapes = 0;
149 : }
150 :
151 : /************************************************************************/
152 : /* GetNextFeature() */
153 : /************************************************************************/
154 :
155 1354 : OGRFeature *OGROGDILayer::GetNextFeature()
156 :
157 : {
158 :
159 : /* Reset reading if we are not the current layer */
160 : /* WARNING : this does not allow interleaved reading of layers */
161 1354 : if (m_poODS->GetCurrentLayer() != this)
162 : {
163 8 : m_poODS->SetCurrentLayer(this);
164 8 : ResetReading();
165 : }
166 :
167 : while (true)
168 : {
169 1630 : OGRFeature *poFeature = GetNextRawFeature();
170 1630 : if (poFeature == nullptr)
171 176 : return nullptr;
172 :
173 : /* --------------------------------------------------------------------
174 : */
175 : /* Do we need to apply an attribute test? */
176 : /* --------------------------------------------------------------------
177 : */
178 2632 : if ((m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poFeature)) ||
179 1178 : (m_poFilterGeom != nullptr &&
180 270 : !FilterGeometry(poFeature->GetGeometryRef())))
181 : {
182 276 : m_nFilteredOutShapes++;
183 276 : delete poFeature;
184 : }
185 : else
186 1178 : return poFeature;
187 276 : }
188 : }
189 :
190 : /************************************************************************/
191 : /* GetNextFeature() */
192 : /************************************************************************/
193 :
194 1648 : OGRFeature *OGROGDILayer::GetNextRawFeature()
195 : {
196 : /* -------------------------------------------------------------------- */
197 : /* Retrieve object from OGDI server and create new feature */
198 : /* -------------------------------------------------------------------- */
199 1648 : ecs_Result *psResult = cln_GetNextObject(m_nClientID);
200 1648 : if (!ECSSUCCESS(psResult))
201 : {
202 176 : if (ECSERROR(psResult) &&
203 176 : (psResult->message == nullptr ||
204 176 : strstr(psResult->message, "End of selection") == nullptr))
205 : {
206 0 : CPLError(CE_Failure, CPLE_AppDefined,
207 : "Access to next object of layer '%s' failed: %s\n",
208 : m_pszOGDILayerName,
209 0 : psResult->message ? psResult->message
210 : : "(no error string)");
211 : }
212 : // We probably reached EOF... keep track of shape count.
213 176 : m_nTotalShapeCount = m_iNextShapeId - m_nFilteredOutShapes;
214 176 : return nullptr;
215 : }
216 :
217 1472 : OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
218 :
219 1472 : poFeature->SetFID(m_iNextShapeId++);
220 1472 : m_nFeaturesRead++;
221 :
222 : /* -------------------------------------------------------------------- */
223 : /* Process geometry */
224 : /* -------------------------------------------------------------------- */
225 1472 : if (m_eFamily == Point)
226 : {
227 1033 : ecs_Point *psPoint = &(ECSGEOM(psResult).point);
228 1033 : OGRPoint *poOGRPoint = new OGRPoint(psPoint->c.x, psPoint->c.y);
229 :
230 1033 : poOGRPoint->assignSpatialReference(m_poSpatialRef);
231 1033 : poFeature->SetGeometryDirectly(poOGRPoint);
232 : }
233 439 : else if (m_eFamily == Line)
234 : {
235 62 : ecs_Line *psLine = &(ECSGEOM(psResult).line);
236 62 : OGRLineString *poOGRLine = new OGRLineString();
237 :
238 62 : poOGRLine->setNumPoints(psLine->c.c_len);
239 :
240 634 : for (int i = 0; i < (int)psLine->c.c_len; i++)
241 : {
242 572 : poOGRLine->setPoint(i, psLine->c.c_val[i].x, psLine->c.c_val[i].y);
243 : }
244 :
245 62 : poOGRLine->assignSpatialReference(m_poSpatialRef);
246 62 : poFeature->SetGeometryDirectly(poOGRLine);
247 : }
248 377 : else if (m_eFamily == Area)
249 : {
250 228 : ecs_Area *psArea = &(ECSGEOM(psResult).area);
251 228 : OGRPolygon *poOGRPolygon = new OGRPolygon();
252 :
253 456 : for (int iRing = 0; iRing < (int)psArea->ring.ring_len; iRing++)
254 : {
255 228 : ecs_FeatureRing *psRing = &(psArea->ring.ring_val[iRing]);
256 228 : OGRLinearRing *poOGRRing = new OGRLinearRing();
257 :
258 228 : poOGRRing->setNumPoints(psRing->c.c_len);
259 :
260 54559 : for (int i = 0; i < (int)psRing->c.c_len; i++)
261 : {
262 54331 : poOGRRing->setPoint(i, psRing->c.c_val[i].x,
263 54331 : psRing->c.c_val[i].y);
264 : }
265 228 : poOGRPolygon->addRingDirectly(poOGRRing);
266 : }
267 :
268 : // __TODO__
269 : // When OGR supports polygon centroids then we should carry them here
270 :
271 228 : poOGRPolygon->assignSpatialReference(m_poSpatialRef);
272 228 : poFeature->SetGeometryDirectly(poOGRPolygon);
273 : }
274 149 : else if (m_eFamily == Text)
275 : {
276 : // __TODO__
277 : // For now text is treated as a point and string is lost
278 : //
279 149 : ecs_Text *psText = &(ECSGEOM(psResult).text);
280 149 : OGRPoint *poOGRPoint = new OGRPoint(psText->c.x, psText->c.y);
281 :
282 149 : poOGRPoint->assignSpatialReference(m_poSpatialRef);
283 149 : poFeature->SetGeometryDirectly(poOGRPoint);
284 : }
285 : else
286 : {
287 0 : CPLAssert(false);
288 : }
289 :
290 : /* -------------------------------------------------------------------- */
291 : /* Set attributes */
292 : /* -------------------------------------------------------------------- */
293 1472 : char *pszAttrList = ECSOBJECTATTR(psResult);
294 :
295 11587 : for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
296 : {
297 10115 : char *pszFieldStart = nullptr;
298 10115 : int nNameLen = 0;
299 :
300 : /* parse out the next attribute value */
301 10115 : if (!ecs_FindElement(pszAttrList, &pszFieldStart, &pszAttrList,
302 : &nNameLen, nullptr))
303 : {
304 0 : nNameLen = 0;
305 0 : pszFieldStart = pszAttrList;
306 : }
307 :
308 : /* Skip any trailing white space (for string constants). */
309 :
310 10115 : if (nNameLen > 0 && pszFieldStart[nNameLen - 1] == ' ')
311 2881 : nNameLen--;
312 :
313 : /* skip leading white space */
314 14128 : while (pszFieldStart[0] == ' ' && nNameLen > 0)
315 : {
316 4013 : pszFieldStart++;
317 4013 : nNameLen--;
318 : }
319 :
320 : /* zero terminate the single field value, but save the */
321 : /* character we overwrote, so we can restore it when done. */
322 :
323 10115 : char chSavedChar = pszFieldStart[nNameLen];
324 10115 : pszFieldStart[nNameLen] = '\0';
325 :
326 : /* OGR takes care of all field type conversions for us! */
327 :
328 10115 : poFeature->SetField(iField, pszFieldStart);
329 :
330 10115 : pszFieldStart[nNameLen] = chSavedChar;
331 : }
332 :
333 : /* -------------------------------------------------------------------- */
334 : /* Apply the text associated with text features if appropriate. */
335 : /* -------------------------------------------------------------------- */
336 1472 : if (m_eFamily == Text)
337 : {
338 149 : poFeature->SetField("text", ECSGEOM(psResult).text.desc);
339 : }
340 :
341 1472 : return poFeature;
342 : }
343 :
344 : /************************************************************************/
345 : /* GetFeature() */
346 : /************************************************************************/
347 :
348 36 : OGRFeature *OGROGDILayer::GetFeature(GIntBig nFeatureId)
349 :
350 : {
351 :
352 36 : if (m_nTotalShapeCount != -1 && nFeatureId > m_nTotalShapeCount)
353 12 : return nullptr;
354 :
355 : /* Unset spatial filter */
356 : OGRGeometry *poOldFilterGeom =
357 24 : (m_poFilterGeom != nullptr) ? m_poFilterGeom->clone() : nullptr;
358 24 : if (poOldFilterGeom != nullptr)
359 6 : SetSpatialFilter(nullptr);
360 :
361 : /* Reset reading if we are not the current layer */
362 : /* WARNING : this does not allow interleaved reading of layers */
363 24 : if (m_poODS->GetCurrentLayer() != this)
364 : {
365 0 : m_poODS->SetCurrentLayer(this);
366 0 : ResetReading();
367 : }
368 24 : else if (nFeatureId < m_iNextShapeId)
369 16 : ResetReading();
370 :
371 88 : while (m_iNextShapeId != nFeatureId)
372 : {
373 70 : ecs_Result *psResult = cln_GetNextObject(m_nClientID);
374 70 : if (ECSSUCCESS(psResult))
375 64 : m_iNextShapeId++;
376 : else
377 : {
378 : // We probably reached EOF... keep track of shape count.
379 6 : m_nTotalShapeCount = m_iNextShapeId;
380 6 : if (poOldFilterGeom != nullptr)
381 : {
382 0 : SetSpatialFilter(poOldFilterGeom);
383 0 : delete poOldFilterGeom;
384 : }
385 6 : return nullptr;
386 : }
387 : }
388 :
389 : // OK, we're ready to read the requested feature...
390 18 : OGRFeature *poFeature = GetNextRawFeature();
391 18 : if (poOldFilterGeom != nullptr)
392 : {
393 6 : SetSpatialFilter(poOldFilterGeom);
394 6 : delete poOldFilterGeom;
395 : }
396 18 : return poFeature;
397 : }
398 :
399 : /************************************************************************/
400 : /* GetFeatureCount() */
401 : /* */
402 : /* If a spatial filter is in effect, we turn control over to */
403 : /* the generic counter. Otherwise we return the total count. */
404 : /* Eventually we should consider implementing a more efficient */
405 : /* way of counting features matching a spatial query. */
406 : /************************************************************************/
407 :
408 88 : GIntBig OGROGDILayer::GetFeatureCount(int bForce)
409 :
410 : {
411 88 : if (m_nTotalShapeCount == -1)
412 : {
413 44 : m_nTotalShapeCount =
414 44 : static_cast<int>(OGRLayer::GetFeatureCount(bForce));
415 : }
416 :
417 88 : return m_nTotalShapeCount;
418 : }
419 :
420 : /************************************************************************/
421 : /* TestCapability() */
422 : /************************************************************************/
423 :
424 210 : int OGROGDILayer::TestCapability(const char *pszCap)
425 :
426 : {
427 : /* -------------------------------------------------------------------- */
428 : /* Hummm... what are the proper capabilities... */
429 : /* Does OGDI have any idea of capabilities??? */
430 : /* For now just return FALSE for everything. */
431 : /* -------------------------------------------------------------------- */
432 : #ifdef __TODO__
433 : if (EQUAL(pszCap, OLCFastFeatureCount))
434 : return m_poFilterGeom == NULL && m_poAttrQuery == NULL;
435 :
436 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
437 : return FALSE;
438 :
439 : else
440 : return FALSE;
441 : #endif
442 :
443 210 : if (EQUAL(pszCap, OLCRandomRead))
444 0 : return TRUE;
445 :
446 : else
447 210 : return FALSE;
448 : }
449 :
450 : /************************************************************************/
451 : /* BuildFeatureDefn() */
452 : /* */
453 : /* (private) Initializes the schema in m_poFeatureDefn */
454 : /************************************************************************/
455 :
456 19 : void OGROGDILayer::BuildFeatureDefn()
457 : {
458 19 : const char *pszGeomName = nullptr;
459 19 : OGRwkbGeometryType eLayerGeomType = wkbUnknown;
460 :
461 : /* -------------------------------------------------------------------- */
462 : /* Feature Defn name will be "<OGDILyrName>_<FeatureFamily>" */
463 : /* -------------------------------------------------------------------- */
464 :
465 19 : switch (m_eFamily)
466 : {
467 3 : case Point:
468 3 : pszGeomName = "point";
469 3 : eLayerGeomType = wkbPoint;
470 3 : break;
471 3 : case Line:
472 3 : pszGeomName = "line";
473 3 : eLayerGeomType = wkbLineString;
474 3 : break;
475 7 : case Area:
476 7 : pszGeomName = "area";
477 7 : eLayerGeomType = wkbPolygon;
478 7 : break;
479 6 : case Text:
480 6 : pszGeomName = "text";
481 6 : eLayerGeomType = wkbPoint;
482 6 : break;
483 0 : default:
484 0 : pszGeomName = "unknown";
485 0 : eLayerGeomType = wkbUnknown;
486 0 : break;
487 : }
488 :
489 19 : char *pszFeatureDefnName = nullptr;
490 19 : if (m_poODS->LaunderLayerNames())
491 : {
492 12 : pszFeatureDefnName = CPLStrdup(m_pszOGDILayerName);
493 12 : char *pszAt = strchr(pszFeatureDefnName, '@');
494 12 : if (pszAt)
495 12 : *pszAt = '_';
496 12 : char *pszLeftParenthesis = strchr(pszFeatureDefnName, '(');
497 12 : if (pszLeftParenthesis)
498 12 : *pszLeftParenthesis = '\0';
499 : }
500 : else
501 : pszFeatureDefnName =
502 7 : CPLStrdup(CPLSPrintf("%s_%s", m_pszOGDILayerName, pszGeomName));
503 :
504 19 : m_poFeatureDefn = new OGRFeatureDefn(pszFeatureDefnName);
505 19 : SetDescription(m_poFeatureDefn->GetName());
506 19 : CPLFree(pszFeatureDefnName);
507 19 : pszFeatureDefnName = nullptr;
508 :
509 19 : m_poFeatureDefn->SetGeomType(eLayerGeomType);
510 19 : m_poFeatureDefn->Reference();
511 19 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSpatialRef);
512 :
513 : /* -------------------------------------------------------------------- */
514 : /* Fetch schema from OGDI server and map to OGR types */
515 : /* -------------------------------------------------------------------- */
516 19 : ecs_Result *psResult = cln_GetAttributesFormat(m_nClientID);
517 19 : if (ECSERROR(psResult))
518 : {
519 0 : CPLError(CE_Failure, CPLE_AppDefined, "ECSERROR: %s\n",
520 0 : psResult->message ? psResult->message : "(no message string)");
521 0 : return;
522 : }
523 :
524 19 : ecs_ObjAttributeFormat *oaf = &(ECSRESULT(psResult).oaf);
525 19 : const int numFields = oaf->oa.oa_len;
526 131 : for (int i = 0; i < numFields; i++)
527 : {
528 224 : OGRFieldDefn oField("", OFTInteger);
529 :
530 112 : oField.SetName(oaf->oa.oa_val[i].name);
531 112 : oField.SetPrecision(0);
532 :
533 112 : switch (oaf->oa.oa_val[i].type)
534 : {
535 77 : case Decimal:
536 : case Smallint:
537 : case Integer:
538 77 : oField.SetType(OFTInteger);
539 : // TODO: Fix spelling - lenght -> length
540 77 : if (oaf->oa.oa_val[i].lenght > 0)
541 77 : oField.SetWidth(oaf->oa.oa_val[i].lenght);
542 : else
543 0 : oField.SetWidth(11);
544 77 : break;
545 :
546 0 : case Numeric:
547 : case Real:
548 : case Float:
549 : case Double:
550 0 : oField.SetType(OFTReal);
551 0 : if (oaf->oa.oa_val[i].lenght > 0)
552 : {
553 0 : oField.SetWidth(oaf->oa.oa_val[i].lenght);
554 0 : oField.SetPrecision(oaf->oa.oa_val[i].precision);
555 : }
556 : else
557 : {
558 0 : oField.SetWidth(18);
559 0 : oField.SetPrecision(7);
560 : }
561 0 : break;
562 :
563 35 : case Char:
564 : case Varchar:
565 : case Longvarchar:
566 : default:
567 35 : oField.SetType(OFTString);
568 35 : if (oaf->oa.oa_val[i].lenght > 0)
569 19 : oField.SetWidth(oaf->oa.oa_val[i].lenght);
570 : else
571 16 : oField.SetWidth(64);
572 35 : break;
573 : }
574 :
575 112 : m_poFeatureDefn->AddFieldDefn(&oField);
576 : }
577 :
578 : /* -------------------------------------------------------------------- */
579 : /* Add a text attribute for text objects. */
580 : /* -------------------------------------------------------------------- */
581 19 : if (m_eFamily == Text)
582 : {
583 12 : OGRFieldDefn oField("text", OFTString);
584 :
585 6 : m_poFeatureDefn->AddFieldDefn(&oField);
586 : }
587 : }
|