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