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