2

I have a custom QGIS 1.9 dev (so 2.0 I guess) plugin which collects and displays a list of polygons in a QTreeWidget. I would like to select a list item and it immediately display a highlighted polygon to which it corresponds. This is possible by using the QgsVectorLayer.select() method, but the method is very slow and I would like it to display almost instantly. I can simulate the highlighting of a polygon by creating a new in-memory vector layer and adding a feature using the geometry of the selected list item, but I don't want to display that new layer in the Layers window.

My code (not 100% usable here) shows the basic implementation so far. My preferred outcome would be a fast 'select' method, or else a way to display a new temporary vector layer without displaying it in the Layer window.

class FindLandParcelDialog(QtGui.QDialog):
    def __init__(self, iface):
        QtGui.QDialog.__init__(self)
        self.ui = Ui_FindLandParcel()
        self.ui.setupUi(self)
        self.parcel_tw = self.ui.treeWidget

        self.parcel_dict = {CP100: (143, 'shape_wkt'), CP101: (144, 'shape_wkt')}

        self.parcel_tw.itemSelectionChanged.connect(self.selectParcel)  # Very slow select
        # self.parcel_tw.itemSelectionChanged.connect(self.highlightParcel)  # displays new layer in Layer window - not wanted

    def selectParcel(self):
        '''Selects polygons with the ID stored in the dictionary linked to the
        QTreeWidget item'''
        self.cLayer = self.canvas.currentLayer()
        idx = self.getTreeCurrentItem(self.parcel_tw)
        parcel_id = self.parcel_dict.items()[idx][1][0]

        self.cLayer.removeSelection()
        for feat in self.cLayer.getFeatures():
            if feat.attributes()[0] == parcel_id:
                self.cLayer.select(feat.id())

    def getTreeCurrentItem(self, QTreeWidget):
        '''Returns index of the selected top level QTreeWidgetItem'''
        currentitem = self.parcel_tw.currentItem()
        idx = QTreeWidget.indexOfTopLevelItem(currentitem)
        return idx

    def highlightParcel(self):
        self.cLayer = self.canvas.currentLayer()
        self.zoomtolayer_btn.setEnabled(True)
        idx = self.getTreeCurrentItem(self.parcel_tw)
        parcel_wkt = self.parcel_dict.items()[idx][1][-1]
        self.createInMemoryFeature(parcel_wkt)

    def createInMemoryFeature(self, wkt):
        layer = QgsVectorLayer('Polygon?', 'temporary_polygons', 'memory')
        pr = layer.dataProvider()
        layer.startEditing()
        feat = QgsFeature()
        feat.setGeometry(QgsGeometry.fromWkt(wkt))
        pr.addFeatures([feat])
        layer.commitChanges()

SOLUTION The working code to display a temporary feature (from Nathan W's answer):

class FindLandParcelDialog(QtGui.QDialog):
    def __init__(self, iface):
        QtGui.QDialog.__init__(self)
        self.ui = Ui_FindLandParcel()
        self.ui.setupUi(self)
        self.parcel_tw = self.ui.treeWidget

        self.parcel_dict = {CP100: (143, 'shape_wkt'), CP101: (144, 'shape_wkt')}

        self.parcel_tw.itemSelectionChanged.connect(self.highlightParcel)

    def highlightParcel(self):
        self.cLayer = self.canvas.currentLayer()
        idx = self.getTreeCurrentItem(self.parcel_tw)
        parcel_wkt = self.parcel_dict.items()[idx][1][-1]
        self.createRubberBandFeature(parcel_wkt)

    def createRubberBandFeature(self, wkt):
        band = QgsRubberBand(self.canvas)
        band.setToGeometry(QgsGeometry.fromWkt(wkt), self.cLayer)
        band.setColor(QtGui.QColor(255,255,0,255))
        band.show()

    def getTreeCurrentItem(self, QTreeWidget):
        '''Returns index of the selected top level QTreeWidgetItem'''
        currentitem = self.parcel_tw.currentItem()
        idx = QTreeWidget.indexOfTopLevelItem(currentitem)
        return idx
6
  • can you use parcel_id as the featureid or store featureids with the parcel_ids in the list to save searching all the fetures each time.
    – Ian Turton
    Commented Aug 7, 2013 at 10:09
  • I just tried to do this on initialisation of the dialog by creating a dictionary containing the feature.id() against the parcel_id for my layer. It seems the bottleneck is the internal select method that QGIS uses, as it still takes time (2-3 seconds). The QgsVectorLayer.removeSelection() method also takes just as long.
    – James N
    Commented Aug 7, 2013 at 10:56
  • How many features are you dealing with? The rendering is what you might be seeing as the slow down because the select method will trigger a redraw.
    – Nathan W
    Commented Aug 7, 2013 at 11:01
  • I have a WMS basemap, plus around 1000 features to draw.
    – James N
    Commented Aug 7, 2013 at 11:04
  • The WMS being querying again is going to be the main slow down but see my code for a start to make things a little quicker.
    – Nathan W
    Commented Aug 7, 2013 at 11:05

1 Answer 1

1

You can store the QgsFeature in the tree item itself for faster retrieval.

# When you build up your tree
item = QTreeWidgetItem("featurename")
item.setData(0, Qt.UserRole,feature)

tree.itemChanged.connect(self.itemchanged)

def itemchanged(self, item, column):
    feature = item.data(0, Qt.UserRole)
    layer.select(feature.id())

I would say your code you are using now for select() is slow because you are looping each feature in the layer to find the one that matches, you are also not breaking out of the loop so if you have a 1000 features in a layer you are searching all of them everytime even if you match the first feature.

Storing the feature as data on the item will mean you don't have to search all the features again and you can just select the item in the canvas.

EDIT: If you can't afford a redraw because of select triggering one you can use the QgsRubberBand class. This is draw as canvas item and doesn't refresh the canvas itself

from PyQt4.QtCore import *
from PyQt4.QtGui import *

band = QgsRubberBand(iface.mapCanvas())
band.setToGeometry(feature.geometry(), layer)
band.setColor(QColor(255,0,0))
band.show()
3
  • Perfect, the QgsRubberBand was the temporary feature I was after. I can't seem to change the transparency, though, even when adding the alpha value to the QColor.
    – James N
    Commented Aug 7, 2013 at 11:38
  • Seems to be hard coded at 63. Should be able to patch it.
    – Nathan W
    Commented Aug 7, 2013 at 11:44
  • Fixed github.com/qgis/Quantum-GIS/commit/…
    – Nathan W
    Commented Aug 7, 2013 at 12:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.