Using QGIS, how to properly link a postgis sql layer?

1.3k views Asked by At

I have no problem using the DB Manager in QGIS 2.18, but if I try to add a layer based on a long running query (1 min+), QGIS becomes entirely unusable. It would appear that every map pan, every mouse click, etc., is triggering some kind of layer refresh.

This seems silly for one, especially given that you can right click the layer and request a refresh. And more important, it makes the application unusable. What am I doing wrong here? Is it not possible to create the layer and only have it update when requested?

Obviously I can create materialized views in postgres for all the spatial queries, but that seems to defeat the point of being able to do it in QGIS

2

There are 2 answers

0
Enrico Ferreguti On BEST ANSWER

Unfortunately the repaintRequested() signal is not triggered refreshing the canvas so I write a little python function that you can paste in QGIS python console editor to generate a materialized view from a query and add to mapcanvas as layer on the fly adding an action to legend menu for refreshing and reloading the view on demand

from qgis.core import QgsVectorLayer, QgsDataSourceURI, QgsMapLayerRegistry
from PyQt4.QtSql import QSqlDatabase
from PyQt4.QtGui import QAction

PSQLHost = "your_db_host"
PSQLPort = 5432
PSQLDatabase = "your_db"
PSQLUsername = "your_db_user"
PSQLPassword = "your_db_password"

LAYERNAME = "my materialized_view_layer"
QUERY = "select * from your_table"

class materialized_layer:

    def __init__(self):
        #setup connection
        geom_field = "geom"
        pkey_field = "id"
        self.db = QSqlDatabase.addDatabase("QPSQL")
        self.db.setHostName(PSQLHost)
        self.db.setPort(PSQLPort)
        self.db.setDatabaseName(PSQLDatabase)
        self.db.setUserName(PSQLUsername)
        self.db.setPassword(PSQLPassword)
        self.db.open()
        # generate materialized view
        create_query = 'CREATE MATERIALIZED VIEW "%s" AS %s' % (LAYERNAME,QUERY)
        self.db.exec_(create_query)
        # add to canvas
        qgis_uri = QgsDataSourceURI()
        qgis_uri.setConnection(PSQLHost,str(PSQLPort),PSQLDatabase,PSQLUsername,PSQLPassword)
        qgis_uri.setDataSource("",LAYERNAME,geom_field,"",pkey_field)
        self.materialized_layer = QgsVectorLayer(qgis_uri.uri(), LAYERNAME, "postgres")
        if self.materialized_layer.isValid():
            #register new qgis layer and add action to layer contextual menu
            QgsMapLayerRegistry.instance().addMapLayer(self.materialized_layer,True)
            refresh_materialized_action = QAction( "Refresh materialized view and reload", iface.legendInterface() )
            iface.legendInterface().addLegendLayerAction(refresh_materialized_action, "","", QgsMapLayer.VectorLayer,False)
            iface.legendInterface().addLegendLayerActionForLayer(refresh_materialized_action, self.materialized_layer)
            refresh_materialized_action.triggered.connect(self.refresh_layer)
        else:
            print "invalid layer"

    def reload_layer(self):
        print "RELOADING MATERIALIZED VIEW"
        self.materialized_layer.reload()

    def refresh_layer(self):
        print "REFRESHING MATERIALIZED VIEW"
        refresh_query = 'REFRESH MATERIALIZED VIEW "%s"' % LAYERNAME
        self.db.exec_(refresh_query)
        self.reload_layer()

l = materialized_layer ()

enter image description here

3
Enrico Ferreguti On

I wrote a plugin called postgisQueryBuilder that helps in db view creation, even with materialized, directive and allows, browsing the db connection available layers, to refresh the materialized views from QGIS. Check if you can be helpful.