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