1 /***************************************************************************
2     qgsgrassfeatureiterator.cpp
3     ---------------------
4     begin                : Juli 2012
5     copyright            : (C) 2012 by Martin Dobias
6     email                : wonder dot sk at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #include <QObject>
17 #include <QTextCodec>
18 
19 #include "qgsgrass.h"
20 #include "qgsgrassfeatureiterator.h"
21 #include "qgsgrassprovider.h"
22 #include "qgsgrassvectormap.h"
23 
24 #include "qgsapplication.h"
25 #include "qgsgeometry.h"
26 #include "qgslogger.h"
27 #include "qgsmessagelog.h"
28 
29 extern "C"
30 {
31 #include <grass/version.h>
32 #include <grass/vector.h>
33 }
34 
copy_boxlist_and_destroy(struct boxlist * blist,struct ilist * list)35 void copy_boxlist_and_destroy( struct boxlist *blist, struct ilist *list )
36 {
37   Vect_reset_list( list );
38   for ( int i = 0; i < blist->n_values; i++ )
39   {
40     Vect_list_append( list, blist->id[i] );
41   }
42   Vect_destroy_boxlist( blist );
43 }
44 
45 #define Vect_select_lines_by_box(map, box, type, list) \
46   { \
47     struct boxlist *blist = Vect_new_boxlist(0);\
48     Vect_select_lines_by_box( (map), (box), (type), blist); \
49     copy_boxlist_and_destroy( blist, (list) );\
50   }
51 #define Vect_select_areas_by_box(map, box, list) \
52   { \
53     struct boxlist *blist = Vect_new_boxlist(0);\
54     Vect_select_areas_by_box( (map), (box), blist); \
55     copy_boxlist_and_destroy( blist, (list) );\
56   }
57 
58 //QMutex QgsGrassFeatureIterator::sMutex;
59 
QgsGrassFeatureIterator(QgsGrassFeatureSource * source,bool ownSource,const QgsFeatureRequest & request)60 QgsGrassFeatureIterator::QgsGrassFeatureIterator( QgsGrassFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
61   : QgsAbstractFeatureIteratorFromSource<QgsGrassFeatureSource>( source, ownSource, request )
62 {
63 
64   // WARNING: the iterator cannot use mutex lock for its whole life, because QgsVectorLayerFeatureIterator is opening
65   // multiple iterators if features are edited -> lock only critical sections
66 
67   // Create selection
68   int size = 1 + std::max( Vect_get_num_lines( mSource->map() ), Vect_get_num_areas( mSource->map() ) );
69   QgsDebugMsg( QString( "mSelection.resize(%1)" ).arg( size ) );
70   mSelection.resize( size );
71 
72   if ( !request.filterRect().isNull() )
73   {
74     setSelectionRect( request.filterRect(), request.flags() & QgsFeatureRequest::ExactIntersect );
75   }
76   else
77   {
78     //no filter - use all features
79     mSelection.fill( true );
80   }
81 
82   connect( mSource->mLayer->map(), &QgsGrassVectorMap::cancelIterators, this, &QgsGrassFeatureIterator::cancel, Qt::DirectConnection );
83 
84   Qt::ConnectionType connectionType = Qt::DirectConnection;
85   if ( mSource->mLayer->map()->thread() != thread() )
86   {
87     // Using BlockingQueuedConnection may be dangerous, if the iterator was later moved to maps thread, it would cause dead lock on emit closeIterators()
88     QgsDebugMsg( "map and iterator are on different threads -> connect closeIterators() with BlockingQueuedConnection" );
89     connectionType = Qt::BlockingQueuedConnection;
90   }
91   connect( mSource->mLayer->map(), &QgsGrassVectorMap::closeIterators, this, &QgsGrassFeatureIterator::doClose, connectionType );
92 }
93 
~QgsGrassFeatureIterator()94 QgsGrassFeatureIterator::~QgsGrassFeatureIterator()
95 {
96   close();
97 }
98 
cancel()99 void QgsGrassFeatureIterator::cancel()
100 {
101   QgsDebugMsg( "cancel" );
102   mCanceled = true;
103 }
104 
doClose()105 void QgsGrassFeatureIterator::doClose()
106 {
107   QgsDebugMsg( "doClose" );
108   close();
109 }
110 
setSelectionRect(const QgsRectangle & rect,bool useIntersect)111 void QgsGrassFeatureIterator::setSelectionRect( const QgsRectangle &rect, bool useIntersect )
112 {
113   QgsDebugMsg( QString( "useIntersect = %1 rect = %2" ).arg( useIntersect ).arg( rect.toString() ) );
114 
115   // TODO: selection of edited lines
116 
117   // Lock because functions using static/global variables are used
118   // (e.g. static LocList in Vect_select_lines_by_box, global BranchBuf in RTreeGetBranches)
119   QgsGrass::lock();
120 
121   mSelection.fill( false );
122 
123   bound_box box;
124   box.N = rect.yMaximum();
125   box.S = rect.yMinimum();
126   box.E = rect.xMaximum();
127   box.W = rect.xMinimum();
128   box.T = PORT_DOUBLE_MAX;
129   box.B = -PORT_DOUBLE_MAX;
130 
131   // Init structures
132   struct ilist *list = Vect_new_list();
133 
134   if ( !useIntersect )
135   {
136     // select by bounding boxes only
137     if ( mSource->mLayerType == QgsGrassProvider::Point || mSource->mLayerType == QgsGrassProvider::Centroid ||
138          mSource->mLayerType == QgsGrassProvider::Line || mSource->mLayerType == QgsGrassProvider::Face ||
139          mSource->mLayerType == QgsGrassProvider::Boundary ||
140          mSource->mLayerType == QgsGrassProvider::TopoPoint || mSource->mLayerType == QgsGrassProvider::TopoLine ||
141          mSource->mEditing )
142     {
143       QgsDebugMsg( "Vect_select_lines_by_box" );
144       int type = mSource->mGrassType;
145       if ( mSource->mEditing )
146       {
147         type = GV_POINTS | GV_LINES;
148       }
149       QgsDebugMsg( QString( "type = %1" ).arg( type ) );
150       Vect_select_lines_by_box( mSource->map(), &box, type, list );
151     }
152     else if ( mSource->mLayerType == QgsGrassProvider::Polygon )
153     {
154       Vect_select_areas_by_box( mSource->map(), &box, list );
155     }
156     else if ( mSource->mLayerType == QgsGrassProvider::TopoNode )
157     {
158       Vect_select_nodes_by_box( mSource->map(), &box, list );
159     }
160   }
161   else
162   {
163     // check intersection
164     struct line_pnts *polygon = Vect_new_line_struct();
165 
166     // Using z coor -PORT_DOUBLE_MAX/PORT_DOUBLE_MAX we cover 3D, Vect_select_lines_by_polygon is
167     // using dig_line_box to get the box, it is not perfect, Vect_select_lines_by_polygon
168     // should clarify better how 2D/3D is treated
169     Vect_append_point( polygon, rect.xMinimum(), rect.yMinimum(), -PORT_DOUBLE_MAX );
170     Vect_append_point( polygon, rect.xMaximum(), rect.yMinimum(), PORT_DOUBLE_MAX );
171     Vect_append_point( polygon, rect.xMaximum(), rect.yMaximum(), 0 );
172     Vect_append_point( polygon, rect.xMinimum(), rect.yMaximum(), 0 );
173     Vect_append_point( polygon, rect.xMinimum(), rect.yMinimum(), 0 );
174 
175     if ( mSource->mLayerType == QgsGrassProvider::Point || mSource->mLayerType == QgsGrassProvider::Centroid ||
176          mSource->mLayerType == QgsGrassProvider::Line || mSource->mLayerType == QgsGrassProvider::Face ||
177          mSource->mLayerType == QgsGrassProvider::Boundary ||
178          mSource->mLayerType == QgsGrassProvider::TopoPoint || mSource->mLayerType == QgsGrassProvider::TopoLine ||
179          mSource->mEditing )
180     {
181       QgsDebugMsg( "Vect_select_lines_by_polygon" );
182       int type = mSource->mGrassType;
183       if ( mSource->mEditing )
184       {
185         type = GV_POINTS | GV_LINES;
186       }
187       QgsDebugMsg( QString( "type = %1" ).arg( type ) );
188       Vect_select_lines_by_polygon( mSource->map(), polygon, 0, nullptr, type, list );
189     }
190     else if ( mSource->mLayerType == QgsGrassProvider::Polygon )
191     {
192       Vect_select_areas_by_polygon( mSource->map(), polygon, 0, nullptr, list );
193     }
194     else if ( mSource->mLayerType == QgsGrassProvider::TopoNode )
195     {
196       // There is no Vect_select_nodes_by_polygon but for nodes it is the same as by box
197       Vect_select_nodes_by_box( mSource->map(), &box, list );
198     }
199 
200     Vect_destroy_line_struct( polygon );
201   }
202   for ( int i = 0; i < list->n_values; i++ )
203   {
204     int lid = list->value[i];
205     if ( lid < 1 || lid >= mSelection.size() ) // should not happen
206     {
207       QgsDebugMsg( QString( "lid %1 out of range <1,%2>" ).arg( lid ).arg( mSelection.size() ) );
208       continue;
209     }
210     mSelection.setBit( lid );
211   }
212   Vect_destroy_list( list );
213 
214   QgsDebugMsg( QString( " %1 features selected" ).arg( list->n_values ) );
215   QgsGrass::unlock();
216 }
217 
fetchFeature(QgsFeature & feature)218 bool QgsGrassFeatureIterator::fetchFeature( QgsFeature &feature )
219 {
220   QgsDebugMsgLevel( "entered", 3 );
221   if ( mClosed )
222   {
223     return false;
224   }
225   if ( mCanceled )
226   {
227     QgsDebugMsg( "iterator is canceled -> close" );
228     close();
229     return false;
230   }
231 
232   feature.setValid( false );
233 
234   // TODO: locking each feature is too expensive. What happens with map structures if lines are
235   // written/rewritten/deleted? Can be read simultaneously?
236   mSource->mLayer->map()->lockReadWrite(); // locks only in editing mode
237   bool filterById = mRequest.filterType() == QgsFeatureRequest::FilterFid;
238   int cat = 0;
239   int type = 0;
240   int lid = 0;
241   QgsFeatureId featureId = 0;
242   QgsAbstractGeometry *oldGeometry = nullptr;
243   int cidxFieldIndex = mSource->mLayer->cidxFieldIndex();
244 
245 #ifdef QGISDEBUG
246   if ( mSource->mEditing )
247   {
248     QgsDebugMsgLevel( "newLids:", 3 );
249     const auto constKeys = mSource->mLayer->map()->newLids().keys();
250     for ( int oldLid : constKeys )
251     {
252       QgsDebugMsgLevel( QString( "%1 -> %2" ).arg( oldLid ).arg( mSource->mLayer->map()->newLids().value( oldLid ) ), 3 );
253     }
254   }
255 #endif
256 
257   if ( filterById )
258   {
259     featureId = mRequest.filterFid();
260     lid = lidFromFid( mRequest.filterFid() );
261     QgsDebugMsg( QString( "featureId = %1 lid = %2" ).arg( featureId ).arg( lid ) );
262 
263     if ( mSource->mEditing )
264     {
265       // Undo needs the oldest version of geometry, but we also need topo_symbol, so we must read
266       // topo_symbol from map if the newLine exists  read
267       if ( mSource->mLayer->map()->oldGeometries().contains( lid ) )
268       {
269         QgsDebugMsg( QString( "use old geometry for lid = %1" ).arg( lid ) );
270         oldGeometry = mSource->mLayer->map()->oldGeometries().value( lid );
271         if ( !oldGeometry )
272         {
273           QgsDebugMsg( "oldGeometry is null" );
274         }
275       }
276       if ( mSource->mLayer->map()->newLids().contains( lid ) )
277       {
278         // newLid may be 0 if line was deleted, in such case use only the old geometry, topo_symbol cannot be read
279         int newLid = mSource->mLayer->map()->newLids().value( lid );
280         QgsDebugMsg( QString( "use newLid = %1 -> lid = %2" ).arg( newLid ).arg( lid ) );
281         lid = newLid;
282       }
283     }
284 
285     if ( lid > 0 )
286     {
287       if ( !Vect_line_alive( mSource->map(), lid ) )
288       {
289         close();
290         mSource->mLayer->map()->unlockReadWrite();
291         return false;
292       }
293       type = Vect_read_line( mSource->map(), nullptr, nullptr, lid );
294 
295       // TODO real cat when line/cat was rewritten?!
296       cat = catFromFid( mRequest.filterFid() );
297       QgsDebugMsg( QString( "lid = %1 cat = %2" ).arg( lid ).arg( cat ) );
298     }
299   }
300   else
301   {
302     // Get next line/area id
303     while ( true )
304     {
305       // TODO: if selection is used, go only through the list of selected values
306       cat = 0;
307       type = 0;
308       lid = 0;
309       QgsDebugMsgLevel( QString( "mNextLid = %1 mNextCidx = %2 numLines() = %3 cidxFieldIndex() = %4 cidxFieldNumCats() = %5" )
310                         .arg( mNextLid ).arg( mNextCidx ).arg( mSource->mLayer->map()->numLines() )
311                         .arg( mSource->mLayer->cidxFieldIndex() ).arg( mSource->mLayer->cidxFieldNumCats() ), 3 );
312       if ( mSource->mEditing )
313       {
314         // TODO should be numLines before editing started (?), but another layer
315         // where editing started later mest have different, because its buffer does not have previous changes
316         // -> editing of more layers must be synchronized or not allowed
317         if ( mNextLid > mSource->mLayer->map()->numLines() )
318         {
319           QgsDebugMsgLevel( "mNextLid > numLines()", 3 );
320           break;
321         }
322 
323         int oldLid = mNextLid;
324         if ( mSource->mLayer->map()->oldLids().contains( mNextLid ) )
325         {
326           oldLid = mSource->mLayer->map()->oldLids().value( mNextLid );
327           QgsDebugMsg( QString( "mNextLid = %1 -> oldLid = %2" ).arg( mNextLid ).arg( oldLid ) );
328         }
329 
330         if ( oldLid < 0 )
331         {
332           QgsDebugMsg( QString( "skip new feature oldLid = %1" ).arg( oldLid ) );
333           mNextCidx = 0;
334           mNextLid++;
335           continue;
336         }
337 
338         if ( !Vect_line_alive( mSource->map(), mNextLid ) ) // should not be necessary for rewritten lines
339         {
340           mNextCidx = 0;
341           mNextLid++;
342           continue;
343         }
344 
345         struct line_cats *cats = Vect_new_cats_struct();
346         int tmpType = Vect_read_line( mSource->map(), nullptr, cats, mNextLid );
347         if ( cats->n_cats == 0 )
348         {
349           lid = mNextLid;
350           type = tmpType;
351           cat = 0;
352           int layer = 0;
353           featureId = makeFeatureId( oldLid, cat, layer );
354           mNextCidx = 0;
355           mNextLid++;
356         }
357         else
358         {
359           if ( mNextCidx >= cats->n_cats )
360           {
361             mNextCidx = 0;
362             mNextLid++;
363             Vect_destroy_cats_struct( cats );
364             continue;
365           }
366           else
367           {
368             lid = mNextLid;
369             type = tmpType;
370             cat = cats->cat[mNextCidx];
371             int layer = cats->field[mNextCidx];
372             QgsDebugMsgLevel( QString( "lid = %1 layer = %2 cat = %3" ).arg( lid ).arg( layer ).arg( cat ), 3 );
373             featureId = makeFeatureId( oldLid, cat, layer );
374             mNextCidx++;
375           }
376         }
377         Vect_destroy_cats_struct( cats );
378       }
379       else if ( mSource->mLayerType == QgsGrassProvider::TopoPoint || mSource->mLayerType == QgsGrassProvider::TopoLine )
380       {
381         if ( mNextLid > Vect_get_num_lines( mSource->map() ) )
382         {
383           break;
384         }
385         lid = mNextLid;
386         type = Vect_read_line( mSource->map(), nullptr, nullptr, mNextLid++ );
387         if ( !( type & mSource->mGrassType ) )
388         {
389           continue;
390         }
391         featureId = lid;
392       }
393       else if ( mSource->mLayerType == QgsGrassProvider::TopoNode )
394       {
395         if ( mNextLid > Vect_get_num_nodes( mSource->map() ) )
396         {
397           break;
398         }
399         lid = mNextLid;
400         type = 0;
401         mNextLid++;
402         featureId = lid;
403       }
404       else // standard layer
405       {
406         QgsDebugMsgLevel( "standard layer", 3 );
407         if ( mNextCidx >= mSource->mLayer->cidxFieldNumCats() )
408         {
409           break;
410         }
411         int tmpLid, tmpType, tmpCat;
412 
413         int numFields = Vect_cidx_get_num_fields( mSource->map() );
414         if ( cidxFieldIndex < 0 || cidxFieldIndex >= numFields )
415         {
416           QgsDebugMsg( QString( "cidxFieldIndex %1 out of range (0,%2)" ).arg( cidxFieldIndex ).arg( numFields - 1 ) );
417           break;
418         }
419 #if 0
420         // debug
421         Vect_topo_dump( mSource->map(), stderr );
422         Vect_cidx_dump( mSource->map(), stderr );
423 #endif
424         Vect_cidx_get_cat_by_index( mSource->map(), cidxFieldIndex, mNextCidx++, &tmpCat, &tmpType, &tmpLid );
425         // Warning: selection array is only of type line/area of current layer -> check type first
426         if ( !( tmpType & mSource->mGrassType ) )
427         {
428           QgsDebugMsgLevel( QString( "tmpType = %1 does not match mGrassType = %2" ).arg( tmpType ).arg( mSource->mGrassType ), 3 );
429           continue;
430         }
431 
432         // The 'id' is a unique id of a GRASS geometry object (point, line, area)
433         // but it cannot be used as QgsFeatureId because one geometry object may
434         // represent more features because it may have more categories.
435         lid = tmpLid;
436         cat = tmpCat;
437         type = tmpType;
438         QgsDebugMsgLevel( QString( "lid = %1 field = %2 cat = %3 type= %4" )
439                           .arg( lid ).arg( mSource->mLayer->field() ).arg( cat ).arg( type ), 3 );
440         featureId = makeFeatureId( lid, cat );
441       }
442 
443       // TODO: fix selection for mEditing
444       //if ( !mSource->mEditing && !mSelection[id] )
445       if ( lid < 1 || lid >= mSelection.size() || !mSelection[lid] )
446       {
447         QgsDebugMsgLevel( QString( "lid = %1 not in selection" ).arg( lid ), 3 );
448         continue;
449       }
450       else
451       {
452         QgsDebugMsgLevel( QString( "lid = %1 in selection" ).arg( lid ), 3 );
453       }
454       break;
455     }
456   }
457   if ( !oldGeometry )
458   {
459     int numLinesOrAreas = ( mSource->mGrassType == GV_AREA && !mSource->mEditing ) ?
460                           mSource->mLayer->map()->numAreas() : mSource->mLayer->map()->numLines();
461     if ( lid == 0 || lid > numLinesOrAreas )
462     {
463       QgsDebugMsg( QString( "lid = %1 > numLinesOrAreas = %2 -> close" ).arg( lid ).arg( numLinesOrAreas ) );
464       close();
465       mSource->mLayer->map()->unlockReadWrite();
466       return false; // No more features
467     }
468     if ( type == 0 && mSource->mLayerType != QgsGrassProvider::TopoNode )
469     {
470       QgsDebugMsg( "unknown type" );
471       close();
472       mSource->mLayer->map()->unlockReadWrite();
473       return false;
474     }
475   }
476   QgsDebugMsgLevel( QString( "lid = %1 type = %2 cat = %3 featureId = %4" ).arg( lid ).arg( type ).arg( cat ).arg( featureId ), 3 );
477 
478   feature.setId( featureId );
479   //feature.initAttributes( mSource->mFields.count() );
480   QgsDebugMsgLevel( QString( "mSource->mFields.size() = %1" ).arg( mSource->mFields.size() ), 3 );
481   feature.setFields( mSource->mFields ); // allow name-based attribute lookups
482 
483   if ( !( mRequest.flags() & QgsFeatureRequest::NoGeometry ) )
484   {
485     if ( oldGeometry )
486     {
487       feature.setGeometry( QgsGeometry( oldGeometry->clone() ) );
488     }
489     else
490     {
491       setFeatureGeometry( feature, lid, type );
492     }
493   }
494 
495   if ( !QgsGrassProvider::isTopoType( mSource->mLayerType ) )
496   {
497     QgsGrassVectorMap::TopoSymbol symbol = QgsGrassVectorMap::TopoUndefined;
498     if ( mSource->mEditing && lid > 0 )
499     {
500       symbol = mSource->mLayer->map()->topoSymbol( lid );
501     }
502 
503     if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes )
504       setFeatureAttributes( cat, &feature, mRequest.subsetOfAttributes(), symbol );
505     else
506       setFeatureAttributes( cat, &feature, symbol );
507   }
508   else
509   {
510     feature.initAttributes( mSource->mFields.size() );
511     feature.setAttribute( 0, lid );
512     /* No more topo points in GRASS 7 */
513     if ( mSource->mLayerType == QgsGrassProvider::TopoLine )
514     {
515       feature.setAttribute( 1, QgsGrass::vectorTypeName( type ) );
516 
517       int node1, node2;
518       Vect_get_line_nodes( mSource->map(), lid, &node1, &node2 );
519       feature.setAttribute( 2, node1 );
520       if ( mSource->mLayerType == QgsGrassProvider::TopoLine )
521       {
522         feature.setAttribute( 3, node2 );
523       }
524     }
525 
526     if ( mSource->mLayerType == QgsGrassProvider::TopoLine )
527     {
528       if ( type == GV_BOUNDARY )
529       {
530         int left, right;
531         Vect_get_line_areas( mSource->map(), lid, &left, &right );
532         feature.setAttribute( 4, left );
533         feature.setAttribute( 5, right );
534       }
535     }
536     else if ( mSource->mLayerType == QgsGrassProvider::TopoNode )
537     {
538       QString lines;
539       int nlines = Vect_get_node_n_lines( mSource->map(), lid );
540       for ( int i = 0; i < nlines; i++ )
541       {
542         int line = Vect_get_node_line( mSource->map(), lid, i );
543         QgsDebugMsg( "cancel" );
544         if ( i > 0 ) lines += QLatin1Char( ',' );
545         lines += QString::number( line );
546       }
547       feature.setAttribute( 1, lines );
548     }
549   }
550   feature.setValid( true );
551   mSource->mLayer->map()->unlockReadWrite();
552 
553   return true;
554 }
555 
rewind()556 bool QgsGrassFeatureIterator::rewind()
557 {
558   if ( mClosed )
559   {
560     QgsDebugMsg( "closed" );
561     return false;
562   }
563 
564   mNextCidx = 0;
565   mNextLid = 1;
566 
567   return true;
568 }
569 
close()570 bool QgsGrassFeatureIterator::close()
571 {
572   if ( mClosed )
573   {
574     QgsDebugMsg( "already closed" );
575     return false;
576   }
577 
578   iteratorClosed();
579 
580   mClosed = true;
581   QgsDebugMsg( "closed" );
582   //sMutex.unlock();
583   return true;
584 }
585 
setFeatureGeometry(QgsFeature & feature,int id,int type)586 void QgsGrassFeatureIterator::setFeatureGeometry( QgsFeature &feature, int id, int type )
587 {
588   QgsDebugMsgLevel( QString( "id = %1 type = %2" ).arg( id ).arg( type ), 3 );
589 
590   QgsAbstractGeometry *geometry = nullptr;
591   if ( type & ( GV_POINTS | GV_LINES | GV_FACE ) )
592   {
593     geometry = mSource->mLayer->map()->lineGeometry( id );
594   }
595   else if ( mSource->mLayerType == QgsGrassProvider::TopoNode )
596   {
597     geometry = mSource->mLayer->map()->nodeGeometry( id );
598   }
599   else if ( type == GV_AREA )
600   {
601     geometry = mSource->mLayer->map()->areaGeometry( id );
602   }
603   else
604   {
605     QgsDebugMsg( QString( "unknown type = %1" ).arg( type ) );
606   }
607   feature.setGeometry( QgsGeometry( geometry ) );
608 }
609 
makeFeatureId(int grassId,int cat,int layer)610 QgsFeatureId QgsGrassFeatureIterator::makeFeatureId( int grassId, int cat, int layer )
611 {
612   // Because GRASS object id and category are both int and QgsFeatureId is qint64
613   // we can create unique QgsFeatureId from GRASS id and cat.
614   // Max  supported layer number is 92 (max 64bit int is 9,223,372,036,854,775,807).
615   QgsFeatureId fid = ( QgsFeatureId )layer * 100000000000000000 + ( QgsFeatureId )grassId * 1000000000 + cat;
616   QgsDebugMsgLevel( QString( "grassId = %1 cat = %2 layer = %3 fid = %4" ).arg( grassId ).arg( cat ).arg( layer ).arg( fid ), 3 );
617   return fid;
618 }
619 
layerFromFid(QgsFeatureId fid)620 int QgsGrassFeatureIterator::layerFromFid( QgsFeatureId fid )
621 {
622   if ( FID_IS_NEW( fid ) )
623   {
624     return 0;
625   }
626   return fid / 100000000000000000;
627 }
628 
lidFromFid(QgsFeatureId fid)629 int QgsGrassFeatureIterator::lidFromFid( QgsFeatureId fid )
630 {
631   // New features have negative fid, we take such fid as (still negative) lid
632   // it is only used for mapping from fid to real lid
633   if ( FID_IS_NEW( fid ) )
634   {
635     return fid;
636   }
637   qint64 lidCat = fid % 100000000000000000;
638   return lidCat / 1000000000;
639 }
640 
catFromFid(QgsFeatureId fid)641 int QgsGrassFeatureIterator::catFromFid( QgsFeatureId fid )
642 {
643   if ( FID_IS_NEW( fid ) )
644   {
645     // TODO: keep track of cats for new features
646     return 0;
647   }
648   return fid % 1000000000;
649 }
650 
nonEditableValue(int layerNumber)651 QVariant QgsGrassFeatureIterator::nonEditableValue( int layerNumber )
652 {
653   if ( layerNumber > 0 )
654   {
655     return tr( "<not editable (layer %1)>" ).arg( layerNumber );
656   }
657   else
658   {
659     // attributes of features without cat (layer = 0) may be edited -> cat created
660     return QVariant();
661   }
662 }
663 
setFeatureAttributes(int cat,QgsFeature * feature,QgsGrassVectorMap::TopoSymbol symbol)664 void QgsGrassFeatureIterator::setFeatureAttributes( int cat, QgsFeature *feature, QgsGrassVectorMap::TopoSymbol symbol )
665 {
666   QgsDebugMsgLevel( QString( "setFeatureAttributes cat = %1" ).arg( cat ), 3 );
667   QgsAttributeList attlist;
668   int nFields = mSource->mLayer->fields().size();
669   if ( nFields > 0 )
670   {
671     for ( int i = 0; i <  mSource->mLayer->fields().size(); i++ )
672     {
673       attlist << i;
674     }
675   }
676   else
677   {
678     attlist << 0;
679   }
680   return setFeatureAttributes( cat, feature, attlist, symbol );
681 }
682 
setFeatureAttributes(int cat,QgsFeature * feature,const QgsAttributeList & attlist,QgsGrassVectorMap::TopoSymbol symbol)683 void QgsGrassFeatureIterator::setFeatureAttributes( int cat, QgsFeature *feature, const QgsAttributeList &attlist, QgsGrassVectorMap::TopoSymbol symbol )
684 {
685   QgsDebugMsgLevel( QString( "setFeatureAttributes cat = %1 symbol = %2" ).arg( cat ).arg( symbol ), 3 );
686   feature->initAttributes( mSource->mLayer->fields().size() );
687 
688   bool isEditedLayer = true;
689   int layerNumber = 0;
690   if ( mSource->mEditing )
691   {
692     layerNumber = layerFromFid( feature->id() );
693     if ( layerNumber != mSource->mLayer->field() )
694     {
695       isEditedLayer = false;
696     }
697   }
698 
699   for ( QgsAttributeList::const_iterator iter = attlist.begin(); iter != attlist.end(); ++iter )
700   {
701     if ( *iter == mSource->mSymbolAttributeIndex )
702     {
703       continue;
704     }
705     QVariant value;
706     if ( isEditedLayer )
707     {
708       value = mSource->mLayer->attribute( cat, *iter );
709       if ( value.type() == QVariant::ByteArray )
710       {
711         value = QVariant( mSource->mEncoding->toUnicode( value.toByteArray() ) );
712       }
713     }
714     else
715     {
716       // We are setting cat of different layer in cat column of this layer
717       if ( *iter == mSource->mLayer->keyColumn() )
718       {
719         value = QVariant( cat );
720       }
721       else if ( layerNumber > 0 )
722       {
723         value = nonEditableValue( layerNumber );
724       }
725     }
726     QgsDebugMsgLevel( QString( "iter = %1 value = %2" ).arg( *iter ).arg( value.toString() ), 3 );
727 
728     feature->setAttribute( *iter, value );
729   }
730 
731 
732   if ( mSource->mEditing && attlist.contains( mSource->mSymbolAttributeIndex ) )
733   {
734     QgsDebugMsgLevel( QString( "set attribute %1 to symbol %2" ).arg( mSource->mSymbolAttributeIndex ).arg( symbol ), 3 );
735     feature->setAttribute( mSource->mSymbolAttributeIndex, QVariant( symbol ) );
736   }
737 }
738 
739 //  ------------------ QgsGrassFeatureSource ------------------
QgsGrassFeatureSource(const QgsGrassProvider * p)740 QgsGrassFeatureSource::QgsGrassFeatureSource( const QgsGrassProvider *p )
741   : mLayer( p->openLayer() )
742   , mLayerType( p->mLayerType )
743   , mGrassType( p->mGrassType )
744   , mQgisType( p->mQgisType )
745   , mFields( p->fields() )
746   , mEncoding( p->textEncoding() ) // no copying - this is a borrowed pointer from Qt
747   , mEditing( p->mEditBuffer )
748 {
749   Q_ASSERT( mLayer );
750 
751   mSymbolAttributeIndex = mFields.indexFromName( QgsGrassVectorMap::topoSymbolFieldName() );
752 }
753 
~QgsGrassFeatureSource()754 QgsGrassFeatureSource::~QgsGrassFeatureSource()
755 {
756   mLayer->close();
757 }
758 
getFeatures(const QgsFeatureRequest & request)759 QgsFeatureIterator QgsGrassFeatureSource::getFeatures( const QgsFeatureRequest &request )
760 {
761   QgsDebugMsg( "QgsGrassFeatureSource::getFeatures" );
762   return QgsFeatureIterator( new QgsGrassFeatureIterator( this, false, request ) );
763 }
764 
map()765 struct Map_info *QgsGrassFeatureSource::map()
766 {
767   return  mLayer->map()->map();
768 }
769