Logo Search packages:      
Sourcecode: qgis version File versions

qgsgrassprovider.cpp

/***************************************************************************
    qgsgrassprovider.cpp -  Data provider for GRASS format
                             -------------------
    begin                : March, 2004
    copyright            : (C) 2004 by Gary E.Sherman, Radim Blazek
    email                : sherman@mrcc.com, blazek@itc.it
 ***************************************************************************/
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include <string.h>
#include <iostream>
#include <vector>
#include <cfloat>

#include <qpixmap.h>
#include <qiconset.h>
#include <qdir.h>
#include <qstring.h>
#include <qdatetime.h>
#include <qmessagebox.h>

#include "../../src/qgis.h"
#include "../../src/qgsdataprovider.h"
#include "../../src/qgsfeature.h"
#include "../../src/qgsfield.h"
#include "../../src/qgsrect.h"
#include "../../src/qgsfeatureattribute.h"

extern "C" {
#include <gis.h>
#include <dbmi.h>
#include <Vect.h>
}

#include "qgsgrass.h"
#include "qgsgrassprovider.h"

std::vector<GLAYER> QgsGrassProvider::mLayers;
std::vector<GMAP> QgsGrassProvider::mMaps;

QgsGrassProvider::QgsGrassProvider(QString uri):mDataSourceUri(uri)
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider URI: " << uri << std::endl;
    #endif

    QTime time;
    time.start();

    mValid = false;
    checkEndian();
    
    // Parse URI 
    QDir dir ( uri );  // it is not a directory in fact
    uri = dir.path();  // no dupl '/'

    mLayer = dir.dirName();
    uri = uri.left( dir.path().findRev('/') );
    dir = QDir(uri);
    mMapName = dir.dirName();
    dir.cdUp(); 
    mMapset = dir.dirName();
    dir.cdUp(); 
    mLocation = dir.dirName();
    dir.cdUp(); 
    mGisdbase = dir.path();
    
    #ifdef QGISDEBUG
    std::cerr << "gisdbase: " << mGisdbase << std::endl;
    std::cerr << "location: " << mLocation << std::endl;
    std::cerr << "mapset: "   << mMapset << std::endl;
    std::cerr << "mapName: "  << mMapName << std::endl;
    std::cerr << "layer: "    << mLayer << std::endl;
    #endif

    /* Parse Layer, supported layers <field>_point, <field>_line, <field>_area
    *  Layer is opened even if it is empty (has no features) 
    */
    mLayerField = -1;       
    if ( mLayer.compare("boundary") == 0 ) { // currently not used
        mLayerType = BOUNDARY;
      mGrassType = GV_BOUNDARY;
    } else if ( mLayer.compare("centroid") == 0 ) { // currently not used
        mLayerType = CENTROID;
      mGrassType = GV_CENTROID;
    } else {
      // Get field number
      int pos = mLayer.find('_');

      if ( pos == -1 ) {
          std::cerr << "Invalid layer name, no underscore found: " << mLayer << std::endl;
          return;
      }

      mLayerField = mLayer.left(pos).toInt();

      QString ts = mLayer.right( mLayer.length() - pos - 1 );
      if ( ts.compare("point") == 0 ) {
          mLayerType = POINT;
          mGrassType = GV_POINT; // ?! centroids may be points
      } else if ( ts.compare("line") == 0 ) {
          mLayerType = LINE;
          mGrassType = GV_LINE | GV_BOUNDARY; 
      } else if ( ts.compare("polygon") == 0 ) {
          mLayerType = POLYGON;
          mGrassType = GV_AREA; 
      } else {
          std::cerr << "Invalid layer name, wrong type: " << ts << std::endl;
          return;
      }
    }
    #ifdef QGISDEBUG
    std::cerr << "mLayerField: " << mLayerField << std::endl;
    std::cerr << "mLayerType: " << mLayerType << std::endl;
    #endif

    if ( mLayerType == BOUNDARY || mLayerType == CENTROID ) {
      std::cerr << "Layer type not supported." << std::endl;
      return;
    }

    // Set QGIS type
    switch ( mLayerType ) {
      case POINT:
      case CENTROID:
            mQgisType = QGis::WKBPoint; 
          break;
      case LINE:
      case BOUNDARY:
            mQgisType = QGis::WKBLineString; 
          break;
      case POLYGON:
          mQgisType = QGis::WKBPolygon;
          break;
    }

    mLayerId = openLayer(mGisdbase, mLocation, mMapset, mMapName, mLayerField);
    if ( mLayerId < 0 ) {
      std::cerr << "Cannot open GRASS layer:" << uri << std::endl;
      return;
    }
    #ifdef QGISDEBUG
    std::cerr << "mLayerId: " << mLayerId << std::endl;
    #endif

    mMap = layerMap(mLayerId);

    // Getting the total number of features in the layer
    mNumberFeatures = 0;
    mCidxFieldIndex = -1;
    if ( mLayerField >= 0 ) {
      mCidxFieldIndex = Vect_cidx_get_field_index ( mMap, mLayerField);
      if ( mCidxFieldIndex >= 0 ) {
          mNumberFeatures = Vect_cidx_get_type_count ( mMap, mLayerField, mGrassType );
          mCidxFieldNumCats = Vect_cidx_get_num_cats_by_index ( mMap, mCidxFieldIndex );
      }
    } else {
      // TODO nofield layers
      mNumberFeatures = 0;
      mCidxFieldNumCats = 0;
    }
    mNextCidx = 0;

    #ifdef QGISDEBUG
    std::cerr << "mNumberFeatures = " << mNumberFeatures << " mCidxFieldIndex = " << mCidxFieldIndex
              << " mCidxFieldNumCats = " << mCidxFieldNumCats << std::endl;
    #endif


    // Create selection array
    mSelectionSize = allocateSelection ( mMap, &mSelection );
    resetSelection(1); // TODO ? - where what reset

    mMapVersion = mMaps[mLayers[mLayerId].mapId].version;

    // Init structures
    mPoints = Vect_new_line_struct ();
    mCats = Vect_new_cats_struct ();
    mList = Vect_new_list ();

    mValid = true;

    #ifdef QGISDEBUG
    std::cerr << "New GRASS layer opened, time (ms): " << time.elapsed() << std::endl;
    #endif
}

00194 void QgsGrassProvider::update ( void )
{
    #ifdef QGISDEBUG
    std::cerr << "*** QgsGrassProvider::update ***" << std::endl;
    #endif

    mValid = false;
    // TODO check if reopened map is valid

    // Getting the total number of features in the layer
    // It may happen that the field disappeares from the map (deleted features, new map without that field)
    mNumberFeatures = 0;
    mCidxFieldIndex = -1;
    if ( mLayerField >= 0 ) {
      mCidxFieldIndex = Vect_cidx_get_field_index ( mMap, mLayerField);
      if ( mCidxFieldIndex >= 0 ) {
          mNumberFeatures = Vect_cidx_get_type_count ( mMap, mLayerField, mGrassType );
          mCidxFieldNumCats = Vect_cidx_get_num_cats_by_index ( mMap, mCidxFieldIndex );
      }
    } else {
      // TODO nofield layers
      mNumberFeatures = 0;
      mCidxFieldNumCats = 0;
    }
    mNextCidx = 0;

    #ifdef QGISDEBUG
    std::cerr << "mNumberFeatures = " << mNumberFeatures << " mCidxFieldIndex = " << mCidxFieldIndex
              << " mCidxFieldNumCats = " << mCidxFieldNumCats << std::endl;
    #endif

    // Create selection array
    if ( mSelection ) free ( mSelection );
    mSelectionSize = allocateSelection ( mMap, &mSelection );
    resetSelection(1); 
    
    mMapVersion = mMaps[mLayers[mLayerId].mapId].version;

    mValid = true;
}

00235 int QgsGrassProvider::allocateSelection( struct Map_info *map, char **selection )
{
    int size;
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::allocateSellection" << std::endl;
    #endif
    
    int nlines = Vect_get_num_lines ( map );
    int nareas = Vect_get_num_areas ( map );
    
    if ( nlines > nareas ) {
      size = nlines + 1;
    } else {
      size = nareas + 1;
    }
    #ifdef QGISDEBUG
    std::cerr << "nlines = " << nlines << " nareas = " << nareas << " size = " << size << std::endl;
    #endif

    *selection = (char *) malloc ( size );

    return size;
}

QgsGrassProvider::~QgsGrassProvider()
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::~QgsGrassProvider()" << std::endl;
    #endif
    closeLayer ( mLayerId );
}

/**
* Get the first feature resutling from a select operation
* @return QgsFeature
*/
00271 QgsFeature *QgsGrassProvider::getFirstFeature(bool fetchAttributes)
{
    #ifdef QGISDEBUG
    std::cout << "QgsGrassProvider::getFirstFeature()" << std::endl;
    #endif

    if ( isEdited() )
      return 0;

    mNextCidx = 0;
      
    return ( getNextFeature(fetchAttributes) );
}

/**
* Get the next feature resulting from a select operation
* @return false if there are no features in the selection set
*/
00289 bool QgsGrassProvider::getNextFeature(QgsFeature &feature, bool fetchAttributes)
{
    #if QGISDEBUG > 3
    std::cout << "QgsGrassProvider::getNextFeature()" << std::endl;
    #endif

    if ( isEdited() )
      return 0;

    // TODO once clear how to do that 
    return false;
}

/**
* Get the next feature resulting from a select operation
* Return 0 if there are no features in the selection set
* @return QgsFeature
*/
00307 QgsFeature *QgsGrassProvider::getNextFeature(bool fetchAttributes)
{
    #if QGISDEBUG > 3
    std::cout << "QgsGrassProvider::getNextFeature() mNextCidx = " << mNextCidx 
            << " fetchAttributes = " << fetchAttributes << std::endl;
    #endif
    
    if ( isEdited() )
      return 0;

    std::list<int> attlist;

    if ( fetchAttributes ) {
      int fc = fieldCount();
      for ( int i = 0; i < fc; i++ ) {
          attlist.push_back(i);
      }
    }

    return ( getNextFeature(attlist) );
}

00329 QgsFeature* QgsGrassProvider::getNextFeature(std::list<int>& attlist)
{
    int cat, type, id, idx;
    unsigned char *wkb;
    int wkbsize;

    #ifdef QGISDEBUG
    std::cout << "QgsGrassProvider::getNextFeature( attlist )" << std::endl;
    #endif

    if ( isEdited() )
      return 0;
    
    // Get next line/area id
    int found = 0;
    while ( mNextCidx < mCidxFieldNumCats ) {
      Vect_cidx_get_cat_by_index ( mMap, mCidxFieldIndex, mNextCidx++, &cat, &type, &id );
      // Warning: selection array is only of type line/area of current layer -> check type first

      if ( !(type & mGrassType) ) continue;
      if ( !mSelection[id] ) continue;
        found = 1;
      break;
    }
    if ( !found ) return 0; // No more features
    #if QGISDEBUG > 3
    std::cout << "cat = " << cat << " type = " << type << " id = " << id << std::endl;
    #endif

    QgsFeature *f = new QgsFeature(id);

    // TODO int may be 64 bits (memcpy)
    if ( type & (GV_POINTS | GV_LINES) ) { /* points or lines */
      Vect_read_line ( mMap, mPoints, mCats, id);
      int npoints = mPoints->n_points;
      
      if ( type & GV_POINTS ) {
          wkbsize = 1 + 4 + 2*8;
      } else { // GV_LINES
          wkbsize = 1+4+4+npoints*2*8;
      }         
      wkb = new unsigned char[wkbsize];
      unsigned char *wkbp = wkb;
      wkbp[0] = (unsigned char) mEndian;
      wkbp += 1;

      /* WKB type */
      memcpy (wkbp, &mQgisType, 4);
      wkbp += 4;
      
      /* number of points */
      if ( type & GV_LINES ) {
          memcpy (wkbp, &npoints, 4);
          wkbp += 4;
      }
      
      for ( int i = 0; i < npoints; i++ ) {
          memcpy (wkbp, &(mPoints->x[i]), 8);
          memcpy (wkbp+8, &(mPoints->y[i]), 8);
          wkbp += 16;
      }
    } else { // GV_AREA
      Vect_get_area_points ( mMap, id, mPoints );
      int npoints = mPoints->n_points;

      wkbsize = 1+4+4+4+npoints*2*8; // size without islands
      wkb = new unsigned char[wkbsize];
      wkb[0] = (unsigned char) mEndian;
      int offset = 1;

      /* WKB type */
      memcpy ( wkb+offset, &mQgisType, 4);
      offset += 4;

      /* Number of rings */
      int nisles = Vect_get_area_num_isles ( mMap, id );
      int nrings = 1 + nisles; 
      memcpy (wkb+offset, &nrings, 4);
      offset += 4;

      /* Outer ring */
      memcpy (wkb+offset, &npoints, 4);
      offset += 4;
      for ( int i = 0; i < npoints; i++ ) {
          memcpy (wkb+offset, &(mPoints->x[i]), 8);
          memcpy (wkb+offset+8, &(mPoints->y[i]), 8);
          offset += 16;
      }
      
      /* Isles */
      for ( int i = 0; i < nisles; i++ ) {
          Vect_get_isle_points ( mMap, Vect_get_area_isle (mMap, id, i), mPoints );
          npoints = mPoints->n_points;
          
          // add space
          wkbsize += 4+npoints*2*8;
          wkb = (unsigned char *) realloc (wkb, wkbsize);

          memcpy (wkb+offset, &npoints, 4);
          offset += 4;
          for ( int i = 0; i < npoints; i++ ) {
            memcpy (wkb+offset, &(mPoints->x[i]), 8);
            memcpy (wkb+offset+8, &(mPoints->y[i]), 8);
            offset += 16;
          }
      }
    }

    f->setGeometry(wkb, wkbsize);

    QgsGrassProvider::setFeatureAttributes( mLayerId, cat, f, attlist );  
    
    return f;

    return 0;//soon
}

void QgsGrassProvider::resetSelection( bool sel)
{
    #ifdef QGISDEBUG
    std::cout << "QgsGrassProvider::resetSelection()" << std::endl;
    #endif
    memset ( mSelection, (int) sel, mSelectionSize );
    mNextCidx = 0;
}

/**
* Select features based on a bounding rectangle. Features can be retrieved
* with calls to getFirstFeature and getNextFeature.
* @param mbr QgsRect containing the extent to use in selecting features
*/
00460 void QgsGrassProvider::select(QgsRect *rect, bool useIntersect)
{
    #ifdef QGISDEBUG
    std::cout << "QgsGrassProvider::select() useIntersect = " << useIntersect << std::endl;
    #endif

    if ( isEdited() )
      return;

    // check if outdated and update if necessary
    int mapId = mLayers[mLayerId].mapId;
    if ( mapOutdated(mapId) ) {
        updateMap ( mapId );
    }
    if ( mMapVersion < mMaps[mapId].version ) {
        update();
    }

    resetSelection(0);
    
    if ( !useIntersect ) { // select by bounding boxes only
      BOUND_BOX box;
      box.N = rect->yMax(); box.S = rect->yMin(); 
      box.E = rect->xMax(); box.W = rect->xMin(); 
      box.T = PORT_DOUBLE_MAX; box.B = -PORT_DOUBLE_MAX; 
      if ( mLayerType == POINT || mLayerType == CENTROID || mLayerType == LINE || mLayerType == BOUNDARY ) {
          Vect_select_lines_by_box(mMap, &box, mGrassType, mList);
      } else if ( mLayerType == POLYGON ) {
          Vect_select_areas_by_box(mMap, &box, mList);
      }

    } else { // check intersection
      struct line_pnts *Polygon;
      
      Polygon = Vect_new_line_struct();

      Vect_append_point( Polygon, rect->xMin(), rect->yMin(), 0);
      Vect_append_point( Polygon, rect->xMax(), rect->yMin(), 0);
      Vect_append_point( Polygon, rect->xMax(), rect->yMax(), 0);
      Vect_append_point( Polygon, rect->xMin(), rect->yMax(), 0);
      Vect_append_point( Polygon, rect->xMin(), rect->yMin(), 0);

      if ( mLayerType == POINT || mLayerType == CENTROID || mLayerType == LINE || mLayerType == BOUNDARY ) {
          Vect_select_lines_by_polygon ( mMap, Polygon, 0, NULL, mGrassType, mList);
      } else if ( mLayerType == POLYGON ) {
          Vect_select_areas_by_polygon ( mMap, Polygon, 0, NULL, mList);
      }

      Vect_destroy_line_struct (Polygon);
    }
    for ( int i = 0; i < mList->n_values; i++ ) {
        if ( mList->value[i] <= mSelectionSize ) {
          mSelection[mList->value[i]] = 1;
      } else {
          std::cerr << "Selected element out of range" << std::endl;
      }
    }
      
    #ifdef QGISDEBUG
    std::cout << mList->n_values << " features selected" << std::endl;
    #endif
}

/**
* Set the data source specification. This may be a path or database
* connection string
* @uri data source specification
*/
00528 void QgsGrassProvider::setDataSourceUri(QString uri)
{
      mDataSourceUri = uri;
}

/**
* Get the data source specification. This may be a path or database
* connection string
* @return data source specification
*/
00538 QString QgsGrassProvider::getDataSourceUri()
{
      return mDataSourceUri;
}

/**
* Identify features within the search radius specified by rect
* @param rect Bounding rectangle of search radius
* @return std::vector containing QgsFeature objects that intersect rect
*/
00548 std::vector<QgsFeature>& QgsGrassProvider::identify(QgsRect * rect)
{
    #ifdef QGISDEBUG
    std::cout << "QgsGrassProvider::identify()" << std::endl;
    #endif

    // TODO: does not return vector of features! Should it?

    if ( !isEdited() ) {
        select(rect, true);
    }
}

/* set endian */
void QgsGrassProvider::checkEndian()
{
      char *chkEndian = new char[4];
      memset(chkEndian, '\0', 4);
      chkEndian[0] = 0xE8;

      int *ce = (int *) chkEndian;
      if (232 == *ce)
          mEndian = NDR;
      else
          mEndian = XDR;
      delete[]chkEndian;
}

00576 int QgsGrassProvider::endian()
{
      return mEndian;
}

00581 QgsRect *QgsGrassProvider::extent()
{
    BOUND_BOX box;
    Vect_get_map_box ( mMap, &box );

    return new QgsRect( box.W, box.S, box.E, box.N);
}

/** 
* Return the feature type
*/
00592 int QgsGrassProvider::geometryType(){
    return mQgisType;
}
/** 
* Return the feature type
*/
00598 long QgsGrassProvider::featureCount(){
    return mNumberFeatures;
}

/**
* Return the number of fields
*/
00605 int QgsGrassProvider::fieldCount(){
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::fieldCount() return:" << mLayers[mLayerId].fields.size() << std::endl;
    #endif
    return mLayers[mLayerId].fields.size();
}

/**
* Return fields
*/
00615 std::vector<QgsField>& QgsGrassProvider::fields(){
      return mLayers[mLayerId].fields;

}

00620 void QgsGrassProvider::reset(){
    resetSelection(1);
    mNextCidx = 0;
}

00625 QString QgsGrassProvider::minValue(int position)
{
    if ( position >= fieldCount() ) {
      std::cerr << "Warning: access requested to invalid position in QgsGrassProvider::minValue()" 
                << std::endl;
    }
    return QString::number( mLayers[mLayerId].minmax[position][0], 'f', 2 );
}

 
00635 QString QgsGrassProvider::maxValue(int position)
{
    if ( position >= fieldCount() ) {
      std::cerr << "Warning: access requested to invalid position in QgsGrassProvider::maxValue()" 
                << std::endl;
    }
    return QString::number( mLayers[mLayerId].minmax[position][1], 'f', 2 );
}

00644 bool QgsGrassProvider::isValid(){
    #ifdef QGISDEBUG
    QString validString = mValid?"true":"false";
    std::cerr << "QgsGrassProvider::isValid() returned: " << validString << std::endl;
    #endif
    return mValid;
}

// ------------------------------------------------------------------------------------------------------
// Compare categories in GATT
static int cmpAtt ( const void *a, const void *b ) {
    GATT *p1 = (GATT *) a;
    GATT *p2 = (GATT *) b;
    return (p1->cat - p2->cat);
}

/* returns layerId or -1 on error */
00661 int QgsGrassProvider::openLayer(QString gisdbase, QString location, QString mapset, QString mapName, int field)
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::openLayer()" << std::endl;
    std::cerr << "gisdbase: " << gisdbase << std::endl;
    std::cerr << "location: " << location << std::endl;
    std::cerr << "mapset: "   << mapset << std::endl;
    std::cerr << "mapName: "  << mapName << std::endl;
    std::cerr << "field: "    << field << std::endl;
    #endif

    // Check if this layer is already opened

    for ( int i = 0; i <  mLayers.size(); i++) {
      if ( !(mLayers[i].valid) ) continue;

      GMAP *mp = &(mMaps[mLayers[i].mapId]);

      if ( mp->gisdbase == gisdbase && mp->location == location && 
           mp->mapset == mapset && mp->mapName == mapName && mLayers[i].field == field )
      {
          // the layer already exists, return layer id
          #ifdef QGISDEBUG
          std::cerr << "The layer is already opened with ID = " << i << std::endl;
          #endif
          mLayers[i].nUsers++;
          return i;
      }
    }

    // Create a new layer
    GLAYER layer;
    layer.valid = false;
    layer.field = field; 

    // Open map
    layer.mapId = openMap ( gisdbase, location, mapset, mapName );
    if ( layer.mapId < 0 ) {
      std::cerr << "Cannot open vector map" << std::endl;
      return -1;
    }
    #ifdef QGISDEBUG
    std::cerr << "layer.mapId = " << layer.mapId << std::endl;
    #endif
    layer.map = mMaps[layer.mapId].map;

    layer.attributes = 0; // because loadLayerSourcesFromMap will release old
    loadLayerSourcesFromMap ( layer );

    layer.valid = true;

    // Add new layer to layers
    mLayers.push_back(layer);
      
    #ifdef QGISDEBUG
    std::cerr << "New layer successfully opened" << layer.nAttributes << std::endl;
    #endif
        
    return mLayers.size() - 1; 
}

00722 void QgsGrassProvider::loadLayerSourcesFromMap ( GLAYER &layer )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::loadLayerSourcesFromMap" << std::endl;
    #endif

    // Reset and free 
    layer.fields.clear();
    if ( layer.attributes ) { 
      for ( int i = 0; i < layer.nAttributes; i ++ ) {
          for ( int j = 0; j < layer.nColumns; j ++ ) {
            if ( layer.attributes[i].values[j] )
                free ( layer.attributes[i].values[j] );
          }
          free ( layer.attributes[i].values );
      }
      free ( layer.attributes );
    }
    
    // Get field info
    layer.fieldInfo = Vect_get_field( layer.map, layer.field); // should work also with field = 0

    // Read attributes
    layer.nColumns = 0;
    layer.nAttributes = 0;
    layer.attributes = 0;
    layer.keyColumn = -1;
    if ( layer.fieldInfo == NULL ) {
        #ifdef QGISDEBUG
      std::cerr << "No field info -> no attribute table" << std::endl;
        #endif
    } else { 
        #ifdef QGISDEBUG
      std::cerr << "Field info found -> open database" << std::endl;
        #endif
      dbDriver *databaseDriver = db_start_driver_open_database ( layer.fieldInfo->driver, 
                                                   layer.fieldInfo->database );

      if ( databaseDriver == NULL ) {
          std::cerr << "Cannot open database " << layer.fieldInfo->database << " by driver " 
                  << layer.fieldInfo->driver << std::endl;
      } else {
            #ifdef QGISDEBUG
          std::cerr << "Database opened -> open select cursor" << std::endl;
            #endif
          dbString dbstr; 
          db_init_string (&dbstr);
          db_set_string (&dbstr, "select * from ");
          db_append_string (&dbstr, layer.fieldInfo->table);
          
            #ifdef QGISDEBUG
          std::cerr << "SQL: " << db_get_string(&dbstr) << std::endl;
            #endif
          dbCursor databaseCursor;
          if ( db_open_select_cursor(databaseDriver, &dbstr, &databaseCursor, DB_SCROLL) != DB_OK ){
            layer.nColumns = 0;
            db_close_database_shutdown_driver ( databaseDriver );
            QMessageBox::warning( 0, "Warning", "Cannot select attributes from table '" + 
                           QString(layer.fieldInfo->table) + "'" );
          } else {
            int nRecords = db_get_num_rows ( &databaseCursor );
                #ifdef QGISDEBUG
            std::cerr << "Number of records: " << nRecords << std::endl;
                #endif
            
            dbTable  *databaseTable = db_get_cursor_table (&databaseCursor);
            layer.nColumns = db_get_table_number_of_columns(databaseTable);

            layer.minmax = new double[layer.nColumns][2];

            // Read columns' description 
            for (int i = 0; i < layer.nColumns; i++) {
                layer.minmax[i][0] = DBL_MAX;
                layer.minmax[i][1] = -DBL_MAX;

                dbColumn *column = db_get_table_column (databaseTable, i);

                int ctype = db_sqltype_to_Ctype ( db_get_column_sqltype(column) );
                    #ifdef QGISDEBUG
                std::cerr << "column = " << db_get_column_name(column) 
                        << " ctype = " << ctype << std::endl;
                    #endif
                
                QString ctypeStr;
                switch ( ctype ) {
                  case DB_C_TYPE_INT:
                      ctypeStr = "integer";
                      break; 
                  case DB_C_TYPE_DOUBLE:
                      ctypeStr = "double";
                      break; 
                  case DB_C_TYPE_STRING:
                      ctypeStr = "string";
                      break; 
                  case DB_C_TYPE_DATETIME:
                      ctypeStr = "datetime";
                      break; 
                }
                layer.fields.push_back ( QgsField( db_get_column_name(column), ctypeStr, 
                                 db_get_column_length(column), db_get_column_precision(column) ) );
                
                if ( G_strcasecmp ( db_get_column_name(column), layer.fieldInfo->key) == 0 ) {
                  layer.keyColumn = i;
                }
            }
    
            if ( layer.keyColumn < 0 ) {
                layer.fields.clear();
                    layer.nColumns = 0;

                QMessageBox::warning( 0, "Warning", "Key column '" + QString(layer.fieldInfo->key) + 
                           "' not found in the table '" + QString(layer.fieldInfo->table) + "'" );
            } else {
                // Read attributes to the memory
                layer.attributes = (GATT *) malloc ( nRecords * sizeof(GATT) );
                while ( 1 ) {
                  int more;
                        
                  if ( db_fetch (&databaseCursor, DB_NEXT, &more) != DB_OK ) {
                      std::cout << "Cannot fetch DB record" << std::endl;
                      break;
                  }
                  if ( !more ) break; // no more records

                  // Check cat value
                  dbColumn *column = db_get_table_column (databaseTable, layer.keyColumn);
                  dbValue *value = db_get_column_value(column);
                  layer.attributes[layer.nAttributes].cat = db_get_value_int (value);
                  if ( layer.attributes[layer.nAttributes].cat < 1 ) continue; 

                  layer.attributes[layer.nAttributes].values = (char **) malloc ( layer.nColumns * sizeof(char*) );

                  for (int i = 0; i < layer.nColumns; i++) {
                      column = db_get_table_column (databaseTable, i);
                      int sqltype = db_get_column_sqltype(column);
                      int ctype = db_sqltype_to_Ctype ( sqltype );
                      value = db_get_column_value(column);
                      db_convert_value_to_string ( value, sqltype, &dbstr);

                      #if QGISDEBUG > 3
                      std::cout << "column: " << db_get_column_name(column) << std::endl;
                      std::cout << "value: " << db_get_string(&dbstr) << std::endl;
                      #endif

                      layer.attributes[layer.nAttributes].values[i] = strdup ( db_get_string(&dbstr) );

                      double dbl;
                      if ( ctype == DB_C_TYPE_INT ) {
                        dbl = db_get_value_int ( value );
                      } else if ( ctype == DB_C_TYPE_DOUBLE ) {
                        dbl = db_get_value_double ( value );
                      } else {
                        dbl = 0;
                      }
                      
                      if ( dbl < layer.minmax[i][0] ) {
                        layer.minmax[i][0] = dbl;
                      }
                      if ( dbl > layer.minmax[i][1] ) {
                        layer.minmax[i][1] = dbl;
                      }
                  }
                  layer.nAttributes++;
                }
                // Sort attributes by category
                qsort ( layer.attributes, layer.nAttributes, sizeof(GATT), cmpAtt );
            }
            db_close_cursor (&databaseCursor);
            db_close_database_shutdown_driver ( databaseDriver );
            db_free_string(&dbstr);

                #ifdef QGISDEBUG
            std::cerr << "fields.size = " << layer.fields.size() << std::endl;
            std::cerr << "number of attributes = " << layer.nAttributes << std::endl;
                #endif

          }
      }
    }

    // Add cat if no attribute fields exist (otherwise qgis crashes)
    if ( layer.nColumns == 0 ) {
      layer.fields.push_back ( QgsField( "cat", "integer", 10, 0) );
      layer.minmax = new double[1][2];
      layer.minmax[0][0] = 0; 
      layer.minmax[0][1] = 0; 

      int cidx = Vect_cidx_get_field_index ( layer.map, layer.field );
      if ( cidx >= 0 ) {
          int ncats, cat, type, id;
          
          ncats = Vect_cidx_get_num_cats_by_index ( layer.map, cidx );

          if ( ncats > 0 ) {
            Vect_cidx_get_cat_by_index ( layer.map, cidx, 0, &cat, &type, &id );
              layer.minmax[0][0] = cat; 

              Vect_cidx_get_cat_by_index ( layer.map, cidx, ncats-1, &cat, &type, &id );
              layer.minmax[0][1] = cat; 
          }
      }
    }
}

00926 void QgsGrassProvider::closeLayer( int layerId )
{
    #ifdef QGISDEBUG
    std::cerr << "Close layer " << layerId << std::endl;
    #endif

    // TODO: not tested because delete is never used for providers
    mLayers[layerId].nUsers--;

    if ( mLayers[layerId].nUsers == 0 ) { // No more users, free sources
        #ifdef QGISDEBUG
        std::cerr << "No more users -> delete layer" << std::endl;
        #endif

        mLayers[layerId].valid = false;

      // Column names/types
      mLayers[layerId].fields.resize(0);
      
      // Attributes
      for ( int i = 0; i < mLayers[layerId].nAttributes; i++ ) {
          free ( mLayers[layerId].attributes[i].values );
      }
      free ( mLayers[layerId].attributes );
            
      delete[] mLayers[layerId].minmax;

      // Field info
      free ( mLayers[layerId].fieldInfo );

      closeMap ( mLayers[layerId].mapId );
    }
}

/* returns mapId or -1 on error */
00961 int QgsGrassProvider::openMap(QString gisdbase, QString location, QString mapset, QString mapName)
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::openMap()" << std::endl;
    #endif

    QString tmpPath = gisdbase + "/" + location + "/" + mapset + "/" + mapName;

    // Check if this map is already opened
    for ( int i = 0; i <  mMaps.size(); i++) {
      if ( mMaps[i].path == tmpPath ) 
      {
          // the map is already opened, return map id
            #ifdef QGISDEBUG
          std::cerr << "The map is already opened with ID = " << i << std::endl;
            #endif
          mMaps[i].nUsers++;
          return i;
      }
    }

    GMAP map;
    map.gisdbase = gisdbase;
    map.location = location;
    map.mapset = mapset;
    map.mapName = mapName;
    map.path = tmpPath;
    map.nUsers = 1;
    map.version = 1;
    map.update = 0;
    map.map = (struct Map_info *) malloc ( sizeof(struct Map_info) );

    // Set GRASS location
    QgsGrass::setLocation ( gisdbase, location ); 
#ifdef QGISDEBUG
      std::cerr << "Setting  gisdbase, location: " << gisdbase << ", " << location << std::endl;
#endif

    // Find the vector
    char *ms = G_find_vector2 ( (char *) mapName.ascii(), (char *) mapset.ascii()) ;

    if ( ms == NULL) {
        std::cerr << "Cannot find GRASS vector" << std::endl;
      return -1;
    }

    // Read the time of vector dir before Vect_open_old, because it may take long time (when the vector
    // could be owerwritten)
    QFileInfo di ( gisdbase + "/" + location + "/" + mapset + "/vector/" + mapName );
    map.lastModified = di.lastModified();

    // Open vector
    QgsGrass::resetError(); // to "catch" error after Vect_open_old()
    Vect_set_open_level (2);
    Vect_open_old ( map.map, (char *) mapName.ascii(), (char *) mapset.ascii());

    if ( QgsGrass::getError() == QgsGrass::FATAL ) {
      std::cerr << "Cannot open GRASS vector: " << QgsGrass::getErrorMessage() << std::endl;
      return -1;
    }
    #ifdef QGISDEBUG
    std::cerr << "GRASS map successfully opened" << std::endl;
    #endif

    // Add new map to maps
    mMaps.push_back(map);

    return mMaps.size() - 1; // map id 
}

01031 void QgsGrassProvider::updateMap ( int mapId )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::updateMap() mapId = " << mapId << std::endl;
    #endif

    /* Close map */
    GMAP *map = &(mMaps[mapId]);

    map->valid = false;
    map->version++;

    QgsGrass::setLocation ( (char *) map->gisdbase.ascii(), (char *) map->location.ascii() ); 

    // TODO: Should be done better / in other place ?
    // TODO: Is it necessary for close ?
    G__setenv( "MAPSET", (char *) map->mapset.ascii() );
    
    Vect_close ( map->map );

    QFileInfo di ( map->gisdbase + "/" + map->location + "/" + map->mapset + "/vector/" + map->mapName );
    map->lastModified = di.lastModified();

    // Reopen vector
    QgsGrass::resetError(); // to "catch" error after Vect_open_old()
    Vect_set_open_level (2);
    Vect_open_old ( map->map, (char *) map->mapName.ascii(), (char *) map->mapset.ascii());

    if ( QgsGrass::getError() == QgsGrass::FATAL ) {
      std::cerr << "Cannot reopen GRASS vector: " << QgsGrass::getErrorMessage() << std::endl;

      // TODO if reopen fails, mLayers should be also updated
      return;
    }

    #ifdef QGISDEBUG
    std::cerr << "GRASS map successfully reopened for reading." << std::endl;
    #endif

    for ( int i = 0; i <  mLayers.size(); i++) {
      // if ( !(mLayers[i].valid) ) continue; // ?

      if  ( mLayers[i].mapId == mapId ) {
            loadLayerSourcesFromMap ( mLayers[i] );
      }
    }

    map->valid = true;
}

01081 void QgsGrassProvider::closeMap( int mapId )
{
    #ifdef QGISDEBUG
    std::cerr << "Close map " << mapId << std::endl;
    #endif

    // TODO: not tested because delete is never used for providers
    mMaps[mapId].nUsers--;

    if ( mMaps[mapId].nUsers == 0 ) { // No more users, free sources
        #ifdef QGISDEBUG
        std::cerr << "No more users -> delete map" << std::endl;
        #endif

        // TODO: do this better, probably maintain QgsGrassEdit as one user
      if ( mMaps[mapId].update ) {
          QMessageBox::warning( 0, "Warning", "The vector was currently edited, "
                                 "you can expect crash soon." );
      }

        mMaps[mapId].valid = false;
      Vect_close ( mMaps[mapId].map );
    }
}

01106 bool QgsGrassProvider::mapOutdated( int mapId )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::mapOutdated()" << std::endl;
    #endif

    GMAP *map = &(mMaps[mapId]);
    
    QString dp = map->gisdbase + "/" + map->location + "/" + map->mapset + "/vector/" + map->mapName;
    QFileInfo di ( dp );

    if ( map->lastModified < di.lastModified() ) {
      #ifdef QGISDEBUG
      std::cerr << "**** The map " << mapId << " was modified ****" << std::endl;
      #endif
      
      return true;
    }

    return false;
}

/** Set feature attributes */
01129 void QgsGrassProvider::setFeatureAttributes ( int layerId, int cat, QgsFeature *feature )
{
    #if QGISDEBUG > 3
    std::cerr << "setFeatureAttributes cat = " << cat << std::endl;
    #endif
    if ( mLayers[layerId].nColumns > 0 ) {
      // find cat
      GATT key;
      key.cat = cat;
      GATT *att = (GATT *) bsearch ( &key, mLayers[layerId].attributes, mLayers[layerId].nAttributes,
                                   sizeof(GATT), cmpAtt);

      for (int i = 0; i < mLayers[layerId].nColumns; i++) {
          if ( att != NULL ) {
            feature->addAttribute ( mLayers[layerId].fields[i].name(), att->values[i]);
          } else { /* it may happen that attributes are missing -> set to empty string */
            feature->addAttribute ( mLayers[layerId].fields[i].name(), "");
          }
      }
    } else { 
      QString tmp;
      tmp.sprintf("%d", cat );
      feature->addAttribute ( "cat", tmp);
    }
}

01155 void QgsGrassProvider::setFeatureAttributes ( int layerId, int cat, QgsFeature *feature, std::list<int>& attlist)
{
#ifdef QGISDEBUG
    std::cerr << "setFeatureAttributes cat = " << cat << std::endl;
    #endif
    if ( mLayers[layerId].nColumns > 0 ) {
      // find cat
      GATT key;
      key.cat = cat;
      GATT *att = (GATT *) bsearch ( &key, mLayers[layerId].attributes, mLayers[layerId].nAttributes,
                                   sizeof(GATT), cmpAtt);

      for (std::list<int>::iterator iter=attlist.begin(); iter!=attlist.end();++iter) {
          if ( att != NULL ) {
            feature->addAttribute ( mLayers[layerId].fields[*iter].name(), att->values[*iter]); 
          } else { /* it may happen that attributes are missing -> set to empty string */
            feature->addAttribute ( mLayers[layerId].fields[*iter].name(), "");     
          } 
      }
    } else { 
      QString tmp;
      tmp.sprintf("%d", cat );
      feature->addAttribute ( "cat", tmp);
    }
}

/** Get pointer to map */
01182 struct Map_info *QgsGrassProvider::layerMap ( int layerId )
{
    return ( mMaps[mLayers[layerId].mapId].map );
}

//-----------------------------------------  Edit -------------------------------------------------------

01189 bool QgsGrassProvider::isGrassEditable ( void )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::isGrassEditable" << std::endl;
    #endif

    if ( !isValid() ) 
      return false;

    /* Check if current user is owner of mapset */
    if ( G__mapset_permissions2((char*)mGisdbase.ascii(),(char*)mLocation.ascii(),(char*)mMapset.ascii()) != 1 )
      return false;

    // TODO: check format? (cannot edit OGR layers)

    return true;
}

01207 bool QgsGrassProvider::isEdited ( void )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::isEdited" << std::endl;
    #endif

    GMAP *map = &(mMaps[mLayers[mLayerId].mapId]);
    return (map->update);
}

01217 bool QgsGrassProvider::startEdit ( void )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::startEdit" << std::endl;
    std::cerr << "  uri = " << mDataSourceUri << std::endl;
    std::cerr << "  mMaps.size() = " << mMaps.size() << std::endl;
    #endif

    if ( !isGrassEditable() )
      return false;

    // Check number of maps (the problem may appear if static variables are not shared - runtime linker)
    if ( mMaps.size() == 0 ) {
      QMessageBox::warning( 0, "Warning", "No maps opened in mMaps, probably problem in runtime linking, " 
                        "static variables are not shared by provider and plugin." );
      return false;
    }

    /* Close map */
    GMAP *map = &(mMaps[mLayers[mLayerId].mapId]);
    map->valid = false;

    QgsGrass::setLocation ( (char *) map->gisdbase.ascii(), (char *) map->location.ascii() ); 

    // Set current mapset (mapset was previously checked by isGrassEditable() )
    // TODO: Should be done better / in other place ?
    G__setenv( "MAPSET", (char *) map->mapset.ascii() );

    Vect_close ( map->map );

    // TODO: Catch error 
     
    int level = Vect_open_update ( map->map, (char *) map->mapName.ascii(), (char *) map->mapset.ascii() );

    if (  level < 2 ) { 
      std::cerr << "Cannot open vector for update on level 2." << std::endl;
      // TODO reopen vector for reading
      return false;
    }

    #ifdef QGISDEBUG
    std::cerr << "Vector successfully reopened for update." << std::endl;
    #endif

    map->update = true;
    map->valid = true;

    return true;
}

01267 bool QgsGrassProvider::closeEdit ( void )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::closeEdit" << std::endl;
    #endif

    if ( !isValid() )
      return false;

    /* Close map */
    GMAP *map = &(mMaps[mLayers[mLayerId].mapId]);
    
    if ( !(map->update) )
      return false;

    map->valid = false;
    map->version++;

    QgsGrass::setLocation ( (char *) map->gisdbase.ascii(), (char *) map->location.ascii() ); 

    // Set current mapset (mapset was previously checked by isGrassEditable() )
    // TODO: Should be done better / in other place ?
    // TODO: Is it necessary for build/close ?
    G__setenv( "MAPSET", (char *) map->mapset.ascii() );
    
    Vect_build_partial ( map->map, GV_BUILD_NONE, NULL);
    Vect_build ( map->map, stderr );
    Vect_close ( map->map );

    QFileInfo di ( mGisdbase + "/" + mLocation + "/" + mMapset + "/vector/" + mMapName );
    map->lastModified = di.lastModified();

    // Reopen vector
    QgsGrass::resetError(); // to "catch" error after Vect_open_old()
    Vect_set_open_level (2);
    Vect_open_old ( map->map, (char *) map->mapName.ascii(), (char *) map->mapset.ascii());

    if ( QgsGrass::getError() == QgsGrass::FATAL ) {
      std::cerr << "Cannot reopen GRASS vector: " << QgsGrass::getErrorMessage() << std::endl;
      return -1;
    }

    #ifdef QGISDEBUG
    std::cerr << "GRASS map successfully reopened for reading." << std::endl;
    #endif

    // Reload sources to layers
    for ( int i = 0; i <  mLayers.size(); i++) {
      // if ( !(mLayers[i].valid) ) continue; // ?

      if  ( mLayers[i].mapId == mLayers[mLayerId].mapId ) {
            loadLayerSourcesFromMap ( mLayers[i] );
      }
    }

    map->update = false;
    map->valid = true;

    return true;
}

01328 int QgsGrassProvider::numLines ( void )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::numLines" << std::endl;
    #endif

    return ( Vect_get_num_lines(mMap) );
}

01337 int QgsGrassProvider::numNodes ( void )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::numNodes" << std::endl;
    #endif

    return ( Vect_get_num_nodes(mMap) );
}

01346 int QgsGrassProvider::readLine ( struct line_pnts *Points, struct line_cats *Cats, int line )
{
    #if QGISDEBUG > 3
    std::cerr << "QgsGrassProvider::readLine" << std::endl;
    #endif

    if ( Points )
      Vect_reset_line ( Points );

    if ( Cats )
      Vect_reset_cats ( Cats );

    if ( !Vect_line_alive(mMap, line) ) return -1;

    return ( Vect_read_line(mMap, Points, Cats, line) );
}

01363 bool QgsGrassProvider::nodeCoor ( int node, double *x, double *y )
{
    #if QGISDEBUG > 3
    std::cerr << "QgsGrassProvider::nodeCoor" << std::endl;
    #endif

    if ( !Vect_node_alive ( mMap, node) ) {
        *x = 0.0;
        *y = 0.0;
      return false;
    }
    
    Vect_get_node_coor ( mMap, node, x, y, NULL);
    return true;
}

01379 bool QgsGrassProvider::lineNodes ( int line, int *node1, int *node2 )
{
    #if QGISDEBUG > 3
    std::cerr << "QgsGrassProvider::lineNodes" << std::endl;
    #endif
    
    if ( !Vect_line_alive(mMap, line) ) {
        *node1 = 0;
        *node2 = 0;
      return false;
    }
    
    Vect_get_line_nodes ( mMap, line, node1, node2 );
    return true;
}

01395 int QgsGrassProvider::writeLine ( int type, struct line_pnts *Points, struct line_cats *Cats )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::writeLine n_points = " << Points->n_points 
            << " n_cats = " << Cats->n_cats << std::endl;
    #endif

    if ( !isEdited() )
      return -1;

    return ( (int) Vect_write_line(mMap,type,Points,Cats) );
}

01408 int QgsGrassProvider::rewriteLine ( int line, int type, struct line_pnts *Points, struct line_cats *Cats )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::rewriteLine n_points = " << Points->n_points 
            << " n_cats = " << Cats->n_cats << std::endl;
    #endif

    if ( !isEdited() )
      return -1;

    return ( Vect_rewrite_line(mMap,line,type,Points,Cats) );
}


01422 int QgsGrassProvider::deleteLine ( int line )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::deleteLine" << std::endl;
    #endif

    if ( !isEdited() )
      return -1;

    return ( Vect_delete_line(mMap,line) );
}

01434 int QgsGrassProvider::findLine ( double x, double y, int type, double threshold )
{
    #if QGISDEBUG > 3
    std::cerr << "QgsGrassProvider::findLine" << std::endl;
    #endif

    return ( Vect_find_line(mMap,x,y,0,type,threshold,0,0) );
}

01443 int QgsGrassProvider::findNode ( double x, double y, double threshold )
{
    return ( Vect_find_node ( mMap, x, y, 0, threshold, 0 ) );
}

01448 bool QgsGrassProvider::lineAreas ( int line, int *left, int *right )
{
    #if QGISDEBUG > 3
    std::cerr << "QgsGrassProvider::lineAreas" << std::endl;
    #endif
    
    if ( !Vect_line_alive(mMap, line) ) {
        *left = 0;
        *right = 0;
      return false;
    }
    
    Vect_get_line_areas ( mMap, line, left, right );
    return true;
}

01464 int QgsGrassProvider::centroidArea ( int centroid )
{
    #if QGISDEBUG > 3
    std::cerr << "QgsGrassProvider::centroidArea" << std::endl;
    #endif
    
    if ( !Vect_line_alive(mMap, centroid) ) {
      return 0;
    }
    
    return ( Vect_get_centroid_area(mMap,centroid) );
}

01477 int QgsGrassProvider::nodeNLines ( int node )
{
    #if QGISDEBUG > 3
    std::cerr << "QgsGrassProvider::nodeNLines" << std::endl;
    #endif
    
    if ( !Vect_node_alive(mMap, node) ) {
      return 0;
    }
    
    return ( Vect_get_node_n_lines(mMap,node) );
}

01490 int QgsGrassProvider::nodeLine ( int node, int idx )
{
    #if QGISDEBUG > 3
    std::cerr << "QgsGrassProvider::nodeLine" << std::endl;
    #endif
    
    if ( !Vect_node_alive(mMap, node) ) {
      return 0;
    }

    return ( Vect_get_node_line(mMap,node,idx) );
}

01503 int QgsGrassProvider::lineAlive ( int line )
{
    #if QGISDEBUG > 3
    std::cerr << "QgsGrassProvider::lineAlive" << std::endl;
    #endif
    
    return ( Vect_line_alive(mMap, line) ) ;
}

01512 int QgsGrassProvider::nodeAlive ( int node )
{
    #if QGISDEBUG > 3
    std::cerr << "QgsGrassProvider::nodeAlive" << std::endl;
    #endif
    
    return ( Vect_node_alive(mMap, node) ) ;
}

01521 int QgsGrassProvider::numUpdatedLines ( void )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::numUpdatedLines" << std::endl;
    std::cerr << "  numUpdatedLines = " << Vect_get_num_updated_lines(mMap) << std::endl;
    #endif
    
    return ( Vect_get_num_updated_lines(mMap) ) ;
}

01531 int QgsGrassProvider::numUpdatedNodes ( void )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::numUpdatedNodes" << std::endl;
    std::cerr << "  numUpdatedNodes = " << Vect_get_num_updated_nodes(mMap) << std::endl;
    #endif
    
    return ( Vect_get_num_updated_nodes(mMap) ) ;
}

01541 int QgsGrassProvider::updatedLine ( int idx )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::updatedLine idx = " << idx << std::endl;
    std::cerr << "  updatedLine = " << Vect_get_updated_line( mMap, idx ) << std::endl;
    #endif
    
    return ( Vect_get_updated_line( mMap, idx ) ) ;
}

01551 int QgsGrassProvider::updatedNode ( int idx )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::updatedNode idx = " << idx << std::endl;
    std::cerr << "  updatedNode = " << Vect_get_updated_node( mMap, idx ) << std::endl;
    #endif
    
    return ( Vect_get_updated_node( mMap, idx ) ) ;
}

// ------------------ Attributes -------------------------------------------------

01563 QString *QgsGrassProvider::key ( int field )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::key() field = " << field << std::endl;
    #endif

    QString *key = new QString();

    struct  field_info *fi = Vect_get_field( mMap, field); // should work also with field = 0

    if ( fi == NULL ) {
        #ifdef QGISDEBUG
      std::cerr << "No field info -> no attributes" << std::endl;
        #endif
      return key;
    }

    key->setAscii(fi->key);
    return key;
}

01584 std::vector<QgsField> *QgsGrassProvider::columns ( int field )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::columns() field = " << field << std::endl;
    #endif

    std::vector<QgsField> *col = new std::vector<QgsField>;
    
    struct  field_info *fi = Vect_get_field( mMap, field); // should work also with field = 0

    // Read attributes
    if ( fi == NULL ) {
        #ifdef QGISDEBUG
      std::cerr << "No field info -> no attributes" << std::endl;
        #endif
      return ( col );
    }

    #ifdef QGISDEBUG
    std::cerr << "Field info found -> open database" << std::endl;
    #endif
    dbDriver *driver = db_start_driver_open_database ( fi->driver, fi->database );

    if ( driver == NULL ) {
      std::cerr << "Cannot open database " << fi->database << " by driver " << fi->driver << std::endl;
      return ( col );
    }

    #ifdef QGISDEBUG
    std::cerr << "Database opened -> describe table" << std::endl;
    #endif

    dbString tableName;
    db_init_string ( &tableName );
    db_set_string ( &tableName, fi->table);
    
    dbTable *table;
    if(db_describe_table (driver, &tableName, &table) != DB_OK) {
      std::cerr << "Cannot describe table" << std::endl;
      return ( col );
    }

    int nCols = db_get_table_number_of_columns(table);

    for (int c = 0; c < nCols; c++) {
        dbColumn *column = db_get_table_column (table, c);

      int ctype = db_sqltype_to_Ctype( db_get_column_sqltype (column) );
      QString type;
      switch ( ctype ) {
          case DB_C_TYPE_INT:
            type = "int";
            break;
          case DB_C_TYPE_DOUBLE:
            type = "double";
            break;
          case DB_C_TYPE_STRING:
            type = "string";
            break;
          case DB_C_TYPE_DATETIME:
            type = "datetime";
            break;
      }
        col->push_back ( QgsField( db_get_column_name (column), type, db_get_column_length(column), 0) );
    }
      
    db_close_database_shutdown_driver ( driver );

    return col;
}

01655 std::vector<QgsFeatureAttribute> *QgsGrassProvider::attributes ( int field, int cat )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::attributes() field = " << field << " cat = " << cat << std::endl;
    #endif

    std::vector<QgsFeatureAttribute> *att = new std::vector<QgsFeatureAttribute>;

    struct  field_info *fi = Vect_get_field( mMap, field); // should work also with field = 0

    // Read attributes
    if ( fi == NULL ) {
        #ifdef QGISDEBUG
      std::cerr << "No field info -> no attributes" << std::endl;
        #endif
      return att;
    }

    #ifdef QGISDEBUG
    std::cerr << "Field info found -> open database" << std::endl;
    #endif
    dbDriver *driver = db_start_driver_open_database ( fi->driver, fi->database );

    if ( driver == NULL ) {
      std::cerr << "Cannot open database " << fi->database << " by driver " << fi->driver << std::endl;
      return att;
    }

    #ifdef QGISDEBUG
    std::cerr << "Database opened -> read attributes" << std::endl;
    #endif

    dbString dbstr; 
    db_init_string (&dbstr);
    QString query;
    query.sprintf("select * from %s where %s = %d", fi->table, fi->key, cat );
    db_set_string (&dbstr, (char *)query.ascii());
    
    #ifdef QGISDEBUG
    std::cerr << "SQL: " << db_get_string(&dbstr) << std::endl;
    #endif

    dbCursor databaseCursor;
    if ( db_open_select_cursor(driver, &dbstr, &databaseCursor, DB_SCROLL) != DB_OK ){
      db_close_database_shutdown_driver ( driver );
      std::cerr << "Cannot select attributes from table" << std::endl;
      return att;
    } 

    int nRecords = db_get_num_rows ( &databaseCursor );
    #ifdef QGISDEBUG
    std::cerr << "Number of records: " << nRecords << std::endl;
    #endif

    if ( nRecords < 1 ) {
        std::cerr << "No DB record" << std::endl;
      return att;
    }
    
    dbTable  *databaseTable = db_get_cursor_table (&databaseCursor);
    int nColumns = db_get_table_number_of_columns(databaseTable);

    int more;
    if ( db_fetch (&databaseCursor, DB_NEXT, &more) != DB_OK ) {
      std::cout << "Cannot fetch DB record" << std::endl;
      return att;
    }

    // Read columns' description 
    for (int i = 0; i < nColumns; i++) {
      dbColumn *column = db_get_table_column (databaseTable, i);
      db_convert_column_value_to_string (column, &dbstr);
      std::cerr << "Value: " << db_get_string(&dbstr) << std::endl;
        att->push_back ( QgsFeatureAttribute( db_get_column_name(column), 
                                          db_get_string(&dbstr) ) );
    }

    db_close_cursor (&databaseCursor);
    db_close_database_shutdown_driver ( driver );
    db_free_string(&dbstr);

    return att;
}

01739 QString *QgsGrassProvider::updateAttributes ( int field, int cat, const QString &values )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::updateAttributes() field = " << field << " cat = " << cat << std::endl;
    #endif

    QString *error = new QString();
    struct  field_info *fi = Vect_get_field( mMap, field); // should work also with field = 0

    // Read attributes
    if ( fi == NULL ) {
        #ifdef QGISDEBUG
      std::cerr << "No field info -> no attributes" << std::endl;
        #endif
      error->setLatin1( "Cannot get field info" );
      return error;
    }

    #ifdef QGISDEBUG
    std::cerr << "Field info found -> open database" << std::endl;
    #endif
    dbDriver *driver = db_start_driver_open_database ( fi->driver, fi->database );

    if ( driver == NULL ) {
      std::cerr << "Cannot open database " << fi->database << " by driver " << fi->driver << std::endl;
      error->setAscii("Cannot open database");
      return error;
    }

    #ifdef QGISDEBUG
    std::cerr << "Database opened -> read attributes" << std::endl;
    #endif

    dbString dbstr; 
    db_init_string (&dbstr);
    QString query;
    
    query.sprintf("update %s set %s where %s = %d", fi->table, values.latin1(), fi->key, cat );
    db_set_string (&dbstr, (char *)query.latin1());
    
    #ifdef QGISDEBUG
    std::cerr << "SQL: " << db_get_string(&dbstr) << std::endl;
    #endif

    int ret = db_execute_immediate (driver, &dbstr);

    if ( ret != DB_OK) { 
        std::cerr << "Error: " <<  db_get_error_msg() << std::endl;
      error->setLatin1( db_get_error_msg() );
    }

    db_close_database_shutdown_driver ( driver );
    db_free_string(&dbstr);

    return error;
}

01796 int QgsGrassProvider::numDbLinks ( void )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::numDbLinks()" << std::endl;
    #endif

    return ( Vect_get_num_dblinks(mMap) );
}

01805 int QgsGrassProvider::dbLinkField ( int link )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::dbLinkField()" << std::endl;
    #endif

    struct  field_info *fi = Vect_get_dblink ( mMap, link );

    if ( fi == NULL ) return 0;

    return ( fi->number );
}



01820 QString *QgsGrassProvider::createTable ( int field, const QString &key, const QString &columns )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::createTable() field = " << field << std::endl;
    #endif

    QString *error = new QString();
    struct  field_info *fi = Vect_get_field( mMap, field); // should work also with field = 0

    // Read attributes
    if ( fi != NULL ) {
      #ifdef QGISDEBUG
      std::cerr << "The table for this field already exists" << std::endl;
      #endif
      error->setLatin1( "The table for this field already exists" );
      return error;
    }

    #ifdef QGISDEBUG
    std::cerr << "Field info not found -> create new table" << std::endl;
    #endif

    int nLinks = Vect_get_num_dblinks( mMap );
    if ( nLinks == 0 ) {
        fi = Vect_default_field_info ( mMap, field, NULL, GV_1TABLE );
    } else {
        fi = Vect_default_field_info ( mMap, field, NULL, GV_MTABLE );
    }
    
    dbDriver *driver = db_start_driver_open_database ( fi->driver, fi->database );

    if ( driver == NULL ) {
      std::cerr << "Cannot open database " << fi->database << " by driver " << fi->driver << std::endl;
      error->setAscii("Cannot open database");
      return error;
    }

    #ifdef QGISDEBUG
    std::cerr << "Database opened -> create table" << std::endl;
    #endif

    dbString dbstr; 
    db_init_string (&dbstr);
    QString query;
    
    query.sprintf("create table %s ( %s )", fi->table, columns.latin1() );
    db_set_string (&dbstr, (char *)query.latin1());
    
    #ifdef QGISDEBUG
    std::cerr << "SQL: " << db_get_string(&dbstr) << std::endl;
    #endif

    int ret = db_execute_immediate (driver, &dbstr);

    if ( ret != DB_OK) { 
        std::cerr << "Error: " <<  db_get_error_msg() << std::endl;
      error->setLatin1( db_get_error_msg() );
    }

    db_close_database_shutdown_driver ( driver );
    db_free_string(&dbstr);

    if ( !error->isEmpty() ) return error;

    ret = Vect_map_add_dblink ( mMap, field, NULL, fi->table, (char *)key.latin1(), 
                                  fi->database, fi->driver);

    if ( ret == -1 ) { 
        std::cerr << "Error: Cannot add dblink" << std::endl;
      error->setLatin1( "Cannot create link to the table. The table was created!" );
    }

    return error;
}

01895 QString *QgsGrassProvider::addColumn ( int field, const QString &column )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::addColumn() field = " << field << std::endl;
    #endif

    QString *error = new QString();
    struct  field_info *fi = Vect_get_field( mMap, field); // should work also with field = 0

    // Read attributes
    if ( fi == NULL ) {
        #ifdef QGISDEBUG
      std::cerr << "No field info" << std::endl;
        #endif
      error->setLatin1( "Cannot get field info" );
      return error;
    }

    #ifdef QGISDEBUG
    std::cerr << "Field info found -> open database" << std::endl;
    #endif
    dbDriver *driver = db_start_driver_open_database ( fi->driver, fi->database );

    if ( driver == NULL ) {
      std::cerr << "Cannot open database " << fi->database << " by driver " << fi->driver << std::endl;
      error->setAscii("Cannot open database");
      return error;
    }

    #ifdef QGISDEBUG
    std::cerr << "Database opened -> add column" << std::endl;
    #endif

    dbString dbstr; 
    db_init_string (&dbstr);
    QString query;
    
    query.sprintf("alter table %s add column %s", fi->table, column.latin1() );
    db_set_string (&dbstr, (char *)query.latin1());
    
    #ifdef QGISDEBUG
    std::cerr << "SQL: " << db_get_string(&dbstr) << std::endl;
    #endif

    int ret = db_execute_immediate (driver, &dbstr);

    if ( ret != DB_OK) { 
        std::cerr << "Error: " <<  db_get_error_msg() << std::endl;
      error->setLatin1( db_get_error_msg() );
    }

    db_close_database_shutdown_driver ( driver );
    db_free_string(&dbstr);

    return error;
}

01952 QString *QgsGrassProvider::insertAttributes ( int field, int cat )
{
    #ifdef QGISDEBUG
    std::cerr << "QgsGrassProvider::insertAttributes() field = " << field << " cat = " << cat << std::endl;
    #endif

    QString *error = new QString();
    struct  field_info *fi = Vect_get_field( mMap, field); // should work also with field = 0

    // Read attributes
    if ( fi == NULL ) {
        #ifdef QGISDEBUG
      std::cerr << "No field info -> no attributes" << std::endl;
        #endif
      error->setLatin1( "Cannot get field info" );
      return error;
    }

    #ifdef QGISDEBUG
    std::cerr << "Field info found -> open database" << std::endl;
    #endif
    dbDriver *driver = db_start_driver_open_database ( fi->driver, fi->database );

    if ( driver == NULL ) {
      std::cerr << "Cannot open database " << fi->database << " by driver " << fi->driver << std::endl;
      error->setAscii("Cannot open database");
      return error;
    }

    #ifdef QGISDEBUG
    std::cerr << "Database opened -> insert new record" << std::endl;
    #endif

    dbString dbstr; 
    db_init_string (&dbstr);
    QString query;
    
    query.sprintf("insert into %s ( %s ) values ( %d )", fi->table, fi->key, cat );
    db_set_string (&dbstr, (char *)query.latin1());
    
    #ifdef QGISDEBUG
    std::cerr << "SQL: " << db_get_string(&dbstr) << std::endl;
    #endif

    int ret = db_execute_immediate (driver, &dbstr);

    if ( ret != DB_OK) { 
        std::cerr << "Error: " <<  db_get_error_msg() << std::endl;
      error->setLatin1( db_get_error_msg() );
    }

    db_close_database_shutdown_driver ( driver );
    db_free_string(&dbstr);

    return error;
}


// -------------------------------------------------------------------------------

02012 int QgsGrassProvider::cidxGetNumFields( void ) 
{
    return ( Vect_cidx_get_num_fields(mMap) );
}

02017 int QgsGrassProvider::cidxGetFieldNumber( int idx ) 
{
    return ( Vect_cidx_get_field_number(mMap, idx) );
}

02022 int QgsGrassProvider::cidxGetMaxCat( int idx ) 
{
    int ncats = Vect_cidx_get_num_cats_by_index ( mMap, idx);

    int cat, type, id;
    Vect_cidx_get_cat_by_index ( mMap, idx, ncats-1, &cat, &type, &id );
    
    return ( cat );
}
    

Generated by  Doxygen 1.6.0   Back to index