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