[GRASS] refactored vector provider, initial new editing (change geometry)

This commit is contained in:
Radim Blazek 2015-09-16 12:45:15 +02:00
parent 0a9bf95ea9
commit 60cce74913
16 changed files with 2798 additions and 1701 deletions

View File

@ -20,6 +20,7 @@ ENDIF (WIN32)
# Files
SET (GRASS_PLUGIN_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/qgsgrasseditrenderer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/qgsgrassplugin.cpp
${CMAKE_CURRENT_SOURCE_DIR}/qgsgrassselect.cpp
${CMAKE_CURRENT_SOURCE_DIR}/qgsgrasstools.cpp
@ -45,6 +46,7 @@ SET (GRASS_PLUGIN_UIS
)
SET (GRASS_PLUGIN_MOC_HDRS
${CMAKE_CURRENT_SOURCE_DIR}/qgsgrasseditrenderer.h
${CMAKE_CURRENT_SOURCE_DIR}/qgsgrassplugin.h
${CMAKE_CURRENT_SOURCE_DIR}/qgsgrassselect.h
${CMAKE_CURRENT_SOURCE_DIR}/qgsgrasstools.h
@ -149,8 +151,13 @@ INCLUDE_DIRECTORIES(
${CMAKE_CURRENT_BINARY_DIR}
../../core
../../core/geometry
../../core/layertree
../../core/raster
../../core/symbology-ng
../../gui
../../gui/editorwidgets
../../gui/symbology-ng
../../gui/layertree
../../providers/grass
${CMAKE_CURRENT_BINARY_DIR}/../../ui
${GDAL_INCLUDE_DIR}

View File

@ -0,0 +1,279 @@
/***************************************************************************
qgsgrasseditrenderer.cpp
-------------------
begin : February, 2015
copyright : (C) 2015 by Radim Blazek
email : radim.blazek@gmail.com
***************************************************************************/
/***************************************************************************
* *
* 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 <QVBoxLayout>
#include "qgscategorizedsymbolrendererv2.h"
#include "qgscategorizedsymbolrendererv2widget.h"
#include "qgsfeature.h"
#include "qgslogger.h"
#include "qgsrendererv2registry.h"
#include "qgssymbollayerv2.h"
#include "qgssymbollayerv2utils.h"
#include "qgssymbolv2.h"
#include "qgsgrasseditrenderer.h"
#include "qgsgrassprovider.h"
QgsGrassEditRenderer::QgsGrassEditRenderer()
: QgsFeatureRendererV2( "grassEdit" )
, mLineRenderer( 0 )
, mMarkerRenderer( 0 )
{
QHash<int, QColor> colors;
colors.insert( QgsGrassProvider::TopoUndefined, QColor( 125, 125, 125 ) );
colors.insert( QgsGrassProvider::TopoLine, QColor( Qt::black ) );
colors.insert( QgsGrassProvider::TopoBoundary0, QColor( Qt::red ) );
colors.insert( QgsGrassProvider::TopoBoundary1, QColor( 255, 125, 0 ) );
colors.insert( QgsGrassProvider::TopoBoundary2, QColor( Qt::green ) );
QHash<int, QString> labels;
labels.insert( QgsGrassProvider::TopoUndefined, "Unknown type" );
labels.insert( QgsGrassProvider::TopoLine, "Line" );
labels.insert( QgsGrassProvider::TopoBoundary0, "Boundary (isolated)" );
labels.insert( QgsGrassProvider::TopoBoundary1, "Boundary (area on one side)" );
labels.insert( QgsGrassProvider::TopoBoundary2, "Boundary (areas on both sides)" );
QgsCategoryList categoryList;
foreach ( int value, colors.keys() )
{
QgsSymbolV2 * symbol = QgsSymbolV2::defaultSymbol( QGis::Line );
symbol->setColor( colors.value( value ) );
categoryList << QgsRendererCategoryV2( QVariant( value ), symbol, labels.value( value ) );
}
mLineRenderer = new QgsCategorizedSymbolRendererV2( "topo_symbol", categoryList );
colors.clear();
labels.clear();
colors.insert( QgsGrassProvider::TopoPoint, QColor( 0, 0, 0 ) );
colors.insert( QgsGrassProvider::TopoCentroidIn, QColor( 0, 255, 0 ) );
colors.insert( QgsGrassProvider::TopoCentroidOut, QColor( 255, 0, 0 ) );
colors.insert( QgsGrassProvider::TopoCentroidDupl, QColor( 255, 0, 255 ) );
labels.insert( QgsGrassProvider::TopoPoint, "Point" );
labels.insert( QgsGrassProvider::TopoCentroidIn, "Centroid in area" );
labels.insert( QgsGrassProvider::TopoCentroidOut, "Centroid outside area" );
labels.insert( QgsGrassProvider::TopoCentroidDupl, "Duplicate centroid" );
categoryList.clear();
foreach ( int value, colors.keys() )
{
QgsSymbolV2 * symbol = QgsSymbolV2::defaultSymbol( QGis::Point );
symbol->setColor( colors.value( value ) );
categoryList << QgsRendererCategoryV2( QVariant( value ), symbol, labels.value( value ) );
}
mMarkerRenderer = new QgsCategorizedSymbolRendererV2( "topo_symbol", categoryList );
}
QgsGrassEditRenderer::~QgsGrassEditRenderer()
{
}
void QgsGrassEditRenderer::setLineRenderer( QgsFeatureRendererV2 *renderer )
{
delete mLineRenderer;
mLineRenderer = renderer;
}
void QgsGrassEditRenderer::setMarkerRenderer( QgsFeatureRendererV2 *renderer )
{
delete mMarkerRenderer;
mMarkerRenderer = renderer;
}
QgsSymbolV2* QgsGrassEditRenderer::symbolForFeature( QgsFeature& feature, QgsRenderContext& context )
{
int symbolCode = feature.attribute( "topo_symbol" ).toInt();
QgsDebugMsgLevel( QString( "fid = %1 symbolCode = %2" ).arg( feature.id() ).arg( symbolCode ), 3 );
QgsSymbolV2* symbol = 0;
if ( symbolCode == QgsGrassProvider::TopoPoint || symbolCode == QgsGrassProvider::TopoCentroidIn ||
symbolCode == QgsGrassProvider::TopoCentroidOut || symbolCode == QgsGrassProvider::TopoCentroidDupl ||
symbolCode == QgsGrassProvider::TopoNode0 || symbolCode == QgsGrassProvider::TopoNode1 ||
symbolCode == QgsGrassProvider::TopoNode2 )
{
symbol = mMarkerRenderer->symbolForFeature( feature, context );
}
else if ( symbolCode == QgsGrassProvider::TopoLine || symbolCode == QgsGrassProvider::TopoBoundary0 ||
symbolCode == QgsGrassProvider::TopoBoundary1 || symbolCode == QgsGrassProvider::TopoBoundary2 )
{
symbol = mLineRenderer->symbolForFeature( feature, context );
}
else
{
// should not happen
QgsDebugMsg( "unknown symbol code" );
}
if ( symbol )
{
QgsDebugMsgLevel( "color = " + symbol->color().name(), 3 );
}
else
{
QgsDebugMsgLevel( "no symbol", 3 );
}
return symbol;
}
void QgsGrassEditRenderer::startRender( QgsRenderContext& context, const QgsFields& fields )
{
Q_UNUSED( fields );
// TODO better
//QgsFields topoFields;
//topoFields.append( QgsField( "topo_symbol", QVariant::Int, "int" ) );
mLineRenderer->startRender( context, fields );
mMarkerRenderer->startRender( context, fields );
}
void QgsGrassEditRenderer::stopRender( QgsRenderContext& context )
{
mLineRenderer->stopRender( context );
mMarkerRenderer->stopRender( context );
}
QList<QString> QgsGrassEditRenderer::usedAttributes()
{
return mLineRenderer->usedAttributes();
}
QgsFeatureRendererV2* QgsGrassEditRenderer::clone() const
{
QgsGrassEditRenderer* r = new QgsGrassEditRenderer();
if ( mLineRenderer )
{
r->mLineRenderer = mLineRenderer->clone();
}
if ( mMarkerRenderer )
{
r->mMarkerRenderer = mMarkerRenderer->clone();
}
return r;
}
QgsSymbolV2List QgsGrassEditRenderer::symbols( QgsRenderContext& context )
{
return mLineRenderer->symbols( context );
}
QString QgsGrassEditRenderer::dump() const
{
return "GRASS edit renderer";
}
QDomElement QgsGrassEditRenderer::save( QDomDocument& doc )
{
QgsDebugMsg( "entered" );
QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
rendererElem.setAttribute( "type", "grassEdit" );
QDomElement lineElem = doc.createElement( "line" );
rendererElem.appendChild( lineElem );
lineElem.appendChild( mLineRenderer->save( doc ) );
QDomElement pointElem = doc.createElement( "marker" );
rendererElem.appendChild( pointElem );
pointElem.appendChild( mMarkerRenderer->save( doc ) );
return rendererElem;
}
QgsFeatureRendererV2* QgsGrassEditRenderer::create( QDomElement& element )
{
QgsDebugMsg( "entered" );
QgsGrassEditRenderer *renderer = new QgsGrassEditRenderer();
QDomElement childElem = element.firstChildElement();
while ( !childElem.isNull() )
{
QDomElement elem = childElem.firstChildElement();
if ( !elem.isNull() )
{
QString rendererType = elem.attribute( "type" );
QgsDebugMsg( "childElem.tagName() = " + childElem.tagName() + " rendererType = " + rendererType );
QgsRendererV2AbstractMetadata* meta = QgsRendererV2Registry::instance()->rendererMetadata( rendererType );
if ( meta )
{
QgsFeatureRendererV2* subRenderer = meta->createRenderer( elem );
if ( subRenderer )
{
QgsDebugMsg( "renderer created : " + renderer->type() );
if ( childElem.tagName() == "line" )
{
renderer->setLineRenderer( subRenderer );
}
else if ( childElem.tagName() == "marker" )
{
renderer->setMarkerRenderer( subRenderer );
}
}
}
}
childElem = childElem.nextSiblingElement();
}
return renderer;
}
//--------------------------------------- QgsGrassEditRendererWidget --------------------------------------------
QgsRendererV2Widget* QgsGrassEditRendererWidget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
{
QgsDebugMsg( "entered" );
return new QgsGrassEditRendererWidget( layer, style, renderer );
}
QgsGrassEditRendererWidget::QgsGrassEditRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
: QgsRendererV2Widget( layer, style )
, mRenderer( 0 )
{
QgsDebugMsg( "entered" );
mRenderer = dynamic_cast<QgsGrassEditRenderer*>( renderer->clone() );
if ( !mRenderer )
{
return;
}
QVBoxLayout* layout = new QVBoxLayout( this );
mLineRendererWidget = QgsCategorizedSymbolRendererV2Widget::create( layer, style, mRenderer->lineRenderer()->clone() );
layout->addWidget( mLineRendererWidget );
mPointRendererWidget = QgsCategorizedSymbolRendererV2Widget::create( layer, style, mRenderer->pointRenderer()->clone() );
layout->addWidget( mPointRendererWidget );
}
QgsGrassEditRendererWidget::~QgsGrassEditRendererWidget()
{
QgsDebugMsg( "entered" );
delete mRenderer;
}
QgsFeatureRendererV2* QgsGrassEditRendererWidget::renderer()
{
QgsDebugMsg( "entered" );
mRenderer->setLineRenderer( mLineRendererWidget->renderer()->clone() );
mRenderer->setMarkerRenderer( mPointRendererWidget->renderer()->clone() );
return mRenderer;
}

View File

@ -0,0 +1,96 @@
/***************************************************************************
qgsgrasseditrenderer.h
-------------------
begin : February, 2015
copyright : (C) 2015 by Radim Blazek
email : radim.blazek@gmail.com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef QGSGRASSEDITRENDERER_H
#define QGSGRASSEDITRENDERER_H
#include "qgis.h"
#include "qgscategorizedsymbolrendererv2.h"
#include "qgsrendererv2.h"
#include "qgssymbolv2.h"
#include "qgscategorizedsymbolrendererv2.h"
#include "qgsrendererv2widget.h"
class QgsGrassEditRenderer : public QgsFeatureRendererV2
{
public:
enum TopoSymbol
{
TopoPoint,
TopoLine,
TopoBoundary0,
TopoBoundary1,
TopoBoundary2,
TopoCentroidIn,
TopoCentroidOut,
TopoCentroidDupl,
TopoNode0,
TopoNode1,
TopoNode2
};
QgsGrassEditRenderer( );
virtual ~QgsGrassEditRenderer();
virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature, QgsRenderContext& context ) override;
virtual void startRender( QgsRenderContext& context, const QgsFields& fields ) override;
virtual void stopRender( QgsRenderContext& context ) override;
virtual QList<QString> usedAttributes() override;
virtual QgsFeatureRendererV2* clone() const override;
virtual QgsSymbolV2List symbols( QgsRenderContext& context ) override;
virtual QString dump() const override;
QgsFeatureRendererV2 *lineRenderer() const { return mLineRenderer; }
QgsFeatureRendererV2 *pointRenderer() const { return mMarkerRenderer; }
void setLineRenderer( QgsFeatureRendererV2 *renderer );
void setMarkerRenderer( QgsFeatureRendererV2 *renderer );
virtual QDomElement save( QDomDocument& doc ) override;
static QgsFeatureRendererV2* create( QDomElement& element );
protected:
QgsFeatureRendererV2 *mLineRenderer;
QgsFeatureRendererV2 *mMarkerRenderer;
};
class GUI_EXPORT QgsGrassEditRendererWidget : public QgsRendererV2Widget
{
Q_OBJECT
public:
static QgsRendererV2Widget* create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer );
QgsGrassEditRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer );
~QgsGrassEditRendererWidget();
virtual QgsFeatureRendererV2* renderer() override;
protected:
QgsGrassEditRenderer* mRenderer;
QgsRendererV2Widget* mLineRendererWidget;
QgsRendererV2Widget* mPointRendererWidget;
};
#endif // QGSGRASSEDITRENDERER_H

View File

@ -17,8 +17,9 @@
#include "qgsgrassplugin.h"
#include "qgis.h"
#include "qgsgrass.h"
#include "qgsgrassprovider.h"
//the gui subclass
#include "qgsgrasseditrenderer.h"
#include "qgsgrassnewmapset.h"
#include "qgsgrassregion.h"
#include "qgsgrassselect.h"
@ -28,10 +29,14 @@
// includes
#include "qgisinterface.h"
#include "qgsapplication.h"
#include "qgslayertreeview.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsmaplayerstylemanager.h"
#include "qgsrubberband.h"
#include "qgsproject.h"
#include "qgsproviderregistry.h"
#include "qgsrendererv2registry.h"
#include "qgsvectorlayer.h"
#include "qgsmaplayerregistry.h"
@ -211,11 +216,103 @@ void QgsGrassPlugin::initGui()
connect( QgsGrass::instance(), SIGNAL( regionChanged() ), SLOT( displayRegion() ) );
connect( QgsGrass::instance(), SIGNAL( regionPenChanged() ), SLOT( displayRegion() ) );
// Connect start/stop editing
connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( onLayerWasAdded( QgsMapLayer* ) ) );
connect( qGisInterface->layerTreeView(), SIGNAL( currentLayerChanged( QgsMapLayer* ) ),
SLOT( onCurrentLayerChanged( QgsMapLayer* ) ) );
mapsetChanged();
// open tools when plugin is loaded so that main app restores tools dock widget state
mTools = new QgsGrassTools( qGisInterface, qGisInterface->mainWindow() );
qGisInterface->addDockWidget( Qt::RightDockWidgetArea, mTools );
}
void QgsGrassPlugin::onLayerWasAdded( QgsMapLayer* theMapLayer )
{
QgsDebugMsg( "name = " + theMapLayer->name() );
QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( theMapLayer );
if ( !vectorLayer )
return;
QgsGrassProvider* grassProvider = dynamic_cast<QgsGrassProvider*>( vectorLayer->dataProvider() );
if ( !grassProvider )
return;
QgsDebugMsg( "connect editing" );
connect( vectorLayer, SIGNAL( editingStarted() ), this, SLOT( onEditingStarted() ) );
}
void QgsGrassPlugin::onCurrentLayerChanged( QgsMapLayer* layer )
{
Q_UNUSED( layer );
QgsDebugMsg( "Entered" );
}
void QgsGrassPlugin::onEditingStarted()
{
QgsDebugMsg( "Entered" );
QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( sender() );
if ( !vectorLayer )
return;
QgsDebugMsg( "started editing of layer " + vectorLayer->name() );
// Set editing renderer
QgsGrassProvider* grassProvider = dynamic_cast<QgsGrassProvider*>( vectorLayer->dataProvider() );
if ( !grassProvider )
return;
QgsRendererV2Registry::instance()->addRenderer( new QgsRendererV2Metadata( "grassEdit",
QObject::tr( "GRASS Edit" ),
QgsGrassEditRenderer::create,
QIcon(),
QgsGrassEditRendererWidget::create ) );
QgsGrassEditRenderer *renderer = new QgsGrassEditRenderer();
mOldStyles[vectorLayer] = vectorLayer->styleManager()->currentStyle();
// Because the edit style may be stored to project:
// - do not translate because it may be loaded in QGIS running with different language
// - do not change the name until really necessary because it could not be found in project
QString editStyleName = "GRASS Edit"; // should not be translated
if ( vectorLayer->styleManager()->styles().contains( editStyleName ) )
{
QgsDebugMsg( editStyleName + " style exists -> set as current" );
vectorLayer->styleManager()->setCurrentStyle( editStyleName );
}
else
{
QgsDebugMsg( "create and set style " + editStyleName );
vectorLayer->styleManager()->addStyleFromLayer( editStyleName );
//vectorLayer->styleManager()->addStyle( editStyleName, QgsMapLayerStyle() );
vectorLayer->styleManager()->setCurrentStyle( editStyleName );
vectorLayer->setRendererV2( renderer );
}
grassProvider->startEditing( vectorLayer );
vectorLayer->updateFields();
connect( vectorLayer, SIGNAL( editingStopped() ), SLOT( onEditingStopped() ) );
}
void QgsGrassPlugin::onEditingStopped()
{
QgsDebugMsg( "entered" );
QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( sender() );
if ( vectorLayer )
{
QString style = mOldStyles.value( vectorLayer );
if ( vectorLayer->styleManager()->currentStyle() == "GRASS Edit" ) // not changed by user
{
QgsDebugMsg( "reset style to " + style );
vectorLayer->styleManager()->setCurrentStyle( style );
}
}
}
void QgsGrassPlugin::mapsetChanged()
@ -490,7 +587,7 @@ void QgsGrassPlugin::projectRead()
void QgsGrassPlugin::newProject()
{
// QgsDebugMsg("entered.");
QgsDebugMsg( "entered" );
}
// Unload the plugin by cleaning up the GUI
@ -519,8 +616,6 @@ void QgsGrassPlugin::unload()
// disconnect slots of QgsGrassPlugin so they're not fired also after unload
disconnect( mCanvas, SIGNAL( renderComplete( QPainter * ) ), this, SLOT( postRender( QPainter * ) ) );
disconnect( qGisInterface, SIGNAL( currentLayerChanged( QgsMapLayer * ) ),
this, SLOT( setEditAction() ) );
QWidget* qgis = qGisInterface->mainWindow();
disconnect( qgis, SIGNAL( projectRead() ), this, SLOT( projectRead() ) );

View File

@ -25,7 +25,9 @@ class QgsGrassNewMapset;
class QgsGrassRegion;
class QgsMapCanvas;
class QgsMapLayer;
class QgsRubberBand;
class QgsVectorLayer;
class QAction;
class QIcon;
@ -109,6 +111,12 @@ class QgsGrassPlugin : public QObject, public QgisPlugin
//! update plugin icons when the app tells us its theme is changed
void setCurrentTheme( QString theThemeName );
void setTransform();
//! Called when a new layer was added to map registry
void onLayerWasAdded( QgsMapLayer* theMapLayer );
//! Called when editing of a layer started
void onEditingStarted();
void onEditingStopped();
void onCurrentLayerChanged( QgsMapLayer* layer );
private:
//! Pointer to our toolbar
QToolBar *mToolBarPointer;
@ -136,6 +144,9 @@ class QgsGrassPlugin : public QObject, public QgisPlugin
QAction *mCloseMapsetAction;
QAction *mOpenToolsAction;
QAction *mNewVectorAction;
// Names of layer styles before editing started
QMap<QgsVectorLayer *, QString> mOldStyles;
};
#endif // QGSGRASSPLUGIN_H

View File

@ -24,10 +24,13 @@ MACRO(ADD_GRASSLIB GRASS_BUILD_VERSION)
QT4_WRAP_CPP(GRASS_LIBRARY_MOC_SRCS
../qgsgrass.h
../qgsgrassfeatureiterator.h
../qgsgrassimport.h
../qgsgrassoptions.h
../qgsgrassprovider.h
../qgsgrassvector.h
../qgsgrassvectormap.h
../qgsgrassvectormaplayer.h
)
SET (GRASS_LIBRARY_SRCS
@ -38,6 +41,8 @@ MACRO(ADD_GRASSLIB GRASS_BUILD_VERSION)
../qgsgrassoptions.cpp
../qgsgrassprovider.cpp
../qgsgrassvector.cpp
../qgsgrassvectormap.cpp
../qgsgrassvectormaplayer.cpp
)
QT4_WRAP_UI (GRASS_LIBRARY_UIS_H
@ -108,8 +113,16 @@ MACRO(ADD_GRASSLIB GRASS_BUILD_VERSION)
#
# GRASS vector provider
#
QT4_WRAP_CPP(GRASS_PROVIDER_MOC_SRCS ../qgsgrassprovidermodule.h)
ADD_LIBRARY(grassprovider${GRASS_BUILD_VERSION} MODULE ../qgsgrassprovidermodule.cpp ${GRASS_PROVIDER_MOC_SRCS})
SET (GRASS_VECTOR_PROVIDER_SRCS
../qgsgrassvectormap.cpp
../qgsgrassvectormaplayer.cpp
../qgsgrassprovidermodule.cpp
)
QT4_WRAP_CPP(GRASS_VECTOR_PROVIDER_MOC_SRCS
../qgsgrassfeatureiterator.h
../qgsgrassprovidermodule.h
)
ADD_LIBRARY(grassprovider${GRASS_BUILD_VERSION} MODULE ${GRASS_VECTOR_PROVIDER_SRCS} ${GRASS_VECTOR_PROVIDER_MOC_SRCS})
SET_TARGET_PROPERTIES(grassprovider${GRASS_BUILD_VERSION} PROPERTIES
COMPILE_FLAGS "-DGRASS_BASE=\\\"${GRASS_PREFIX}\\\" \"-DGRASS_EXPORT=${DLLEXPORT}\" \"-DGRASS_LIB_EXPORT=${DLLIMPORT}\""
)

View File

@ -593,6 +593,18 @@ QgsGrass *QgsGrass::instance()
return &sInstance;
}
void QgsGrass::lock()
{
QgsDebugMsg( "lock" );
sMutex.lock();
}
void QgsGrass::unlock()
{
QgsDebugMsg( "unlock" );
sMutex.unlock();
}
bool QgsGrass::activeMode()
{
return active;
@ -895,7 +907,7 @@ QString QgsGrass::openMapset( const QString& gisdbase,
out.close();
// Set GISRC environment variable
// Mapset must be set before Vect_close()
/* _Correct_ putenv() implementation is not making copy! */
putEnv( "GISRC", mGisrc );

View File

@ -165,8 +165,8 @@ class GRASS_LIB_EXPORT QgsGrass : public QObject
static QgsGrass* instance();
/** Global GRASS library lock */
static void lock() { sMutex.lock(); }
static void unlock() { sMutex.unlock(); }
static void lock();
static void unlock();
/** Path to where GRASS is installed (GISBASE) */
static QString gisbase() { return mGisbase; }

File diff suppressed because it is too large Load Diff

View File

@ -18,9 +18,11 @@
#include "qgsfeatureiterator.h"
#include <QMutex>
#include <QBitArray>
class QgsGrassProvider;
#include "qgsgrassprovider.h"
//class QgsGrassProvider;
class QgsGrassVectorMapLayer;
class QgsGrassFeatureSource : public QgsAbstractFeatureSource
{
@ -31,11 +33,28 @@ class QgsGrassFeatureSource : public QgsAbstractFeatureSource
virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request ) override;
protected:
struct Map_info* mMap;
#if 0
enum Selection
{
NotSelected = 0, /*!< not selected */
Selected = 1, /*!< line/area selected */
Used = 2 /*!< the line was already used to create feature read in this cycle.
* The codes Used must be reset to Selected if getFirstFeature() or select() is called.
* Distinction between Selected and Used is used if attribute table exists, in which case
* attributes are read from the table and line geometry is attached to attributes and selection
* for that line is set to Used. In the end the selection is scanned for Selected (attributes missing)
* and the geometry is returned without attributes. */
};
#endif
struct Map_info* map();
QgsGrassVectorMapLayer * mLayer;
int mLayerType; // layer type POINT, LINE, ...
int mGrassType; // grass feature type: GV_POINT, GV_LINE | GV_BOUNDARY, GV_AREA,
int mLayerId; // ID used in layers
QGis::WkbType mQgisType;// WKBPoint, WKBLineString, ...
QGis::WkbType mQgisType; // WKBPoint, WKBLineString, ...
int mCidxFieldIndex; // !UPDATE! Index for layerField in category index or -1 if no such field
int mCidxFieldNumCats; // !UPDATE! Number of records in field index
@ -43,12 +62,14 @@ class QgsGrassFeatureSource : public QgsAbstractFeatureSource
QgsFields mFields;
QTextCodec* mEncoding;
bool mEditing; // Standard QGIS editing mode
friend class QgsGrassFeatureIterator;
};
class QgsGrassFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsGrassFeatureSource>
class QgsGrassFeatureIterator : public QObject, public QgsAbstractFeatureIteratorFromSource<QgsGrassFeatureSource>
{
Q_OBJECT
public:
QgsGrassFeatureIterator( QgsGrassFeatureSource* source, bool ownSource, const QgsFeatureRequest& request );
@ -63,60 +84,65 @@ class QgsGrassFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsG
//! end of iterating: free the resources / lock
virtual bool close() override;
protected:
// create QgsFeatureId from GRASS geometry object id and cat
static QgsFeatureId makeFeatureId( int grassId, int cat );
// Get GRASS line id from QGIS fid
static int lidFormFid( QgsFeatureId fid );
// Get GRASS cat from QGIS fid
static int catFormFid( QgsFeatureId fid );
public slots:
/** Cancel iterator, iterator will be closed on next occasion, probably when next getFeature() gets called.
* This function can be called directly from other threads (setting bool is atomic) */
void cancel();
void doClose();
protected:
//void lock();
//void unlock();
/** Reset selection */
void resetSelection( bool value );
void setSelectionRect( const QgsRectangle& rect, bool useIntersect );
void setFeatureGeometry( QgsFeature& feature, int id, int type );
/** Set feature attributes.
* @param feature
* @param cat category number
*/
void setFeatureAttributes( int cat, QgsFeature *feature );
void setFeatureAttributes( int cat, QgsFeature *feature, QgsGrassProvider::TopoSymbol symbol );
/** Set feature attributes.
* @param feature
* @param cat category number
* @param attlist a list containing the index number of the fields to set
*/
void setFeatureAttributes( int cat, QgsFeature *feature, const QgsAttributeList & attlist );
void setFeatureAttributes( int cat, QgsFeature *feature, const QgsAttributeList & attlist, QgsGrassProvider::TopoSymbol symbol );
struct line_pnts *mPoints; // points structure
struct line_cats *mCats; // cats structure
struct ilist *mList;
/** Get topology symbol code
* @param lid line or area number
* @param type geometry type */
QgsGrassProvider::TopoSymbol topoSymbol( int lid, int type );
// selection: array of size nlines or nareas + 1, set to 1 - selected or 0 - not selected, 2 - read
// Code 2 means that the line was already read in this cycle, all 2 must be reset to 1
// if getFirstFeature() or select() is calles.
// Distinction between 1 and 2 is used if attribute table exists, in that case attributes are
// read from the table and geometry is append and selection set to 2.
// In the end the selection array is scanned for 1 (attributes missing), and the geometry
// is returned without attributes
char *mSelection; // !UPDATE!
int mSelectionSize; // !UPDATE! Size of selection array
/** Canceled -> close when possible */
bool mCanceled;
// Either mNextCidx or mNextTopoId is used according to type
int mNextCidx; // !UPDATE! Next index in cidxFieldIndex to be read, used to find nextFeature
int mNextTopoId; // !UPDATE! Next topology id to be read, used to find nextFeature, starts from 1
/** Selection array */
QBitArray mSelection; // !UPDATE!
// Edit mode is using mNextLid + mNextCidx
// Next index in cidxFieldIndex to be read in standard mode or next index of line Cats in editing mode
int mNextCidx;
// Next topology line/node id to be read in topo mode or next line id in edit mode, starts from 1
int mNextLid;
/** Reset selection */
void resetSelection( bool sel );
/** Allocate sellection array for given map id. The array is large enough for lines or areas
* (bigger from num lines and num areas)
* @param map pointer to map structure
*/
void allocateSelection( struct Map_info *map );
//! Mutex that protects GRASS library from parallel access from multiple iterators at once.
//! The library uses static/global variables in various places when accessing vector data,
//! making it non-reentrant and thus impossible to use from multiple threads.
//! (e.g. static LocList in Vect_select_lines_by_box, global BranchBuf in RTreeGetBranches)
static QMutex sMutex;
};

File diff suppressed because it is too large Load Diff

View File

@ -16,15 +16,18 @@
#ifndef QGSGRASSPROVIDER_H
#define QGSGRASSPROVIDER_H
class QgsFeature;
class QgsField;
class QgsGrassFeatureIterator;
#include <QDateTime>
#include "qgsvectordataprovider.h"
#include <vector>
#include "qgsgrassvectormap.h"
#include "qgsgrassvectormaplayer.h"
class QgsFeature;
class QgsField;
class QgsVectorLayerEditBuffer;
class QgsGrassFeatureIterator;
/* Update.
* Vectors are updated (reloaded) if:
@ -45,69 +48,6 @@ class QgsGrassFeatureIterator;
* This is not however solution for multiple instances of QGIS.
*/
/* Editing.
* If editing is started by startEdit, vector map is reopened in update mode, and GMAP.update
* is set to true. All data loaded from the map to QgsGrassProvider remain unchanged
* untill closeEdit is called.
* During editing:
* nextFeature() and getFirstFeature() returns 0
* featureCount() returns 0
* fieldCount() returns original (old) number of fields
*/
/* Attributes. Cache of database attributes (because selection from database is slow). */
struct GATT
{
int cat; // category
char **values; // pointer to array of pointers to values
};
/* Grass layer (unique vector+field). */
struct GLAYER
{
QString path; // path to the layer gisdbase+location+mapset+mapName
int field; // field number
bool valid; // valid is true if layer is opened, once the layer is closed,
// valid is set to false and no more used
int mapId; // map ID in maps vector
struct Map_info *map; // map header
struct field_info *fieldInfo; // field info
int nColumns; // number of columns in database table, if 0, attributes are not available
// and category (column name 'cat') is used instead
int keyColumn; // number of key column
QgsFields fields; // description of layer fields
int nAttributes; // number of attributes read to the memory (may be < nRecords)
GATT *attributes; // vector of attributes
double( *minmax )[2]; // minimum and maximum values of attributes
int nUsers; // number of instances using this layer, increased by open(),
// decreased by close()
};
/* Grass vector map. */
struct GMAP
{
QString gisdbase; // map gisdabase
QString location; // map location name (not path!)
QString mapset; // map mapset
QString mapName; // map name
QString path; // path to the layer gisdbase+location+mapset+mapName
bool valid; // true if map is opened, once the map is closed,
// valid is set to false and no more used
// Vector temporally disabled. Necessary for GRASS Tools on Windows
bool frozen;
struct Map_info *map; // map header
int nUsers; // number layers using this map
int update; // true if the map is opened in update mode -> disabled standard reading
// through nextFeature(), featureCount() returns 0
QDateTime lastModified; // last modified time of the vector directory, when the map was opened
QDateTime lastAttributesModified; // last modified time of the vector 'dbln' file, when the map was opened
// or attributes were updated. The 'dbln' file is updated by v.to.db etc.
// when attributes are changed
int version; // version, increased by each closeEdit() and updateMap()
};
/**
\class QgsGrassProvider
\brief Data provider for GRASS vectors
@ -117,11 +57,28 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
Q_OBJECT
public:
enum TopoSymbol
{
TopoUndefined = 0,
TopoPoint,
TopoLine,
TopoBoundary0,
TopoBoundary1,
TopoBoundary2,
TopoCentroidIn,
TopoCentroidOut,
TopoCentroidDupl,
TopoNode0,
TopoNode1,
TopoNode2
};
QgsGrassProvider( QString uri = QString() );
virtual ~QgsGrassProvider();
virtual int capabilities() const override;
virtual QgsAbstractFeatureSource* featureSource() const override;
/**
@ -182,6 +139,14 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
QgsCoordinateReferenceSystem crs() override;
//----------------------------------------------------------------------------
QgsGrassObject grassObject() const { return mGrassObject; }
// ----------------------------------- New edit --------------------------------
bool changeGeometryValues( QgsGeometryMap & geometry_map ) override { Q_UNUSED( geometry_map ); return true; }
// ----------------------------------- Edit ----------------------------------
/** Is the layer editable? I.e. the layer is valid and current user is owner of the mapset
@ -203,11 +168,9 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
*/
bool isFrozen();
/** Start editing. Reopen the vector for update and set GMAP.update = true
* @return true is frozen
* @return false is not frozen
*/
bool startEdit();
/* Start standard QGIS editing */
//void startEditing( QgsVectorLayerEditBuffer* buffer );
void startEditing( QgsVectorLayer *vectorLayer );
/** Freeze vector.
*/
@ -352,13 +315,7 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
*/
int findNode( double x, double y, double threshold );
/** Get columns' definitions
* @param field
* @param cat
* @return vector of attributes
*/
QVector<QgsField> *columns( int field );
// TODO is it used?
/** Read attributes from DB
* @param field
* @param cat
@ -394,7 +351,7 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
* @param field
* @param cat
* @param update comma separated update string, e.g.: col1 = 5, col2 = 'Val d''Aosta'
* @return empty string or error message
* @return empty string or error messagemLayer
*/
QString updateAttributes( int field, int cat, const QString &values );
@ -459,40 +416,12 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
*/
static int grassLayerType( QString );
/** Return a provider name
Essentially just returns the provider key. Should be used to build file
dialogs so that providers can be shown with their supported types. Thus
if more than one provider supports a given format, the user is able to
select a specific provider to open that file.
@note
Instead of being pure virtual, might be better to generalize this
behavior and presume that none of the sub-classes are going to do
anything strange with regards to their name or description?
*/
/** Return a provider name */
QString name() const override;
/** Return description
Return a terse string describing what the provider is.
@note
Instead of being pure virtual, might be better to generalize this
behavior and presume that none of the sub-classes are going to do
anything strange with regards to their name or description?
*/
/** Return description */
QString description() const override;
// Layer type (layerType)
enum TYPE // layer name:
{
@ -509,120 +438,45 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
TOPO_NODE // topology nodes
};
public slots:
void bufferGeometryChanged( QgsFeatureId fid, QgsGeometry &geom );
void onBeforeCommitChanges();
void onEditingStopped();
void onUndoIndexChanged( int index );
protected:
// used by QgsGrassFeatureSource
QgsGrassVectorMapLayer *openLayer() const;
private:
QString mGisdbase; // map gisdabase
QString mLocation; // map location name (not path!)
QString mMapset; // map mapset
QString mMapName; // map name
QString mLayer; // layer name
int mLayerField; // field part of layer or -1 if no field specified
int mLayerType; // layer type POINT, LINE, ...
int mGrassType; // grass feature type: GV_POINT, GV_LINE | GV_BOUNDARY, GV_AREA,
// ( GV_BOUNDARY, GV_CENTROID )
QGis::WkbType mQgisType;// WKBPoint, WKBLineString, ...
int mLayerId; // ID used in layers
struct Map_info *mMap; // vector header pointer
int mMapVersion; // The version of the map for which the instance was last time updated
struct Map_info * map();
void setMapset();
int mCidxFieldIndex; // !UPDATE! Index for layerField in category index or -1 if no such field
int mCidxFieldNumCats; // !UPDATE! Number of records in field index
QgsGrassObject mGrassObject;
// field part of layer or -1 if no field specified
int mLayerField;
// layer type POINT, LINE, ...
int mLayerType;
// grass feature type: GV_POINT, GV_LINE | GV_BOUNDARY, GV_AREA, ( GV_BOUNDARY, GV_CENTROID )
int mGrassType;
// WKBPoint, WKBLineString, ...
QGis::WkbType mQgisType;
QString mLayerName;
QgsGrassVectorMapLayer *mLayer;
// The version of the map for which the instance was last time updated
int mMapVersion;
bool mValid; // !UPDATE!
long mNumberFeatures; // !UPDATE!
// Index for layerField in category index or -1 if no such field
int mCidxFieldIndex;
// Number of records in field index
int mCidxFieldNumCats;
bool mValid;
long mNumberFeatures;
// create QgsFeatureId from GRASS geometry object id and cat
static QgsFeatureId makeFeatureId( int grassId, int cat );
// Reopen map after edit or freeze
bool reopenMap();
// -----------------------------------------------------------------------------------------
/* Static variables and methods.
* These methods opens GRASS vectors and loads some parts of vectors to the memory.
* it maintains the list of opened layers so that sources are not duplicated in the memory.
* Layers are identified by layer ID.
* The layers have unique URI, if next layer of the same URI is requested,
* nUsers is increased and ID of the layer which is already opened is returned.
* Attributes are loaded from DB and stored in the memory when layer is opened.
*/
/** Open layer. Layer for QgsGrassVector means Map+field
* @param gisdbase
* @param location
* @param mapset
* @param mapName
* @param field
* @return layer ID
* @return -1 cannot open
*/
static int openLayer( QString gisdbase, QString location, QString mapset, QString mapName, int field );
/** Load sources from the map.
* Must be set: layer.mapId, layer.map, layer.field
* Updates: layer.fieldInfo, layer.nColumns, layer.nAttributes, layer.attributes, layer.keyColumn
* Unchanged: layer.valid
*
* Old sources are released, namely: layer.fields and layer.attributes
*
* layer.attributes must be pointer to existing array or 0
*/
static void loadLayerSourcesFromMap( GLAYER &layer );
/** Load attributes from database table.
* Must be set: layer.mapId, layer.map, layer.field
* Updates: layer.fieldInfo, layer.nColumns, layer.nAttributes, layer.attributes, layer.keyColumn
* Unchanged: layer.valid
*
* Old sources are released, namely: layer.attributes
*
* layer.attributes must be pointer to existing array or 0
*/
static void loadAttributes( GLAYER &layer );
/** Close layer.
* @param layerId
*/
static void closeLayer( int layerId );
/** Open map.
* @param gisdbase
* @param location
* @param mapset
* @param mapName
* @return map ID
* @return -1 cannot open
*/
static int openMap( QString gisdbase, QString location, QString mapset, QString mapName );
/** Close map.
* @param mapId
*/
static void closeMap( int mapId );
/** Update map. Close and reopen vector, all layers in mLayers using this map are also updated.
* Instances of QgsGrassProvider are not updated and should call update() method.
* @param mapId
*/
static void updateMap( int mapId );
/** The map is outdated. The map was for example rewritten by GRASS module outside QGIS.
* This function checks internal timestamp stored in QGIS.
* @param mapId
*/
static bool mapOutdated( int mapId );
/** The attributes are outdated. The table was for example updated by GRASS module outside QGIS.
* This function checks internal timestamp stored in QGIS.
* @param mapId
*/
static bool attributesOutdated( int mapId );
/** Get layer map.
* @param layerId
* @return pointer to Map_info structure
*/
static struct Map_info *layerMap( int layerId );
/** Get attribute by category(key) and attribute number.
* @param layerId
* @param category (key)
@ -641,17 +495,15 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
void setTopoFields();
/* Static arrays of opened layers and vectors */
static QVector<GLAYER> mLayers; // Map + field/attributes
static QVector<GMAP> mMaps; // Map
/** Fields used for topo layers */
QgsFields mTopoFields;
QgsFields mEditFields;
QgsVectorLayerEditBuffer* mEditBuffer;
friend class QgsGrassFeatureSource;
friend class QgsGrassFeatureIterator;
static int cmpAtt( const void *a, const void *b );
};
#endif // QGSGRASSPROVIDER_H

View File

@ -0,0 +1,685 @@
/***************************************************************************
qgsgrassvectormap.cpp
-------------------
begin : September, 2015
copyright : (C) 2015 by Radim Blazek
email : radim.blazek@gmail.com
***************************************************************************/
/***************************************************************************
* *
* 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 <QFileInfo>
#include <QMessageBox>
#include "qgslinestringv2.h"
#include "qgspolygonv2.h"
#include "qgspointv2.h"
#include "qgslogger.h"
#include "qgsgeometry.h"
#include "qgsgrass.h"
#include "qgsgrassvectormap.h"
#include "qgsgrassvectormaplayer.h"
extern "C"
{
#include <grass/version.h>
#include <grass/gprojects.h>
#include <grass/gis.h>
#include <grass/dbmi.h>
#if GRASS_VERSION_MAJOR < 7
#include <grass/Vect.h>
#else
#include <grass/vector.h>
#define BOUND_BOX bound_box
#endif
}
QgsGrassVectorMap::QgsGrassVectorMap( const QgsGrassObject & grassObject )
: mGrassObject( grassObject )
, mValid( false )
, mOpen( false )
, mFrozen( false )
, mIsEdited( false )
, mVersion( 0 )
, mMap( 0 )
, mIs3d( false )
, mOldNumLines( 0 )
{
QgsDebugMsg( "grassObject = " + grassObject.toString() );
openMap();
mOpen = true;
}
QgsGrassVectorMap::~QgsGrassVectorMap()
{
QgsDebugMsg( "grassObject = " + mGrassObject.toString() );
// TODO close
QgsGrass::vectDestroyMapStruct( mMap );
}
int QgsGrassVectorMap::userCount() const
{
int count = 0;
foreach ( QgsGrassVectorMapLayer *layer, mLayers )
{
count += layer->userCount();
}
return count;
}
bool QgsGrassVectorMap::open()
{
QgsDebugMsg( toString() );
if ( mOpen )
{
QgsDebugMsg( "already open" );
return true;
}
lockOpenClose();
bool result = openMap();
mOpen = true;
unlockOpenClose();
return result;
}
void QgsGrassVectorMap::close()
{
QgsDebugMsg( toString() );
if ( mOpen )
{
QgsDebugMsg( "is not open" );
return;
}
lockOpenClose();
closeAllIterators(); // blocking
closeMap();
mOpen = false;
unlockOpenClose();
return;
}
bool QgsGrassVectorMap::openMap()
{
// TODO: refresh layers (reopen)
QgsDebugMsg( toString() );
QgsGrass::lock();
QgsGrass::setLocation( mGrassObject.gisdbase(), mGrassObject.location() );
// Find the vector
const char *ms = G_find_vector2( mGrassObject.name().toUtf8().data(), mGrassObject.mapset().toUtf8().data() );
if ( !ms )
{
QgsDebugMsg( "Cannot find GRASS vector" );
QgsGrass::unlock();
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( mGrassObject.mapsetPath() + "/vector/" + mGrassObject.name() );
mLastModified = di.lastModified();
di.setFile( mGrassObject.mapsetPath() + "/vector/" + mGrassObject.name() + "/dbln" );
mLastAttributesModified = di.lastModified();
mMap = QgsGrass::vectNewMapStruct();
// Do we have topology and cidx (level2)
int level = -1;
G_TRY
{
//Vect_set_open_level( 2 );
level = Vect_open_old_head( mMap, mGrassObject.name().toUtf8().data(), mGrassObject.mapset().toUtf8().data() );
Vect_close( mMap );
}
G_CATCH( QgsGrass::Exception &e )
{
QgsGrass::warning( e );
level = -1;
}
if ( level == -1 )
{
QgsDebugMsg( "Cannot open GRASS vector head" );
QgsGrass::unlock();
return -1;
}
else if ( level == 1 )
{
QMessageBox::StandardButton ret = QMessageBox::question( 0, "Warning",
QObject::tr( "GRASS vector map %1 does not have topology. Build topology?" ).arg( mGrassObject.name() ),
QMessageBox::Ok | QMessageBox::Cancel );
if ( ret == QMessageBox::Cancel )
{
QgsGrass::unlock();
return -1;
}
}
// Open vector
G_TRY
{
Vect_set_open_level( level );
Vect_open_old( mMap, mGrassObject.name().toUtf8().data(), mGrassObject.mapset().toUtf8().data() );
}
G_CATCH( QgsGrass::Exception &e )
{
QgsGrass::warning( QString( "Cannot open GRASS vector: %1" ).arg( e.what() ) );
QgsGrass::unlock();
return -1;
}
if ( level == 1 )
{
G_TRY
{
#if defined(GRASS_VERSION_MAJOR) && defined(GRASS_VERSION_MINOR) && \
( ( GRASS_VERSION_MAJOR == 6 && GRASS_VERSION_MINOR >= 4 ) || GRASS_VERSION_MAJOR > 6 )
Vect_build( mMap );
#else
Vect_build( mMap, stderr );
#endif
}
G_CATCH( QgsGrass::Exception &e )
{
QgsGrass::warning( QString( "Cannot build topology: %1" ).arg( e.what() ) );
QgsGrass::unlock();
return -1;
}
}
QgsDebugMsg( "GRASS map successfully opened" );
mIs3d = Vect_is_3d( mMap );
QgsGrass::unlock();
mValid = true;
return true;
}
bool QgsGrassVectorMap::startEdit()
{
QgsDebugMsg( toString() );
lockOpenClose();
closeAllIterators(); // blocking
// TODO: Can it still happen? QgsGrassVectorMapStore singleton is used now.
#if 0
// 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;
}
#endif
/* Close map */
mValid = false;
QgsGrass::lock();
// Mapset must be set before Vect_close()
QgsGrass::setMapset( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset() );
int level = -1;
G_TRY
{
Vect_close( mMap );
level = Vect_open_update( mMap, mGrassObject.name().toUtf8().data(), mGrassObject.mapset().toUtf8().data() );
if ( level < 2 )
{
QgsDebugMsg( "Cannot open GRASS vector for update on level 2." );
}
}
G_CATCH( QgsGrass::Exception &e )
{
Q_UNUSED( e );
QgsDebugMsg( QString( "Cannot open GRASS vector for update: %1" ).arg( e.what() ) );
}
if ( level < 2 )
{
// reopen vector for reading
G_TRY
{
Vect_set_open_level( 2 );
level = Vect_open_old( mMap, mGrassObject.name().toUtf8().data(), mGrassObject.mapset().toUtf8().data() );
if ( level < 2 )
{
QgsDebugMsg( QString( "Cannot reopen GRASS vector: %1" ).arg( QgsGrass::errorMessage() ) );
}
}
G_CATCH( QgsGrass::Exception &e )
{
Q_UNUSED( e );
QgsDebugMsg( QString( "Cannot reopen GRASS vector: %1" ).arg( e.what() ) );
}
if ( level >= 2 )
{
mValid = true;
}
QgsGrass::unlock();
unlockOpenClose();
return false;
}
Vect_set_category_index_update( mMap );
// Write history
Vect_hist_command( mMap );
mOldNumLines = Vect_get_num_lines( mMap );
QgsDebugMsg( QString( "Vector successfully reopened for update mOldNumLines = %1" ).arg( mOldNumLines ) );
mIsEdited = true;
mValid = true;
QgsGrass::unlock();
unlockOpenClose();
return true;
}
bool QgsGrassVectorMap::closeEdit( bool newMap )
{
Q_UNUSED( newMap );
QgsDebugMsg( toString() );
if ( !mValid || !mIsEdited )
{
return false;
}
// mValid = false; // close() is checking mValid
lockOpenClose();
closeAllIterators(); // blocking
QgsGrass::lock();
mOldLids.clear();
mNewLids.clear();
mOldGeometries.clear();
// Mapset must be set before Vect_close()
QgsGrass::setMapset( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset() );
#if defined(GRASS_VERSION_MAJOR) && defined(GRASS_VERSION_MINOR) && \
( ( GRASS_VERSION_MAJOR == 6 && GRASS_VERSION_MINOR >= 4 ) || GRASS_VERSION_MAJOR > 6 )
Vect_build_partial( mMap, GV_BUILD_NONE );
Vect_build( mMap );
#else
Vect_build_partial( mMap, GV_BUILD_NONE, NULL );
Vect_build( mMap, stderr );
#endif
// TODO?
#if 0
// If a new map was created close the map and return
if ( newMap )
{
QgsDebugMsg( QString( "mLayers.size() = %1" ).arg( mLayers.size() ) );
mUpdate = false;
// Map must be set as valid otherwise it is not closed and topo is not written
mValid = true;
// TODO refresh layers ?
//closeLayer( mLayerId );
QgsGrass::unlock();
unlockOpenClose();
return true;
}
#endif
mIsEdited = false;
QgsGrass::unlock();closeAllIterators(); // blocking
closeMap();
openMap();
mVersion++;
unlockOpenClose();
QgsDebugMsg( "edit closed" );
return mValid;
}
QgsGrassVectorMapLayer * QgsGrassVectorMap::openLayer( int field )
{
QgsDebugMsg( QString( "%1 field = %2" ).arg( toString() ).arg( field ) );
// There are 2 locks on openLayer(), it must be locked when the map is being opened/closed/updated
// but that lock must not block closeLayer() because close/update map closes first all iterators
// which call closeLayer() and using single lock would result in dead lock.
lockOpenCloseLayer();
lockOpenClose();
QgsGrassVectorMapLayer *layer = 0;
// Check if this layer is already open
foreach ( QgsGrassVectorMapLayer *l, mLayers )
{
if ( l->field() == field )
{
QgsDebugMsg( "Layer exists" );
layer = l;
}
}
if ( !layer )
{
layer = new QgsGrassVectorMapLayer( this, field ) ;
layer->load();
mLayers << layer;
}
layer->addUser();
unlockOpenClose();
unlockOpenCloseLayer();
return layer;
}
void QgsGrassVectorMap::closeLayer( QgsGrassVectorMapLayer * layer )
{
if ( !layer || !layer->map() )
{
return;
}
QgsDebugMsg( QString( "Close layer %1 usersCount = %2" ).arg( toString() ).arg( layer->userCount() ) );
lockOpenCloseLayer();
layer->removeUser();
if ( layer->userCount() == 0 ) // No more users, free sources
{
QgsDebugMsg( "No more users -> clear" );
layer->clear();
}
if ( layer->map()->userCount() == 0 )
{
QgsDebugMsg( "No more map users -> close" );
// TODO: attention about dead lock, probably move to QgsGrassVectorMapStore
//layer->map()->close();
}
QgsDebugMsg( "layer closed" );
unlockOpenCloseLayer();
}
void QgsGrassVectorMap::closeMap()
{
QgsDebugMsg( toString() );
QgsGrass::lock();
if ( !mValid )
{
QgsDebugMsg( "map is not valid" );
}
else
{
// Mapset must be set before Vect_close()
QgsGrass::setMapset( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset() );
G_TRY
{
Vect_close( mMap );
QgsDebugMsg( "map closed" );
}
G_CATCH( QgsGrass::Exception &e )
{
QgsDebugMsg( "Vect_close failed:" + QString( e.what() ) );
}
}
QgsGrass::vectDestroyMapStruct( mMap );
mMap = 0;
mOldNumLines = 0;
mValid = false;
QgsGrass::unlock();
}
void QgsGrassVectorMap::update()
{
QgsDebugMsg( toString() );
lockOpenClose();
closeAllIterators(); // blocking
closeMap();
openMap();
unlockOpenClose();
}
bool QgsGrassVectorMap::mapOutdated()
{
QgsDebugMsg( "entered" );
QString dp = mGrassObject.mapsetPath() + "/vector/" + mGrassObject.name();
QFileInfo di( dp );
if ( mLastModified < di.lastModified() )
{
// If the cidx file has been deleted, the map is currently being modified
// by an external tool. Do not update until the cidx file has been recreated.
if ( !QFileInfo( dp + "/cidx" ).exists() )
{
QgsDebugMsg( "The map is being modified and is unavailable : " + mGrassObject.toString() );
return false;
}
QgsDebugMsg( "The map was modified : " + mGrassObject.toString() );
return true;
}
return false;
}
bool QgsGrassVectorMap::attributesOutdated( )
{
QgsDebugMsg( "entered" );
QString dp = mGrassObject.mapsetPath() + "/vector/" + mGrassObject.name() + "/dbln";
QFileInfo di( dp );
if ( mLastAttributesModified < di.lastModified() )
{
QgsDebugMsg( "The attributes of the layer were modified : " + mGrassObject.toString() );
return true;
}
return false;
}
int QgsGrassVectorMap::numLines()
{
QgsDebugMsg( "entered" );
return ( Vect_get_num_lines( mMap ) );
}
QString QgsGrassVectorMap::toString()
{
return mGrassObject.mapsetPath() + "/" + mGrassObject.name();
}
void QgsGrassVectorMap::lockOpenClose()
{
QgsDebugMsg( "lockOpenClose" );
mOpenCloseMutex.lock();
}
void QgsGrassVectorMap::unlockOpenClose()
{
QgsDebugMsg( "unlockOpenClose" );
mOpenCloseMutex.unlock();
}
void QgsGrassVectorMap::lockOpenCloseLayer()
{
QgsDebugMsg( "lockOpenCloseLayer" );
mOpenCloseLayerMutex.lock();
}
void QgsGrassVectorMap::unlockOpenCloseLayer()
{
QgsDebugMsg( "unlockOpenCloseLayer" );
mOpenCloseLayerMutex.unlock();
}
void QgsGrassVectorMap::lockReadWrite()
{
if ( isEdited() )
{
QgsDebugMsgLevel( "lockReadWrite", 3 );
mReadWriteMutex.lock();
}
}
void QgsGrassVectorMap::unlockReadWrite()
{
if ( isEdited() )
{
QgsDebugMsgLevel( "unlockReadWrite", 3 );
mReadWriteMutex.unlock();
}
}
QgsAbstractGeometryV2 * QgsGrassVectorMap::lineGeometry( int id )
{
QgsDebugMsgLevel( QString( "id = %1" ).arg( id ), 3 );
if ( !Vect_line_alive( mMap, id ) ) // should not happen (update mode!)?
{
QgsDebugMsg( QString( "line %1 is dead" ).arg( id ) );
return 0;
}
struct line_pnts *points = Vect_new_line_struct();
int type = Vect_read_line( mMap, points, 0, id );
QList<QgsPointV2> pointList;
for ( int i = 0; i < points->n_points; i++ )
{
pointList << QgsPointV2( is3d() ? QgsWKBTypes::PointZ : QgsWKBTypes::Point, points->x[i], points->y[i], points->z[i] );
}
Vect_destroy_line_struct( points );
if ( type & GV_POINTS )
{
return pointList.first().clone();
}
else if ( type & GV_LINES )
{
QgsLineStringV2 * line = new QgsLineStringV2();
line->setPoints( pointList );
return line;
}
else if ( type & GV_FACE )
{
QgsPolygonV2 * polygon = new QgsPolygonV2();
QgsLineStringV2 * ring = new QgsLineStringV2();
ring->setPoints( pointList );
polygon->setExteriorRing( ring );
return polygon;
}
QgsDebugMsg( QString( "unknown type = %1" ).arg( type ) );
return 0;
}
QgsAbstractGeometryV2 * QgsGrassVectorMap::nodeGeometry( int id )
{
QgsDebugMsgLevel( QString( "id = %1" ).arg( id ), 3 );
double x, y, z;
Vect_get_node_coor( mMap, id, &x, &y, &z );
return new QgsPointV2( is3d() ? QgsWKBTypes::PointZ : QgsWKBTypes::Point, x, y, z );
}
QgsAbstractGeometryV2 * QgsGrassVectorMap::areaGeometry( int id )
{
QgsDebugMsgLevel( QString( "id = %1" ).arg( id ), 3 );
QgsPolygonV2 * polygon = new QgsPolygonV2();
struct line_pnts *points = Vect_new_line_struct();
Vect_get_area_points( mMap, id, points );
QList<QgsPointV2> pointList;
for ( int i = 0; i < points->n_points; i++ )
{
pointList << QgsPointV2( is3d() ? QgsWKBTypes::PointZ : QgsWKBTypes::Point, points->x[i], points->y[i], points->z[i] );
}
QgsLineStringV2 * ring = new QgsLineStringV2();
ring->setPoints( pointList );
polygon->setExteriorRing( ring );
int nIsles = Vect_get_area_num_isles( mMap, id );
for ( int i = 0; i < nIsles; i++ )
{
pointList.clear();
int isle = Vect_get_area_isle( mMap, id, i );
Vect_get_isle_points( mMap, isle, points );
for ( int i = 0; i < points->n_points; i++ )
{
pointList << QgsPointV2( is3d() ? QgsWKBTypes::PointZ : QgsWKBTypes::Point, points->x[i], points->y[i], points->z[i] );
}
ring = new QgsLineStringV2();
ring->setPoints( pointList );
polygon->addInteriorRing( ring );
}
Vect_destroy_line_struct( points );
return polygon;
}
void QgsGrassVectorMap::closeAllIterators()
{
QgsDebugMsg( toString() );
// cancel and close all iterator
// Iterators must be connected properly, otherwise may it result in dead lock!
emit cancelIterators(); // non blocking
emit closeIterators(); // blocking
QgsDebugMsg( "iterators closed" );
}
//------------------------------------ QgsGrassVectorMapStore ------------------------------------
QgsGrassVectorMapStore::QgsGrassVectorMapStore()
{
}
QgsGrassVectorMapStore::~QgsGrassVectorMapStore()
{
QgsDebugMsg( "entered" );
}
QgsGrassVectorMapStore *QgsGrassVectorMapStore::instance()
{
static QgsGrassVectorMapStore instance;
return &instance;
}
QgsGrassVectorMap * QgsGrassVectorMapStore::openMap( const QgsGrassObject & grassObject )
{
QgsDebugMsg( "grassObject = " + grassObject.toString() );
mMutex.lock();
QgsGrassVectorMap *map = 0;
// Check if this map is already open
foreach ( QgsGrassVectorMap *m, mMaps )
{
if ( m->grassObject() == grassObject )
{
QgsDebugMsg( "The map is already open" );
map = m;
}
}
if ( !map )
{
map = new QgsGrassVectorMap( grassObject );
mMaps << map;
}
mMutex.unlock();
return map;
}

View File

@ -0,0 +1,189 @@
/***************************************************************************
qgsgrassvectormap.cpp
-------------------
begin : September, 2015
copyright : (C) 2015 by Radim Blazek
email : radim.blazek@gmail.com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef QGSGRASSVECTORMAP_H
#define QGSGRASSVECTORMAP_H
#include <QDateTime>
#include <QObject>
#include "qgsabstractgeometryv2.h"
#include "qgsgrass.h"
#include "qgsgrassvectormaplayer.h"
class GRASS_LIB_EXPORT QgsGrassVectorMap : public QObject
{
Q_OBJECT
public:
QgsGrassVectorMap( const QgsGrassObject & grassObject );
~QgsGrassVectorMap();
QgsGrassObject grassObject() const { return mGrassObject; }
struct Map_info *map() { return mMap; }
bool isValid() const { return mValid; }
bool isFrozen() const { return mFrozen; }
bool isEdited() const { return mIsEdited; }
int version() const { return mVersion; }
int oldNumLines() const { return mOldNumLines; }
// number of instances using this map
int userCount() const;
/** Get current number of lines.
* @return number of lines */
int numLines();
// 3D map with z coordinates
bool is3d() { return mIs3d; }
// Lock open / close
void lockOpenClose();
void unlockOpenClose();
// Lock open / close
void lockOpenCloseLayer();
void unlockOpenCloseLayer();
// Lock reading and writing
void lockReadWrite();
void unlockReadWrite();
QHash<int, int> & oldLids() { return mOldLids; }
QHash<int, int> & newLids() { return mNewLids; }
QHash<int, QgsAbstractGeometryV2*> & oldGeometries() { return mOldGeometries; }
/** Get geometry of line.
* @return geometry (point,line or polygon(GV_FACE)) or 0 */
QgsAbstractGeometryV2 * lineGeometry( int id );
QgsAbstractGeometryV2 * nodeGeometry( int id );
QgsAbstractGeometryV2 * areaGeometry( int id );
/** Open map if not yet open. Open/close lock */
bool open();
/** Close map. All iterators are closed first. Open/close lock. */
void close();
/** Open GRASS map, no open/close locking */
bool openMap();
/** Close GRASS map, no open/close locking */
void closeMap();
bool startEdit();
bool closeEdit( bool newMap );
/** Get layer, layer is created and loaded if not yet.
* @param field
* @return pointer to layer or 0 if layer doe not exist */
QgsGrassVectorMapLayer * openLayer( int field );
/** Close layer and release cached data if there are no more users and close map
* if there are no more map users.
* @param layer */
void closeLayer( QgsGrassVectorMapLayer * layer );
/** Update map. Close and reopen vector and refresh layers.
* Instances of QgsGrassProvider are not updated and should call update() method */
void update();
/** The map is outdated. The map was for example rewritten by GRASS module outside QGIS.
* This function checks internal timestamp stored in QGIS.
*/
bool mapOutdated();
/** The attributes are outdated. The table was for example updated by GRASS module outside QGIS.
* This function checks internal timestamp stored in QGIS.
*/
bool attributesOutdated();
/** Map descripton for debugging */
QString toString();
signals:
/** Ask all iterators to cancel iteration when possible. Connected to iterators with
* Qt::DirectConnection (non blocking) */
void cancelIterators();
/** Close all iterators. Connected to iterators in different threads with Qt::BlockingQueuedConnection */
void closeIterators();
private:
/** Close iterators, blocking */
void closeAllIterators();
QgsGrassObject mGrassObject;
// true if map is open, once the map is closed, valid is set to false and no more used
bool mValid;
// Indicates if map is open, it may be open but invalide
bool mOpen;
// Vector temporally disabled. Necessary for GRASS Tools on Windows
bool mFrozen;
// true if the map is opened in update mode
bool mIsEdited;
// version, increased by each closeEdit() and updateMap()
int mVersion;
// last modified time of the vector directory, when the map was opened
QDateTime mLastModified;
// last modified time of the vector 'dbln' file, when the map was opened
// or attributes were updated. The 'dbln' file is updated by v.to.db etc.
QDateTime mLastAttributesModified;
// when attributes are changed
// map header
struct Map_info *mMap;
// Is 3D, has z coordinates
bool mIs3d;
// Vector layers
QList<QgsGrassVectorMapLayer*> mLayers;
// Number of lines in vector before editing started
int mOldNumLines;
// Original line ids of rewritten GRASS lines (new lid -> old lid)
QHash<int, int> mOldLids;
// Current line ids for old line ids (old lid -> new lid)
QHash<int, int> mNewLids;
// Hash of original lines' geometries of lines which were changed, keys are GRASS lid
QHash<int, QgsAbstractGeometryV2*> mOldGeometries;
// Mutex used to avoid concurrent read/write, used only in editing mode
QMutex mReadWriteMutex;
// Open / close mutex
QMutex mOpenCloseMutex;
// Open / close mutex
QMutex mOpenCloseLayerMutex;
};
class QgsGrassVectorMapStore
{
public:
QgsGrassVectorMapStore();
~QgsGrassVectorMapStore();
static QgsGrassVectorMapStore *instance();
/** Open map.
* @param grassObject
* @return map, the map may be invalide */
QgsGrassVectorMap * openMap( const QgsGrassObject & grassObject );
private:
/** Open vector maps */
QList<QgsGrassVectorMap*> mMaps;
// Lock open/close map
QMutex mMutex;
};
#endif // QGSGRASSVECTORMAP_H

View File

@ -0,0 +1,317 @@
/***************************************************************************
qgsgrassvectormaplayer.cpp
-------------------
begin : September, 2015
copyright : (C) 2015 by Radim Blazek
email : radim.blazek@gmail.com
***************************************************************************/
/***************************************************************************
* *
* 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 <QFileInfo>
#include "qgslogger.h"
#include "qgsgrass.h"
#include "qgsgrassvectormap.h"
#include "qgsgrassvectormaplayer.h"
extern "C"
{
#include <grass/version.h>
#include <grass/gprojects.h>
#include <grass/gis.h>
#include <grass/dbmi.h>
#if GRASS_VERSION_MAJOR < 7
#include <grass/Vect.h>
#else
#include <grass/vector.h>
#define BOUND_BOX bound_box
#endif
}
QgsGrassVectorMapLayer::QgsGrassVectorMapLayer( QgsGrassVectorMap *map, int field )
: mField( field )
, mValid( false )
, mMap( map )
, mFieldInfo( 0 )
, mHasTable( false )
, mKeyColumn( -1 )
, mUsers( 0 )
{
}
void QgsGrassVectorMapLayer::clear()
{
QgsDebugMsg( "entered" );
mFields.clear();
mAttributes.clear();
mMinMax.clear();
mKeyColumn = -1;
mValid = false;
G_free( mFieldInfo );
mFieldInfo = 0;
}
void QgsGrassVectorMapLayer::load()
{
QgsDebugMsg( "entered" );
clear();
if ( !mMap )
{
return;
}
// Attributes are not loaded for topo layers in which case field == 0
if ( mField == 0 )
{
return;
}
mFieldInfo = Vect_get_field( mMap->map(), mField ); // should work also with field = 0
if ( !mFieldInfo )
{
QgsDebugMsg( "No field info -> no attribute table" );
}
else
{
QgsDebugMsg( "Field info found -> open database" );
QFileInfo di( mMap->grassObject().mapsetPath() + "/vector/" + mMap->grassObject().name() + "/dbln" );
mLastLoaded = di.lastModified();
QgsGrass::lock();
dbDriver *databaseDriver = 0;
QString error = QString( "Cannot open database %1 by driver %2" ).arg( mFieldInfo->database ).arg( mFieldInfo->driver );
G_TRY
{
databaseDriver = db_start_driver_open_database( mFieldInfo->driver, mFieldInfo->database );
}
G_CATCH( QgsGrass::Exception &e )
{
QgsGrass::warning( error + " : " + e.what() );
}
if ( !databaseDriver )
{
QgsDebugMsg( error );
}
else
{
QgsDebugMsg( "Database opened -> open select cursor" );
dbString dbstr;
db_init_string( &dbstr );
db_set_string( &dbstr, ( char * )"select * from " );
db_append_string( &dbstr, mFieldInfo->table );
QgsDebugMsg( QString( "SQL: %1" ).arg( db_get_string( &dbstr ) ) );
dbCursor databaseCursor;
if ( db_open_select_cursor( databaseDriver, &dbstr, &databaseCursor, DB_SCROLL ) != DB_OK )
{
db_close_database_shutdown_driver( databaseDriver );
QgsGrass::warning( "Cannot select attributes from table '" + QString( mFieldInfo->table ) + "'" );
}
else
{
int nRecords = db_get_num_rows( &databaseCursor );
QgsDebugMsg( QString( "Number of records: %1" ).arg( nRecords ) );
dbTable *databaseTable = db_get_cursor_table( &databaseCursor );
int nColumns = db_get_table_number_of_columns( databaseTable );
// Read columns' description
for ( int i = 0; i < nColumns; i++ )
{
QPair<double, double> minMax( DBL_MAX, -DBL_MAX );
dbColumn *column = db_get_table_column( databaseTable, i );
int ctype = db_sqltype_to_Ctype( db_get_column_sqltype( column ) );
QVariant::Type qtype = QVariant::String; //default to string
QgsDebugMsg( QString( "column = %1 ctype = %2" ).arg( db_get_column_name( column ) ).arg( ctype ) );
QString ctypeStr;
switch ( ctype )
{
case DB_C_TYPE_INT:
ctypeStr = "integer";
qtype = QVariant::Int;
break;
case DB_C_TYPE_DOUBLE:
ctypeStr = "double";
qtype = QVariant::Double;
break;
case DB_C_TYPE_STRING:
ctypeStr = "string";
qtype = QVariant::String;
break;
case DB_C_TYPE_DATETIME:
ctypeStr = "datetime";
qtype = QVariant::String;
break;
}
mFields.append( QgsField( db_get_column_name( column ), qtype, ctypeStr,
db_get_column_length( column ), db_get_column_precision( column ) ) );
mMinMax << minMax;
if ( G_strcasecmp( db_get_column_name( column ), mFieldInfo->key ) == 0 )
{
mKeyColumn = i;
}
}
if ( mKeyColumn < 0 )
{
mFields.clear();
QgsGrass::warning( QObject::tr( "Key column '%1' not found in the table '%2'" ).arg( mFieldInfo->key ).arg( mFieldInfo->table ) );
}
else
{
mHasTable = true;
// Read attributes to the memory
while ( true )
{
int more;
if ( db_fetch( &databaseCursor, DB_NEXT, &more ) != DB_OK )
{
QgsDebugMsg( "Cannot fetch DB record" );
break;
}
if ( !more )
{
break; // no more records
}
// Check cat value
dbColumn *column = db_get_table_column( databaseTable, mKeyColumn );
dbValue *value = db_get_column_value( column );
if ( db_test_value_isnull( value ) )
{
continue;
}
int cat = db_get_value_int( value );
if ( cat < 0 )
{
continue;
}
QList<QVariant> values;
for ( int i = 0; i < 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 );
QgsDebugMsgLevel( QString( "column = %1 value = %2" ).arg( db_get_column_name( column ) ).arg( db_get_string( &dbstr ) ), 3 );
QVariant variant;
if ( !db_test_value_isnull( value ) )
{
int iv;
double dv;
//layer.mAttributes[layer.nAttributes].values[i] = strdup( db_get_string( &dbstr ) );
switch ( ctype )
{
case DB_C_TYPE_INT:
iv = db_get_value_int( value );
variant = QVariant( iv );
mMinMax[i].first = qMin( mMinMax[i].first, ( double )iv );
mMinMax[i].second = qMin( mMinMax[i].second, ( double )iv );
break;
case DB_C_TYPE_DOUBLE:
dv = db_get_value_double( value );
variant = QVariant( dv );
mMinMax[i].first = qMin( mMinMax[i].first, dv );
mMinMax[i].second = qMin( mMinMax[i].second, dv );
break;
case DB_C_TYPE_STRING:
// Store as byte array so that codec may be used later
variant = QVariant( QByteArray( db_get_value_string( value ) ) );
break;
case DB_C_TYPE_DATETIME:
variant = QVariant( QByteArray( db_get_string( &dbstr ) ) );
default:
variant = QVariant( QByteArray( db_get_string( &dbstr ) ) );
}
}
QgsDebugMsgLevel( QString( "column = %1 variant = %2" ).arg( db_get_column_name( column ) ).arg( variant.toString() ), 3 );
values << variant;
}
mAttributes.insert( cat, values );
}
}
mValid = true;
db_close_cursor( &databaseCursor );
db_close_database_shutdown_driver( databaseDriver );
db_free_string( &dbstr );
QgsDebugMsg( QString( "mFields.size = %1" ).arg( mFields.size() ) );
QgsDebugMsg( QString( "number of attributes = %1" ).arg( mAttributes.size() ) );
}
}
QgsGrass::unlock();
}
// Add cat if no attribute fields exist (otherwise qgis crashes)
if ( mFields.size() == 0 )
{
mKeyColumn = 0;
mFields.append( QgsField( "cat", QVariant::Int, "integer" ) );
QPair<double, double> minMax( 0, 0 );
int cidx = Vect_cidx_get_field_index( mMap->map(), mField );
if ( cidx >= 0 )
{
int ncats, cat, type, id;
ncats = Vect_cidx_get_num_cats_by_index( mMap->map(), cidx );
if ( ncats > 0 )
{
Vect_cidx_get_cat_by_index( mMap->map(), cidx, 0, &cat, &type, &id );
minMax.first = cat;
Vect_cidx_get_cat_by_index( mMap->map(), cidx, ncats - 1, &cat, &type, &id );
minMax.second = cat;
}
}
mMinMax << minMax;
}
// TODO
// add topo field for editing
mFields.append( QgsField( "topo_symbol", QVariant::Int, "integer" ) );
QgsDebugMsg( QString( "layer loaded mFields.size() = %1 mAttributes.size() = %2" ).arg( mFields.size() ).arg( mAttributes.size() ) );
mValid = true;
}
void QgsGrassVectorMapLayer::addUser()
{
mUsers++;
QgsDebugMsg( QString( "user added mUsers = %1" ).arg( mUsers ) );
}
void QgsGrassVectorMapLayer::removeUser()
{
mUsers--;
QgsDebugMsg( QString( "user removed mUsers = %1" ).arg( mUsers ) );
}
void QgsGrassVectorMapLayer::close()
{
QgsDebugMsg( "close" );
//removeUser(); // removed by map
mMap->closeLayer( this );
}

View File

@ -0,0 +1,76 @@
/***************************************************************************
qgsgrassvectormaplayer.h
-------------------
begin : September, 2015
copyright : (C) 2015 by Radim Blazek
email : radim.blazek@gmail.com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef QGSGRASSVECTORMAPLAYER_H
#define QGSGRASSVECTORMAPLAYER_H
#include <QDateTime>
#include <QMap>
#include <QObject>
#include <QPair>
#include "qgsfield.h"
class QgsGrassVectorMap;
class GRASS_LIB_EXPORT QgsGrassVectorMapLayer : public QObject
{
Q_OBJECT
public:
QgsGrassVectorMapLayer( QgsGrassVectorMap *map, int field );
int field() const { return mField; }
bool isValid() const { return mValid; }
QgsGrassVectorMap *map() { return mMap; }
QgsFields & fields() { return mFields; }
QMap<int, QList<QVariant>> & attributes() { return mAttributes; }
bool hasTable() { return mHasTable; }
int keyColumn() { return mKeyColumn; }
QList<QPair<double, double>> minMax() { return mMinMax; }
int userCount() { return mUsers; }
void addUser();
void removeUser();
/** Load attributes from the map. Old sources are released. */
void load();
/** Clear all cached data */
void clear();
/** Decrease number of users and clear if no more users */
void close();
private:
int mField;
bool mValid;
QgsGrassVectorMap *mMap;
struct field_info *mFieldInfo;
bool mHasTable;
// index of key column
int mKeyColumn;
QgsFields mFields;
// Map of attributes with cat as key
QMap<int, QList<QVariant>> mAttributes;
// minimum and maximum values of attributes
QList<QPair<double, double>> mMinMax;
// timestamp when attributes were loaded
QDateTime mLastLoaded;
// number of instances using this layer
int mUsers;
};
#endif // QGSGRASSVECTORMAPLAYER_H