1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Implements OGRGRASSLayer class.
5  * Author:   Radim Blazek, radim.blazek@gmail.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2005, Radim Blazek <radim.blazek@gmail.com>
9  * Copyright (c) 2008-2020, Even Rouault <even dot rouault at spatialys.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include <signal.h>
31 #include "ogrgrass.h"
32 #include "cpl_conv.h"
33 
34 CPL_CVSID("$Id: ogrgrasslayer.cpp a832da5b936bac8438f9c50e7c20e563fd94c9ff 2020-09-22 13:06:27 +0200 Markus Neteler $")
35 
36 /************************************************************************/
37 /*                           OGRGRASSLayer()                            */
38 /************************************************************************/
OGRGRASSLayer(int layerIndex,struct Map_info * map)39 OGRGRASSLayer::OGRGRASSLayer( int layerIndex,  struct Map_info * map )
40 {
41     CPLDebug ( "GRASS", "OGRGRASSLayer::OGRGRASSLayer layerIndex = %d", layerIndex );
42 
43     iLayerIndex = layerIndex;
44     poMap = map;
45     poSRS = NULL;
46     iNextId = 0;
47     poPoints = Vect_new_line_struct();
48     poCats = Vect_new_cats_struct();
49     pszQuery = NULL;
50     paQueryMatch = NULL;
51     paSpatialMatch = NULL;
52     iCurrentCat = 0;
53 
54     iLayer = Vect_cidx_get_field_number ( poMap, iLayerIndex);
55     CPLDebug ( "GRASS", "iLayer = %d", iLayer );
56 
57     poLink = Vect_get_field ( poMap, iLayer ); // May be NULL if not defined
58 
59     // Layer name
60     if ( poLink && poLink->name )
61     {
62         pszName = CPLStrdup( poLink->name );
63     }
64     else
65     {
66         char buf[20];
67         snprintf ( buf, sizeof(buf), "%d", iLayer );
68         pszName = CPLStrdup( buf );
69     }
70 
71     // Because we don't represent centroids as any simple feature, we have to scan
72     // category index and create index of feature IDs pointing to category index
73     nTotalCount = Vect_cidx_get_type_count(poMap,iLayer, GV_POINT|GV_LINES|GV_AREA);
74     CPLDebug ( "GRASS", "nTotalCount = %d", nTotalCount );
75     paFeatureIndex = (int *) CPLMalloc ( nTotalCount * sizeof(int) );
76 
77     int n = Vect_cidx_get_type_count(poMap,iLayer, GV_POINTS|GV_LINES|GV_AREA);
78     int cnt = 0;
79     for ( int i = 0; i < n; i++ )
80     {
81         int cat,type, id;
82 
83         Vect_cidx_get_cat_by_index ( poMap, iLayerIndex, i, &cat, &type, &id );
84 
85         if ( !( type & (GV_POINT|GV_LINES|GV_AREA) ) ) continue;
86         paFeatureIndex[cnt++] = i;
87     }
88 
89     poFeatureDefn = new OGRFeatureDefn( pszName );
90     SetDescription( poFeatureDefn->GetName() );
91     poFeatureDefn->Reference();
92 
93     // Get type definition
94     int nTypes = Vect_cidx_get_num_types_by_index ( poMap, iLayerIndex );
95     int types = 0;
96     for ( int i = 0; i < nTypes; i++ ) {
97         int type, count;
98         Vect_cidx_get_type_count_by_index ( poMap, iLayerIndex, i, &type, &count);
99         if ( !(type & (GV_POINT|GV_LINES|GV_AREA) ) ) continue;
100         types |= type;
101         CPLDebug ( "GRASS", "type = %d types = %d", type, types );
102     }
103 
104     OGRwkbGeometryType eGeomType = wkbUnknown;
105     if ( types == GV_LINE || types == GV_BOUNDARY || types == GV_LINES )
106     {
107         eGeomType = wkbLineString;
108     }
109     else if ( types == GV_POINT )
110     {
111         eGeomType = wkbPoint;
112     }
113     else if ( types == GV_AREA )
114     {
115         CPLDebug ( "GRASS", "set wkbPolygon" );
116         eGeomType = wkbPolygon;
117     }
118 
119     if (Vect_is_3d(poMap))
120         poFeatureDefn->SetGeomType ( wkbSetZ(eGeomType) );
121     else
122         poFeatureDefn->SetGeomType ( eGeomType );
123 
124     // Get attributes definition
125     poDbString = (dbString*) CPLMalloc ( sizeof(dbString) );
126     poCursor = (dbCursor*) CPLMalloc ( sizeof(dbCursor) );
127     bCursorOpened = FALSE;
128 
129     poDriver = NULL;
130     bHaveAttributes = false;
131     db_init_string ( poDbString );
132     if ( poLink )
133     {
134         if ( StartDbDriver() )
135         {
136             db_set_string ( poDbString, poLink->table );
137             dbTable *table = NULL;
138             if ( db_describe_table ( poDriver, poDbString, &table) == DB_OK )
139             {
140                 nFields = db_get_table_number_of_columns ( table );
141                 iCatField = -1;
142                 for ( int i = 0; i < nFields; i++)
143                 {
144                     dbColumn *column = db_get_table_column ( table, i );
145                     int ctype = db_sqltype_to_Ctype ( db_get_column_sqltype(column) );
146 
147                     OGRFieldType ogrFtype = OFTInteger;
148                     switch ( ctype ) {
149                          case DB_C_TYPE_INT:
150                             ogrFtype = OFTInteger;
151                             break;
152                          case DB_C_TYPE_DOUBLE:
153                             ogrFtype = OFTReal;
154                             break;
155                          case DB_C_TYPE_STRING:
156                             ogrFtype = OFTString;
157                             break;
158                          case DB_C_TYPE_DATETIME:
159                             ogrFtype = OFTDateTime;
160                             break;
161                     }
162 
163                     CPLDebug ( "GRASS", "column = %s type = %d",
164                                db_get_column_name(column), ctype );
165 
166                     OGRFieldDefn oField ( db_get_column_name(column), ogrFtype );
167                     poFeatureDefn->AddFieldDefn( &oField );
168 
169                     if ( G_strcasecmp(db_get_column_name(column),poLink->key) == 0 )
170                     {
171                         iCatField = i;
172                     }
173                 }
174                 if ( iCatField >= 0  )
175                 {
176                     bHaveAttributes = true;
177                 }
178                 else
179                 {
180                     CPLError( CE_Failure, CPLE_AppDefined, "Cannot find key field" );
181                     db_close_database_shutdown_driver ( poDriver );
182                     poDriver = NULL;
183                 }
184             }
185             else
186             {
187                 CPLError( CE_Failure, CPLE_AppDefined, "Cannot describe table %s",
188                           poLink->table );
189             }
190             db_close_database_shutdown_driver ( poDriver );
191             poDriver = NULL;
192         }
193     }
194 
195     if ( !bHaveAttributes && iLayer > 0 ) // Because features in layer 0 have no cats
196     {
197         OGRFieldDefn oField("cat", OFTInteger);
198         poFeatureDefn->AddFieldDefn( &oField );
199     }
200 
201     if ( getenv("GISBASE") )  // We have some projection info in GISBASE
202     {
203         struct Key_Value *projinfo, *projunits;
204 
205         // Note: we do not have to reset GISDBASE and LOCATION_NAME because
206         // OGRGRASSLayer constructor is called from OGRGRASSDataSource::Open
207         // where those variables are set
208 
209         projinfo = G_get_projinfo();
210         projunits = G_get_projunits();
211 
212         char *srsWkt = GPJ_grass_to_wkt ( projinfo, projunits, 0, 0);
213         if ( srsWkt )
214         {
215             poSRS = new OGRSpatialReference ( srsWkt );
216             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
217             G_free ( srsWkt );
218         }
219 
220         G_free_key_value(projinfo);
221         G_free_key_value(projunits);
222     }
223 }
224 
225 /************************************************************************/
226 /*                           ~OGRGRASSLayer()                           */
227 /************************************************************************/
~OGRGRASSLayer()228 OGRGRASSLayer::~OGRGRASSLayer()
229 {
230     if ( bCursorOpened )
231     {
232         db_close_cursor ( poCursor);
233     }
234 
235     if ( poDriver )
236     {
237         StopDbDriver();
238     }
239 
240     if ( pszName ) CPLFree ( pszName );
241     if ( poFeatureDefn )
242         poFeatureDefn->Release();
243     if ( poSRS )
244         poSRS->Release();
245 
246     if ( pszQuery ) CPLFree ( pszQuery );
247 
248     if ( paFeatureIndex ) CPLFree ( paFeatureIndex );
249 
250     if ( poLink ) G_free ( poLink );
251 
252     Vect_destroy_line_struct ( poPoints );
253     Vect_destroy_cats_struct ( poCats );
254 
255     db_free_string ( poDbString );
256     CPLFree ( poDbString );
257     CPLFree ( poCursor );
258 
259     if ( paSpatialMatch ) CPLFree ( paSpatialMatch );
260     if ( paQueryMatch ) CPLFree ( paQueryMatch );
261 }
262 
263 /************************************************************************/
264 /*                            StartDbDriver                             */
265 /************************************************************************/
StartDbDriver()266 bool OGRGRASSLayer::StartDbDriver()
267 {
268     CPLDebug ( "GRASS", "StartDbDriver()" );
269 
270     bCursorOpened = false;
271 
272     if ( !poLink )
273     {
274         return false;
275     }
276     poDriver = db_start_driver_open_database ( poLink->driver, poLink->database );
277 
278     if ( poDriver == NULL)
279     {
280         CPLError( CE_Failure, CPLE_AppDefined, "Cannot open database %s by driver %s, "
281                   "check if GISBASE environment variable is set, the driver is available "
282                   " and the database is accessible.", poLink->driver, poLink->database );
283         return false;
284     }
285     return true;
286 }
287 
288 /************************************************************************/
289 /*                            StopDbDriver                              */
290 /************************************************************************/
StopDbDriver()291 bool OGRGRASSLayer::StopDbDriver()
292 {
293     if ( !poDriver )
294     {
295         CPLError( CE_Failure, CPLE_AppDefined, "Driver is not started" );
296         return true; // I think that true is OK here
297     }
298 
299     // TODO!!!: Because of bug in GRASS library it is impossible
300     // to stop drivers in FIFO order. Until this is fixed
301     // we have to use kill
302     CPLDebug ( "GRASS", "driver PID = %d", poDriver->pid );
303 
304 #if defined(_WIN32) || defined(__WIN32__)
305     db_close_database_shutdown_driver ( poDriver );
306 #else
307     if ( kill (poDriver->pid, SIGINT) != 0 )
308     {
309         if ( kill (poDriver->pid, SIGKILL) != 0 )
310         {
311             CPLError( CE_Failure, CPLE_AppDefined, "Cannot stop database "
312                       "driver pid = %d", poDriver->pid );
313         }
314     }
315 #endif
316 
317     bCursorOpened = false;
318 
319     return true;
320 }
321 
322 /************************************************************************/
323 /*                            ResetReading()                            */
324 /************************************************************************/
ResetReading()325 void OGRGRASSLayer::ResetReading()
326 {
327     iNextId = 0;
328 
329     if ( bCursorOpened ) {
330         ResetSequentialCursor();
331     }
332 }
333 
334 /************************************************************************/
335 /*                           SetNextByIndex()                           */
336 /*                                                                      */
337 /*      If we already have an FID list, we can easily reposition        */
338 /*      ourselves in it.                                                */
339 /************************************************************************/
SetNextByIndex(GIntBig nIndex)340 OGRErr OGRGRASSLayer::SetNextByIndex( GIntBig nIndex )
341 {
342     if( m_poFilterGeom != NULL || m_poAttrQuery != NULL )
343     {
344         iNextId = 0;
345         int count = 0;
346 
347         while ( true ) {
348             if( iNextId >= nTotalCount ) break;
349             if ( count == nIndex ) break;
350 
351             // Attributes
352             if( pszQuery != NULL && !paQueryMatch[iNextId] ) {
353                 iNextId++;
354                 continue;
355             }
356 
357             // Spatial
358             if( m_poFilterGeom && !paSpatialMatch[iNextId] ) {
359                 iNextId++;
360                 continue;
361             }
362             count++;
363         }
364     }
365 
366     iNextId = nIndex;
367 
368     return OGRERR_NONE;
369 }
370 
371 /************************************************************************/
372 /*                           SetAttributeFilter                         */
373 /************************************************************************/
SetAttributeFilter(const char * query)374 OGRErr OGRGRASSLayer::SetAttributeFilter( const char *query )
375 {
376     CPLDebug ( "GRASS", "SetAttributeFilter: %s", query  );
377 
378     if ( query == NULL ) {
379         // Release old if any
380         if ( pszQuery ) {
381             CPLFree ( pszQuery );
382             pszQuery = NULL;
383         }
384         if ( paQueryMatch ) {
385             CPLFree ( paQueryMatch );
386             paQueryMatch = NULL;
387         }
388         return OGRERR_NONE;
389     }
390 
391     paQueryMatch = (char *) CPLMalloc ( nTotalCount );
392     memset ( paQueryMatch, 0x0, nTotalCount );
393     pszQuery = CPLStrdup(query);
394 
395     OGRLayer::SetAttributeFilter(query); // Otherwise crash on delete
396 
397     if ( bHaveAttributes ) {
398 
399         if ( !poDriver )
400         {
401             StartDbDriver();
402         }
403 
404         if ( poDriver )
405         {
406             if ( bCursorOpened )
407             {
408                 db_close_cursor ( poCursor );
409                 bCursorOpened = false;
410             }
411             OpenSequentialCursor();
412             if ( bCursorOpened )
413             {
414                 SetQueryMatch();
415                 db_close_cursor ( poCursor );
416                 bCursorOpened = false;
417             }
418             else
419             {
420                 CPLFree ( pszQuery );
421                 pszQuery = NULL;
422                 return OGRERR_FAILURE;
423             }
424             db_close_database_shutdown_driver ( poDriver );
425             poDriver = NULL;
426         }
427         else
428         {
429             CPLFree ( pszQuery );
430             pszQuery = NULL;
431             return OGRERR_FAILURE;
432         }
433     }
434     else
435     {
436         // Use OGR to evaluate category match
437         for ( int i = 0; i < nTotalCount; i++ )
438         {
439             OGRFeature *feature = GetFeature(i);
440             CPLDebug ( "GRASS", "i = %d eval = %d", i, m_poAttrQuery->Evaluate ( feature ) );
441             if ( m_poAttrQuery->Evaluate ( feature ) )
442             {
443                 paQueryMatch[i] = 1;
444             }
445         }
446     }
447 
448     return OGRERR_NONE;
449 }
450 
451 /************************************************************************/
452 /*                           SetQueryMatch                              */
453 /************************************************************************/
SetQueryMatch()454 bool OGRGRASSLayer::SetQueryMatch()
455 {
456     CPLDebug ( "GRASS", "SetQueryMatch" );
457 
458     // NOTE: we don't have to call ResetSequentialCursor() first because
459     // this method is called immediately after OpenSequentialCursor()
460 
461     if ( !bCursorOpened ) {
462         CPLError( CE_Failure, CPLE_AppDefined, "Cursor is not opened.");
463         return false;
464     }
465 
466     int more;
467     int cidx = 0; // index to category index
468     int fidx = 0; // index to feature index (paFeatureIndex)
469     // number of categories in category index
470     int ncats = Vect_cidx_get_num_cats_by_index ( poMap, iLayerIndex );
471     dbTable *table = db_get_cursor_table ( poCursor );
472     while ( true ) {
473         if( db_fetch ( poCursor, DB_NEXT, &more) != DB_OK )
474         {
475             CPLError( CE_Failure, CPLE_AppDefined, "Cannot fetch attributes.");
476             return false;
477         }
478         if ( !more ) break;
479 
480         dbColumn *column = db_get_table_column ( table, iCatField );
481         dbValue *value = db_get_column_value ( column );
482         int cat = db_get_value_int ( value );
483 
484         // NOTE: because of bug in GRASS library it is impossible to use
485         //       Vect_cidx_find_next
486 
487         // Go through category index until first record of current category
488         // is found or a category > current is found
489         int cidxcat, type, id;
490         while ( cidx < ncats ) {
491             Vect_cidx_get_cat_by_index ( poMap, iLayerIndex, cidx,
492                                          &cidxcat, &type, &id );
493 
494             if ( cidxcat < cat ) {
495                 cidx++;
496                 continue;
497             }
498             if ( cidxcat > cat ) break; // Not found
499 
500             // We have the category we want, check type
501             if ( !(type & (GV_POINT|GV_LINES|GV_AREA)) )
502             {
503                 cidx++;
504                 continue;
505             }
506 
507             // Both category and type match -> find feature and set it on
508             while ( true ) {
509                 if ( fidx > nTotalCount || paFeatureIndex[fidx] > cidx ) {
510                     // should not happen
511                     break;
512                 }
513 
514                 if ( paFeatureIndex[fidx] == cidx ) {
515                     paQueryMatch[fidx] = 1;
516                     fidx++;
517                     break;
518                 }
519                 fidx++;
520             }
521             cidx++;
522         }
523 
524         if ( id < 0 ) continue; // not found
525     }
526 
527     return true;
528 }
529 
530 /************************************************************************/
531 /*                           OpenSequentialCursor                       */
532 /************************************************************************/
OpenSequentialCursor()533 bool OGRGRASSLayer::OpenSequentialCursor()
534 {
535     CPLDebug ( "GRASS", "OpenSequentialCursor: %s", pszQuery  );
536 
537     if ( !poDriver )
538     {
539         CPLError( CE_Failure, CPLE_AppDefined, "Driver not opened.");
540         return false;
541     }
542 
543     if ( bCursorOpened )
544     {
545         db_close_cursor ( poCursor );
546         bCursorOpened = false;
547     }
548 
549     char buf[2000];
550     snprintf ( buf, sizeof(buf), "SELECT * FROM %s ", poLink->table );
551     db_set_string ( poDbString, buf);
552 
553     if ( pszQuery ) {
554         snprintf ( buf, sizeof(buf), "WHERE %s ", pszQuery );
555         db_append_string ( poDbString, buf);
556     }
557 
558     snprintf ( buf, sizeof(buf), "ORDER BY %s", poLink->key);
559     db_append_string ( poDbString, buf);
560 
561     CPLDebug ( "GRASS", "Query: %s", db_get_string(poDbString) );
562 
563     if ( db_open_select_cursor ( poDriver, poDbString,
564                 poCursor, DB_SCROLL) == DB_OK )
565     {
566         iCurrentCat = -1;
567         bCursorOpened = true;
568         CPLDebug ( "GRASS", "num rows = %d", db_get_num_rows ( poCursor ) );
569     }
570     else
571     {
572         CPLError( CE_Failure, CPLE_AppDefined, "Cannot open cursor.");
573         return false;
574     }
575     return true;
576 }
577 
578 /************************************************************************/
579 /*                           ResetSequentialCursor                      */
580 /************************************************************************/
ResetSequentialCursor()581 bool OGRGRASSLayer::ResetSequentialCursor()
582 {
583     CPLDebug ( "GRASS", "ResetSequentialCursor" );
584 
585     int more;
586     if( db_fetch ( poCursor, DB_FIRST, &more) != DB_OK )
587     {
588         CPLError( CE_Failure, CPLE_AppDefined, "Cannot reset cursor.");
589         return false;
590     }
591     if( db_fetch ( poCursor, DB_PREVIOUS, &more) != DB_OK )
592     {
593         CPLError( CE_Failure, CPLE_AppDefined, "Cannot reset cursor.");
594         return false;
595     }
596     return true;
597 }
598 
599 /************************************************************************/
600 /*                           SetSpatialFilter                           */
601 /************************************************************************/
SetSpatialFilter(OGRGeometry * poGeomIn)602 void OGRGRASSLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
603 {
604     CPLDebug ( "GRASS", "SetSpatialFilter" );
605 
606     OGRLayer::SetSpatialFilter ( poGeomIn );
607 
608     if ( poGeomIn == NULL ) {
609         // Release old if any
610         if ( paSpatialMatch ) {
611             CPLFree ( paSpatialMatch );
612             paSpatialMatch = NULL;
613         }
614         return;
615     }
616 
617     SetSpatialMatch();
618 }
619 
620 /************************************************************************/
621 /*                           SetSpatialMatch                            */
622 /************************************************************************/
SetSpatialMatch()623 bool OGRGRASSLayer::SetSpatialMatch()
624 {
625     CPLDebug ( "GRASS", "SetSpatialMatch" );
626 
627     if ( !paSpatialMatch )
628     {
629         paSpatialMatch = (char *) CPLMalloc ( nTotalCount );
630     }
631     memset ( paSpatialMatch, 0x0, nTotalCount );
632 
633     OGRLineString *lstring = new OGRLineString();
634     lstring->setNumPoints ( 5 );
635     OGRGeometry *geom = lstring;
636 
637     for ( int i = 0; i < nTotalCount; i++ ) {
638         int cidx = paFeatureIndex[i];
639 
640         int cat, type, id;
641 
642         Vect_cidx_get_cat_by_index ( poMap, iLayerIndex, cidx, &cat, &type, &id );
643 
644         struct bound_box box;
645 
646         switch ( type )
647         {
648             case GV_POINT:
649             case GV_LINE:
650             case GV_BOUNDARY:
651                 Vect_get_line_box ( poMap, id, &box );
652                 break;
653 
654             case GV_AREA:
655                 Vect_get_area_box ( poMap, id, &box );
656                 break;
657         }
658 
659         lstring->setPoint( 0, box.W, box.N, 0. );
660         lstring->setPoint( 1, box.W, box.S, 0. );
661         lstring->setPoint( 2, box.E, box.S, 0. );
662         lstring->setPoint( 3, box.E, box.N, 0. );
663         lstring->setPoint( 4, box.W, box.N, 0. );
664 
665         if ( FilterGeometry(geom) ) {
666             CPLDebug ( "GRASS", "Feature %d in filter", i );
667             paSpatialMatch[i] = 1;
668         }
669     }
670     delete lstring;
671     return true;
672 }
673 
674 /************************************************************************/
675 /*                           GetNextFeature()                           */
676 /************************************************************************/
GetNextFeature()677 OGRFeature *OGRGRASSLayer::GetNextFeature()
678 {
679     CPLDebug ( "GRASS", "OGRGRASSLayer::GetNextFeature" );
680     OGRFeature  *poFeature = NULL;
681 
682     int cat;
683 
684     // Get next iNextId
685     while ( true ) {
686         if( iNextId >= nTotalCount ) // No more features
687         {
688             // Close cursor / driver if opened
689             if ( bCursorOpened )
690             {
691                 db_close_cursor ( poCursor);
692                 bCursorOpened = false;
693             }
694             if ( poDriver )
695             {
696                 db_close_database_shutdown_driver ( poDriver );
697                 poDriver = NULL;
698             }
699 
700             return NULL;
701         }
702 
703         // Attributes
704         if( pszQuery != NULL && !paQueryMatch[iNextId] ) {
705             iNextId++;
706             continue;
707         }
708 
709         // Spatial
710         if( m_poFilterGeom && !paSpatialMatch[iNextId] ) {
711             iNextId++;
712             continue;
713         }
714 
715         break; // Attributes & spatial filter match
716     }
717 
718     OGRGeometry *poOGR = GetFeatureGeometry ( iNextId, &cat );
719 
720     poFeature = new OGRFeature( poFeatureDefn );
721     poFeature->SetGeometryDirectly( poOGR );
722     poFeature->SetFID ( iNextId );
723     iNextId++;
724 
725     // Get attributes
726     CPLDebug ( "GRASS", "bHaveAttributes = %d", bHaveAttributes );
727     if ( bHaveAttributes )
728     {
729         if ( !poDriver )
730         {
731             StartDbDriver();
732         }
733         if ( poDriver ) {
734             if ( !bCursorOpened )
735             {
736                 OpenSequentialCursor();
737             }
738             if ( bCursorOpened )
739             {
740                 dbTable  *table = db_get_cursor_table ( poCursor );
741                 if ( iCurrentCat < cat )
742                 {
743                     while ( true ) {
744                         int more;
745                         if( db_fetch ( poCursor, DB_NEXT, &more) != DB_OK )
746                         {
747                             CPLError( CE_Failure, CPLE_AppDefined,
748                                       "Cannot fetch attributes.");
749                             break;
750                         }
751                         if ( !more ) break;
752 
753                         dbColumn *column = db_get_table_column ( table, iCatField );
754                         dbValue *value = db_get_column_value ( column );
755                         iCurrentCat = db_get_value_int ( value );
756 
757                         if ( iCurrentCat >= cat ) break;
758                     }
759                 }
760                 if ( cat == iCurrentCat )
761                 {
762                     SetAttributes ( poFeature, table );
763                 }
764                 else
765                 {
766                     CPLError( CE_Failure, CPLE_AppDefined, "Attributes not found.");
767                 }
768             }
769         }
770     }
771     else if ( iLayer > 0 ) // Add category
772     {
773         poFeature->SetField( 0, cat );
774     }
775 
776     m_nFeaturesRead++;
777     return poFeature;
778 }
779 /************************************************************************/
780 /*                             GetFeature()                             */
781 /************************************************************************/
GetFeature(GIntBig nFeatureId)782 OGRFeature *OGRGRASSLayer::GetFeature( GIntBig nFeatureId )
783 
784 {
785     CPLDebug ( "GRASS", "OGRGRASSLayer::GetFeature nFeatureId = " CPL_FRMT_GIB,
786                nFeatureId );
787 
788     int cat;
789     OGRGeometry *poOGR = GetFeatureGeometry ( nFeatureId, &cat );
790 
791     OGRFeature* poFeature = new OGRFeature( poFeatureDefn );
792     poFeature->SetGeometryDirectly( poOGR );
793     poFeature->SetFID ( nFeatureId );
794 
795     // Get attributes
796     if ( bHaveAttributes && !poDriver )
797     {
798         StartDbDriver();
799     }
800     if ( poDriver )
801     {
802         if ( bCursorOpened )
803         {
804             db_close_cursor ( poCursor);
805             bCursorOpened = false;
806         }
807         CPLDebug ( "GRASS", "Open cursor for key = %d", cat );
808         char buf[2000];
809         snprintf ( buf, sizeof(buf), "SELECT * FROM %s WHERE %s = %d",
810                        poLink->table, poLink->key, cat );
811         db_set_string ( poDbString, buf);
812         if ( db_open_select_cursor ( poDriver, poDbString,
813                     poCursor, DB_SEQUENTIAL) == DB_OK )
814         {
815             iCurrentCat = cat; // Not important
816             bCursorOpened = true;
817         }
818         else
819         {
820             CPLError( CE_Failure, CPLE_AppDefined, "Cannot open cursor.");
821         }
822 
823         if ( bCursorOpened )
824         {
825             int more;
826             if( db_fetch ( poCursor, DB_NEXT, &more) != DB_OK )
827             {
828                 CPLError( CE_Failure, CPLE_AppDefined, "Cannot fetch attributes.");
829             }
830             else
831             {
832                 if ( !more )
833                 {
834                     CPLError( CE_Failure, CPLE_AppDefined, "Attributes not found.");
835                 }
836                 else
837                 {
838                     dbTable *table = db_get_cursor_table ( poCursor );
839                     SetAttributes ( poFeature, table );
840                 }
841             }
842             db_close_cursor ( poCursor);
843             bCursorOpened = false;
844         }
845     }
846     else if ( iLayer > 0 ) // Add category
847     {
848         poFeature->SetField( 0, cat );
849     }
850 
851     m_nFeaturesRead++;
852     return poFeature;
853 }
854 
855 /************************************************************************/
856 /*                             GetFeatureGeometry()                     */
857 /************************************************************************/
GetFeatureGeometry(long nFeatureId,int * cat)858 OGRGeometry *OGRGRASSLayer::GetFeatureGeometry ( long nFeatureId, int *cat )
859 {
860     CPLDebug ( "GRASS", "OGRGRASSLayer::GetFeatureGeometry nFeatureId = %ld", nFeatureId );
861 
862     int cidx = paFeatureIndex[(int)nFeatureId];
863 
864     int type, id;
865     Vect_cidx_get_cat_by_index ( poMap, iLayerIndex, cidx, cat, &type, &id );
866 
867     //CPLDebug ( "GRASS", "cat = %d type = %d id = %d", *cat, type, id );
868 
869     OGRGeometry *poOGR = NULL;
870     int bIs3D = Vect_is_3d(poMap);
871 
872     switch ( type ) {
873         case GV_POINT:
874         {
875             Vect_read_line ( poMap, poPoints, poCats, id);
876             if (bIs3D)
877                 poOGR = new OGRPoint( poPoints->x[0], poPoints->y[0], poPoints->z[0] );
878             else
879                 poOGR = new OGRPoint( poPoints->x[0], poPoints->y[0] );
880         }
881         break;
882 
883         case GV_LINE:
884         case GV_BOUNDARY:
885         {
886             Vect_read_line ( poMap, poPoints, poCats, id);
887             OGRLineString *poOGRLine = new OGRLineString();
888             if (bIs3D)
889                 poOGRLine->setPoints( poPoints->n_points,
890                                       poPoints->x, poPoints->y, poPoints->z );
891             else
892                 poOGRLine->setPoints( poPoints->n_points,
893                                       poPoints->x, poPoints->y );
894 
895             poOGR = poOGRLine;
896         }
897         break;
898 
899         case GV_AREA:
900         {
901             Vect_get_area_points ( poMap, id, poPoints );
902 
903             OGRPolygon *poOGRPoly = new OGRPolygon();
904 
905             OGRLinearRing *poRing = new OGRLinearRing();
906             if (bIs3D)
907                 poRing->setPoints( poPoints->n_points,
908                                 poPoints->x, poPoints->y, poPoints->z );
909             else
910                 poRing->setPoints( poPoints->n_points,
911                                 poPoints->x, poPoints->y );
912 
913             poOGRPoly->addRingDirectly( poRing );
914 
915             // Islands
916             int nisles = Vect_get_area_num_isles ( poMap, id );
917             for ( int i = 0; i < nisles; i++ ) {
918                 int isle =  Vect_get_area_isle ( poMap, id, i );
919                 Vect_get_isle_points ( poMap, isle, poPoints );
920 
921                 poRing = new OGRLinearRing();
922                 if (bIs3D)
923                     poRing->setPoints( poPoints->n_points,
924                                     poPoints->x, poPoints->y, poPoints->z );
925                 else
926                     poRing->setPoints( poPoints->n_points,
927                                     poPoints->x, poPoints->y );
928 
929                 poOGRPoly->addRingDirectly( poRing );
930             }
931 
932             poOGR = poOGRPoly;
933         }
934         break;
935 
936         default: // Should not happen
937         {
938             CPLError( CE_Failure, CPLE_AppDefined, "Unknown GRASS feature type.");
939             return NULL;
940         }
941     }
942 
943     return poOGR;
944 }
945 
946 /************************************************************************/
947 /*                          SetAttributes()                             */
948 /************************************************************************/
SetAttributes(OGRFeature * poFeature,dbTable * table)949 bool OGRGRASSLayer::SetAttributes ( OGRFeature *poFeature, dbTable *table )
950 {
951     CPLDebug ( "GRASS", "OGRGRASSLayer::SetAttributes" );
952 
953     for ( int i = 0; i < nFields; i++)
954     {
955         dbColumn *column = db_get_table_column ( table, i );
956         dbValue *value = db_get_column_value ( column );
957 
958         int ctype = db_sqltype_to_Ctype ( db_get_column_sqltype(column) );
959 
960         if ( !db_test_value_isnull(value) )
961         {
962             switch ( ctype ) {
963                 case DB_C_TYPE_INT:
964                     poFeature->SetField( i, db_get_value_int ( value ));
965                     break;
966                 case DB_C_TYPE_DOUBLE:
967                     poFeature->SetField( i, db_get_value_double ( value ));
968                     break;
969                 case DB_C_TYPE_STRING:
970                     poFeature->SetField( i, db_get_value_string ( value ));
971                     break;
972                 case DB_C_TYPE_DATETIME:
973                     db_convert_column_value_to_string ( column, poDbString );
974                     poFeature->SetField( i, db_get_string ( poDbString ));
975                     break;
976             }
977         }
978 
979         db_convert_column_value_to_string ( column, poDbString );
980         // CPLDebug ( "GRASS", "val = %s", db_get_string ( poDbString ));
981     }
982     return true;
983 }
984 
985 /************************************************************************/
986 /*                          GetFeatureCount()                           */
987 /*                                                                      */
988 /*      If a spatial filter is in effect, we turn control over to       */
989 /*      the generic counter.  Otherwise we return the total count.      */
990 /*      Eventually we should consider implementing a more efficient     */
991 /*      way of counting features matching a spatial query.              */
992 /************************************************************************/
GetFeatureCount(int bForce)993 GIntBig OGRGRASSLayer::GetFeatureCount( int bForce )
994 {
995     if( m_poFilterGeom != NULL || m_poAttrQuery != NULL )
996         return OGRLayer::GetFeatureCount( bForce );
997 
998     return nTotalCount;
999 }
1000 
1001 /************************************************************************/
1002 /*                             GetExtent()                              */
1003 /*                                                                      */
1004 /*      Fetch extent of the data currently stored in the dataset.       */
1005 /*      The bForce flag has no effect on SHO files since that value     */
1006 /*      is always in the header.                                        */
1007 /*                                                                      */
1008 /*      Returns OGRERR_NONE/OGRRERR_FAILURE.                            */
1009 /************************************************************************/
GetExtent(OGREnvelope * psExtent,int)1010 OGRErr OGRGRASSLayer::GetExtent (OGREnvelope *psExtent, int /*bForce*/)
1011 {
1012     struct bound_box box;
1013 
1014     Vect_get_map_box ( poMap, &box );
1015 
1016     psExtent->MinX = box.W;
1017     psExtent->MinY = box.S;
1018     psExtent->MaxX = box.E;
1019     psExtent->MaxY = box.N;
1020 
1021     return OGRERR_NONE;
1022 }
1023 
1024 /************************************************************************/
1025 /*                           TestCapability()                           */
1026 /************************************************************************/
TestCapability(const char * pszCap)1027 int OGRGRASSLayer::TestCapability( const char * pszCap )
1028 {
1029     if( EQUAL(pszCap,OLCRandomRead) )
1030         return TRUE;
1031 
1032     else if( EQUAL(pszCap,OLCFastFeatureCount) )
1033         return TRUE;
1034 
1035     else if( EQUAL(pszCap,OLCFastSpatialFilter) )
1036         return FALSE;
1037 
1038     else if( EQUAL(pszCap,OLCFastGetExtent) )
1039         return TRUE;
1040 
1041     else if( EQUAL(pszCap,OLCFastSetNextByIndex) )
1042         return TRUE;
1043 
1044     else
1045         return FALSE;
1046 }
1047 
1048 /************************************************************************/
1049 /*                           GetSpatialRef()                            */
1050 /************************************************************************/
GetSpatialRef()1051 OGRSpatialReference *OGRGRASSLayer::GetSpatialRef()
1052 {
1053     return poSRS;
1054 }
1055