Skip to content

Commit 5f3954a

Browse files
committedNov 6, 2015
[GRASS] enable to attache attributes to geom without cat, fixes #13739
1 parent 9f3bd1d commit 5f3954a

File tree

8 files changed

+232
-14
lines changed

8 files changed

+232
-14
lines changed
 

‎src/providers/grass/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ MACRO(ADD_GRASSLIB GRASS_BUILD_VERSION)
4646
../qgsgrassoptions.cpp
4747
../qgsgrassprovider.cpp
4848
../qgsgrassrasterprovider.cpp
49+
../qgsgrassundocommand.cpp
4950
../qgsgrassvector.cpp
5051
../qgsgrassvectormap.cpp
5152
../qgsgrassvectormaplayer.cpp

‎src/providers/grass/qgsgrassfeatureiterator.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ bool QgsGrassFeatureIterator::fetchFeature( QgsFeature& feature )
411411
}
412412
else // standard layer
413413
{
414+
QgsDebugMsgLevel( "standard layer", 3 );
414415
if ( mNextCidx >= mSource->mLayer->cidxFieldNumCats() )
415416
{
416417
break;
@@ -423,10 +424,16 @@ bool QgsGrassFeatureIterator::fetchFeature( QgsFeature& feature )
423424
QgsDebugMsg( QString( "cidxFieldIndex %1 out of range (0,%2)" ).arg( cidxFieldIndex ).arg( numFields - 1 ) );
424425
break;
425426
}
427+
#if 0
428+
// debug
429+
Vect_topo_dump( mSource->map(), stderr );
430+
Vect_cidx_dump( mSource->map(), stderr );
431+
#endif
426432
Vect_cidx_get_cat_by_index( mSource->map(), cidxFieldIndex, mNextCidx++, &tmpCat, &tmpType, &tmpLid );
427433
// Warning: selection array is only of type line/area of current layer -> check type first
428434
if ( !( tmpType & mSource->mGrassType ) )
429435
{
436+
QgsDebugMsgLevel( QString( "tmpType = %1 does not match mGrassType = %2" ).arg( tmpType ).arg( mSource->mGrassType ), 3 );
430437
continue;
431438
}
432439

@@ -436,6 +443,8 @@ bool QgsGrassFeatureIterator::fetchFeature( QgsFeature& feature )
436443
lid = tmpLid;
437444
cat = tmpCat;
438445
type = tmpType;
446+
QgsDebugMsgLevel( QString( "lid = %1 field = %2 cat = %3 type= %4" )
447+
.arg( lid ).arg( mSource->mLayer->field() ).arg( cat ).arg( type ), 3 );
439448
featureId = makeFeatureId( lid, cat );
440449
}
441450

@@ -455,9 +464,11 @@ bool QgsGrassFeatureIterator::fetchFeature( QgsFeature& feature )
455464
}
456465
if ( !oldGeometry )
457466
{
458-
if ( lid == 0 || lid > mSource->mLayer->map()->numLines() )
467+
int numLinesOrAreas = ( mSource->mGrassType == GV_AREA && !mSource->mEditing ) ?
468+
mSource->mLayer->map()->numAreas() : mSource->mLayer->map()->numLines();
469+
if ( lid == 0 || lid > numLinesOrAreas )
459470
{
460-
QgsDebugMsg( QString( "lid = %1 -> close" ).arg( lid ) );
471+
QgsDebugMsg( QString( "lid = %1 > numLinesOrAreas = %2 -> close" ).arg( lid ).arg( numLinesOrAreas ) );
461472
close();
462473
mSource->mLayer->map()->unlockReadWrite();
463474
return false; // No more features
@@ -651,7 +662,15 @@ int QgsGrassFeatureIterator::catFromFid( QgsFeatureId fid )
651662

652663
QVariant QgsGrassFeatureIterator::nonEditableValue( int layerNumber )
653664
{
654-
return tr( "<not editable (layer %1)>" ).arg( layerNumber );
665+
if ( layerNumber > 0 )
666+
{
667+
return tr( "<not editable (layer %1)>" ).arg( layerNumber );
668+
}
669+
else
670+
{
671+
// attributes of features without cat (layer = 0) may be edited -> cat created
672+
return QVariant();
673+
}
655674
}
656675

657676
void QgsGrassFeatureIterator::setFeatureAttributes( int cat, QgsFeature *feature, QgsGrassVectorMap::TopoSymbol symbol )
@@ -711,7 +730,7 @@ void QgsGrassFeatureIterator::setFeatureAttributes( int cat, QgsFeature *feature
711730
{
712731
value = QVariant( cat );
713732
}
714-
else
733+
else if ( layerNumber > 0 )
715734
{
716735
value = nonEditableValue( layerNumber );
717736
}

‎src/providers/grass/qgsgrassprovider.cpp

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "qgsgrassprovider.h"
3737
#include "qgsgrassfeatureiterator.h"
3838
#include "qgsgrassvector.h"
39+
#include "qgsgrassundocommand.h"
3940

4041
#include "qgsapplication.h"
4142
#include "qgscoordinatereferencesystem.h"
@@ -797,7 +798,8 @@ int QgsGrassProvider::rewriteLine( int oldLid, int type, struct line_pnts *Point
797798
oldestLid = mLayer->map()->oldLids().value( oldLid );
798799
}
799800

800-
QgsDebugMsg( QString( "oldLid = %1 oldestLid = %2 newLine = %3" ).arg( oldLid ).arg( oldestLid ).arg( newLid ) );
801+
QgsDebugMsg( QString( "oldLid = %1 oldestLid = %2 newLine = %3 numLines = %4" )
802+
.arg( oldLid ).arg( oldestLid ).arg( newLid ).arg( mLayer->map()->numLines() ) );
801803
QgsDebugMsg( QString( "oldLids : %1 -> %2" ).arg( newLid ).arg( oldestLid ) );
802804
mLayer->map()->oldLids()[newLid] = oldestLid;
803805
QgsDebugMsg( QString( "newLids : %1 -> %2" ).arg( oldestLid ).arg( newLid ) );
@@ -1725,7 +1727,7 @@ void QgsGrassProvider::onAttributeValueChanged( QgsFeatureId fid, int idx, const
17251727
int cat = QgsGrassFeatureIterator::catFromFid( fid );
17261728
QgsDebugMsg( QString( "layerField = %1" ).arg( layerField ) );
17271729

1728-
if ( !FID_IS_NEW( fid ) && layerField != mLayerField )
1730+
if ( !FID_IS_NEW( fid ) && ( layerField > 0 && layerField != mLayerField ) )
17291731
{
17301732
QgsDebugMsg( "changing attributes in different layer is not allowed" );
17311733
// reset the value
@@ -1802,6 +1804,7 @@ void QgsGrassProvider::onAttributeValueChanged( QgsFeatureId fid, int idx, const
18021804
mLayer->map()->lockReadWrite();
18031805
int newLid = rewriteLine( realLine, type, mPoints, mCats );
18041806
Q_UNUSED( newLid )
1807+
mLayer->map()->newCats()[fid] = newCat;
18051808

18061809
// TODO: - store the new cat somewhere for cats mapping
18071810
// - insert record if does not exist
@@ -1813,15 +1816,27 @@ void QgsGrassProvider::onAttributeValueChanged( QgsFeatureId fid, int idx, const
18131816
}
18141817
else
18151818
{
1816-
1819+
int undoIndex = mEditLayer->undoStack()->index();
1820+
QgsDebugMsg( QString( "undoIndex = %1" ).arg( undoIndex ) );
18171821
if ( realCat > 0 )
18181822
{
18191823
QString error;
1824+
bool recordExists = mLayer->recordExists( realCat, error );
1825+
if ( !error.isEmpty() )
1826+
{
1827+
QgsGrass::warning( error );
1828+
}
1829+
error.clear();
18201830
mLayer->changeAttributeValue( realCat, field, value, error );
18211831
if ( !error.isEmpty() )
18221832
{
18231833
QgsGrass::warning( error );
18241834
}
1835+
if ( !recordExists )
1836+
{
1837+
mLayer->map()->undoCommands()[undoIndex]
1838+
<< new QgsGrassUndoCommandChangeAttribute( this, fid, realLine, mLayerField, realCat, false, true );
1839+
}
18251840
}
18261841
else
18271842
{
@@ -1837,17 +1852,26 @@ void QgsGrassProvider::onAttributeValueChanged( QgsFeatureId fid, int idx, const
18371852
Vect_cat_set( mCats, mLayerField, newCat );
18381853
mLayer->map()->lockReadWrite();
18391854
int newLid = rewriteLine( realLine, type, mPoints, mCats );
1840-
Q_UNUSED( newLid )
1855+
Q_UNUSED( newLid );
1856+
mLayer->map()->newCats()[fid] = newCat;
18411857

1842-
// TODO: - store the new cat somewhere for cats mapping
18431858
QString error;
1859+
bool recordExists = mLayer->recordExists( newCat, error );
1860+
if ( !error.isEmpty() )
1861+
{
1862+
QgsGrass::warning( error );
1863+
}
1864+
error.clear();
18441865
// it does insert new record if it doesn't exist
18451866
mLayer->changeAttributeValue( newCat, field, value, error );
18461867
if ( !error.isEmpty() )
18471868
{
18481869
QgsGrass::warning( error );
18491870
}
18501871

1872+
mLayer->map()->undoCommands()[undoIndex]
1873+
<< new QgsGrassUndoCommandChangeAttribute( this, fid, newLid, mLayerField, newCat, true, !recordExists );
1874+
18511875
mLayer->map()->unlockReadWrite();
18521876
}
18531877
}
@@ -1933,12 +1957,33 @@ void QgsGrassProvider::setAddedFeaturesSymbol()
19331957
}
19341958
}
19351959

1936-
void QgsGrassProvider::onUndoIndexChanged( int index )
1960+
void QgsGrassProvider::onUndoIndexChanged( int currentIndex )
19371961
{
1938-
Q_UNUSED( index )
1939-
QgsDebugMsg( QString( "index = %1" ).arg( index ) );
1940-
}
1962+
QgsDebugMsg( QString( "currentIndex = %1" ).arg( currentIndex ) );
1963+
// multiple commands maybe undone with single undoIndexChanged signal
1964+
QList<int> indexes = mLayer->map()->undoCommands().keys();
1965+
qSort( indexes );
1966+
for ( int i = indexes.size() - 1; i >= 0; i-- )
1967+
{
1968+
int index = indexes[i];
1969+
if ( index < currentIndex )
1970+
{
1971+
break;
1972+
}
1973+
QgsDebugMsg( QString( "index = %1" ).arg( index ) );
1974+
if ( mLayer->map()->undoCommands().contains( index ) )
1975+
{
1976+
QgsDebugMsg( QString( "%1 undo commands" ).arg( mLayer->map()->undoCommands()[index].size() ) );
19411977

1978+
for ( int j = 0; j < mLayer->map()->undoCommands()[index].size(); j++ )
1979+
{
1980+
mLayer->map()->undoCommands()[index][j]->undo();
1981+
delete mLayer->map()->undoCommands()[index][j];
1982+
}
1983+
mLayer->map()->undoCommands().remove( index );
1984+
}
1985+
}
1986+
}
19421987

19431988
bool QgsGrassProvider::addAttributes( const QList<QgsField> &attributes )
19441989
{
@@ -1959,6 +2004,7 @@ bool QgsGrassProvider::deleteAttributes( const QgsAttributeIds &attributes )
19592004
void QgsGrassProvider::onBeforeCommitChanges()
19602005
{
19612006
QgsDebugMsg( "entered" );
2007+
mLayer->map()->clearUndoCommands();
19622008
}
19632009

19642010
void QgsGrassProvider::onBeforeRollBack()

‎src/providers/grass/qgsgrassprovider.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
391391
void onBeforeCommitChanges();
392392
void onBeforeRollBack();
393393
void onEditingStopped();
394-
void onUndoIndexChanged( int index );
394+
void onUndoIndexChanged( int currentIndex );
395395

396396
void onDataChanged();
397397

@@ -486,6 +486,7 @@ class GRASS_LIB_EXPORT QgsGrassProvider : public QgsVectorDataProvider
486486

487487
friend class QgsGrassFeatureSource;
488488
friend class QgsGrassFeatureIterator;
489+
friend class QgsGrassUndoCommandChangeAttribute;
489490
};
490491

491492
#endif // QGSGRASSPROVIDER_H
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/***************************************************************************
2+
qgsgrassundocommand.cpp
3+
-------------------
4+
begin : November, 2015
5+
copyright : (C) 2015 by Radim Blazek
6+
email : radim.blazek@gmail.com
7+
***************************************************************************/
8+
/***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
#include "qgsgrassundocommand.h"
17+
18+
#include "qgsgrassprovider.h"
19+
#include "qgslogger.h"
20+
21+
QgsGrassUndoCommandChangeAttribute::QgsGrassUndoCommandChangeAttribute( QgsGrassProvider * provider, int fid, int lid, int field, int cat, bool deleteCat, bool deleteRecord )
22+
: mProvider( provider )
23+
, mFid(fid)
24+
, mLid(lid)
25+
, mField(field)
26+
, mCat(cat)
27+
, mDeleteCat(deleteCat)
28+
, mDeleteRecord( deleteRecord )
29+
{
30+
}
31+
32+
void QgsGrassUndoCommandChangeAttribute::undo()
33+
{
34+
QgsDebugMsg( QString("mLid = %1 mField = %2, mCat = %3").arg(mLid).arg(mField).arg(mCat) );
35+
if ( mDeleteCat )
36+
{
37+
int realLine = mLid;
38+
if ( mProvider->mLayer->map()->newLids().contains( mLid ) )
39+
{
40+
realLine = mProvider->mLayer->map()->newLids().value( mLid );
41+
}
42+
QgsDebugMsg( QString("realLine = %1").arg(realLine) );
43+
44+
int type = mProvider->readLine( mProvider->mPoints, mProvider->mCats, realLine );
45+
if ( type <= 0 )
46+
{
47+
QgsDebugMsg( "cannot read line" );
48+
}
49+
else
50+
{
51+
if ( Vect_field_cat_del( mProvider->mCats, mProvider->mLayerField, mCat ) == 0 )
52+
{
53+
// should not happen
54+
QgsDebugMsg( "the line does not have the category" );
55+
}
56+
else
57+
{
58+
mProvider->mLayer->map()->lockReadWrite();
59+
int newLid = mProvider->rewriteLine( realLine, type, mProvider->mPoints, mProvider->mCats );
60+
Q_UNUSED( newLid );
61+
mProvider->mLayer->map()->newCats().remove(mFid);
62+
mProvider->mLayer->map()->unlockReadWrite();
63+
}
64+
}
65+
}
66+
if ( mDeleteRecord )
67+
{
68+
QString error;
69+
mProvider->mLayer->deleteAttribute( mCat, error );
70+
if ( !error.isEmpty() )
71+
{
72+
QgsGrass::warning( error );
73+
}
74+
}
75+
}
76+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/***************************************************************************
2+
qgsgrassundocommand.h
3+
-------------------
4+
begin : November, 2015
5+
copyright : (C) 2015 by Radim Blazek
6+
email : radim.blazek@gmail.com
7+
***************************************************************************/
8+
/***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
#ifndef QGSGRASSUNDOCOMMAND_H
17+
#define QGSGRASSUNDOCOMMAND_H
18+
19+
class QgsGrassProvider;
20+
21+
class GRASS_LIB_EXPORT QgsGrassUndoCommand
22+
{
23+
public:
24+
virtual ~QgsGrassUndoCommand() {}
25+
virtual void undo() {}
26+
};
27+
28+
// This class is used to store information that a new cat was attached to a line
29+
// when attribute was changed.
30+
class GRASS_LIB_EXPORT QgsGrassUndoCommandChangeAttribute : public QgsGrassUndoCommand
31+
{
32+
public:
33+
QgsGrassUndoCommandChangeAttribute( QgsGrassProvider * provider, int fid, int lid, int field, int cat, bool deleteCat, bool deleteRecord );
34+
~QgsGrassUndoCommandChangeAttribute() {}
35+
void undo() override;
36+
private:
37+
QgsGrassProvider *mProvider;
38+
int mFid;
39+
int mLid;
40+
int mField;
41+
int mCat;
42+
bool mDeleteCat;
43+
bool mDeleteRecord;
44+
};
45+
46+
#endif // QGSGRASSUNDOCOMMAND_H

‎src/providers/grass/qgsgrassvectormap.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "qgsgrass.h"
2828
#include "qgsgrassvectormap.h"
2929
#include "qgsgrassvectormaplayer.h"
30+
#include "qgsgrassundocommand.h"
3031

3132
extern "C"
3233
{
@@ -313,6 +314,7 @@ bool QgsGrassVectorMap::closeEdit( bool newMap )
313314
mNewLids.clear();
314315
mOldGeometries.clear();
315316
mNewCats.clear();
317+
clearUndoCommands();
316318

317319
// Mapset must be set before Vect_close()
318320
QgsGrass::setMapset( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset() );
@@ -357,6 +359,18 @@ bool QgsGrassVectorMap::closeEdit( bool newMap )
357359
return mValid;
358360
}
359361

362+
void QgsGrassVectorMap::clearUndoCommands()
363+
{
364+
Q_FOREACH ( int index, mUndoCommands.keys() )
365+
{
366+
Q_FOREACH ( QgsGrassUndoCommand *command, mUndoCommands[index] )
367+
{
368+
delete command;
369+
}
370+
}
371+
mUndoCommands.clear();
372+
}
373+
360374
QgsGrassVectorMapLayer * QgsGrassVectorMap::openLayer( int field )
361375
{
362376
QgsDebugMsg( QString( "%1 field = %2" ).arg( toString() ).arg( field ) );
@@ -522,6 +536,13 @@ int QgsGrassVectorMap::numLines()
522536
return ( Vect_get_num_lines( mMap ) );
523537
}
524538

539+
int QgsGrassVectorMap::numAreas()
540+
{
541+
QgsDebugMsg( "entered" );
542+
543+
return ( Vect_get_num_areas( mMap ) );
544+
}
545+
525546
QString QgsGrassVectorMap::toString()
526547
{
527548
return mGrassObject.mapsetPath() + "/" + mGrassObject.name();

‎src/providers/grass/qgsgrassvectormap.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include "qgsgrass.h"
2626
#include "qgsgrassvectormaplayer.h"
2727

28+
class QgsGrassUndoCommand;
29+
2830
class GRASS_LIB_EXPORT QgsGrassVectorMap : public QObject
2931
{
3032
Q_OBJECT
@@ -62,6 +64,7 @@ class GRASS_LIB_EXPORT QgsGrassVectorMap : public QObject
6264
/** Get current number of lines.
6365
* @return number of lines */
6466
int numLines();
67+
int numAreas();
6568
// 3D map with z coordinates
6669
bool is3d() { return mIs3d; }
6770

@@ -82,6 +85,7 @@ class GRASS_LIB_EXPORT QgsGrassVectorMap : public QObject
8285
QHash<int, QgsAbstractGeometryV2*> & oldGeometries() { return mOldGeometries; }
8386
QHash<int, int> & oldTypes() { return mOldTypes; }
8487
QHash<QgsFeatureId, int> & newCats() { return mNewCats; }
88+
QMap<int, QList<QgsGrassUndoCommand *> > & undoCommands() { return mUndoCommands; }
8589

8690
/** Get geometry of line.
8791
* @return geometry (point,line or polygon(GV_FACE)) or 0 */
@@ -106,6 +110,7 @@ class GRASS_LIB_EXPORT QgsGrassVectorMap : public QObject
106110

107111
bool startEdit();
108112
bool closeEdit( bool newMap );
113+
void clearUndoCommands();
109114

110115
/** Get layer, layer is created and loaded if not yet.
111116
* @param field
@@ -195,6 +200,9 @@ class GRASS_LIB_EXPORT QgsGrassVectorMap : public QObject
195200
// fid -> cat, the fid may be old fid without category or new (negative) feature id
196201
QHash<QgsFeatureId, int> mNewCats;
197202

203+
// Map of undo commands with undo stack index as key.
204+
QMap<int, QList<QgsGrassUndoCommand *> > mUndoCommands;
205+
198206
// Mutex used to avoid concurrent read/write, used only in editing mode
199207
QMutex mReadWriteMutex;
200208

0 commit comments

Comments
 (0)
Please sign in to comment.