1 /***************************************************************************
2 qgsgrassprovider.cpp - Data provider for GRASS format
3 -------------------
4 begin : March, 2004
5 copyright : (C) 2004 by Gary E.Sherman, Radim Blazek
6 email : sherman@mrcc.com, blazek@itc.it
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
17 #include <cstring>
18 #include <vector>
19 #include <cfloat>
20
21 #include <QString>
22 #include <QDateTime>
23 #include <QElapsedTimer>
24
25 #include "qgis.h"
26 #include "qgsdataprovider.h"
27 #include "qgsfeature.h"
28 #include "qgsfields.h"
29 #include "qgslinestring.h"
30 #include "qgspoint.h"
31 #include "qgspolygon.h"
32 #include "qgsrectangle.h"
33 #include "qgsvectorlayer.h"
34 #include "qgsvectorlayereditbuffer.h"
35
36 #include "qgsgrass.h"
37 #include "qgsgrassprovider.h"
38 #include "qgsgrassfeatureiterator.h"
39 #include "qgsgrassvector.h"
40 #include "qgsgrassundocommand.h"
41
42 #include "qgsapplication.h"
43 #include "qgscoordinatereferencesystem.h"
44 #include "qgslogger.h"
45
46 #include <QByteArray>
47 #include <QDir>
48 #include <QFile>
49 #include <QFileInfo>
50 #include <QMessageBox>
51 #include <QTextCodec>
52
53 #ifdef _MSC_VER
54 // enable grass prototypes
55 #define __STDC__ 1
56 #endif
57
58 extern "C"
59 {
60 #include <grass/version.h>
61 #if defined(_MSC_VER) && defined(M_PI_4)
62 #undef M_PI_4 //avoid redefinition warning
63 #endif
64 #if defined(PROJ_VERSION_MAJOR) && PROJ_VERSION_MAJOR>=6 && PROJ_VERSION_MAJOR<8
65 #define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H
66 #endif
67 #include <grass/gprojects.h>
68 #include <grass/gis.h>
69 #include <grass/dbmi.h>
70 #include <grass/vector.h>
71 }
72
73 #ifdef _MSC_VER
74 #undef __STDC__
75 #endif
76
77 // Vect_rewrite_line and Vect_delete_line use int in GRASS 6 and off_t in GRASS 7 for line id argument.
78 // off_t size is not specified by C, POSIX specifies it as signed integer and its size depends on compiler.
79 // In OSGeo4W 32bit the off_t size is 8 bytes in GRASS (32bit, compiled with MinGW, 'g.version -g' prints build_off_t_size=8 )
80 // and 4 bytes QGIS (32bit, compiled with MSVC), which is causing crashes.
81 // The problem with off_t size was also reported for custom build of QGIS on Xubuntu 14.04 LTS using GRASS 7.0.1-2~ubuntu14.04.1.
82 // See: https://lists.osgeo.org/pipermail/grass-dev/2015-June/075539.html
83 // https://lists.osgeo.org/pipermail/grass-dev/2015-September/076338.html
84 // TODO: get real off_t size from GRASS, probably contribute a patch which will save 'g.version -g' to a header file during compilation
85 #ifdef Q_OS_WIN
86 typedef qint64 grass_off_t;
87 #else
88 #if defined(GRASS_OFF_T_SIZE) && GRASS_OFF_T_SIZE == 4
89 typedef qint32 grass_off_t;
90 #elif defined(GRASS_OFF_T_SIZE) && GRASS_OFF_T_SIZE == 8
91 typedef qint64 grass_off_t;
92 #else
93 typedef off_t grass_off_t; // GRASS_OFF_T_SIZE undefined, default to off_t
94 #endif
95 #endif
96 typedef grass_off_t Vect_rewrite_line_function_type( struct Map_info *, grass_off_t, int, const struct line_pnts *, const struct line_cats * );
97 typedef int Vect_delete_line_function_type( struct Map_info *, grass_off_t );
98 Vect_rewrite_line_function_type *Vect_rewrite_line_function_pointer = ( Vect_rewrite_line_function_type * )Vect_rewrite_line;
99 Vect_delete_line_function_type *Vect_delete_line_function_pointer = ( Vect_delete_line_function_type * )Vect_delete_line;
100
101 static QString GRASS_KEY = QStringLiteral( "grass" );
102
103 int QgsGrassProvider::sLastType = -9999;
104 int QgsGrassProvider::sEditedCount = 0;
105
QgsGrassProvider(const QString & uri)106 QgsGrassProvider::QgsGrassProvider( const QString &uri )
107 : QgsVectorDataProvider( uri )
108 {
109 QgsDebugMsg( "uri = " + uri );
110
111 mValid = false;
112 if ( !QgsGrass::init() )
113 {
114 appendError( QgsGrass::initError() );
115 return;
116 }
117
118 QElapsedTimer time;
119 time.start();
120
121 mPoints = Vect_new_line_struct();
122 mCats = Vect_new_cats_struct();
123
124 // Parse URI
125 QDir dir( uri ); // it is not a directory in fact
126 QString myURI = dir.path(); // no dupl '/'
127
128 mLayerName = dir.dirName();
129 myURI = myURI.left( dir.path().lastIndexOf( '/' ) );
130 dir = QDir( myURI );
131 QString mapName = dir.dirName();
132 dir.cdUp();
133 QString mapset = dir.dirName();
134 dir.cdUp();
135 QString location = dir.dirName();
136 dir.cdUp();
137 QString gisdbase = dir.path();
138
139 mGrassObject = QgsGrassObject( gisdbase, location, mapset, mapName );
140 QgsDebugMsg( "mGrassObject = " + mGrassObject.toString() + " mLayerName = " + mLayerName );
141
142 /* Parse Layer, supported layers <field>_point, <field>_line, <field>_area
143 * Layer is opened even if it is empty (has no features)
144 */
145 if ( mLayerName.compare( QLatin1String( "boundary" ) ) == 0 ) // currently not used
146 {
147 mLayerType = Boundary;
148 mGrassType = GV_BOUNDARY;
149 }
150 else if ( mLayerName.compare( QLatin1String( "centroid" ) ) == 0 ) // currently not used
151 {
152 mLayerType = Centroid;
153 mGrassType = GV_CENTROID;
154 }
155 else if ( mLayerName == QLatin1String( "topo_point" ) )
156 {
157 mLayerType = TopoPoint;
158 mGrassType = GV_POINTS;
159 }
160 else if ( mLayerName == QLatin1String( "topo_line" ) )
161 {
162 mLayerType = TopoLine;
163 mGrassType = GV_LINES;
164 }
165 else if ( mLayerName == QLatin1String( "topo_node" ) )
166 {
167 mLayerType = TopoNode;
168 mGrassType = 0;
169 }
170 else
171 {
172 mLayerField = grassLayer( mLayerName );
173 if ( mLayerField == -1 )
174 {
175 QgsDebugMsg( QString( "Invalid layer name, no underscore found: %1" ).arg( mLayerName ) );
176 return;
177 }
178
179 mGrassType = grassLayerType( mLayerName );
180
181 if ( mGrassType == GV_POINT )
182 {
183 mLayerType = Point;
184 }
185 else if ( mGrassType == GV_LINES )
186 {
187 mLayerType = Line;
188 }
189 else if ( mGrassType == GV_FACE )
190 {
191 mLayerType = Face;
192 }
193 else if ( mGrassType == GV_AREA )
194 {
195 mLayerType = Polygon;
196 }
197 else
198 {
199 QgsDebugMsg( QString( "Invalid layer name, wrong type: %1" ).arg( mLayerName ) );
200 return;
201 }
202
203 }
204 QgsDebugMsg( QString( "mLayerField: %1" ).arg( mLayerField ) );
205 QgsDebugMsg( QString( "mLayerType: %1" ).arg( mLayerType ) );
206
207 if ( mLayerType == Boundary || mLayerType == Centroid )
208 {
209 QgsDebugMsg( "Layer type not supported." );
210 return;
211 }
212
213 // Set QGIS type
214 switch ( mLayerType )
215 {
216 case Point:
217 case Centroid:
218 case TopoPoint:
219 case TopoNode:
220 mQgisType = QgsWkbTypes::Point;
221 break;
222 case Line:
223 case Boundary:
224 case TopoLine:
225 mQgisType = QgsWkbTypes::LineString;
226 break;
227 case Polygon:
228 case Face:
229 mQgisType = QgsWkbTypes::Polygon;
230 break;
231 }
232
233 if ( !openLayer() )
234 {
235 QgsDebugMsg( "Cannot open layer" );
236 return;
237 }
238
239 loadMapInfo();
240 setTopoFields();
241
242 connect( mLayer->map(),
243 &QgsGrassVectorMap::dataChanged, this, &QgsGrassProvider::onDataChanged );
244
245 // TODO: types according to database
246 setNativeTypes( QList<NativeType>()
247 << QgsVectorDataProvider::NativeType( tr( "Whole number (integer)" ), QStringLiteral( "integer" ), QVariant::Int, -1, -1, -1, -1 )
248 << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "double precision" ), QVariant::Double, -1, -1, -1, -1 )
249 << QgsVectorDataProvider::NativeType( tr( "Text" ), QStringLiteral( "text" ), QVariant::String )
250 // TODO:
251 // << QgsVectorDataProvider::NativeType( tr( "Date" ), "date", QVariant::Date, 8, 8 );
252 );
253
254 // Assign default encoding
255 if ( !textEncoding() )
256 QgsVectorDataProvider::setEncoding( QStringLiteral( "UTF-8" ) );
257
258 mValid = true;
259
260 QgsDebugMsg( QString( "New GRASS layer opened, time (ms): %1" ).arg( time.elapsed() ) );
261 }
262
~QgsGrassProvider()263 QgsGrassProvider::~QgsGrassProvider()
264 {
265 if ( mLayer )
266 {
267 mLayer->close();
268 }
269 if ( mPoints )
270 {
271 Vect_destroy_line_struct( mPoints );
272 }
273 if ( mCats )
274 {
275 Vect_destroy_cats_struct( mCats );
276 }
277 }
278
capabilities() const279 QgsVectorDataProvider::Capabilities QgsGrassProvider::capabilities() const
280 {
281 // Because of bug in GRASS https://trac.osgeo.org/grass/ticket/2775 it is not possible
282 // close db drivers in random order on Unix and probably Mac -> disable editing if another layer is edited
283 #ifndef Q_OS_WIN
284 if ( sEditedCount > 0 && !mEditBuffer )
285 {
286 return nullptr;
287 }
288 #endif
289 // for now, only one map may be edited at time
290 if ( mEditBuffer || ( mLayer && mLayer->map() && !mLayer->map()->isEdited() ) )
291 {
292 return AddFeatures | DeleteFeatures | ChangeGeometries | AddAttributes | DeleteAttributes | ChangeAttributeValues;
293 }
294 return nullptr;
295 }
296
openLayer()297 bool QgsGrassProvider::openLayer()
298 {
299 // the map may be invalid (e.g. wrong uri or open failed)
300 QgsGrassVectorMap *vectorMap = QgsGrassVectorMapStore::instance()->openMap( mGrassObject );
301 if ( !vectorMap ) // should not happen
302 {
303 QgsDebugMsg( "Cannot open map" );
304 return false;
305 }
306 if ( !vectorMap->isValid() ) // may happen
307 {
308 QgsDebugMsg( "vectorMap is not valid" );
309 return false;
310 }
311
312 mLayer = vectorMap->openLayer( mLayerField );
313
314 if ( !mLayer ) // should not happen
315 {
316 QgsDebugMsg( "Cannot open layer" );
317 return false;
318 }
319 if ( !mLayer->map() || !mLayer->map()->map() ) // should not happen
320 {
321 QgsDebugMsg( "map is null" );
322 return false;
323 }
324 mMapVersion = mLayer->map()->version();
325 return true;
326 }
327
loadMapInfo()328 void QgsGrassProvider::loadMapInfo()
329 {
330 // Getting the total number of features in the layer
331 int cidxFieldIndex = -1;
332 mNumberFeatures = 0;
333 if ( mLayerType == TopoPoint )
334 {
335 mNumberFeatures = Vect_get_num_primitives( map(), GV_POINTS );
336 }
337 else if ( mLayerType == TopoLine )
338 {
339 mNumberFeatures = Vect_get_num_primitives( map(), GV_LINES );
340 }
341 else if ( mLayerType == TopoNode )
342 {
343 mNumberFeatures = Vect_get_num_nodes( map() );
344 }
345 else
346 {
347 if ( mLayerField >= 0 )
348 {
349 cidxFieldIndex = Vect_cidx_get_field_index( map(), mLayerField );
350 if ( cidxFieldIndex >= 0 )
351 {
352 mNumberFeatures = Vect_cidx_get_type_count( map(), mLayerField, mGrassType );
353 }
354 }
355 else
356 {
357 // TODO nofield layers
358 mNumberFeatures = 0;
359 }
360 }
361 QgsDebugMsg( QString( "mNumberFeatures = %1 cidxFieldIndex = %2" ).arg( mNumberFeatures ).arg( cidxFieldIndex ) );
362 }
363
update()364 void QgsGrassProvider::update()
365 {
366
367 mValid = false;
368
369 if ( mLayer )
370 {
371 mLayer->close();
372 mLayer = nullptr;
373 }
374
375 if ( !openLayer() )
376 {
377 QgsDebugMsg( "Cannot open layer" );
378 return;
379 }
380
381 loadMapInfo();
382
383 mValid = true;
384 }
385
featureSource() const386 QgsAbstractFeatureSource *QgsGrassProvider::featureSource() const
387 {
388 const_cast<QgsGrassProvider *>( this )->ensureUpdated();
389 return new QgsGrassFeatureSource( this );
390 }
391
392
storageType() const393 QString QgsGrassProvider::storageType() const
394 {
395 return QStringLiteral( "GRASS (Geographic Resources Analysis and Support System) file" );
396 }
397
398
399
getFeatures(const QgsFeatureRequest & request) const400 QgsFeatureIterator QgsGrassProvider::getFeatures( const QgsFeatureRequest &request ) const
401 {
402 if ( !mValid )
403 {
404 return QgsFeatureIterator();
405 }
406
407 // check if outdated and update if necessary
408 ensureUpdated();
409
410 QgsGrassFeatureSource *source = new QgsGrassFeatureSource( this );
411 QgsGrassFeatureIterator *iterator = new QgsGrassFeatureIterator( source, true, request );
412 return QgsFeatureIterator( iterator );
413 }
414
extent() const415 QgsRectangle QgsGrassProvider::extent() const
416 {
417 if ( isValid() )
418 {
419 bound_box box;
420 Vect_get_map_box( map(), &box );
421 return QgsRectangle( box.W, box.S, box.E, box.N );
422 }
423 return QgsRectangle();
424 }
425
426 /**
427 * Return the feature type
428 */
wkbType() const429 QgsWkbTypes::Type QgsGrassProvider::wkbType() const
430 {
431 return mQgisType;
432 }
433
featureCount() const434 long QgsGrassProvider::featureCount() const
435 {
436 return mNumberFeatures;
437 }
438
fields() const439 QgsFields QgsGrassProvider::fields() const
440 {
441 if ( isTopoType() )
442 {
443 return mTopoFields;
444 }
445 else
446 {
447 // Original fields must be returned during editing because edit buffer updates fields by indices
448 if ( mEditBuffer )
449 {
450 return mLayer->fields();
451 }
452 else
453 {
454 return mLayer->tableFields();
455 }
456 }
457 }
458
keyField()459 int QgsGrassProvider::keyField()
460 {
461 return mLayer->keyColumn();
462 }
463
minimumValue(int index) const464 QVariant QgsGrassProvider::minimumValue( int index ) const
465 {
466 if ( isValid() )
467 {
468 return mLayer->minMax().value( index ).first;
469 }
470 return QVariant();
471 }
472
473
maxValue(int index)474 QVariant QgsGrassProvider::maxValue( int index )
475 {
476 if ( isValid() )
477 {
478 return mLayer->minMax().value( index ).second;
479 }
480 return QVariant();
481 }
482
isValid() const483 bool QgsGrassProvider::isValid() const
484 {
485 bool valid = mValid && mLayer && mLayer->map() && mLayer->map()->map();
486 QgsDebugMsg( QString( "valid = %1" ).arg( valid ) );
487 return valid;
488 }
489
crs() const490 QgsCoordinateReferenceSystem QgsGrassProvider::crs() const
491 {
492 QString error;
493 return QgsGrass::crs( mGrassObject.gisdbase(), mGrassObject.location(), error );
494 }
495
grassLayer()496 int QgsGrassProvider::grassLayer()
497 {
498 return mLayerField;
499 }
500
grassLayer(const QString & name)501 int QgsGrassProvider::grassLayer( const QString &name )
502 {
503 // Get field number
504 int pos = name.indexOf( '_' );
505
506 if ( pos == -1 )
507 {
508 return -1;
509 }
510
511 return name.leftRef( pos ).toInt();
512 }
513
grassLayerType(const QString & name)514 int QgsGrassProvider::grassLayerType( const QString &name )
515 {
516 int pos = name.indexOf( '_' );
517
518 if ( pos == -1 )
519 {
520 return -1;
521 }
522
523 QString ts = name.right( name.length() - pos - 1 );
524 if ( ts.compare( QLatin1String( "point" ) ) == 0 )
525 {
526 return GV_POINT; // ?! centroids may be points
527 }
528 else if ( ts.compare( QLatin1String( "line" ) ) == 0 )
529 {
530 return GV_LINES;
531 }
532 else if ( ts.compare( QLatin1String( "face" ) ) == 0 )
533 {
534 return GV_FACE;
535 }
536 else if ( ts.compare( QLatin1String( "polygon" ) ) == 0 )
537 {
538 return GV_AREA;
539 }
540
541 return -1;
542 }
543
onDataChanged()544 void QgsGrassProvider::onDataChanged()
545 {
546 update();
547 emit dataChanged();
548 }
549
550 //----------------------------------------- Edit -------------------------------------------------------
551
isGrassEditable(void)552 bool QgsGrassProvider::isGrassEditable( void )
553 {
554
555 if ( !isValid() )
556 return false;
557
558 /* Check if current user is owner of mapset */
559 if ( G_mapset_permissions2( mGrassObject.gisdbase().toUtf8().constData(), mGrassObject.location().toUtf8().constData(), mGrassObject.mapset().toUtf8().constData() ) != 1 )
560 return false;
561
562 // TODO: check format? (cannot edit OGR layers)
563
564 return true;
565 }
566
isEdited(void)567 bool QgsGrassProvider::isEdited( void )
568 {
569 QgsDebugMsgLevel( "entered", 3 );
570 return ( mEditBuffer );
571 }
572
freeze()573 void QgsGrassProvider::freeze()
574 {
575
576 if ( !isValid() )
577 {
578 return;
579 }
580
581 mValid = false;
582
583 if ( mLayer )
584 {
585 mLayer->close();
586 mLayer->map()->close(); // closes all iterators, blocking
587 mLayer = nullptr;
588 }
589 }
590
thaw()591 void QgsGrassProvider::thaw()
592 {
593
594 if ( !openLayer() )
595 {
596 QgsDebugMsg( "Cannot open layer" );
597 return;
598 }
599
600 loadMapInfo();
601
602 mValid = true;
603 }
604
closeEdit(bool newMap,QgsVectorLayer * vectorLayer)605 bool QgsGrassProvider::closeEdit( bool newMap, QgsVectorLayer *vectorLayer )
606 {
607
608 if ( !isValid() )
609 {
610 QgsDebugMsg( "not valid" );
611 return false;
612 }
613
614 mEditBuffer = nullptr;
615 mEditLayer = nullptr;
616 // drivers must be closed in reversed order in which were opened
617 // TODO: close driver order for simultaneous editing of multiple vector maps
618 for ( int i = mOtherEditLayers.size() - 1; i >= 0; i-- )
619 {
620 QgsGrassVectorMapLayer *layer = mOtherEditLayers[i];
621 layer->closeEdit();
622 mLayer->map()->closeLayer( layer );
623 }
624 mOtherEditLayers.clear();
625 mLayer->closeEdit();
626 if ( mLayer->map()->closeEdit( newMap ) )
627 {
628 loadMapInfo();
629 if ( vectorLayer )
630 {
631 vectorLayer->updateFields();
632 }
633 connect( mLayer->map(), &QgsGrassVectorMap::dataChanged, this, &QgsGrassProvider::onDataChanged );
634 emit fullExtentCalculated();
635 sEditedCount--;
636 return true;
637 }
638 return false;
639 }
640
ensureUpdated() const641 void QgsGrassProvider::ensureUpdated() const
642 {
643 // TODO
644 #if 0
645 int mapId = mLayers[mLayerId].mapId;
646 if ( mapOutdated( mapId ) )
647 {
648 updateMap( mapId );
649 }
650 if ( map()Version < map()s[mapId].version )
651 {
652 update();
653 }
654 if ( attributesOutdated( mapId ) )
655 {
656 loadAttributes( mLayers[mLayerId] );
657 }
658 #endif
659 }
660
numLines(void)661 int QgsGrassProvider::numLines( void )
662 {
663
664 //return ( Vect_get_num_lines( map() ) );
665 return mLayer->map()->numLines();
666 }
667
numNodes(void)668 int QgsGrassProvider::numNodes( void )
669 {
670
671 return ( Vect_get_num_nodes( map() ) );
672 }
673
readLine(struct line_pnts * Points,struct line_cats * Cats,int line)674 int QgsGrassProvider::readLine( struct line_pnts *Points, struct line_cats *Cats, int line )
675 {
676 QgsDebugMsgLevel( "entered", 3 );
677
678 if ( Points )
679 {
680 Vect_reset_line( Points );
681 }
682
683 if ( Cats )
684 {
685 Vect_reset_cats( Cats );
686 }
687
688 if ( !map() )
689 {
690 return -1;
691 }
692
693 if ( !Vect_line_alive( map(), line ) )
694 {
695 return -1;
696 }
697
698 int type = -1;
699 G_TRY
700 {
701 type = Vect_read_line( map(), mPoints, mCats, line );
702 }
703 G_CATCH( QgsGrass::Exception & e )
704 {
705 QgsDebugMsg( QString( "Cannot read line : %1" ).arg( e.what() ) );
706 }
707 return type;
708 }
709
nodeCoor(int node,double * x,double * y)710 bool QgsGrassProvider::nodeCoor( int node, double *x, double *y )
711 {
712 QgsDebugMsgLevel( "entered", 3 );
713
714 if ( !Vect_node_alive( map(), node ) )
715 {
716 *x = 0.0;
717 *y = 0.0;
718 return false;
719 }
720
721 Vect_get_node_coor( map(), node, x, y, nullptr );
722 return true;
723 }
724
lineNodes(int line,int * node1,int * node2)725 bool QgsGrassProvider::lineNodes( int line, int *node1, int *node2 )
726 {
727 QgsDebugMsgLevel( "entered", 3 );
728
729 if ( !Vect_line_alive( map(), line ) )
730 {
731 *node1 = 0;
732 *node2 = 0;
733 return false;
734 }
735
736 /* points don't have topology in GRASS >= 7 */
737 *node1 = 0;
738 *node2 = 0;
739 return true;
740 }
741
writeLine(int type,struct line_pnts * Points,struct line_cats * Cats)742 int QgsGrassProvider::writeLine( int type, struct line_pnts *Points, struct line_cats *Cats )
743 {
744 QgsDebugMsg( QString( "n_points = %1 n_cats = %2" ).arg( Points->n_points ).arg( Cats->n_cats ) );
745
746 if ( !isEdited() )
747 return -1;
748
749 return ( ( int ) Vect_write_line( map(), type, Points, Cats ) );
750 }
751
rewriteLine(int oldLid,int type,struct line_pnts * Points,struct line_cats * Cats)752 int QgsGrassProvider::rewriteLine( int oldLid, int type, struct line_pnts *Points, struct line_cats *Cats )
753 {
754 QgsDebugMsg( QString( "n_points = %1 n_cats = %2" ).arg( Points->n_points ).arg( Cats->n_cats ) );
755
756 if ( !map() || !isEdited() )
757 {
758 return -1;
759 }
760
761 int newLid = -1;
762 G_TRY
763 {
764 newLid = Vect_rewrite_line_function_pointer( map(), oldLid, type, Points, Cats );
765
766 // oldLids are mapping to the very first, original version (used by undo)
767 int oldestLid = oldLid;
768 if ( mLayer->map()->oldLids().contains( oldLid ) ) // if it was changed already
769 {
770 oldestLid = mLayer->map()->oldLids().value( oldLid );
771 }
772
773 QgsDebugMsg( QString( "oldLid = %1 oldestLid = %2 newLine = %3 numLines = %4" )
774 .arg( oldLid ).arg( oldestLid ).arg( newLid ).arg( mLayer->map()->numLines() ) );
775 QgsDebugMsg( QString( "oldLids : %1 -> %2" ).arg( newLid ).arg( oldestLid ) );
776 mLayer->map()->oldLids()[newLid] = oldestLid;
777 QgsDebugMsg( QString( "newLids : %1 -> %2" ).arg( oldestLid ).arg( newLid ) );
778 mLayer->map()->newLids()[oldestLid] = newLid;
779 }
780 G_CATCH( QgsGrass::Exception & e )
781 {
782 QgsDebugMsg( QString( "Cannot write line : %1" ).arg( e.what() ) );
783 }
784 return newLid;
785 }
786
787
deleteLine(int line)788 int QgsGrassProvider::deleteLine( int line )
789 {
790
791 if ( !isEdited() )
792 return -1;
793
794 return ( Vect_delete_line_function_pointer( map(), line ) );
795 }
796
findLine(double x,double y,int type,double threshold)797 int QgsGrassProvider::findLine( double x, double y, int type, double threshold )
798 {
799 QgsDebugMsgLevel( "entered", 3 );
800
801 return ( Vect_find_line( map(), x, y, 0, type, threshold, 0, 0 ) );
802 }
803
findNode(double x,double y,double threshold)804 int QgsGrassProvider::findNode( double x, double y, double threshold )
805 {
806 return ( Vect_find_node( map(), x, y, 0, threshold, 0 ) );
807 }
808
lineAreas(int line,int * left,int * right)809 bool QgsGrassProvider::lineAreas( int line, int *left, int *right )
810 {
811 QgsDebugMsgLevel( "entered", 3 );
812
813 if ( !Vect_line_alive( map(), line ) )
814 {
815 *left = 0;
816 *right = 0;
817 return false;
818 }
819
820 Vect_get_line_areas( map(), line, left, right );
821 return true;
822 }
823
isleArea(int isle)824 int QgsGrassProvider::isleArea( int isle )
825 {
826 QgsDebugMsgLevel( "entered", 3 );
827
828 if ( !Vect_isle_alive( map(), isle ) )
829 {
830 return 0;
831 }
832
833 return ( Vect_get_isle_area( map(), isle ) );
834 }
835
centroidArea(int centroid)836 int QgsGrassProvider::centroidArea( int centroid )
837 {
838 QgsDebugMsgLevel( "entered", 3 );
839
840 if ( !Vect_line_alive( map(), centroid ) )
841 {
842 return 0;
843 }
844
845 return ( Vect_get_centroid_area( map(), centroid ) );
846 }
847
nodeNLines(int node)848 int QgsGrassProvider::nodeNLines( int node )
849 {
850 QgsDebugMsgLevel( "entered", 3 );
851
852 if ( !Vect_node_alive( map(), node ) )
853 {
854 return 0;
855 }
856
857 return ( Vect_get_node_n_lines( map(), node ) );
858 }
859
nodeLine(int node,int idx)860 int QgsGrassProvider::nodeLine( int node, int idx )
861 {
862 QgsDebugMsgLevel( "entered", 3 );
863
864 if ( !Vect_node_alive( map(), node ) )
865 {
866 return 0;
867 }
868
869 return ( Vect_get_node_line( map(), node, idx ) );
870 }
871
lineAlive(int line)872 int QgsGrassProvider::lineAlive( int line )
873 {
874 QgsDebugMsgLevel( "entered", 3 );
875
876 return ( Vect_line_alive( map(), line ) );
877 }
878
nodeAlive(int node)879 int QgsGrassProvider::nodeAlive( int node )
880 {
881 QgsDebugMsgLevel( "QgsGrassProvider::nodeAlive", 3 );
882
883 return ( Vect_node_alive( map(), node ) );
884 }
885
numUpdatedLines(void)886 int QgsGrassProvider::numUpdatedLines( void )
887 {
888 QgsDebugMsg( QString( "numUpdatedLines = %1" ).arg( Vect_get_num_updated_lines( map() ) ) );
889
890 return ( Vect_get_num_updated_lines( map() ) );
891 }
892
numUpdatedNodes(void)893 int QgsGrassProvider::numUpdatedNodes( void )
894 {
895 QgsDebugMsg( QString( "numUpdatedNodes = %1" ).arg( Vect_get_num_updated_nodes( map() ) ) );
896
897 return ( Vect_get_num_updated_nodes( map() ) );
898 }
899
updatedLine(int idx)900 int QgsGrassProvider::updatedLine( int idx )
901 {
902 QgsDebugMsg( QString( "idx = %1" ).arg( idx ) );
903 QgsDebugMsg( QString( " updatedLine = %1" ).arg( Vect_get_updated_line( map(), idx ) ) );
904
905 return ( Vect_get_updated_line( map(), idx ) );
906 }
907
updatedNode(int idx)908 int QgsGrassProvider::updatedNode( int idx )
909 {
910 QgsDebugMsg( QString( "idx = %1" ).arg( idx ) );
911 QgsDebugMsg( QString( " updatedNode = %1" ).arg( Vect_get_updated_node( map(), idx ) ) );
912
913 return ( Vect_get_updated_node( map(), idx ) );
914 }
915
916 // ------------------ Attributes -------------------------------------------------
917
key(int field)918 QString QgsGrassProvider::key( int field )
919 {
920 QgsDebugMsg( QString( "field = %1" ).arg( field ) );
921
922 struct field_info *fi = Vect_get_field( map(), field ); // should work also with field = 0
923 if ( !fi )
924 {
925 QgsDebugMsg( "No field info -> no attributes" );
926 return QString();
927 }
928
929 return QString::fromUtf8( fi->key );
930 }
931
attributes(int field,int cat)932 QgsAttributeMap *QgsGrassProvider::attributes( int field, int cat )
933 {
934 QgsDebugMsg( QString( "field = %1 cat = %2" ).arg( field ).arg( cat ) );
935
936 QgsAttributeMap *att = new QgsAttributeMap;
937
938 struct field_info *fi = Vect_get_field( map(), field ); // should work also with field = 0
939
940 // Read attributes
941 if ( !fi )
942 {
943 QgsDebugMsg( "No field info -> no attributes" );
944 return att;
945 }
946
947 QgsDebugMsg( "Field info found -> open database" );
948 setMapset();
949 dbDriver *driver = db_start_driver_open_database( fi->driver, fi->database );
950
951 if ( !driver )
952 {
953 QgsDebugMsg( QString( "Cannot open database %1 by driver %2" ).arg( fi->database, fi->driver ) );
954 return att;
955 }
956
957 QgsDebugMsg( "Database opened -> read attributes" );
958
959 dbString dbstr;
960 db_init_string( &dbstr );
961 QString query = QStringLiteral( "select * from %1 where %2=%3" ).arg( fi->table, fi->key ).arg( cat );
962 db_set_string( &dbstr, query.toUtf8().constData() );
963
964 QgsDebugMsg( QString( "SQL: %1" ).arg( db_get_string( &dbstr ) ) );
965
966 dbCursor databaseCursor;
967 if ( db_open_select_cursor( driver, &dbstr, &databaseCursor, DB_SCROLL ) != DB_OK )
968 {
969 db_close_database_shutdown_driver( driver );
970 QgsDebugMsg( "Cannot select attributes from table" );
971 return att;
972 }
973
974 int nRecords = db_get_num_rows( &databaseCursor );
975 QgsDebugMsg( QString( "Number of records: %1" ).arg( nRecords ) );
976
977 if ( nRecords < 1 )
978 {
979 db_close_database_shutdown_driver( driver );
980 QgsDebugMsg( "No DB record" );
981 return att;
982 }
983
984 dbTable *databaseTable = db_get_cursor_table( &databaseCursor );
985 int nColumns = db_get_table_number_of_columns( databaseTable );
986
987 int more;
988 if ( db_fetch( &databaseCursor, DB_NEXT, &more ) != DB_OK )
989 {
990 db_close_database_shutdown_driver( driver );
991 QgsDebugMsg( "Cannot fetch DB record" );
992 return att;
993 }
994
995 // Read columns' description
996 for ( int i = 0; i < nColumns; i++ )
997 {
998 dbColumn *column = db_get_table_column( databaseTable, i );
999 db_convert_column_value_to_string( column, &dbstr );
1000
1001 QString v = textEncoding()->toUnicode( db_get_string( &dbstr ) );
1002 QgsDebugMsg( QString( "Value: %1" ).arg( v ) );
1003 att->insert( i, QVariant( v ) );
1004 }
1005
1006 db_close_cursor( &databaseCursor );
1007 db_close_database_shutdown_driver( driver );
1008 db_free_string( &dbstr );
1009
1010 return att;
1011 }
1012
1013
1014
numDbLinks(void)1015 int QgsGrassProvider::numDbLinks( void )
1016 {
1017
1018 return ( Vect_get_num_dblinks( map() ) );
1019 }
1020
dbLinkField(int link)1021 int QgsGrassProvider::dbLinkField( int link )
1022 {
1023
1024 struct field_info *fi = Vect_get_dblink( map(), link );
1025
1026 if ( !fi )
1027 return 0;
1028
1029 return ( fi->number );
1030 }
1031
isTopoType() const1032 bool QgsGrassProvider::isTopoType() const
1033 {
1034 return isTopoType( mLayerType );
1035 }
1036
isTopoType(int layerType)1037 bool QgsGrassProvider::isTopoType( int layerType )
1038 {
1039 return layerType == TopoPoint || layerType == TopoLine || layerType == TopoNode;
1040 }
1041
setTopoFields()1042 void QgsGrassProvider::setTopoFields()
1043 {
1044 mTopoFields.append( QgsField( QStringLiteral( "id" ), QVariant::Int ) );
1045
1046 if ( mLayerType == TopoPoint )
1047 {
1048 mTopoFields.append( QgsField( QStringLiteral( "type" ), QVariant::String ) );
1049 mTopoFields.append( QgsField( QStringLiteral( "node" ), QVariant::Int ) );
1050 }
1051 else if ( mLayerType == TopoLine )
1052 {
1053 mTopoFields.append( QgsField( QStringLiteral( "type" ), QVariant::String ) );
1054 mTopoFields.append( QgsField( QStringLiteral( "node1" ), QVariant::Int ) );
1055 mTopoFields.append( QgsField( QStringLiteral( "node2" ), QVariant::Int ) );
1056 mTopoFields.append( QgsField( QStringLiteral( "left" ), QVariant::Int ) );
1057 mTopoFields.append( QgsField( QStringLiteral( "right" ), QVariant::Int ) );
1058 }
1059 else if ( mLayerType == TopoNode )
1060 {
1061 mTopoFields.append( QgsField( QStringLiteral( "lines" ), QVariant::String ) );
1062 }
1063 }
1064
startEditing(QgsVectorLayer * vectorLayer)1065 void QgsGrassProvider::startEditing( QgsVectorLayer *vectorLayer )
1066 {
1067 QgsDebugMsg( "uri = " + dataSourceUri() );
1068 if ( !vectorLayer || !vectorLayer->editBuffer() )
1069 {
1070 QgsDebugMsg( "vector or buffer is null" );
1071 return;
1072 }
1073 mEditLayer = vectorLayer;
1074 if ( !isValid() || !isGrassEditable() )
1075 {
1076 QgsDebugMsg( "not valid or not editable" );
1077 return;
1078 }
1079 if ( mEditBuffer )
1080 {
1081 QgsDebugMsg( "already edited" );
1082 return;
1083 }
1084
1085 // disconnect dataChanged() because the changes are done here and we know about them
1086 disconnect( mLayer->map(), &QgsGrassVectorMap::dataChanged, this, &QgsGrassProvider::onDataChanged );
1087 mLayer->map()->startEdit();
1088 mLayer->startEdit();
1089
1090 mEditBuffer = vectorLayer->editBuffer();
1091 connect( mEditBuffer, &QgsVectorLayerEditBuffer::featureAdded, this, &QgsGrassProvider::onFeatureAdded );
1092 connect( mEditBuffer, &QgsVectorLayerEditBuffer::featureDeleted, this, &QgsGrassProvider::onFeatureDeleted );
1093 connect( mEditBuffer, &QgsVectorLayerEditBuffer::geometryChanged, this, &QgsGrassProvider::onGeometryChanged );
1094 connect( mEditBuffer, &QgsVectorLayerEditBuffer::attributeValueChanged, this, &QgsGrassProvider::onAttributeValueChanged );
1095 connect( mEditBuffer, &QgsVectorLayerEditBuffer::attributeAdded, this, &QgsGrassProvider::onAttributeAdded );
1096 connect( mEditBuffer, &QgsVectorLayerEditBuffer::attributeDeleted, this, &QgsGrassProvider::onAttributeDeleted );
1097 connect( vectorLayer, &QgsVectorLayer::beforeCommitChanges, this, &QgsGrassProvider::onBeforeCommitChanges );
1098 connect( vectorLayer, &QgsVectorLayer::beforeRollBack, this, &QgsGrassProvider::onBeforeRollBack );
1099 connect( vectorLayer, &QgsVectorLayer::editingStopped, this, &QgsGrassProvider::onEditingStopped );
1100
1101 connect( vectorLayer->undoStack(), &QUndoStack::indexChanged, this, &QgsGrassProvider::onUndoIndexChanged );
1102
1103 // let qgis know (attribute table etc.) that we added topo symbol field
1104 vectorLayer->updateFields();
1105 mEditLayerFields = vectorLayer->fields();
1106
1107 // TODO: enable cats editing once all consequences are implemented
1108 // disable cat and topo symbol editing
1109 QgsEditFormConfig formConfig = vectorLayer->editFormConfig();
1110 formConfig.setReadOnly( mLayer->keyColumn(), true );
1111 formConfig.setReadOnly( mLayer->fields().size() - 1, true );
1112 vectorLayer->setEditFormConfig( formConfig );
1113
1114 sEditedCount++;
1115
1116 QgsDebugMsg( "edit started" );
1117 }
1118
setPoints(struct line_pnts * points,const QgsAbstractGeometry * geometry)1119 void QgsGrassProvider::setPoints( struct line_pnts *points, const QgsAbstractGeometry *geometry )
1120 {
1121 if ( !points )
1122 {
1123 return;
1124 }
1125 Vect_reset_line( points );
1126 if ( !geometry )
1127 {
1128 return;
1129 }
1130 if ( geometry->wkbType() == QgsWkbTypes::Point || geometry->wkbType() == QgsWkbTypes::PointZ )
1131 {
1132 const QgsPoint *point = dynamic_cast<const QgsPoint *>( geometry );
1133 if ( point )
1134 {
1135 Vect_append_point( points, point->x(), point->y(), point->z() );
1136 QgsDebugMsg( QString( "x = %1 y = %2" ).arg( point->x() ).arg( point->y() ) );
1137 }
1138 }
1139 else if ( geometry->wkbType() == QgsWkbTypes::LineString || geometry->wkbType() == QgsWkbTypes::LineStringZ )
1140 {
1141 const QgsLineString *lineString = dynamic_cast<const QgsLineString *>( geometry );
1142 if ( lineString )
1143 {
1144 for ( int i = 0; i < lineString->numPoints(); i++ )
1145 {
1146 QgsPoint point = lineString->pointN( i );
1147 Vect_append_point( points, point.x(), point.y(), point.z() );
1148 }
1149 }
1150 }
1151 else if ( geometry->wkbType() == QgsWkbTypes::Polygon || geometry->wkbType() == QgsWkbTypes::PolygonZ )
1152 {
1153 const QgsPolygon *polygon = dynamic_cast<const QgsPolygon *>( geometry );
1154 if ( polygon && polygon->exteriorRing() )
1155 {
1156 QgsLineString *lineString = polygon->exteriorRing()->curveToLine();
1157 if ( lineString )
1158 {
1159 for ( int i = 0; i < lineString->numPoints(); i++ )
1160 {
1161 QgsPoint point = lineString->pointN( i );
1162 Vect_append_point( points, point.x(), point.y(), point.z() );
1163 }
1164 }
1165 }
1166 }
1167 else
1168 {
1169 QgsDebugMsg( "unknown type : " + geometry->geometryType() );
1170 }
1171 }
1172
onFeatureAdded(QgsFeatureId fid)1173 void QgsGrassProvider::onFeatureAdded( QgsFeatureId fid )
1174 {
1175 // fid is negative for new features
1176
1177 int lid = QgsGrassFeatureIterator::lidFromFid( fid );
1178 int cat = QgsGrassFeatureIterator::catFromFid( fid );
1179
1180 QgsDebugMsg( QString( "fid = %1 lid = %2 cat = %3" ).arg( fid ).arg( lid ).arg( cat ) );
1181
1182 Vect_reset_cats( mCats );
1183 int realLine = 0; // number of line to be rewritten if exists
1184 int newCat = 0;
1185
1186 // TODO: get also old type if it is feature previously deleted
1187 int type = 0;
1188
1189 if ( FID_IS_NEW( fid ) )
1190 {
1191 if ( mNewFeatureType == QgsGrassProvider::sLastType )
1192 {
1193 type = mLastType;
1194 QgsDebugMsg( QString( "use mLastType = %1" ).arg( mLastType ) );
1195 }
1196 else
1197 {
1198 type = mNewFeatureType == GV_AREA ? GV_BOUNDARY : mNewFeatureType;
1199 }
1200 // geometry
1201 const QgsAbstractGeometry *geometry = nullptr;
1202 if ( !mEditBuffer->isFeatureAdded( fid ) )
1203 {
1204 #ifdef QGISDEBUG
1205 QgsDebugMsg( "the feature is missing in buffer addedFeatures :" );
1206
1207 const auto constKeys = mEditBuffer->addedFeatures().keys();
1208 for ( QgsFeatureId id : constKeys )
1209 {
1210 QgsDebugMsg( QString( "addedFeatures : id = %1" ).arg( id ) );
1211 }
1212 #endif
1213 return;
1214 }
1215 QgsFeature feature = mEditBuffer->addedFeatures().value( fid );
1216 QgsGeometry featureGeometry = feature.geometry();
1217 geometry = featureGeometry.constGet();
1218 if ( !geometry )
1219 {
1220 QgsDebugMsg( "geometry is null" );
1221 return;
1222 }
1223
1224 setPoints( mPoints, geometry );
1225
1226 QgsFeatureMap &addedFeatures = mEditBuffer->mAddedFeatures;
1227
1228 // change polygon to linestring
1229 QgsWkbTypes::Type wkbType = QgsWkbTypes::flatType( geometry->wkbType() );
1230 if ( wkbType == QgsWkbTypes::Polygon )
1231 {
1232 QgsGeometry addedFeatureGeom = addedFeatures[fid].geometry();
1233 const QgsPolygon *polygon = dynamic_cast<const QgsPolygon *>( addedFeatureGeom.constGet() );
1234 if ( polygon )
1235 {
1236 QgsLineString *lineString = polygon->exteriorRing()->curveToLine();
1237 addedFeatures[fid].setGeometry( QgsGeometry( lineString ) );
1238 }
1239 // TODO: create also centroid and add it to undo
1240 }
1241
1242 // add new category
1243 // TODO: add category also to boundary if user defined at least one attribute?
1244 if ( type != GV_BOUNDARY )
1245 {
1246 // TODO: redo of deleted new features - save new cats somewhere,
1247 // resetting fid probably is not possible because it is stored in undo commands and used in buffer maps
1248
1249 // It may be that user manually entered cat value
1250 QgsFeatureMap &addedFeatures = mEditBuffer->mAddedFeatures;
1251 QgsFeature &feature = addedFeatures[fid];
1252 int catIndex = feature.fields().indexFromName( mLayer->keyColumnName() );
1253 if ( catIndex != -1 )
1254 {
1255 QVariant userCatVariant = feature.attributes().value( catIndex );
1256 if ( !userCatVariant.isNull() )
1257 {
1258 newCat = userCatVariant.toInt();
1259 QgsDebugMsg( QString( "user defined newCat = %1" ).arg( newCat ) );
1260 }
1261 }
1262 if ( newCat == 0 )
1263 {
1264 newCat = getNewCat();
1265 }
1266 QgsDebugMsg( QString( "newCat = %1" ).arg( newCat ) );
1267 Vect_cat_set( mCats, mLayerField, newCat );
1268
1269 if ( mLayer->hasTable() )
1270 {
1271 addedFeatures[fid].setAttribute( mLayer->keyColumn(), newCat );
1272 }
1273 else
1274 {
1275 addedFeatures[fid].setAttribute( 0, newCat );
1276 }
1277 mLayer->map()->newCats()[fid] = newCat;
1278 QgsDebugMsg( QString( "newCats[%1] = %2" ).arg( fid ).arg( newCat ) );
1279
1280 // Currently neither entering new cat nor changing existing cat is allowed
1281 #if 0
1282 // There may be other new features with the same cat which we have to update
1283 const auto constKeys = addedFeatures.keys();
1284 for ( QgsFeatureId addedFid : constKeys )
1285 {
1286 if ( addedFid == fid )
1287 {
1288 continue;
1289 }
1290 int addedCat = mLayer->map()->newCats().value( addedFid ); // it should always exist
1291 QgsDebugMsg( QString( "addedFid = %1 addedCat = %2" ).arg( addedFid ).arg( addedCat ) );
1292 if ( addedCat == newCat )
1293 {
1294 QgsFeature addedFeature = addedFeatures[addedFid];
1295 // TODO: better to update form mLayer->attributes() ?
1296 for ( int i = 0; i < feature.fields()->size(); i++ )
1297 {
1298 if ( feature.fields()->field( i ).name() == QgsGrassVectorMap::topoSymbolFieldName() )
1299 {
1300 continue;
1301 }
1302 if ( feature.attributes().at( i ).isNull() )
1303 {
1304 continue;
1305 }
1306 addedFeature.setAttribute( i, feature.attributes().at( i ) );
1307 }
1308 addedFeatures[addedFid] = addedFeature;
1309 }
1310 }
1311
1312 // Update all changed attributes
1313 QgsChangedAttributesMap &changedAttributes = const_cast<QgsChangedAttributesMap &>( mEditBuffer->changedAttributeValues() );
1314 const auto constKeys = changedAttributes.keys();
1315 for ( QgsFeatureId changedFid : constKeys )
1316 {
1317 int changedCat = QgsGrassFeatureIterator::catFromFid( changedFid );
1318 int realChangedCat = changedCat;
1319 if ( mLayer->map()->newCats().contains( changedFid ) )
1320 {
1321 realChangedCat = mLayer->map()->newCats().value( changedFid );
1322 }
1323 QgsDebugMsg( QString( "changedFid = %1 changedCat = %2 realChangedCat = %3" )
1324 .arg( changedFid ).arg( changedCat ).arg( realChangedCat ) );
1325 if ( realChangedCat == newCat )
1326 {
1327 QgsAttributeMap attributeMap = changedAttributes[changedFid];
1328 const auto constKeys = attributeMap.keys();
1329 for ( int index : constKeys )
1330 {
1331 attributeMap[index] = feature.attributes().value( index );
1332 }
1333 changedAttributes[changedFid] = attributeMap;
1334 }
1335 }
1336 #endif
1337
1338 if ( mLayer->hasTable() )
1339 {
1340 QString error;
1341 // The record may exist if cat is manually defined by user (currently editing of cat column is disabled )
1342 bool recordExists = mLayer->recordExists( newCat, error );
1343 if ( !error.isEmpty() )
1344 {
1345 QgsGrass::warning( error );
1346 }
1347 else
1348 {
1349 error.clear();
1350 if ( !recordExists )
1351 {
1352 QgsDebugMsg( "record does not exist" );
1353 if ( mLayer->attributes().contains( newCat ) )
1354 {
1355 QgsDebugMsg( "attributes exist -> reinsert" );
1356 mLayer->reinsertAttributes( newCat, error );
1357 }
1358 else
1359 {
1360 mLayer->insertAttributes( newCat, feature, error );
1361 }
1362 }
1363 else
1364 {
1365 // Currently disabled
1366 #if 0
1367 // Manual entry of cat is not currently allowed
1368 // TODO: open warning dialog?
1369 // For now we are expecting that user knows what he is doing.
1370 // We update existing record by non null values and set feature null values to existing values
1371 mLayer->updateAttributes( newCat, feature, error ); // also updates feature by existing non null attributes
1372 #endif
1373 }
1374 if ( !error.isEmpty() )
1375 {
1376 QgsGrass::warning( error );
1377 }
1378 }
1379 }
1380
1381 // update table
1382 emit dataChanged();
1383 }
1384 }
1385 else
1386 {
1387 QgsDebugMsg( "undo of old deleted feature" );
1388 int oldLid = lid;
1389 realLine = oldLid;
1390 int realCat = cat;
1391 int layerField = QgsGrassFeatureIterator::layerFromFid( fid );
1392 if ( mLayer->map()->newLids().contains( oldLid ) ) // if it was changed already
1393 {
1394 realLine = mLayer->map()->newLids().value( oldLid );
1395 }
1396 if ( mLayer->map()->newCats().contains( fid ) )
1397 {
1398 realCat = mLayer->map()->newCats().value( fid );
1399 }
1400 QgsDebugMsg( QString( "fid = %1 lid = %2 realLine = %3 cat = %4 realCat = %5 layerField = %6" )
1401 .arg( fid ).arg( lid ).arg( realLine ).arg( cat ).arg( realCat ).arg( layerField ) );
1402
1403 if ( realLine > 0 )
1404 {
1405 QgsDebugMsg( QString( "reading realLine = %1" ).arg( realLine ) );
1406 int realType = readLine( mPoints, mCats, realLine );
1407 if ( realType > 0 )
1408 {
1409 QgsDebugMsg( QString( "the line exists realType = %1, add the cat to that line" ).arg( realType ) );
1410 type = realType;
1411 }
1412 else // should not happen
1413 {
1414 QgsDebugMsg( "cannot read realLine" );
1415 return;
1416 }
1417 }
1418 else
1419 {
1420 QgsDebugMsg( QString( "the line does not exist -> restore old geometry" ) );
1421 const QgsAbstractGeometry *geometry = nullptr;
1422
1423 // If it is not new feature, we should have the geometry in oldGeometries
1424 if ( mLayer->map()->oldGeometries().contains( lid ) )
1425 {
1426 geometry = mLayer->map()->oldGeometries().value( lid );
1427 type = mLayer->map()->oldTypes().value( lid );
1428 }
1429 else
1430 {
1431 QgsDebugMsg( "geometry of old, previously deleted feature not found" );
1432 return;
1433 }
1434 if ( !geometry )
1435 {
1436 QgsDebugMsg( "geometry is null" );
1437 return;
1438 }
1439 setPoints( mPoints, geometry );
1440 }
1441
1442 QgsDebugMsg( QString( "layerField = %1 realCat = %2" ).arg( layerField ).arg( realCat ) );
1443 if ( realCat > 0 && layerField > 0 )
1444 {
1445 Vect_cat_set( mCats, layerField, realCat );
1446
1447 // restore attributes
1448 QgsGrassVectorMapLayer *layer = mLayer;
1449 if ( layerField != mLayer->field() )
1450 {
1451 layer = otherEditLayer( layerField );
1452 }
1453 if ( !layer )
1454 {
1455 QgsDebugMsg( "Cannot get layer" );
1456 }
1457 else
1458 {
1459 QString error;
1460 bool recordExists = layer->recordExists( realCat, error );
1461 QgsDebugMsg( QString( "recordExists = %1 error = %2" ).arg( recordExists ).arg( error ) );
1462 if ( !recordExists && error.isEmpty() )
1463 {
1464 QgsDebugMsg( "record does not exist -> restore attributes" );
1465 error.clear();
1466 layer->reinsertAttributes( realCat, error );
1467 if ( !error.isEmpty() )
1468 {
1469 QgsGrass::warning( tr( "Cannot restore record with cat %1" ).arg( realCat ) );
1470 }
1471 }
1472 }
1473 }
1474 }
1475
1476 QgsDebugMsg( QString( "type = %1 mPoints->n_points = %2" ).arg( type ).arg( mPoints->n_points ) );
1477 if ( type > 0 && mPoints->n_points > 0 )
1478 {
1479 int newLid = 0;
1480 mLayer->map()->lockReadWrite();
1481 if ( realLine > 0 )
1482 {
1483 newLid = rewriteLine( realLine, type, mPoints, mCats );
1484 }
1485 else
1486 {
1487 // TODO: use writeLine() and move setting oldLids, newLids there
1488 newLid = Vect_write_line( map(), type, mPoints, mCats );
1489 // fid may be new (negative) or old, if this is delete undo
1490 int oldLid = QgsGrassFeatureIterator::lidFromFid( fid );
1491
1492 mLayer->map()->oldLids()[newLid] = oldLid;
1493 mLayer->map()->newLids()[oldLid] = newLid;
1494 QgsDebugMsg( QString( "oldLid = %1 newLine = %2" ).arg( oldLid ).arg( newLid ) );
1495
1496 QgsDebugMsg( QString( "oldLids : %1 -> %2" ).arg( newLid ).arg( oldLid ) );
1497 mLayer->map()->oldLids()[newLid] = oldLid;
1498 QgsDebugMsg( QString( "newLids : %1 -> %2" ).arg( oldLid ).arg( newLid ) );
1499 mLayer->map()->newLids()[oldLid] = newLid;
1500 }
1501 mLayer->map()->unlockReadWrite();
1502
1503 QgsDebugMsg( QString( "newLine = %1" ).arg( newLid ) );
1504
1505 setAddedFeaturesSymbol();
1506 }
1507 QgsDebugMsg( QString( "mLayer->cidxFieldIndex() = %1 cidxFieldNumCats() = %2" )
1508 .arg( mLayer->cidxFieldIndex() ).arg( mLayer->cidxFieldNumCats() ) );
1509 }
1510
onFeatureDeleted(QgsFeatureId fid)1511 void QgsGrassProvider::onFeatureDeleted( QgsFeatureId fid )
1512 {
1513 QgsDebugMsg( QString( "fid = %1" ).arg( fid ) );
1514
1515 int oldLid = QgsGrassFeatureIterator::lidFromFid( fid );
1516 int cat = QgsGrassFeatureIterator::catFromFid( fid );
1517 int layerField = 0;
1518 if ( FID_IS_NEW( fid ) )
1519 {
1520 layerField = mLayerField;
1521 }
1522 else
1523 {
1524 layerField = QgsGrassFeatureIterator::layerFromFid( fid );
1525 }
1526
1527 int realLine = oldLid;
1528 int realCat = cat;
1529 if ( mLayer->map()->newLids().contains( oldLid ) ) // if it was changed already
1530 {
1531 realLine = mLayer->map()->newLids().value( oldLid );
1532 }
1533 if ( mLayer->map()->newCats().contains( fid ) )
1534 {
1535 realCat = mLayer->map()->newCats().value( fid );
1536 }
1537
1538 QgsDebugMsg( QString( "fid = %1 oldLid = %2 realLine = %3 cat = %4 realCat = %5 layerField = %6" )
1539 .arg( fid ).arg( oldLid ).arg( realLine ).arg( cat ).arg( realCat ).arg( layerField ) );
1540
1541 int type = 0;
1542 mLayer->map()->lockReadWrite();
1543 G_TRY
1544 {
1545 int type = readLine( mPoints, mCats, realLine );
1546 if ( type <= 0 )
1547 {
1548 QgsDebugMsg( "cannot read line" );
1549 }
1550 else
1551 {
1552 // store only the first original geometry if it is not new feature, changed geometries are stored in the buffer
1553 if ( oldLid > 0 && !mLayer->map()->oldGeometries().contains( oldLid ) )
1554 {
1555 QgsAbstractGeometry *geometry = mLayer->map()->lineGeometry( oldLid );
1556 if ( geometry )
1557 {
1558 QgsDebugMsg( QString( "save old geometry of oldLid = %1" ).arg( oldLid ) );
1559 mLayer->map()->oldGeometries().insert( oldLid, geometry );
1560 mLayer->map()->oldTypes().insert( oldLid, type );
1561 }
1562 else
1563 {
1564 QgsDebugMsg( QString( "cannot read geometry of oldLid = %1" ).arg( oldLid ) );
1565 }
1566 }
1567
1568 if ( realCat > 0 && layerField > 0 )
1569 {
1570 if ( Vect_field_cat_del( mCats, layerField, realCat ) == 0 )
1571 {
1572 // should not happen
1573 QgsDebugMsg( "the line does not have old category" );
1574 }
1575 }
1576 QgsDebugMsg( QString( "mCats->n_cats = %1" ).arg( mCats->n_cats ) );
1577 if ( mCats->n_cats > 0 )
1578 {
1579 QgsDebugMsg( "the line has more cats -> rewrite" );
1580 int newLid = rewriteLine( realLine, type, mPoints, mCats );
1581 Q_UNUSED( newLid )
1582 }
1583 else
1584 {
1585 QgsDebugMsg( "no more cats on the line -> delete" );
1586
1587 Vect_delete_line_function_pointer( map(), realLine );
1588 // oldLids are mapping to the very first, original version (used by undo)
1589 int oldestLid = oldLid;
1590 if ( mLayer->map()->oldLids().contains( oldLid ) )
1591 {
1592 oldestLid = mLayer->map()->oldLids().value( oldLid );
1593 }
1594 QgsDebugMsg( QString( "oldLid = %1 oldestLid = %2" ).arg( oldLid ).arg( oldestLid ) );
1595 QgsDebugMsg( QString( "newLids : %1 -> 0" ).arg( oldestLid ) );
1596 mLayer->map()->newLids()[oldestLid] = 0;
1597 }
1598
1599 // Delete record if orphan
1600 if ( realCat > 0 && layerField > 0 )
1601 {
1602 QgsGrassVectorMapLayer *layer = mLayer;
1603 if ( layerField != mLayer->field() )
1604 {
1605 layer = otherEditLayer( layerField );
1606 }
1607 if ( !layer )
1608 {
1609 QgsDebugMsg( "Cannot get layer" );
1610 }
1611 else
1612 {
1613 QString error;
1614 bool orphan = layer->isOrphan( realCat, error );
1615 QgsDebugMsg( QString( "orphan = %1 error = %2" ).arg( orphan ).arg( error ) );
1616 if ( orphan && error.isEmpty() )
1617 {
1618 QgsDebugMsg( QString( "realCat = %1 is orphan -> delete record" ).arg( realCat ) );
1619 error.clear();
1620 layer->deleteAttribute( realCat, error );
1621 if ( !error.isEmpty() )
1622 {
1623 QgsGrass::warning( tr( "Cannot delete orphan record with cat %1" ).arg( realCat ) );
1624 }
1625 }
1626 }
1627 }
1628 }
1629 }
1630 G_CATCH( QgsGrass::Exception & e )
1631 {
1632 QgsDebugMsg( QString( "Cannot rewrite/delete line : %1" ).arg( e.what() ) );
1633 }
1634 mLayer->map()->unlockReadWrite();
1635
1636 if ( type == GV_BOUNDARY || type == GV_CENTROID )
1637 {
1638 setAddedFeaturesSymbol();
1639 }
1640 }
1641
onGeometryChanged(QgsFeatureId fid,const QgsGeometry & geom)1642 void QgsGrassProvider::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
1643 {
1644 int oldLid = QgsGrassFeatureIterator::lidFromFid( fid );
1645 int realLine = oldLid;
1646 if ( mLayer->map()->newLids().contains( oldLid ) ) // if it was changed already
1647 {
1648 realLine = mLayer->map()->newLids().value( oldLid );
1649 }
1650 QgsDebugMsg( QString( "fid = %1 oldLid = %2 realLine = %3" ).arg( fid ).arg( oldLid ).arg( realLine ) );
1651
1652 int type = readLine( mPoints, mCats, realLine );
1653 QgsDebugMsg( QString( "type = %1 n_points = %2" ).arg( type ).arg( mPoints->n_points ) );
1654 if ( type < 1 ) // error
1655 {
1656 return;
1657 }
1658 mLastType = type;
1659
1660 // store only the first original geometry if it is not new feature, changed geometries are stored in the buffer
1661 if ( oldLid > 0 && !mLayer->map()->oldGeometries().contains( oldLid ) )
1662 {
1663 QgsAbstractGeometry *geometry = mLayer->map()->lineGeometry( oldLid );
1664 if ( geometry )
1665 {
1666 QgsDebugMsg( QString( "save old geometry of oldLid = %1" ).arg( oldLid ) );
1667 mLayer->map()->oldGeometries().insert( oldLid, geometry );
1668 mLayer->map()->oldTypes().insert( oldLid, type );
1669 }
1670 else
1671 {
1672 QgsDebugMsg( QString( "cannot read geometry of oldLid = %1" ).arg( oldLid ) );
1673 }
1674 }
1675
1676 setPoints( mPoints, geom.constGet() );
1677
1678 mLayer->map()->lockReadWrite();
1679 // Vect_rewrite_line may delete/write the line with a new id
1680 int newLid = rewriteLine( realLine, type, mPoints, mCats );
1681 Q_UNUSED( newLid )
1682
1683 mLayer->map()->unlockReadWrite();
1684
1685 if ( type == GV_BOUNDARY || type == GV_CENTROID )
1686 {
1687 setAddedFeaturesSymbol();
1688 }
1689 }
1690
onAttributeValueChanged(QgsFeatureId fid,int idx,const QVariant & value)1691 void QgsGrassProvider::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
1692 {
1693 QgsDebugMsg( QString( "fid = %1 idx = %2 value = %3" ).arg( fid ).arg( idx ).arg( value.toString() ) );
1694
1695 int layerField = QgsGrassFeatureIterator::layerFromFid( fid );
1696 int cat = QgsGrassFeatureIterator::catFromFid( fid );
1697 QgsDebugMsg( QString( "layerField = %1" ).arg( layerField ) );
1698
1699 if ( !FID_IS_NEW( fid ) && ( layerField > 0 && layerField != mLayerField ) )
1700 {
1701 QgsDebugMsg( "changing attributes in different layer is not allowed" );
1702 // reset the value
1703 QgsChangedAttributesMap &changedAttributes = mEditBuffer->mChangedAttributeValues;
1704 if ( idx == mLayer->keyColumn() )
1705 {
1706 // should not happen because cat field is not editable
1707 changedAttributes[fid][idx] = cat;
1708 }
1709 else
1710 {
1711 changedAttributes[fid][idx] = QgsGrassFeatureIterator::nonEditableValue( layerField );
1712 }
1713 // update table
1714 // TODO: This would be too slow with bulk updates (field calculator for example), causing update
1715 // of the whole table after each change. How to update single row?
1716 //emit dataChanged();
1717 return;
1718 }
1719
1720 int oldLid = QgsGrassFeatureIterator::lidFromFid( fid );
1721
1722 int realLine = oldLid;
1723 int realCat = cat;
1724 if ( mLayer->map()->newLids().contains( oldLid ) ) // if it was changed already
1725 {
1726 realLine = mLayer->map()->newLids().value( oldLid );
1727 }
1728 if ( mLayer->map()->newCats().contains( fid ) )
1729 {
1730 realCat = mLayer->map()->newCats().value( fid );
1731 }
1732 QgsDebugMsg( QString( "fid = %1 oldLid = %2 realLine = %3 cat = %4 realCat = %5" )
1733 .arg( fid ).arg( oldLid ).arg( realLine ).arg( cat ).arg( realCat ) );
1734
1735 // index is for current fields
1736 if ( idx < 0 || idx > mEditLayer->fields().size() )
1737 {
1738 QgsDebugMsg( "index out of range" );
1739 return;
1740 }
1741 QgsField field = mEditLayer->fields().at( idx );
1742
1743 QgsDebugMsg( "field.name() = " + field.name() + " keyColumnName() = " + mLayer->keyColumnName() );
1744 // TODO: Changing existing category is currently disabled (read only widget set on layer)
1745 // because it makes it all too complicated
1746 if ( field.name() == mLayer->keyColumnName() )
1747 {
1748 // user changed category -> rewrite line
1749 QgsDebugMsg( "cat changed -> rewrite line" );
1750 int type = readLine( mPoints, mCats, realLine );
1751 if ( type <= 0 )
1752 {
1753 QgsDebugMsg( "cannot read line" );
1754 }
1755 else
1756 {
1757 if ( Vect_field_cat_del( mCats, mLayerField, realCat ) == 0 )
1758 {
1759 // should not happen
1760 QgsDebugMsg( "the line does not have old category" );
1761 }
1762
1763 int newCat = value.toInt();
1764 QgsDebugMsg( QString( "realCat = %1 newCat = %2" ).arg( realCat ).arg( newCat ) );
1765 if ( newCat == 0 )
1766 {
1767 QgsDebugMsg( "new category is 0" );
1768 // TODO: remove category from line?
1769 }
1770 else
1771 {
1772 Vect_cat_set( mCats, mLayerField, newCat );
1773 mLayer->map()->lockReadWrite();
1774 int newLid = rewriteLine( realLine, type, mPoints, mCats );
1775 Q_UNUSED( newLid )
1776 mLayer->map()->newCats()[fid] = newCat;
1777
1778 // TODO: - store the new cat somewhere for cats mapping
1779 // - insert record if does not exist
1780 // - update feature attributes by existing record?
1781
1782 mLayer->map()->unlockReadWrite();
1783 }
1784 }
1785 }
1786 else
1787 {
1788 int undoIndex = mEditLayer->undoStack()->index();
1789 QgsDebugMsg( QString( "undoIndex = %1" ).arg( undoIndex ) );
1790 if ( realCat > 0 )
1791 {
1792 QString error;
1793 bool recordExists = mLayer->recordExists( realCat, error );
1794 if ( !error.isEmpty() )
1795 {
1796 QgsGrass::warning( error );
1797 }
1798 error.clear();
1799 mLayer->changeAttributeValue( realCat, field, value, error );
1800 if ( !error.isEmpty() )
1801 {
1802 QgsGrass::warning( error );
1803 }
1804 if ( !recordExists )
1805 {
1806 mLayer->map()->undoCommands()[undoIndex]
1807 << new QgsGrassUndoCommandChangeAttribute( this, fid, realLine, mLayerField, realCat, false, true );
1808 }
1809 }
1810 else
1811 {
1812 int newCat = getNewCat();
1813 QgsDebugMsg( QString( "no cat -> add new cat %1 to line" ).arg( newCat ) );
1814 int type = readLine( mPoints, mCats, realLine );
1815 if ( type <= 0 )
1816 {
1817 QgsDebugMsg( "cannot read line" );
1818 }
1819 else
1820 {
1821 Vect_cat_set( mCats, mLayerField, newCat );
1822 mLayer->map()->lockReadWrite();
1823 int newLid = rewriteLine( realLine, type, mPoints, mCats );
1824 Q_UNUSED( newLid )
1825 mLayer->map()->newCats()[fid] = newCat;
1826
1827 QString error;
1828 bool recordExists = mLayer->recordExists( newCat, error );
1829 if ( !error.isEmpty() )
1830 {
1831 QgsGrass::warning( error );
1832 }
1833 error.clear();
1834 // it does insert new record if it doesn't exist
1835 mLayer->changeAttributeValue( newCat, field, value, error );
1836 if ( !error.isEmpty() )
1837 {
1838 QgsGrass::warning( error );
1839 }
1840
1841 mLayer->map()->undoCommands()[undoIndex]
1842 << new QgsGrassUndoCommandChangeAttribute( this, fid, newLid, mLayerField, newCat, true, !recordExists );
1843
1844 mLayer->map()->unlockReadWrite();
1845 }
1846 }
1847 }
1848 }
1849
onAttributeAdded(int idx)1850 void QgsGrassProvider::onAttributeAdded( int idx )
1851 {
1852 QgsDebugMsg( QString( "idx = %1" ).arg( idx ) );
1853 if ( idx < 0 || idx >= mEditLayer->fields().size() )
1854 {
1855 QgsDebugMsg( "index out of range" );
1856 }
1857 QString error;
1858 mLayer->addColumn( mEditLayer->fields().at( idx ), error );
1859 if ( !error.isEmpty() )
1860 {
1861 QgsDebugMsg( error );
1862 QgsGrass::warning( error );
1863 // TODO: remove the column somehow from the layer/buffer - undo?
1864 }
1865 else
1866 {
1867 mEditLayerFields = mEditLayer->fields();
1868 emit fieldsChanged();
1869 }
1870 }
1871
onAttributeDeleted(int idx)1872 void QgsGrassProvider::onAttributeDeleted( int idx )
1873 {
1874 QgsDebugMsg( QString( "idx = %1 mEditLayerFields.size() = %2" ).arg( idx ).arg( mEditLayerFields.size() ) );
1875 // The field was already removed from mEditLayer->fields() -> using stored last version of fields
1876 if ( idx < 0 || idx >= mEditLayerFields.size() )
1877 {
1878 QgsDebugMsg( "index out of range" );
1879 return;
1880 }
1881 QgsField deletedField = mEditLayerFields.at( idx );
1882 QgsDebugMsg( QString( "deletedField.name() = %1" ).arg( deletedField.name() ) );
1883
1884 QString error;
1885 mLayer->deleteColumn( deletedField, error );
1886 if ( !error.isEmpty() )
1887 {
1888 QgsDebugMsg( error );
1889 QgsGrass::warning( error );
1890 // TODO: get back the column somehow to the layer/buffer - undo?
1891 }
1892 else
1893 {
1894 mEditLayerFields = mEditLayer->fields();
1895 emit fieldsChanged();
1896 }
1897 }
1898
setAddedFeaturesSymbol()1899 void QgsGrassProvider::setAddedFeaturesSymbol()
1900 {
1901 if ( !mEditBuffer )
1902 {
1903 return;
1904 }
1905 QgsFeatureMap &features = mEditBuffer->mAddedFeatures;
1906 for ( auto it = features.constBegin(); it != features.constEnd(); ++it )
1907 {
1908 QgsFeature feature = it.value();
1909 if ( !feature.hasGeometry() )
1910 {
1911 continue;
1912 }
1913 int lid = QgsGrassFeatureIterator::lidFromFid( it.key() );
1914 int realLid = lid;
1915 if ( mLayer->map()->newLids().contains( lid ) )
1916 {
1917 realLid = mLayer->map()->newLids().value( lid );
1918 }
1919 QgsDebugMsg( QString( "fid = %1 lid = %2 realLid = %3" ).arg( it.key() ).arg( lid ).arg( realLid ) );
1920 QgsGrassVectorMap::TopoSymbol symbol = mLayer->map()->topoSymbol( realLid );
1921 // the feature may be without fields and set attribute by name does not work
1922 int index = mLayer->fields().indexFromName( QgsGrassVectorMap::topoSymbolFieldName() );
1923 feature.setAttribute( index, symbol );
1924 features[it.key()] = feature;
1925 }
1926 }
1927
onUndoIndexChanged(int currentIndex)1928 void QgsGrassProvider::onUndoIndexChanged( int currentIndex )
1929 {
1930 QgsDebugMsg( QString( "currentIndex = %1" ).arg( currentIndex ) );
1931 // multiple commands maybe undone with single undoIndexChanged signal
1932 QList<int> indexes = mLayer->map()->undoCommands().keys();
1933 std::sort( indexes.begin(), indexes.end() );
1934 for ( int i = indexes.size() - 1; i >= 0; i-- )
1935 {
1936 int index = indexes[i];
1937 if ( index < currentIndex )
1938 {
1939 break;
1940 }
1941 QgsDebugMsg( QString( "index = %1" ).arg( index ) );
1942 if ( mLayer->map()->undoCommands().contains( index ) )
1943 {
1944 QgsDebugMsg( QString( "%1 undo commands" ).arg( mLayer->map()->undoCommands()[index].size() ) );
1945
1946 for ( int j = 0; j < mLayer->map()->undoCommands()[index].size(); j++ )
1947 {
1948 mLayer->map()->undoCommands()[index][j]->undo();
1949 delete mLayer->map()->undoCommands()[index][j];
1950 }
1951 mLayer->map()->undoCommands().remove( index );
1952 }
1953 }
1954 }
1955
addAttributes(const QList<QgsField> & attributes)1956 bool QgsGrassProvider::addAttributes( const QList<QgsField> &attributes )
1957 {
1958 Q_UNUSED( attributes )
1959 // update fields because QgsVectorLayerEditBuffer::commitChanges() checks old /new fields count
1960 mLayer->updateFields();
1961 return true;
1962 }
1963
deleteAttributes(const QgsAttributeIds & attributes)1964 bool QgsGrassProvider::deleteAttributes( const QgsAttributeIds &attributes )
1965 {
1966 Q_UNUSED( attributes )
1967 // update fields because QgsVectorLayerEditBuffer::commitChanges() checks old /new fields count
1968 mLayer->updateFields();
1969 return true;
1970 }
1971
onBeforeCommitChanges()1972 void QgsGrassProvider::onBeforeCommitChanges()
1973 {
1974 mLayer->map()->clearUndoCommands();
1975 }
1976
onBeforeRollBack()1977 void QgsGrassProvider::onBeforeRollBack()
1978 {
1979 }
1980
onEditingStopped()1981 void QgsGrassProvider::onEditingStopped()
1982 {
1983 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( sender() );
1984 closeEdit( false, vectorLayer );
1985 }
1986
1987 // -------------------------------------------------------------------------------
1988
cidxGetNumFields()1989 int QgsGrassProvider::cidxGetNumFields()
1990 {
1991 return ( Vect_cidx_get_num_fields( map() ) );
1992 }
1993
cidxGetFieldNumber(int idx)1994 int QgsGrassProvider::cidxGetFieldNumber( int idx )
1995 {
1996 if ( idx < 0 || idx >= cidxGetNumFields() )
1997 {
1998 QgsDebugMsg( QString( "idx %1 out of range (0,%2)" ).arg( idx ).arg( cidxGetNumFields() - 1 ) );
1999 return 0;
2000 }
2001 return ( Vect_cidx_get_field_number( map(), idx ) );
2002 }
2003
cidxGetMaxCat(int idx)2004 int QgsGrassProvider::cidxGetMaxCat( int idx )
2005 {
2006 QgsDebugMsg( QString( "idx = %1" ).arg( idx ) );
2007 if ( idx < 0 || idx >= cidxGetNumFields() )
2008 {
2009 QgsDebugMsg( QString( "idx %1 out of range (0,%2)" ).arg( idx ).arg( cidxGetNumFields() - 1 ) );
2010 return 0;
2011 }
2012
2013 int ncats = Vect_cidx_get_num_cats_by_index( map(), idx );
2014 QgsDebugMsg( QString( "ncats = %1" ).arg( ncats ) );
2015
2016 if ( ncats == 0 )
2017 {
2018 return 0;
2019 }
2020
2021 int cat, type, id;
2022 Vect_cidx_get_cat_by_index( map(), idx, ncats - 1, &cat, &type, &id );
2023
2024 return cat;
2025 }
2026
getNewCat()2027 int QgsGrassProvider::getNewCat()
2028 {
2029 QgsDebugMsg( QString( "get new cat for cidxFieldIndex() = %1" ).arg( mLayer->cidxFieldIndex() ) );
2030 if ( mLayer->cidxFieldIndex() == -1 )
2031 {
2032 // No features with this field yet in map
2033 return 1;
2034 }
2035 else
2036 {
2037 return cidxGetMaxCat( mLayer->cidxFieldIndex() ) + 1;
2038 }
2039 }
2040
openLayer() const2041 QgsGrassVectorMapLayer *QgsGrassProvider::openLayer() const
2042 {
2043 return mLayer->map()->openLayer( mLayerField );
2044 }
2045
map() const2046 struct Map_info *QgsGrassProvider::map() const
2047 {
2048 Q_ASSERT( mLayer );
2049 // cppcheck-suppress assertWithSideEffect
2050 Q_ASSERT( mLayer->map() );
2051 // cppcheck-suppress assertWithSideEffect
2052 Q_ASSERT( mLayer->map()->map() );
2053 return mLayer->map()->map();
2054 }
2055
setMapset()2056 void QgsGrassProvider::setMapset()
2057 {
2058 QgsGrass::setMapset( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset() );
2059 }
2060
otherEditLayer(int layerField)2061 QgsGrassVectorMapLayer *QgsGrassProvider::otherEditLayer( int layerField )
2062 {
2063 const auto constMOtherEditLayers = mOtherEditLayers;
2064 for ( QgsGrassVectorMapLayer *layer : constMOtherEditLayers )
2065 {
2066 if ( layer->field() == layerField )
2067 {
2068 return layer;
2069 }
2070 }
2071 QgsGrassVectorMapLayer *layer = mLayer->map()->openLayer( layerField );
2072 if ( layer )
2073 {
2074 layer->startEdit();
2075 mOtherEditLayers << layer;
2076 }
2077 return layer;
2078 }
2079
name() const2080 QString QgsGrassProvider::name() const
2081 {
2082 return GRASS_KEY;
2083 }
2084
description() const2085 QString QgsGrassProvider::description() const
2086 {
2087 return tr( "GRASS %1 vector provider" ).arg( GRASS_VERSION_MAJOR );
2088 }
2089
2090