1 /******************************************************************************
2  * $Id: ogrgrasslayer.cpp 28831 2015-04-01 16:46:05Z rouault $
3  *
4  * Project:  OpenGIS Simple Features Reference Implementation
5  * Purpose:  Implements OGRGRASSLayer class.
6  * Author:   Radim Blazek, radim.blazek@gmail.com
7  *
8  ******************************************************************************
9  * Copyright (c) 2005, Radim Blazek <radim.blazek@gmail.com>
10  * Copyright (c) 2008-2010, Even Rouault <even dot rouault at mines-paris dot org>
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 #include <signal.h>
32 #include "ogrgrass.h"
33 #include "cpl_conv.h"
34 
35 CPL_CVSID("$Id: ogrgrasslayer.cpp 28831 2015-04-01 16:46:05Z rouault $");
36 
37 /************************************************************************/
38 /*                           OGRGRASSLayer()                            */
39 /************************************************************************/
OGRGRASSLayer(int layerIndex,struct Map_info * map)40 OGRGRASSLayer::OGRGRASSLayer( int layerIndex,  struct Map_info * map )
41 {
42     CPLDebug ( "GRASS", "OGRGRASSLayer::OGRGRASSLayer layerIndex = %d", layerIndex );
43 
44     iLayerIndex = layerIndex;
45     poMap = map;
46     poSRS = NULL;
47     iNextId = 0;
48     poPoints = Vect_new_line_struct();
49     poCats = Vect_new_cats_struct();
50     pszQuery = NULL;
51     paQueryMatch = NULL;
52     paSpatialMatch = NULL;
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 	sprintf ( 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;
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 	    }
191 	    db_close_database_shutdown_driver ( poDriver );
192 	    poDriver = NULL;
193 	}
194     }
195 
196     if ( !bHaveAttributes && iLayer > 0 ) // Because features in layer 0 have no cats
197     {
198 	OGRFieldDefn oField("cat", OFTInteger);
199 	poFeatureDefn->AddFieldDefn( &oField );
200     }
201 
202     if ( getenv("GISBASE") )  // We have some projection info in GISBASE
203     {
204         struct Key_Value *projinfo, *projunits;
205 
206 	// Note: we dont have to reset GISDBASE and LOCATION_NAME because
207 	// OGRGRASSLayer constructor is called from OGRGRASSDataSource::Open
208 	// where those variables are set
209 
210         projinfo = G_get_projinfo();
211 	projunits = G_get_projunits();
212 
213 	char *srsWkt = GPJ_grass_to_wkt ( projinfo, projunits, 0, 0);
214 	if ( srsWkt )
215 	{
216 	    poSRS = new OGRSpatialReference ( srsWkt );
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 resposition       */
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     sprintf ( buf, "SELECT * FROM %s ", poLink->table );
551     db_set_string ( poDbString, buf);
552 
553     if ( pszQuery ) {
554 	sprintf ( buf, "WHERE %s ", pszQuery );
555 	db_append_string ( poDbString, buf);
556     }
557 
558     sprintf ( 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     OGRGeometry *geom;
634     OGRLineString *lstring = new OGRLineString();
635     lstring->setNumPoints ( 5 );
636     geom = lstring;
637 
638     for ( int i = 0; i < nTotalCount; i++ ) {
639 	int cidx = paFeatureIndex[i];
640 
641 	int cat, type, id;
642 
643 	Vect_cidx_get_cat_by_index ( poMap, iLayerIndex, cidx, &cat, &type, &id );
644 
645 #if GRASS_VERSION_MAJOR  >= 7
646     struct bound_box box;
647 #else
648 	BOUND_BOX box;
649 #endif
650 
651 	switch ( type )
652 	{
653 	    case GV_POINT:
654 	    case GV_LINE:
655 	    case GV_BOUNDARY:
656 		Vect_get_line_box ( poMap, id, &box );
657 		break;
658 
659 	    case GV_AREA:
660 		Vect_get_area_box ( poMap, id, &box );
661 		break;
662 	}
663 
664 	lstring->setPoint( 0, box.W, box.N, 0. );
665 	lstring->setPoint( 1, box.W, box.S, 0. );
666 	lstring->setPoint( 2, box.E, box.S, 0. );
667 	lstring->setPoint( 3, box.E, box.N, 0. );
668 	lstring->setPoint( 4, box.W, box.N, 0. );
669 
670 	if ( FilterGeometry(geom) ) {
671     	    CPLDebug ( "GRASS", "Feature %d in filter", i );
672 	    paSpatialMatch[i] = 1;
673 	}
674     }
675     delete lstring;
676     return true;
677 }
678 
679 /************************************************************************/
680 /*                           GetNextFeature()                           */
681 /************************************************************************/
GetNextFeature()682 OGRFeature *OGRGRASSLayer::GetNextFeature()
683 {
684     CPLDebug ( "GRASS", "OGRGRASSLayer::GetNextFeature" );
685     OGRFeature  *poFeature = NULL;
686 
687     int cat;
688 
689     // Get next iNextId
690     while ( true ) {
691 	if( iNextId >= nTotalCount ) // No more features
692 	{
693 	    // Close cursor / driver if opened
694 	    if ( bCursorOpened )
695 	    {
696 	    	db_close_cursor ( poCursor);
697 	    	bCursorOpened = false;
698 	    }
699 	    if ( poDriver )
700 	    {
701     	    	db_close_database_shutdown_driver ( poDriver );
702 		poDriver = NULL;
703 	    }
704 
705 	    return NULL;
706 	}
707 
708 	// Attributes
709 	if( pszQuery != NULL && !paQueryMatch[iNextId] ) {
710 	    iNextId++;
711 	    continue;
712 	}
713 
714 	// Spatial
715 	if( m_poFilterGeom && !paSpatialMatch[iNextId] ) {
716 	    iNextId++;
717 	    continue;
718 	}
719 
720 	break; // Attributes & spatial filter match
721     }
722 
723     OGRGeometry *poOGR = GetFeatureGeometry ( iNextId, &cat );
724 
725     poFeature = new OGRFeature( poFeatureDefn );
726     poFeature->SetGeometryDirectly( poOGR );
727     poFeature->SetFID ( iNextId );
728     iNextId++;
729 
730     // Get attributes
731     CPLDebug ( "GRASS", "bHaveAttributes = %d", bHaveAttributes );
732     if ( bHaveAttributes )
733     {
734 	if ( !poDriver )
735 	{
736 	    StartDbDriver();
737 	}
738 	if ( poDriver ) {
739 	    if ( !bCursorOpened )
740 	    {
741 		OpenSequentialCursor();
742 	    }
743 	    if ( bCursorOpened )
744 	    {
745 		dbTable  *table = db_get_cursor_table ( poCursor );
746 		if ( iCurrentCat < cat )
747 		{
748 		    while ( true ) {
749 			int more;
750 			if( db_fetch ( poCursor, DB_NEXT, &more) != DB_OK )
751 			{
752 			    CPLError( CE_Failure, CPLE_AppDefined,
753 				      "Cannot fetch attributes.");
754 			    break;
755 			}
756 			if ( !more ) break;
757 
758 			dbColumn *column = db_get_table_column ( table, iCatField );
759 			dbValue *value = db_get_column_value ( column );
760 			iCurrentCat = db_get_value_int ( value );
761 
762 			if ( iCurrentCat >= cat ) break;
763 		    }
764 		}
765 		if ( cat == iCurrentCat )
766 		{
767 		    SetAttributes ( poFeature, table );
768 		}
769 		else
770 		{
771 		    CPLError( CE_Failure, CPLE_AppDefined, "Attributes not found.");
772 		}
773 	    }
774 	}
775     }
776     else if ( iLayer > 0 ) // Add category
777     {
778 	poFeature->SetField( 0, cat );
779     }
780 
781     m_nFeaturesRead++;
782     return poFeature;
783 }
784 /************************************************************************/
785 /*                             GetFeature()                             */
786 /************************************************************************/
GetFeature(GIntBig nFeatureId)787 OGRFeature *OGRGRASSLayer::GetFeature( GIntBig nFeatureId )
788 
789 {
790     CPLDebug ( "GRASS", "OGRGRASSLayer::GetFeature nFeatureId = %ld", nFeatureId );
791 
792     int cat;
793     OGRFeature *poFeature = NULL;
794 
795     OGRGeometry *poOGR = GetFeatureGeometry ( nFeatureId, &cat );
796 
797     poFeature = new OGRFeature( poFeatureDefn );
798     poFeature->SetGeometryDirectly( poOGR );
799     poFeature->SetFID ( nFeatureId );
800 
801     // Get attributes
802     if ( bHaveAttributes && !poDriver )
803     {
804 	StartDbDriver();
805     }
806     if ( poDriver )
807     {
808 	if ( bCursorOpened )
809 	{
810 	    db_close_cursor ( poCursor);
811 	    bCursorOpened = false;
812 	}
813 	CPLDebug ( "GRASS", "Open cursor for key = %d", cat );
814 	char buf[2000];
815 	sprintf ( buf, "SELECT * FROM %s WHERE %s = %d",
816 		       poLink->table, poLink->key, cat );
817 	db_set_string ( poDbString, buf);
818 	if ( db_open_select_cursor ( poDriver, poDbString,
819 		    poCursor, DB_SEQUENTIAL) == DB_OK )
820 	{
821 	    iCurrentCat = cat; // Not important
822 	    bCursorOpened = true;
823 	}
824 	else
825 	{
826 	    CPLError( CE_Failure, CPLE_AppDefined, "Cannot open cursor.");
827 	}
828 
829 	if ( bCursorOpened )
830 	{
831 	    int more;
832 	    if( db_fetch ( poCursor, DB_NEXT, &more) != DB_OK )
833 	    {
834 		CPLError( CE_Failure, CPLE_AppDefined, "Cannot fetch attributes.");
835 	    }
836 	    else
837 	    {
838 		if ( !more )
839 		{
840 		    CPLError( CE_Failure, CPLE_AppDefined, "Attributes not found.");
841 		}
842 		else
843 		{
844 	    	    dbTable *table = db_get_cursor_table ( poCursor );
845 		    SetAttributes ( poFeature, table );
846 		}
847 	    }
848 	    db_close_cursor ( poCursor);
849 	    bCursorOpened = false;
850 	}
851     }
852     else if ( iLayer > 0 ) // Add category
853     {
854 	poFeature->SetField( 0, cat );
855     }
856 
857     m_nFeaturesRead++;
858     return poFeature;
859 }
860 
861 /************************************************************************/
862 /*                             GetFeatureGeometry()                     */
863 /************************************************************************/
GetFeatureGeometry(long nFeatureId,int * cat)864 OGRGeometry *OGRGRASSLayer::GetFeatureGeometry ( long nFeatureId, int *cat )
865 {
866     CPLDebug ( "GRASS", "OGRGRASSLayer::GetFeatureGeometry nFeatureId = %ld", nFeatureId );
867 
868     int cidx = paFeatureIndex[(int)nFeatureId];
869 
870     int type, id;
871     Vect_cidx_get_cat_by_index ( poMap, iLayerIndex, cidx, cat, &type, &id );
872 
873     //CPLDebug ( "GRASS", "cat = %d type = %d id = %d", *cat, type, id );
874 
875     OGRGeometry *poOGR = NULL;
876     int bIs3D = Vect_is_3d(poMap);
877 
878     switch ( type ) {
879 	case GV_POINT:
880         {
881 	    Vect_read_line ( poMap, poPoints, poCats, id);
882             if (bIs3D)
883                 poOGR = new OGRPoint( poPoints->x[0], poPoints->y[0], poPoints->z[0] );
884             else
885                 poOGR = new OGRPoint( poPoints->x[0], poPoints->y[0] );
886         }
887         break;
888 
889 	case GV_LINE:
890 	case GV_BOUNDARY:
891         {
892 	    Vect_read_line ( poMap, poPoints, poCats, id);
893 	    OGRLineString *poOGRLine = new OGRLineString();
894             if (bIs3D)
895                 poOGRLine->setPoints( poPoints->n_points,
896                                       poPoints->x, poPoints->y, poPoints->z );
897             else
898                 poOGRLine->setPoints( poPoints->n_points,
899                                       poPoints->x, poPoints->y );
900 
901             poOGR = poOGRLine;
902         }
903         break;
904 
905 	case GV_AREA:
906         {
907 	    Vect_get_area_points ( poMap, id, poPoints );
908 
909 	    OGRPolygon 		*poOGRPoly;
910 	    poOGRPoly = new OGRPolygon();
911 
912 	    OGRLinearRing       *poRing;
913 	    poRing = new OGRLinearRing();
914             if (bIs3D)
915                 poRing->setPoints( poPoints->n_points,
916                                 poPoints->x, poPoints->y, poPoints->z );
917             else
918                 poRing->setPoints( poPoints->n_points,
919                                 poPoints->x, poPoints->y );
920 
921 	    poOGRPoly->addRingDirectly( poRing );
922 
923 	    // Islands
924 	    int nisles = Vect_get_area_num_isles ( poMap, id );
925 	    for ( int i = 0; i < nisles; i++ ) {
926 		int isle =  Vect_get_area_isle ( poMap, id, i );
927 		Vect_get_isle_points ( poMap, isle, poPoints );
928 
929 		poRing = new OGRLinearRing();
930                 if (bIs3D)
931                     poRing->setPoints( poPoints->n_points,
932                                     poPoints->x, poPoints->y, poPoints->z );
933                 else
934                     poRing->setPoints( poPoints->n_points,
935                                     poPoints->x, poPoints->y );
936 
937 		poOGRPoly->addRingDirectly( poRing );
938 	    }
939 
940 	    poOGR = poOGRPoly;
941         }
942         break;
943 
944 	default: // Should not happen
945         {
946 	    CPLError( CE_Failure, CPLE_AppDefined, "Unknown GRASS feature type.");
947 	    return NULL;
948         }
949     }
950 
951     return poOGR;
952 }
953 
954 /************************************************************************/
955 /*                          SetAttributes()                             */
956 /************************************************************************/
SetAttributes(OGRFeature * poFeature,dbTable * table)957 bool OGRGRASSLayer::SetAttributes ( OGRFeature *poFeature, dbTable *table )
958 {
959     CPLDebug ( "GRASS", "OGRGRASSLayer::SetAttributes" );
960 
961     for ( int i = 0; i < nFields; i++)
962     {
963 	dbColumn *column = db_get_table_column ( table, i );
964 	dbValue *value = db_get_column_value ( column );
965 
966 	int ctype = db_sqltype_to_Ctype ( db_get_column_sqltype(column) );
967 
968 	if ( !db_test_value_isnull(value) )
969 	{
970 	    switch ( ctype ) {
971 		case DB_C_TYPE_INT:
972 		    poFeature->SetField( i, db_get_value_int ( value ));
973 		    break;
974 		case DB_C_TYPE_DOUBLE:
975 		    poFeature->SetField( i, db_get_value_double ( value ));
976 		    break;
977 		case DB_C_TYPE_STRING:
978 		    poFeature->SetField( i, db_get_value_string ( value ));
979 		    break;
980 		case DB_C_TYPE_DATETIME:
981 		    db_convert_column_value_to_string ( column, poDbString );
982 		    poFeature->SetField( i, db_get_string ( poDbString ));
983 		    break;
984 	    }
985 	}
986 
987 	db_convert_column_value_to_string ( column, poDbString );
988 	//CPLDebug ( "GRASS", "val = %s", db_get_string ( poDbString ));
989     }
990     return true;
991 }
992 
993 /************************************************************************/
994 /*                             ISetFeature()                             */
995 /************************************************************************/
ISetFeature(OGRFeature * poFeature)996 OGRErr OGRGRASSLayer::ISetFeature( OGRFeature *poFeature )
997 {
998     return OGRERR_FAILURE;
999 }
1000 
1001 /************************************************************************/
1002 /*                           ICreateFeature()                            */
1003 /************************************************************************/
ICreateFeature(OGRFeature * poFeature)1004 OGRErr OGRGRASSLayer::ICreateFeature( OGRFeature *poFeature )
1005 {
1006     return OGRERR_FAILURE;
1007 }
1008 
1009 /************************************************************************/
1010 /*                          GetFeatureCount()                           */
1011 /*                                                                      */
1012 /*      If a spatial filter is in effect, we turn control over to       */
1013 /*      the generic counter.  Otherwise we return the total count.      */
1014 /*      Eventually we should consider implementing a more efficient     */
1015 /*      way of counting features matching a spatial query.              */
1016 /************************************************************************/
GetFeatureCount(int bForce)1017 GIntBig OGRGRASSLayer::GetFeatureCount( int bForce )
1018 {
1019     if( m_poFilterGeom != NULL || m_poAttrQuery != NULL )
1020         return OGRLayer::GetFeatureCount( bForce );
1021 
1022     return nTotalCount;
1023 }
1024 
1025 /************************************************************************/
1026 /*                             GetExtent()                              */
1027 /*                                                                      */
1028 /*      Fetch extent of the data currently stored in the dataset.       */
1029 /*      The bForce flag has no effect on SHO files since that value     */
1030 /*      is always in the header.                                        */
1031 /*                                                                      */
1032 /*      Returns OGRERR_NONE/OGRRERR_FAILURE.                            */
1033 /************************************************************************/
GetExtent(OGREnvelope * psExtent,int bForce)1034 OGRErr OGRGRASSLayer::GetExtent (OGREnvelope *psExtent, int bForce)
1035 {
1036 #if GRASS_VERSION_MAJOR  >= 7
1037     struct bound_box box;
1038 #else
1039     BOUND_BOX box;
1040 #endif
1041 
1042     Vect_get_map_box ( poMap, &box );
1043 
1044     psExtent->MinX = box.W;
1045     psExtent->MinY = box.S;
1046     psExtent->MaxX = box.E;
1047     psExtent->MaxY = box.N;
1048 
1049     return OGRERR_NONE;
1050 }
1051 
1052 /************************************************************************/
1053 /*                           TestCapability()                           */
1054 /************************************************************************/
TestCapability(const char * pszCap)1055 int OGRGRASSLayer::TestCapability( const char * pszCap )
1056 {
1057     if( EQUAL(pszCap,OLCRandomRead) )
1058         return TRUE;
1059 
1060     else if( EQUAL(pszCap,OLCFastFeatureCount) )
1061         return TRUE;
1062 
1063     else if( EQUAL(pszCap,OLCFastSpatialFilter) )
1064         return FALSE;
1065 
1066     else if( EQUAL(pszCap,OLCFastGetExtent) )
1067         return TRUE;
1068 
1069     else if( EQUAL(pszCap,OLCFastSetNextByIndex) )
1070         return TRUE;
1071 
1072     else
1073         return FALSE;
1074 }
1075 
1076 /************************************************************************/
1077 /*                            CreateField()                             */
1078 /************************************************************************/
CreateField(OGRFieldDefn * poField,int bApproxOK)1079 OGRErr OGRGRASSLayer::CreateField( OGRFieldDefn *poField, int bApproxOK )
1080 {
1081     CPLError( CE_Failure, CPLE_NotSupported,
1082                   "Can't create fields on a GRASS layer.\n");
1083 
1084     return OGRERR_FAILURE;
1085 }
1086 
1087 /************************************************************************/
1088 /*                           GetSpatialRef()                            */
1089 /************************************************************************/
GetSpatialRef()1090 OGRSpatialReference *OGRGRASSLayer::GetSpatialRef()
1091 {
1092     return poSRS;
1093 }
1094 
1095