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