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