From 0180cf13fc92bd7cb6c59d6bf99befc377e3e744 Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Tue, 19 Jun 2018 16:17:41 +0530 Subject: [PATCH 01/32] fixed init.py --- __init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/__init__.py b/__init__.py index c0d0b49..40cd2de 100644 --- a/__init__.py +++ b/__init__.py @@ -32,5 +32,5 @@ def icon(): def qgisMinimumVersion(): return "1.7" def classFactory(iface): - from pgRoutingLayer import pgRoutingLayer - return pgRoutingLayer(iface) + from pgRoutingLayer import PgRoutingLayer + return PgRoutingLayer(iface) From 652715076ae88bc80ab325219c2d0ad9b36f569f Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Fri, 22 Jun 2018 01:21:59 +0200 Subject: [PATCH 02/32] fixed class factory --- __init__.py | 7 ++++--- tests/__init__.py | 0 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 tests/__init__.py diff --git a/__init__.py b/__init__.py index 40cd2de..bf7b898 100644 --- a/__init__.py +++ b/__init__.py @@ -2,8 +2,8 @@ /*************************************************************************** pgRouting Layer a QGIS plugin - - based on "Fast SQL Layer" plugin Copyright 2011 Pablo Torres Carreira + + based on "Fast SQL Layer" plugin Copyright 2011 Pablo Torres Carreira ------------------- begin : 2011-11-25 copyright : (c) 2011 by Anita Graser @@ -21,6 +21,7 @@ This script initializes the plugin, making it known to QGIS. """ + def name(): return "pgRouting Layer" def description(): @@ -32,5 +33,5 @@ def icon(): def qgisMinimumVersion(): return "1.7" def classFactory(iface): - from pgRoutingLayer import PgRoutingLayer + from pgRoutingLayer.pgRoutingLayer import PgRoutingLayer return PgRoutingLayer(iface) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 From e16ae544fc136ff3a3273e19afb1a03066c3a8e6 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Fri, 22 Jun 2018 01:22:28 +0200 Subject: [PATCH 03/32] fixed geometry types handling --- pgRoutingLayer_utils.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pgRoutingLayer_utils.py b/pgRoutingLayer_utils.py index 6da6916..f3480a0 100644 --- a/pgRoutingLayer_utils.py +++ b/pgRoutingLayer_utils.py @@ -1,4 +1,4 @@ -from qgis.core import QgsMessageLog,Qgis +from qgis.core import QgsMessageLog, Qgis, QgsWkbTypes from qgis.gui import QgsMapCanvas from qgis.PyQt.QtCore import QVariant, QSettings #from PyQt4.QtGui import * @@ -13,7 +13,7 @@ def getSridAndGeomType(con, table, geometry): cur = con.cursor() cur.execute(""" SELECT ST_SRID(%(geometry)s), ST_GeometryType(%(geometry)s) - FROM %(table)s + FROM %(table)s LIMIT 1 """ % args) row = cur.fetchone() @@ -56,13 +56,13 @@ def getBoolValue(settings, key, value): return settings.value(key, QVariant(value)).toBool() def isQGISv1(): - return QGis.QGIS_VERSION_INT < 10900 + return Qgis.QGIS_VERSION_INT < 10900 def getDestinationCrs(mapCanvas): if isQGISv1(): return mapCanvas.mapRenderer().destinationSrs() else: - if QGis.QGIS_VERSION_INT < 20400: + if Qgis.QGIS_VERSION_INT < 20400: return mapCanvas.mapRenderer().destinationCrs() else: return mapCanvas.mapSettings().destinationCrs() @@ -84,12 +84,12 @@ def getRubberBandType(isPolygon): return isPolygon else: if isPolygon: - return QGis.Polygon + return QgsWkbTypes.PolygonGeometry else: - return QGis.Line + return QgsWkbTypes.LineGeometry def refreshMapCanvas(mapCanvas): - if QGis.QGIS_VERSION_INT < 20400: + if Qgis.QGIS_VERSION_INT < 20400: return mapCanvas.clear() else: return mapCanvas.refresh() @@ -132,4 +132,3 @@ def getPgrVersion(con): return 0; except SystemError as e: return 0 - From e2e00ecfe6a7c12c3ad7756e98e604b161457f5b Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Fri, 22 Jun 2018 01:22:46 +0200 Subject: [PATCH 04/32] fixed functions importing --- pgRoutingLayer.py | 283 +++++++++++++++++++++++----------------------- 1 file changed, 141 insertions(+), 142 deletions(-) diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index 1d8d010..1002b12 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -2,8 +2,8 @@ /*************************************************************************** pgRouting Layer a QGIS plugin - - based on "Fast SQL Layer" plugin. Copyright 2011 Pablo Torres Carreira + + based on "Fast SQL Layer" plugin. Copyright 2011 Pablo Torres Carreira ------------------- begin : 2011-11-25 copyright : (c) 2011 by Anita Graser @@ -93,11 +93,11 @@ class PgRoutingLayer(object): FRACTION_DECIMAL_PLACES = 2 version = 2.0 functions = {} - + def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface - + self.idsVertexMarkers = [] self.targetIdsVertexMarkers = [] self.sourceIdsVertexMarkers = [] @@ -116,7 +116,7 @@ def __init__(self, iface): self.targetIdRubberBand = QgsRubberBand(self.iface.mapCanvas(), Utils.getRubberBandType(False)) self.targetIdRubberBand.setColor(Qt.yellow) self.targetIdRubberBand.setWidth(4) - + self.canvasItemList = {} self.canvasItemList['markers'] = [] self.canvasItemList['annotations'] = [] @@ -131,19 +131,19 @@ def __init__(self, iface): if not Utils.isQGISv1(): resultAreaRubberBand.setBrushStyle(Qt.Dense4Pattern) self.canvasItemList['area'] = resultAreaRubberBand - + def initGui(self): # Create action that will start plugin configuration self.action = QAction(QIcon(":/plugins/pgRoutingLayer/icon.png"), "pgRouting Layer", self.iface.mainWindow()) #Add toolbar button and menu item self.iface.addPluginToDatabaseMenu("&pgRouting Layer", self.action) #self.iface.addToolBarIcon(self.action) - + #load the form path = os.path.dirname(os.path.abspath(__file__)) self.dock = uic.loadUi(os.path.join(path, "ui_pgRoutingLayer.ui")) self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dock) - + self.idsEmitPoint = QgsMapToolEmitPoint(self.iface.mapCanvas()) self.sourceIdEmitPoint = QgsMapToolEmitPoint(self.iface.mapCanvas()) self.targetIdEmitPoint = QgsMapToolEmitPoint(self.iface.mapCanvas()) @@ -154,7 +154,7 @@ def initGui(self): #self.targetIdEmitPoint.setButton(buttonSelectTargetId) #self.sourceIdEmitPoint.setButton(buttonSelectSourceId) #self.targetIdsEmitPoint.setButton(buttonSelectTargetId) - + #connect the action to each method self.action.triggered.connect(self.show) self.dock.buttonReloadConnections.clicked.connect(self.reloadConnections) @@ -189,11 +189,11 @@ def initGui(self): self.functions = {} for funcfname in self.SUPPORTED_FUNCTIONS: # import the function - exec("from functions import %s as function" % funcfname) + exec("from pgRoutingLayer.functions import %s as function" % funcfname) funcname = function.Function.getName() self.functions[funcname] = function.Function(self.dock) self.dock.comboBoxFunction.addItem(funcname) - + self.dock.lineEditIds.setValidator(QRegExpValidator(QRegExp("[0-9,]+"), self.dock)) self.dock.lineEditPcts.setValidator(QRegExpValidator(QRegExp("[0-9,.]+"), self.dock)) @@ -209,24 +209,24 @@ def initGui(self): self.dock.lineEditDistance.setValidator(QDoubleValidator()) self.dock.lineEditAlpha.setValidator(QDoubleValidator()) self.dock.lineEditPaths.setValidator(QIntValidator()) - + #populate the combo with connections self.reloadMessage = False self.reloadConnections() self.loadSettings() Utils.logMessage("startup version " + str(self.version)) self.reloadMessage = True - + def show(self): self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dock) - + def unload(self): self.clear() self.saveSettings() # Remove the plugin menu item and icon self.iface.removePluginDatabaseMenu("&pgRouting Layer", self.action) self.iface.removeDockWidget(self.dock) - + def reloadConnections(self): oldReloadMessage = self.reloadMessage self.reloadMessage = False @@ -256,7 +256,7 @@ def reloadConnections(self): db.con.close() idx = self.dock.comboConnections.findText(database) - + if idx >= 0: self.dock.comboConnections.setCurrentIndex(idx) else: @@ -275,7 +275,7 @@ def updateConnectionEnabled(self): con = db.con self.version = Utils.getPgrVersion(con) if self.reloadMessage: - QMessageBox.information(self.dock, self.dock.windowTitle(), + QMessageBox.information(self.dock, self.dock.windowTitle(), 'Selected database: ' + dbname + '\npgRouting version: ' + str(self.version)) @@ -307,17 +307,17 @@ def updateFunctionEnabled(self, text): return self.clear() function = self.functions[str(text)] - + self.toggleSelectButton(None) - + for controlName in self.TOGGLE_CONTROL_NAMES: control = getattr(self.dock, controlName) control.setVisible(False) - + for controlName in function.getControlNames(self.version): control = getattr(self.dock, controlName) control.setVisible(True) - + # for initial display self.dock.gridLayoutSqlColumns.invalidate() self.dock.gridLayoutArguments.invalidate() @@ -328,18 +328,18 @@ def updateFunctionEnabled(self, text): if (not self.dock.checkBoxHasReverseCost.isChecked()) or (not self.dock.checkBoxHasReverseCost.isEnabled()): self.dock.lineEditReverseCost.setEnabled(False) - + # if type(edge/node) changed, clear input if (self.prevType != None) and (self.prevType != function.isEdgeBase()): self.clear() - + self.prevType = function.isEdgeBase() canExport = function.canExport() self.dock.buttonExport.setEnabled(canExport) canExportMerged = function.canExportMerged() self.dock.buttonExportMerged.setEnabled(canExportMerged) - + def selectIds(self, checked): if checked: self.toggleSelectButton(self.dock.buttonSelectIds) @@ -356,7 +356,7 @@ def selectIds(self, checked): self.iface.mapCanvas().setMapTool(self.idsEmitPoint) else: self.iface.mapCanvas().unsetMapTool(self.idsEmitPoint) - + def setIds(self, pt): function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getBaseArguments() @@ -407,7 +407,7 @@ def setIds(self, pt): vertexMarker.setCenter(pointGeom.asPoint()) self.idsVertexMarkers.append(vertexMarker) Utils.refreshMapCanvas(mapCanvas) - + def selectSourceId(self, checked): if checked: self.toggleSelectButton(self.dock.buttonSelectSourceId) @@ -417,7 +417,7 @@ def selectSourceId(self, checked): self.iface.mapCanvas().setMapTool(self.sourceIdEmitPoint) else: self.iface.mapCanvas().unsetMapTool(self.sourceIdEmitPoint) - + def setSourceId(self, pt): function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getBaseArguments() @@ -447,8 +447,8 @@ def setSourceId(self, pt): self.sourceIdVertexMarker.setVisible(True) self.dock.buttonSelectSourceId.click() Utils.refreshMapCanvas(self.iface.mapCanvas()) - - + + def selectSourceIds(self, checked): if checked: self.toggleSelectButton(self.dock.buttonSelectSourceIds) @@ -460,7 +460,7 @@ def selectSourceIds(self, checked): self.iface.mapCanvas().setMapTool(self.sourceIdsEmitPoint) else: self.iface.mapCanvas().unsetMapTool(self.sourceIdsEmitPoint) - + def setSourceIds(self, pt): args = self.getBaseArguments() result, id, wkt = self.findNearestNode(args, pt) @@ -489,7 +489,7 @@ def selectTargetId(self, checked): self.iface.mapCanvas().setMapTool(self.targetIdEmitPoint) else: self.iface.mapCanvas().unsetMapTool(self.targetIdEmitPoint) - + def setTargetId(self, pt): function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getBaseArguments() @@ -519,7 +519,7 @@ def setTargetId(self, pt): self.targetIdVertexMarker.setVisible(True) self.dock.buttonSelectTargetId.click() Utils.refreshMapCanvas(self.iface.mapCanvas()) - + def selectTargetIds(self, checked): if checked: self.toggleSelectButton(self.dock.buttonSelectTargetIds) @@ -531,7 +531,7 @@ def selectTargetIds(self, checked): self.iface.mapCanvas().setMapTool(self.targetIdsEmitPoint) else: self.iface.mapCanvas().unsetMapTool(self.targetIdsEmitPoint) - + def setTargetIds(self, pt): args = self.getBaseArguments() result, id, wkt = self.findNearestNode(args, pt) @@ -549,74 +549,74 @@ def setTargetIds(self, pt): vertexMarker.setCenter(geom.asPoint()) self.targetIdsVertexMarkers.append(vertexMarker) Utils.refreshMapCanvas(mapCanvas) - + def updateReverseCostEnabled(self, state): if state == Qt.Checked: self.dock.lineEditReverseCost.setEnabled(True) else: self.dock.lineEditReverseCost.setEnabled(False) - + def run(self): """ Draws a Preview on the canvas""" QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - + function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getArguments(function.getControlNames(self.version)) - + empties = [] for key in list(args.keys()): if not args[key]: empties.append(key) - + if len(empties) > 0: QApplication.restoreOverrideCursor() QMessageBox.warning(self.dock, self.dock.windowTitle(), 'Following argument is not specified.\n' + ','.join(empties)) return - + db = None try: dbname = str(self.dock.comboConnections.currentText()) db = self.actionsDb[dbname].connect() - + con = db.con - + version = Utils.getPgrVersion(con) args['version'] = version - + srid, geomType = Utils.getSridAndGeomType(con, args['edge_table'], args['geometry']) if (function.getName() == 'tsp(euclid)'): args['node_query'] = Utils.getNodeQuery(args, geomType) - + function.prepare(self.canvasItemList) - - args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) + + args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) query = function.getQuery(args) #QMessageBox.information(self.dock, self.dock.windowTitle(), 'Geometry Query:' + query) - + cur = con.cursor() cur.execute(query) rows = cur.fetchall() if len(rows) == 0: QMessageBox.information(self.dock, self.dock.windowTitle(), 'No paths found in ' + self.getLayerName(args)) - + args['srid'] = srid args['canvas_srid'] = Utils.getCanvasSrid(Utils.getDestinationCrs(self.iface.mapCanvas())) Utils.setTransformQuotes(args, srid, args['canvas_srid']) function.draw(rows, con, args, geomType, self.canvasItemList, self.iface.mapCanvas()) - + except psycopg2.DatabaseError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + except SystemError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + except AssertionError as e: QApplication.restoreOverrideCursor() QMessageBox.warning(self.dock, self.dock.windowTitle(), '%s' % e) - + finally: QApplication.restoreOverrideCursor() if db and db.con: @@ -628,28 +628,28 @@ def run(self): def export(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - + function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getArguments(function.getControlNames(self.version)) - + empties = [] for key in list(args.keys()): if not args[key]: empties.append(key) - + if len(empties) > 0: QApplication.restoreOverrideCursor() QMessageBox.warning(self.dock, self.dock.windowTitle(), 'Following argument is not specified.\n' + ','.join(empties)) return - + db = None try: dbname = str(self.dock.comboConnections.currentText()) db = self.actionsDb[dbname].connect() - + con = db.con - + version = Utils.getPgrVersion(con) args['version'] = version @@ -659,18 +659,18 @@ def export(self): srid, geomType = Utils.getSridAndGeomType(con, '%(edge_table)s' % args, '%(geometry)s' % args) - args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) + args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) #get the EXPORT query msgQuery = function.getExportQuery(args) #QMessageBox.information(self.dock, self.dock.windowTitle(), 'Geometry Query:\n' + msgQuery) Utils.logMessage('Export:\n' + msgQuery) - + query = self.cleanQuery(msgQuery) - + uri = db.getURI() uri.setDataSource("", "(" + query + ")", "path_geom", "", "seq") - + layerName = self.getLayerName(args) vl = self.iface.addVectorLayer(uri.uri(), layerName, db.getProviderName()) @@ -678,15 +678,15 @@ def export(self): QMessageBox.information(self.dock, self.dock.windowTitle(), 'Invalid Layer:\n - No paths found or\n - Failed to create vector layer from query') #QMessageBox.information(self.dock, self.dock.windowTitle(), 'pgRouting Query:' + function.getQuery(args)) #QMessageBox.information(self.dock, self.dock.windowTitle(), 'Geometry Query:' + msgQuery) - + except psycopg2.DatabaseError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + except SystemError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + finally: QApplication.restoreOverrideCursor() if db and db.con: @@ -724,50 +724,50 @@ def getBBOX(self, srid, use_bbox): %(xMax)s, %(yMax)s, %(srid)s ) """ % bbox, text - - + + def exportMerged(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - + function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getArguments(function.getControlNames(self.version)) - + empties = [] for key in list(args.keys()): if not args[key]: empties.append(key) - + if len(empties) > 0: QApplication.restoreOverrideCursor() QMessageBox.warning(self.dock, self.dock.windowTitle(), 'Following argument is not specified.\n' + ','.join(empties)) return - + db = None try: dbname = str(self.dock.comboConnections.currentText()) db = self.actionsDb[dbname].connect() - + con = db.con - + version = Utils.getPgrVersion(con) args['version'] = version - + srid, geomType = Utils.getSridAndGeomType(con, '%(edge_table)s' % args, '%(geometry)s' % args) - args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) + args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) # get the exportMerge query msgQuery = function.getExportMergeQuery(args) Utils.logMessage('Export merged:\n' + msgQuery) query = self.cleanQuery(msgQuery) - + uri = db.getURI() uri.setDataSource("", "(" + query + ")", "path_geom", "", "seq") - + # add vector layer to map layerName = self.getLayerName(args, 'M') - + vl = self.iface.addVectorLayer(uri.uri(), layerName, db.getProviderName()) if not vl: @@ -781,15 +781,15 @@ def exportMerged(self): QMessageBox.information(self.dock, self.dock.windowTitle(), 'Invalid Layer:\n - No paths found') else: QMessageBox.information(self.dock, self.dock.windowTitle(), 'Invalid Layer:\n - No paths found or\n - Failed to create vector layer from query') - + except psycopg2.DatabaseError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + except SystemError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + finally: QApplication.restoreOverrideCursor() if db and db.con: @@ -798,11 +798,11 @@ def exportMerged(self): except: QMessageBox.critical(self.dock, self.dock.windowTitle(), 'server closed the connection unexpectedly') - + def getLayerName(self, args, letter=''): function = self.functions[str(self.dock.comboBoxFunction.currentText())] - layerName = "(" + letter + layerName = "(" + letter if 'directed' in args and args['directed'] == 'true': layerName += "D) " @@ -837,7 +837,7 @@ def getLayerName(self, args, letter=''): layerName += " " + args['printBBOX'] return layerName - + @@ -885,7 +885,7 @@ def clear(self): self.canvasItemList['paths'] = [] self.canvasItemList['path'].reset(Utils.getRubberBandType(False)) self.canvasItemList['area'].reset(Utils.getRubberBandType(True)) - + def toggleSelectButton(self, button): selectButtons = [ self.dock.buttonSelectIds, @@ -896,7 +896,7 @@ def toggleSelectButton(self, button): if selectButton != button: if selectButton.isChecked(): selectButton.click() - + def getArguments(self, controls): args = {} args['edge_table'] = self.dock.lineEditTable.text() @@ -906,34 +906,34 @@ def getArguments(self, controls): if 'lineEditSource' in controls: args['source'] = self.dock.lineEditSource.text() - + if 'lineEditTarget' in controls: args['target'] = self.dock.lineEditTarget.text() - + if 'lineEditCost' in controls: args['cost'] = self.dock.lineEditCost.text() - + if 'lineEditReverseCost' in controls: args['reverse_cost'] = self.dock.lineEditReverseCost.text() - + if 'lineEditX1' in controls: args['x1'] = self.dock.lineEditX1.text() - + if 'lineEditY1' in controls: args['y1'] = self.dock.lineEditY1.text() - + if 'lineEditX2' in controls: args['x2'] = self.dock.lineEditX2.text() - + if 'lineEditY2' in controls: args['y2'] = self.dock.lineEditY2.text() - + if 'lineEditRule' in controls: args['rule'] = self.dock.lineEditRule.text() - + if 'lineEditToCost' in controls: args['to_cost'] = self.dock.lineEditToCost.text() - + if 'lineEditIds' in controls: args['ids'] = self.dock.lineEditIds.text() @@ -942,37 +942,37 @@ def getArguments(self, controls): if 'lineEditSourceId' in controls: args['source_id'] = self.dock.lineEditSourceId.text() - + if 'lineEditSourcePos' in controls: args['source_pos'] = self.dock.lineEditSourcePos.text() - + if 'lineEditSourceIds' in controls: args['source_ids'] = self.dock.lineEditSourceIds.text() - + if 'lineEditTargetId' in controls: args['target_id'] = self.dock.lineEditTargetId.text() - + if 'lineEditTargetPos' in controls: args['target_pos'] = self.dock.lineEditTargetPos.text() - + if 'lineEditTargetIds' in controls: args['target_ids'] = self.dock.lineEditTargetIds.text() - + if 'lineEditDistance' in controls: args['distance'] = self.dock.lineEditDistance.text() - + if 'lineEditAlpha' in controls: args['alpha'] = self.dock.lineEditAlpha.text() - + if 'lineEditPaths' in controls: args['paths'] = self.dock.lineEditPaths.text() - + if 'checkBoxDirected' in controls: args['directed'] = str(self.dock.checkBoxDirected.isChecked()).lower() - + if 'checkBoxHeapPaths' in controls: args['heap_paths'] = str(self.dock.checkBoxHeapPaths.isChecked()).lower() - + if 'checkBoxUseBBOX' in controls: args['use_bbox'] = str(self.dock.checkBoxUseBBOX.isChecked()).lower() else: @@ -986,12 +986,12 @@ def getArguments(self, controls): args['reverse_cost'] = ' ' else: args['reverse_cost'] = ', ' + args['reverse_cost'] + '::float8 AS reverse_cost' - + if 'plainTextEditTurnRestrictSql' in controls: args['turn_restrict_sql'] = self.dock.plainTextEditTurnRestrictSql.toPlainText() - + return args - + def getBaseArguments(self): args = {} args['edge_table'] = self.dock.lineEditTable.text() @@ -999,21 +999,21 @@ def getBaseArguments(self): args['id'] = self.dock.lineEditId.text() args['source'] = self.dock.lineEditSource.text() args['target'] = self.dock.lineEditTarget.text() - + empties = [] for key in list(args.keys()): if not args[key]: empties.append(key) - + if len(empties) > 0: QApplication.restoreOverrideCursor() QMessageBox.warning(self.dock, self.dock.windowTitle(), 'Following argument is not specified.\n' + ','.join(empties)) return None - + return args - - + + # emulate "matching.sql" - "find_nearest_node_within_distance" def findNearestNode(self, args, pt): distance = self.iface.mapCanvas().getCoordinateTransform().mapUnitsPerPixel() * self.FIND_RADIUS @@ -1023,7 +1023,7 @@ def findNearestNode(self, args, pt): try: dbname = str(self.dock.comboConnections.currentText()) db = self.actionsDb[dbname].connect() - + con = db.con #srid, geomType = self.getSridAndGeomType(con, args) #srid, geomType = Utils.getSridAndGeomType(con, args['edge_table'], args['geometry']) @@ -1034,7 +1034,7 @@ def findNearestNode(self, args, pt): trans = QgsCoordinateTransform(canvasCrs, layerCrs) pt = trans.transform(pt) rect = trans.transform(rect) - + args['canvas_srid'] = Utils.getCanvasSrid(canvasCrs) args['srid'] = srid args['x'] = pt.x() @@ -1043,12 +1043,12 @@ def findNearestNode(self, args, pt): args['miny'] = rect.yMinimum() args['maxx'] = rect.xMaximum() args['maxy'] = rect.yMaximum() - + Utils.setStartPoint(geomType, args) Utils.setEndPoint(geomType, args) #Utils.setTransformQuotes(args) Utils.setTransformQuotes(args, srid, args['canvas_srid']) - + # Getting nearest source query1 = """ SELECT %(source)s, @@ -1060,7 +1060,7 @@ def findNearestNode(self, args, pt): FROM %(edge_table)s WHERE ST_SetSRID('BOX3D(%(minx)f %(miny)f, %(maxx)f %(maxy)f)'::BOX3D, %(srid)d) && %(geometry)s ORDER BY dist ASC LIMIT 1""" % args - + ##Utils.logMessage(query1) cur1 = con.cursor() cur1.execute(query1) @@ -1072,7 +1072,7 @@ def findNearestNode(self, args, pt): d1 = row1[1] source = row1[0] wkt1 = row1[2] - + # Getting nearest target query2 = """ SELECT %(target)s, @@ -1084,7 +1084,7 @@ def findNearestNode(self, args, pt): FROM %(edge_table)s WHERE ST_SetSRID('BOX3D(%(minx)f %(miny)f, %(maxx)f %(maxy)f)'::BOX3D, %(srid)d) && %(geometry)s ORDER BY dist ASC LIMIT 1""" % args - + ##Utils.logMessage(query2) cur2 = con.cursor() cur2.execute(query2) @@ -1096,7 +1096,7 @@ def findNearestNode(self, args, pt): d2 = row2[1] target = row2[0] wkt2 = row2[2] - + # Checking what is nearer - source or target d = None node = None @@ -1118,24 +1118,24 @@ def findNearestNode(self, args, pt): node = target d = d2 wkt = wkt2 - + ##Utils.logMessage(str(d)) if (d == None) or (d > distance): node = None wkt = None return False, None, None - + return True, node, wkt - + except psycopg2.DatabaseError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) return False, None, None - + finally: if db and db.con: db.con.close() - + # emulate "matching.sql" - "find_nearest_link_within_distance" def findNearestLink(self, args, pt): distance = self.iface.mapCanvas().getCoordinateTransform().mapUnitsPerPixel() * self.FIND_RADIUS @@ -1145,7 +1145,7 @@ def findNearestLink(self, args, pt): try: dbname = str(self.dock.comboConnections.currentText()) db = self.actionsDb[dbname].connect() - + con = db.con cur = con.cursor() @@ -1157,7 +1157,7 @@ def findNearestLink(self, args, pt): trans = QgsCoordinateTransform(canvasCrs, layerCrs) pt = trans.transform(pt) rect = trans.transform(rect) - + args['canvas_srid'] = Utils.getCanvasSrid(canvasCrs) args['srid'] = srid args['x'] = pt.x() @@ -1167,10 +1167,10 @@ def findNearestLink(self, args, pt): args['maxx'] = rect.xMaximum() args['maxy'] = rect.yMaximum() args['decimal_places'] = self.FRACTION_DECIMAL_PLACES - + #Utils.setTransformQuotes(args) Utils.setTransformQuotes(args, srid, args['canvas_srid']) - + # Searching for a link within the distance query = """ WITH point AS ( @@ -1185,7 +1185,7 @@ def findNearestLink(self, args, pt): FROM %(edge_table)s, point WHERE ST_SetSRID('BOX3D(%(minx)f %(miny)f, %(maxx)f %(maxy)f)'::BOX3D, %(srid)d) && %(geometry)s ORDER BY dist ASC LIMIT 1""" % args - + ##Utils.logMessage(query) cur = con.cursor() cur.execute(query) @@ -1196,18 +1196,18 @@ def findNearestLink(self, args, pt): wkt = row[2] pos = row[3] pointWkt = row[4] - + return True, link, wkt, pos, pointWkt - + except psycopg2.DatabaseError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) return False, None, None - + finally: if db and db.con: db.con.close() - + def loadSettings(self): settings = QSettings() idx = self.dock.comboConnections.findText(Utils.getStringValue(settings, '/pgRoutingLayer/Database', '')) @@ -1216,7 +1216,7 @@ def loadSettings(self): idx = self.dock.comboBoxFunction.findText(Utils.getStringValue(settings, '/pgRoutingLayer/Function', 'dijkstra')) if idx >= 0: self.dock.comboBoxFunction.setCurrentIndex(idx) - + self.dock.lineEditTable.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/edge_table', 'roads')) self.dock.lineEditGeometry.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/geometry', 'the_geom')) self.dock.lineEditId.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/id', 'id')) @@ -1230,7 +1230,7 @@ def loadSettings(self): self.dock.lineEditY2.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/y2', 'y2')) self.dock.lineEditRule.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/rule', 'rule')) self.dock.lineEditToCost.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/to_cost', 'to_cost')) - + self.dock.lineEditIds.setText(Utils.getStringValue(settings, '/pgRoutingLayer/ids', '')) self.dock.lineEditPcts.setText(Utils.getStringValue(settings, '/pgRoutingLayer/pcts', '')) @@ -1250,12 +1250,12 @@ def loadSettings(self): self.dock.checkBoxHeapPaths.setChecked(Utils.getBoolValue(settings, '/pgRoutingLayer/heap_paths', False)) self.dock.checkBoxHasReverseCost.setChecked(Utils.getBoolValue(settings, '/pgRoutingLayer/has_reverse_cost', False)) self.dock.plainTextEditTurnRestrictSql.setPlainText(Utils.getStringValue(settings, '/pgRoutingLayer/turn_restrict_sql', 'null')) - + def saveSettings(self): settings = QSettings() settings.setValue('/pgRoutingLayer/Database', self.dock.comboConnections.currentText()) settings.setValue('/pgRoutingLayer/Function', self.dock.comboBoxFunction.currentText()) - + settings.setValue('/pgRoutingLayer/sql/edge_table', self.dock.lineEditTable.text()) settings.setValue('/pgRoutingLayer/sql/geometry', self.dock.lineEditGeometry.text()) @@ -1272,7 +1272,7 @@ def saveSettings(self): settings.setValue('/pgRoutingLayer/sql/rule', self.dock.lineEditRule.text()) settings.setValue('/pgRoutingLayer/sql/to_cost', self.dock.lineEditToCost.text()) - + settings.setValue('/pgRoutingLayer/ids', self.dock.lineEditIds.text()) settings.setValue('/pgRoutingLayer/pcts', self.dock.lineEditPcts.text()) settings.setValue('/pgRoutingLayer/source_pos', self.dock.lineEditSourcePos.text()) @@ -1291,4 +1291,3 @@ def saveSettings(self): settings.setValue('/pgRoutingLayer/heap_paths', self.dock.checkBoxHeapPaths.isChecked()) settings.setValue('/pgRoutingLayer/has_reverse_cost', self.dock.checkBoxHasReverseCost.isChecked()) settings.setValue('/pgRoutingLayer/turn_restrict_sql', self.dock.plainTextEditTurnRestrictSql.toPlainText()) - From dded72aec1e7b1b9eb86ac8983e318f616cc3cd1 Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Sun, 24 Jun 2018 21:15:31 +0530 Subject: [PATCH 05/32] "API changes_2 to function files" --- functions/__init__.py | 0 functions/dijkstraCost.py | 10 +- functions/drivingDistance.py | 2 +- functions/kdijkstra_cost.py | 10 +- functions/kdijkstra_path.py | 8 +- functions/ksp.py | 8 +- functions/trsp_edge.py | 11 +- functions/trsp_vertex.py | 10 +- functions/trsp_via_edges.py | 6 +- functions/trsp_via_vertices.py | 8 +- functions/tsp_euclid.py | 22 +-- pgRoutingLayer.py | 305 ++++++++++++++++----------------- 12 files changed, 200 insertions(+), 200 deletions(-) mode change 100644 => 100755 functions/__init__.py diff --git a/functions/__init__.py b/functions/__init__.py old mode 100644 new mode 100755 diff --git a/functions/dijkstraCost.py b/functions/dijkstraCost.py index f08f1b6..62eb7b1 100755 --- a/functions/dijkstraCost.py +++ b/functions/dijkstraCost.py @@ -2,8 +2,8 @@ from builtins import str from qgis.PyQt.QtCore import QSizeF, QPointF from qgis.PyQt.QtGui import QColor -from qgis.core import (QgsGeometry, QGis) -from qgis.gui import (QgsRubberBand, QgsTextAnnotationItem) +from qgis.core import QgsGeometry, Qgis +from qgis.gui import QgsRubberBand, QgsTextAnnotation import psycopg2 from .. import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase @@ -108,11 +108,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QGis.WKBMultiLineString: + if geom.wkbType() == Qgis.WKBMultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == Qgis.WKBLineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) @@ -142,7 +142,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): geom = QgsGeometry().fromWkt(str(row2[0])) pt = geom.asPoint() textDocument = QTextDocument("%(result_target_id)d:%(result_cost)f" % args) - textAnnotation = QgsTextAnnotationItem(mapCanvas) + textAnnotation = QgsTextAnnotation(mapCanvas) textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) textAnnotation.setOffsetFromReferencePoint(QPointF(20, -40)) diff --git a/functions/drivingDistance.py b/functions/drivingDistance.py index e191423..5b331f4 100755 --- a/functions/drivingDistance.py +++ b/functions/drivingDistance.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from builtins import str from qgis.PyQt.QtCore import Qt -#from PyQt4.QtGui import * +#from PyQt.QtGui import * from qgis.core import QgsGeometry, QgsPoint from qgis.gui import QgsVertexMarker import psycopg2 diff --git a/functions/kdijkstra_cost.py b/functions/kdijkstra_cost.py index 5ba6764..cb22513 100755 --- a/functions/kdijkstra_cost.py +++ b/functions/kdijkstra_cost.py @@ -2,8 +2,8 @@ from builtins import str from qgis.PyQt.QtCore import QSizeF, QPointF from qgis.PyQt.QtGui import QColor -from qgis.core import QGis, QgsGeometry -from qgis.gui import QgsRubberBand, QgsTextAnnotationItem +from qgis.core import Qgis, QgsGeometry +from qgis.gui import QgsRubberBand, QgsTextAnnotation import psycopg2 from .. import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase @@ -105,11 +105,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QGis.WKBMultiLineString: + if geom.wkbType() == Qgis.WKBMultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == Qgis.WKBLineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) @@ -141,7 +141,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): geom = QgsGeometry().fromWkt(str(row2[0])) pt = geom.asPoint() textDocument = QTextDocument("%(result_target_id)d:%(result_cost)f" % args) - textAnnotation = QgsTextAnnotationItem(mapCanvas) + textAnnotation = QgsTextAnnotation(mapCanvas) textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) textAnnotation.setOffsetFromReferencePoint(QPointF(20, -40)) diff --git a/functions/kdijkstra_path.py b/functions/kdijkstra_path.py index ca893c8..614c030 100755 --- a/functions/kdijkstra_path.py +++ b/functions/kdijkstra_path.py @@ -1,8 +1,8 @@ from __future__ import absolute_import -#from PyQt4.QtCore import * +from qgis.PyQt.QtCore import * from builtins import str from qgis.PyQt.QtGui import QColor -from qgis.core import QgsGeometry, QGis +from qgis.core import QgsGeometry, Qgis from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -138,11 +138,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QGis.WKBMultiLineString: + if geom.wkbType() == Qgis.WKBMultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == Qgis.WKBLineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) diff --git a/functions/ksp.py b/functions/ksp.py index c7db894..9dc0872 100755 --- a/functions/ksp.py +++ b/functions/ksp.py @@ -1,8 +1,8 @@ from __future__ import absolute_import -#from PyQt4.QtCore import * +from qgis.PyQt.QtCore import * from builtins import str from qgis.PyQt.QtGui import QColor -from qgis.core import QGis, QgsGeometry +from qgis.core import Qgis, QgsGeometry from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -232,11 +232,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (route_id:%(result_route_id)d, node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QGis.WKBMultiLineString: + if geom.wkbType() == Qgis.WKBMultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == Qgis.WKBLineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) diff --git a/functions/trsp_edge.py b/functions/trsp_edge.py index 6be39fd..c39d7c6 100755 --- a/functions/trsp_edge.py +++ b/functions/trsp_edge.py @@ -1,8 +1,9 @@ from __future__ import absolute_import -#from PyQt4.QtCore import * -#from PyQt4.QtGui import * +from qgis.PyQt.QtCore import * +from PyQt.QtGui import * +from PyQt.QtWidgets import * from builtins import str -from qgis.core import QGis, QgsGeometry +from qgis.core import Qgis, QgsGeometry from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -173,11 +174,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QGis.WKBMultiLineString: + if geom.wkbType() == Qgis.WKBMultiLineString: for line in geom.asMultiPolyline(): for pt in line: resultPathRubberBand.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == Qgis.WKBLineString: for pt in geom.asPolyline(): resultPathRubberBand.addPoint(pt) diff --git a/functions/trsp_vertex.py b/functions/trsp_vertex.py index 1aa63b1..3415a4f 100755 --- a/functions/trsp_vertex.py +++ b/functions/trsp_vertex.py @@ -1,8 +1,8 @@ from __future__ import absolute_import -#from PyQt4.QtCore import * -#from PyQt4.QtGui import * +from qgis.PyQt.QtCore import * +from qgis.PyQt.QtGui import * from builtins import str -from qgis.core import QGis, QgsGeometry +from qgis.core import Qgis, QgsGeometry from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -76,11 +76,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QGis.WKBMultiLineString: + if geom.wkbType() == Qgis.WKBMultiLineString: for line in geom.asMultiPolyline(): for pt in line: resultPathRubberBand.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == Qgis.WKBLineString: for pt in geom.asPolyline(): resultPathRubberBand.addPoint(pt) diff --git a/functions/trsp_via_edges.py b/functions/trsp_via_edges.py index 69b085d..ee05f4c 100755 --- a/functions/trsp_via_edges.py +++ b/functions/trsp_via_edges.py @@ -2,7 +2,7 @@ #from PyQt4.QtCore import * from builtins import str from qgis.PyQt.QtGui import QColor -from qgis.core import QGis, QgsGeometry +from qgis.core import Qis, QgsGeometry from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -257,11 +257,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QGis.WKBMultiLineString: + if geom.wkbType() == Qgis.WKBMultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == Qgis.WKBLineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) diff --git a/functions/trsp_via_vertices.py b/functions/trsp_via_vertices.py index ef317a9..7e1ea7f 100755 --- a/functions/trsp_via_vertices.py +++ b/functions/trsp_via_vertices.py @@ -1,8 +1,8 @@ from __future__ import absolute_import -#from PyQt4.QtCore import * +from qgis.PyQt4.QtCore import * from builtins import str from qgis.PyQt.QtGui import QColor -from qgis.core import QGis, QgsGeometry +from qgis.core import Qgis, QgsGeometry from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -105,11 +105,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QGis.WKBMultiLineString: + if geom.wkbType() == Qgis.WKBMultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == Qgis.WKBLineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) diff --git a/functions/tsp_euclid.py b/functions/tsp_euclid.py index 007fde1..ae0597c 100755 --- a/functions/tsp_euclid.py +++ b/functions/tsp_euclid.py @@ -1,10 +1,12 @@ -from PyQt4.QtCore import * -from PyQt4.QtGui import * -from qgis.core import * -from qgis.gui import * +from __future__ import absolute_import +from builtins import str +from qgis.PyQt.QtCore import * +from qgis.PyQt.QtGui import QTextDocument +from qgis.core import QgsGeometry, Qgis +from qgis.gui import QgsTextAnnotation import psycopg2 from .. import pgRoutingLayer_utils as Utils -from FunctionBase import FunctionBase +from .FunctionBase import FunctionBase class Function(FunctionBase): @@ -95,11 +97,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QGis.WKBMultiLineString: + if geom.wkbType() == Qgis.WKBMultiLineString: for line in geom.asMultiPolyline(): for pt in line: resultPathsRubberBands.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == Qgis.WKBLineString: for pt in geom.asPolyline(): resultPathsRubberBands.addPoint(pt) prevrow = row @@ -121,11 +123,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QGis.WKBMultiLineString: + if geom.wkbType() == Qgis.WKBMultiLineString: for line in geom.asMultiPolyline(): for pt in line: resultPathsRubberBands.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == Qgis.WKBLineString: for pt in geom.asPolyline(): resultPathsRubberBands.addPoint(pt) @@ -158,7 +160,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): geom = QgsGeometry().fromWkt(str(row2[0])) pt = geom.asPoint() textDocument = QTextDocument("%(result_seq)d:%(result_node_id)d" % args) - textAnnotation = QgsTextAnnotationItem(mapCanvas) + textAnnotation = QgsTextAnnotation(mapCanvas) textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) textAnnotation.setOffsetFromReferencePoint(QPointF(20, -40)) diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index 1d8d010..ef3aabd 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -2,8 +2,8 @@ /*************************************************************************** pgRouting Layer a QGIS plugin - - based on "Fast SQL Layer" plugin. Copyright 2011 Pablo Torres Carreira + + based on "Fast SQL Layer" plugin. Copyright 2011 Pablo Torres Carreira ------------------- begin : 2011-11-25 copyright : (c) 2011 by Anita Graser @@ -25,9 +25,8 @@ from builtins import object from qgis.PyQt import uic from qgis.PyQt.QtCore import Qt, QObject, pyqtSignal, QRegExp, QSettings - from qgis.PyQt.QtGui import QColor, QIcon, QIntValidator, QDoubleValidator -from qgis.PyQt.QtWidgets import QAction +from qgis.PyQt.QtWidgets import QAction,QDockWidget,QApplication,QLabel,QLineEdit,QPushButton,QWidget from qgis.core import QgsMessageLog,Qgis from qgis.gui import QgsVertexMarker,QgsRubberBand,QgsMapToolEmitPoint from . import dbConnection @@ -44,17 +43,17 @@ class PgRoutingLayer(object): SUPPORTED_FUNCTIONS = [ 'dijkstra', - 'dijkstraCost', + 'trsp_vertex', 'astar', 'drivingDistance', 'alphashape', + 'bdDijkstra', + 'bdAstar', + 'dijkstraCost', 'tsp_euclid', - 'trsp_vertex', - 'trsp_edge', 'kdijkstra_cost', + 'trsp_edge', 'kdijkstra_path', - 'bdDijkstra', - 'bdAstar', 'ksp', 'trsp_via_vertices', 'trsp_via_edges' @@ -91,13 +90,13 @@ class PgRoutingLayer(object): ] FIND_RADIUS = 10 FRACTION_DECIMAL_PLACES = 2 - version = 2.0 + version = 3.0 functions = {} - + def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface - + self.idsVertexMarkers = [] self.targetIdsVertexMarkers = [] self.sourceIdsVertexMarkers = [] @@ -116,7 +115,7 @@ def __init__(self, iface): self.targetIdRubberBand = QgsRubberBand(self.iface.mapCanvas(), Utils.getRubberBandType(False)) self.targetIdRubberBand.setColor(Qt.yellow) self.targetIdRubberBand.setWidth(4) - + self.canvasItemList = {} self.canvasItemList['markers'] = [] self.canvasItemList['annotations'] = [] @@ -131,19 +130,19 @@ def __init__(self, iface): if not Utils.isQGISv1(): resultAreaRubberBand.setBrushStyle(Qt.Dense4Pattern) self.canvasItemList['area'] = resultAreaRubberBand - + def initGui(self): # Create action that will start plugin configuration self.action = QAction(QIcon(":/plugins/pgRoutingLayer/icon.png"), "pgRouting Layer", self.iface.mainWindow()) #Add toolbar button and menu item self.iface.addPluginToDatabaseMenu("&pgRouting Layer", self.action) #self.iface.addToolBarIcon(self.action) - + #load the form path = os.path.dirname(os.path.abspath(__file__)) self.dock = uic.loadUi(os.path.join(path, "ui_pgRoutingLayer.ui")) self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dock) - + self.idsEmitPoint = QgsMapToolEmitPoint(self.iface.mapCanvas()) self.sourceIdEmitPoint = QgsMapToolEmitPoint(self.iface.mapCanvas()) self.targetIdEmitPoint = QgsMapToolEmitPoint(self.iface.mapCanvas()) @@ -154,7 +153,7 @@ def initGui(self): #self.targetIdEmitPoint.setButton(buttonSelectTargetId) #self.sourceIdEmitPoint.setButton(buttonSelectSourceId) #self.targetIdsEmitPoint.setButton(buttonSelectTargetId) - + #connect the action to each method self.action.triggered.connect(self.show) self.dock.buttonReloadConnections.clicked.connect(self.reloadConnections) @@ -189,11 +188,12 @@ def initGui(self): self.functions = {} for funcfname in self.SUPPORTED_FUNCTIONS: # import the function - exec("from functions import %s as function" % funcfname) + exec("from pgRoutingLayer.functions import %s as function" % funcfname, globals(),globals()) funcname = function.Function.getName() + print(funcname) self.functions[funcname] = function.Function(self.dock) self.dock.comboBoxFunction.addItem(funcname) - + self.dock.lineEditIds.setValidator(QRegExpValidator(QRegExp("[0-9,]+"), self.dock)) self.dock.lineEditPcts.setValidator(QRegExpValidator(QRegExp("[0-9,.]+"), self.dock)) @@ -209,26 +209,26 @@ def initGui(self): self.dock.lineEditDistance.setValidator(QDoubleValidator()) self.dock.lineEditAlpha.setValidator(QDoubleValidator()) self.dock.lineEditPaths.setValidator(QIntValidator()) - + #populate the combo with connections self.reloadMessage = False self.reloadConnections() self.loadSettings() Utils.logMessage("startup version " + str(self.version)) self.reloadMessage = True - + def show(self): self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dock) - + def unload(self): self.clear() self.saveSettings() # Remove the plugin menu item and icon self.iface.removePluginDatabaseMenu("&pgRouting Layer", self.action) self.iface.removeDockWidget(self.dock) - + def reloadConnections(self): - oldReloadMessage = self.reloadMessage + oldReloadMessage = False self.reloadMessage = False database = str(self.dock.comboConnections.currentText()) @@ -256,7 +256,7 @@ def reloadConnections(self): db.con.close() idx = self.dock.comboConnections.findText(database) - + if idx >= 0: self.dock.comboConnections.setCurrentIndex(idx) else: @@ -275,7 +275,7 @@ def updateConnectionEnabled(self): con = db.con self.version = Utils.getPgrVersion(con) if self.reloadMessage: - QMessageBox.information(self.dock, self.dock.windowTitle(), + QMessageBox.information(self.dock, self.dock.windowTitle(), 'Selected database: ' + dbname + '\npgRouting version: ' + str(self.version)) @@ -303,21 +303,19 @@ def loadFunctionsForVersion(self): def updateFunctionEnabled(self, text): - if text == '': - return - self.clear() - function = self.functions[str(text)] + function = self.functions.get(str(text)) + self.toggleSelectButton(None) - + for controlName in self.TOGGLE_CONTROL_NAMES: control = getattr(self.dock, controlName) control.setVisible(False) - + for controlName in function.getControlNames(self.version): control = getattr(self.dock, controlName) control.setVisible(True) - + # for initial display self.dock.gridLayoutSqlColumns.invalidate() self.dock.gridLayoutArguments.invalidate() @@ -328,18 +326,18 @@ def updateFunctionEnabled(self, text): if (not self.dock.checkBoxHasReverseCost.isChecked()) or (not self.dock.checkBoxHasReverseCost.isEnabled()): self.dock.lineEditReverseCost.setEnabled(False) - + # if type(edge/node) changed, clear input if (self.prevType != None) and (self.prevType != function.isEdgeBase()): self.clear() - + self.prevType = function.isEdgeBase() canExport = function.canExport() self.dock.buttonExport.setEnabled(canExport) canExportMerged = function.canExportMerged() self.dock.buttonExportMerged.setEnabled(canExportMerged) - + def selectIds(self, checked): if checked: self.toggleSelectButton(self.dock.buttonSelectIds) @@ -356,7 +354,7 @@ def selectIds(self, checked): self.iface.mapCanvas().setMapTool(self.idsEmitPoint) else: self.iface.mapCanvas().unsetMapTool(self.idsEmitPoint) - + def setIds(self, pt): function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getBaseArguments() @@ -407,7 +405,7 @@ def setIds(self, pt): vertexMarker.setCenter(pointGeom.asPoint()) self.idsVertexMarkers.append(vertexMarker) Utils.refreshMapCanvas(mapCanvas) - + def selectSourceId(self, checked): if checked: self.toggleSelectButton(self.dock.buttonSelectSourceId) @@ -417,7 +415,7 @@ def selectSourceId(self, checked): self.iface.mapCanvas().setMapTool(self.sourceIdEmitPoint) else: self.iface.mapCanvas().unsetMapTool(self.sourceIdEmitPoint) - + def setSourceId(self, pt): function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getBaseArguments() @@ -447,8 +445,8 @@ def setSourceId(self, pt): self.sourceIdVertexMarker.setVisible(True) self.dock.buttonSelectSourceId.click() Utils.refreshMapCanvas(self.iface.mapCanvas()) - - + + def selectSourceIds(self, checked): if checked: self.toggleSelectButton(self.dock.buttonSelectSourceIds) @@ -460,7 +458,7 @@ def selectSourceIds(self, checked): self.iface.mapCanvas().setMapTool(self.sourceIdsEmitPoint) else: self.iface.mapCanvas().unsetMapTool(self.sourceIdsEmitPoint) - + def setSourceIds(self, pt): args = self.getBaseArguments() result, id, wkt = self.findNearestNode(args, pt) @@ -489,7 +487,7 @@ def selectTargetId(self, checked): self.iface.mapCanvas().setMapTool(self.targetIdEmitPoint) else: self.iface.mapCanvas().unsetMapTool(self.targetIdEmitPoint) - + def setTargetId(self, pt): function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getBaseArguments() @@ -519,7 +517,7 @@ def setTargetId(self, pt): self.targetIdVertexMarker.setVisible(True) self.dock.buttonSelectTargetId.click() Utils.refreshMapCanvas(self.iface.mapCanvas()) - + def selectTargetIds(self, checked): if checked: self.toggleSelectButton(self.dock.buttonSelectTargetIds) @@ -531,7 +529,7 @@ def selectTargetIds(self, checked): self.iface.mapCanvas().setMapTool(self.targetIdsEmitPoint) else: self.iface.mapCanvas().unsetMapTool(self.targetIdsEmitPoint) - + def setTargetIds(self, pt): args = self.getBaseArguments() result, id, wkt = self.findNearestNode(args, pt) @@ -549,74 +547,74 @@ def setTargetIds(self, pt): vertexMarker.setCenter(geom.asPoint()) self.targetIdsVertexMarkers.append(vertexMarker) Utils.refreshMapCanvas(mapCanvas) - + def updateReverseCostEnabled(self, state): if state == Qt.Checked: self.dock.lineEditReverseCost.setEnabled(True) else: self.dock.lineEditReverseCost.setEnabled(False) - + def run(self): """ Draws a Preview on the canvas""" QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - + function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getArguments(function.getControlNames(self.version)) - + empties = [] for key in list(args.keys()): if not args[key]: empties.append(key) - + if len(empties) > 0: QApplication.restoreOverrideCursor() QMessageBox.warning(self.dock, self.dock.windowTitle(), 'Following argument is not specified.\n' + ','.join(empties)) return - + db = None try: dbname = str(self.dock.comboConnections.currentText()) db = self.actionsDb[dbname].connect() - + con = db.con - + version = Utils.getPgrVersion(con) args['version'] = version - + srid, geomType = Utils.getSridAndGeomType(con, args['edge_table'], args['geometry']) if (function.getName() == 'tsp(euclid)'): args['node_query'] = Utils.getNodeQuery(args, geomType) - + function.prepare(self.canvasItemList) - - args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) + + args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) query = function.getQuery(args) #QMessageBox.information(self.dock, self.dock.windowTitle(), 'Geometry Query:' + query) - + cur = con.cursor() cur.execute(query) rows = cur.fetchall() if len(rows) == 0: QMessageBox.information(self.dock, self.dock.windowTitle(), 'No paths found in ' + self.getLayerName(args)) - + args['srid'] = srid args['canvas_srid'] = Utils.getCanvasSrid(Utils.getDestinationCrs(self.iface.mapCanvas())) Utils.setTransformQuotes(args, srid, args['canvas_srid']) function.draw(rows, con, args, geomType, self.canvasItemList, self.iface.mapCanvas()) - + except psycopg2.DatabaseError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + except SystemError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + except AssertionError as e: QApplication.restoreOverrideCursor() QMessageBox.warning(self.dock, self.dock.windowTitle(), '%s' % e) - + finally: QApplication.restoreOverrideCursor() if db and db.con: @@ -628,28 +626,28 @@ def run(self): def export(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - + function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getArguments(function.getControlNames(self.version)) - + empties = [] for key in list(args.keys()): if not args[key]: empties.append(key) - + if len(empties) > 0: QApplication.restoreOverrideCursor() QMessageBox.warning(self.dock, self.dock.windowTitle(), 'Following argument is not specified.\n' + ','.join(empties)) return - + db = None try: dbname = str(self.dock.comboConnections.currentText()) db = self.actionsDb[dbname].connect() - + con = db.con - + version = Utils.getPgrVersion(con) args['version'] = version @@ -659,18 +657,18 @@ def export(self): srid, geomType = Utils.getSridAndGeomType(con, '%(edge_table)s' % args, '%(geometry)s' % args) - args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) + args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) #get the EXPORT query msgQuery = function.getExportQuery(args) #QMessageBox.information(self.dock, self.dock.windowTitle(), 'Geometry Query:\n' + msgQuery) Utils.logMessage('Export:\n' + msgQuery) - + query = self.cleanQuery(msgQuery) - + uri = db.getURI() uri.setDataSource("", "(" + query + ")", "path_geom", "", "seq") - + layerName = self.getLayerName(args) vl = self.iface.addVectorLayer(uri.uri(), layerName, db.getProviderName()) @@ -678,15 +676,15 @@ def export(self): QMessageBox.information(self.dock, self.dock.windowTitle(), 'Invalid Layer:\n - No paths found or\n - Failed to create vector layer from query') #QMessageBox.information(self.dock, self.dock.windowTitle(), 'pgRouting Query:' + function.getQuery(args)) #QMessageBox.information(self.dock, self.dock.windowTitle(), 'Geometry Query:' + msgQuery) - + except psycopg2.DatabaseError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + except SystemError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + finally: QApplication.restoreOverrideCursor() if db and db.con: @@ -724,50 +722,50 @@ def getBBOX(self, srid, use_bbox): %(xMax)s, %(yMax)s, %(srid)s ) """ % bbox, text - - + + def exportMerged(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) - + function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getArguments(function.getControlNames(self.version)) - + empties = [] for key in list(args.keys()): if not args[key]: empties.append(key) - + if len(empties) > 0: QApplication.restoreOverrideCursor() QMessageBox.warning(self.dock, self.dock.windowTitle(), 'Following argument is not specified.\n' + ','.join(empties)) return - + db = None try: dbname = str(self.dock.comboConnections.currentText()) db = self.actionsDb[dbname].connect() - + con = db.con - + version = Utils.getPgrVersion(con) args['version'] = version - + srid, geomType = Utils.getSridAndGeomType(con, '%(edge_table)s' % args, '%(geometry)s' % args) - args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) + args['BBOX'], args['printBBOX'] = self.getBBOX(srid, args['use_bbox']) # get the exportMerge query msgQuery = function.getExportMergeQuery(args) Utils.logMessage('Export merged:\n' + msgQuery) query = self.cleanQuery(msgQuery) - + uri = db.getURI() uri.setDataSource("", "(" + query + ")", "path_geom", "", "seq") - + # add vector layer to map layerName = self.getLayerName(args, 'M') - + vl = self.iface.addVectorLayer(uri.uri(), layerName, db.getProviderName()) if not vl: @@ -781,15 +779,15 @@ def exportMerged(self): QMessageBox.information(self.dock, self.dock.windowTitle(), 'Invalid Layer:\n - No paths found') else: QMessageBox.information(self.dock, self.dock.windowTitle(), 'Invalid Layer:\n - No paths found or\n - Failed to create vector layer from query') - + except psycopg2.DatabaseError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + except SystemError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) - + finally: QApplication.restoreOverrideCursor() if db and db.con: @@ -798,11 +796,11 @@ def exportMerged(self): except: QMessageBox.critical(self.dock, self.dock.windowTitle(), 'server closed the connection unexpectedly') - + def getLayerName(self, args, letter=''): function = self.functions[str(self.dock.comboBoxFunction.currentText())] - layerName = "(" + letter + layerName = "(" + letter if 'directed' in args and args['directed'] == 'true': layerName += "D) " @@ -837,7 +835,7 @@ def getLayerName(self, args, letter=''): layerName += " " + args['printBBOX'] return layerName - + @@ -885,7 +883,7 @@ def clear(self): self.canvasItemList['paths'] = [] self.canvasItemList['path'].reset(Utils.getRubberBandType(False)) self.canvasItemList['area'].reset(Utils.getRubberBandType(True)) - + def toggleSelectButton(self, button): selectButtons = [ self.dock.buttonSelectIds, @@ -896,7 +894,7 @@ def toggleSelectButton(self, button): if selectButton != button: if selectButton.isChecked(): selectButton.click() - + def getArguments(self, controls): args = {} args['edge_table'] = self.dock.lineEditTable.text() @@ -906,34 +904,34 @@ def getArguments(self, controls): if 'lineEditSource' in controls: args['source'] = self.dock.lineEditSource.text() - + if 'lineEditTarget' in controls: args['target'] = self.dock.lineEditTarget.text() - + if 'lineEditCost' in controls: args['cost'] = self.dock.lineEditCost.text() - + if 'lineEditReverseCost' in controls: args['reverse_cost'] = self.dock.lineEditReverseCost.text() - + if 'lineEditX1' in controls: args['x1'] = self.dock.lineEditX1.text() - + if 'lineEditY1' in controls: args['y1'] = self.dock.lineEditY1.text() - + if 'lineEditX2' in controls: args['x2'] = self.dock.lineEditX2.text() - + if 'lineEditY2' in controls: args['y2'] = self.dock.lineEditY2.text() - + if 'lineEditRule' in controls: args['rule'] = self.dock.lineEditRule.text() - + if 'lineEditToCost' in controls: args['to_cost'] = self.dock.lineEditToCost.text() - + if 'lineEditIds' in controls: args['ids'] = self.dock.lineEditIds.text() @@ -942,37 +940,37 @@ def getArguments(self, controls): if 'lineEditSourceId' in controls: args['source_id'] = self.dock.lineEditSourceId.text() - + if 'lineEditSourcePos' in controls: args['source_pos'] = self.dock.lineEditSourcePos.text() - + if 'lineEditSourceIds' in controls: args['source_ids'] = self.dock.lineEditSourceIds.text() - + if 'lineEditTargetId' in controls: args['target_id'] = self.dock.lineEditTargetId.text() - + if 'lineEditTargetPos' in controls: args['target_pos'] = self.dock.lineEditTargetPos.text() - + if 'lineEditTargetIds' in controls: args['target_ids'] = self.dock.lineEditTargetIds.text() - + if 'lineEditDistance' in controls: args['distance'] = self.dock.lineEditDistance.text() - + if 'lineEditAlpha' in controls: args['alpha'] = self.dock.lineEditAlpha.text() - + if 'lineEditPaths' in controls: args['paths'] = self.dock.lineEditPaths.text() - + if 'checkBoxDirected' in controls: args['directed'] = str(self.dock.checkBoxDirected.isChecked()).lower() - + if 'checkBoxHeapPaths' in controls: args['heap_paths'] = str(self.dock.checkBoxHeapPaths.isChecked()).lower() - + if 'checkBoxUseBBOX' in controls: args['use_bbox'] = str(self.dock.checkBoxUseBBOX.isChecked()).lower() else: @@ -986,12 +984,12 @@ def getArguments(self, controls): args['reverse_cost'] = ' ' else: args['reverse_cost'] = ', ' + args['reverse_cost'] + '::float8 AS reverse_cost' - + if 'plainTextEditTurnRestrictSql' in controls: args['turn_restrict_sql'] = self.dock.plainTextEditTurnRestrictSql.toPlainText() - + return args - + def getBaseArguments(self): args = {} args['edge_table'] = self.dock.lineEditTable.text() @@ -999,21 +997,21 @@ def getBaseArguments(self): args['id'] = self.dock.lineEditId.text() args['source'] = self.dock.lineEditSource.text() args['target'] = self.dock.lineEditTarget.text() - + empties = [] for key in list(args.keys()): if not args[key]: empties.append(key) - + if len(empties) > 0: QApplication.restoreOverrideCursor() QMessageBox.warning(self.dock, self.dock.windowTitle(), 'Following argument is not specified.\n' + ','.join(empties)) return None - + return args - - + + # emulate "matching.sql" - "find_nearest_node_within_distance" def findNearestNode(self, args, pt): distance = self.iface.mapCanvas().getCoordinateTransform().mapUnitsPerPixel() * self.FIND_RADIUS @@ -1023,7 +1021,7 @@ def findNearestNode(self, args, pt): try: dbname = str(self.dock.comboConnections.currentText()) db = self.actionsDb[dbname].connect() - + con = db.con #srid, geomType = self.getSridAndGeomType(con, args) #srid, geomType = Utils.getSridAndGeomType(con, args['edge_table'], args['geometry']) @@ -1034,7 +1032,7 @@ def findNearestNode(self, args, pt): trans = QgsCoordinateTransform(canvasCrs, layerCrs) pt = trans.transform(pt) rect = trans.transform(rect) - + args['canvas_srid'] = Utils.getCanvasSrid(canvasCrs) args['srid'] = srid args['x'] = pt.x() @@ -1043,12 +1041,12 @@ def findNearestNode(self, args, pt): args['miny'] = rect.yMinimum() args['maxx'] = rect.xMaximum() args['maxy'] = rect.yMaximum() - + Utils.setStartPoint(geomType, args) Utils.setEndPoint(geomType, args) #Utils.setTransformQuotes(args) Utils.setTransformQuotes(args, srid, args['canvas_srid']) - + # Getting nearest source query1 = """ SELECT %(source)s, @@ -1060,7 +1058,7 @@ def findNearestNode(self, args, pt): FROM %(edge_table)s WHERE ST_SetSRID('BOX3D(%(minx)f %(miny)f, %(maxx)f %(maxy)f)'::BOX3D, %(srid)d) && %(geometry)s ORDER BY dist ASC LIMIT 1""" % args - + ##Utils.logMessage(query1) cur1 = con.cursor() cur1.execute(query1) @@ -1072,7 +1070,7 @@ def findNearestNode(self, args, pt): d1 = row1[1] source = row1[0] wkt1 = row1[2] - + # Getting nearest target query2 = """ SELECT %(target)s, @@ -1084,7 +1082,7 @@ def findNearestNode(self, args, pt): FROM %(edge_table)s WHERE ST_SetSRID('BOX3D(%(minx)f %(miny)f, %(maxx)f %(maxy)f)'::BOX3D, %(srid)d) && %(geometry)s ORDER BY dist ASC LIMIT 1""" % args - + ##Utils.logMessage(query2) cur2 = con.cursor() cur2.execute(query2) @@ -1096,7 +1094,7 @@ def findNearestNode(self, args, pt): d2 = row2[1] target = row2[0] wkt2 = row2[2] - + # Checking what is nearer - source or target d = None node = None @@ -1118,24 +1116,24 @@ def findNearestNode(self, args, pt): node = target d = d2 wkt = wkt2 - + ##Utils.logMessage(str(d)) if (d == None) or (d > distance): node = None wkt = None return False, None, None - + return True, node, wkt - + except psycopg2.DatabaseError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) return False, None, None - + finally: if db and db.con: db.con.close() - + # emulate "matching.sql" - "find_nearest_link_within_distance" def findNearestLink(self, args, pt): distance = self.iface.mapCanvas().getCoordinateTransform().mapUnitsPerPixel() * self.FIND_RADIUS @@ -1145,7 +1143,7 @@ def findNearestLink(self, args, pt): try: dbname = str(self.dock.comboConnections.currentText()) db = self.actionsDb[dbname].connect() - + con = db.con cur = con.cursor() @@ -1157,7 +1155,7 @@ def findNearestLink(self, args, pt): trans = QgsCoordinateTransform(canvasCrs, layerCrs) pt = trans.transform(pt) rect = trans.transform(rect) - + args['canvas_srid'] = Utils.getCanvasSrid(canvasCrs) args['srid'] = srid args['x'] = pt.x() @@ -1167,10 +1165,10 @@ def findNearestLink(self, args, pt): args['maxx'] = rect.xMaximum() args['maxy'] = rect.yMaximum() args['decimal_places'] = self.FRACTION_DECIMAL_PLACES - + #Utils.setTransformQuotes(args) Utils.setTransformQuotes(args, srid, args['canvas_srid']) - + # Searching for a link within the distance query = """ WITH point AS ( @@ -1185,7 +1183,7 @@ def findNearestLink(self, args, pt): FROM %(edge_table)s, point WHERE ST_SetSRID('BOX3D(%(minx)f %(miny)f, %(maxx)f %(maxy)f)'::BOX3D, %(srid)d) && %(geometry)s ORDER BY dist ASC LIMIT 1""" % args - + ##Utils.logMessage(query) cur = con.cursor() cur.execute(query) @@ -1196,18 +1194,18 @@ def findNearestLink(self, args, pt): wkt = row[2] pos = row[3] pointWkt = row[4] - + return True, link, wkt, pos, pointWkt - + except psycopg2.DatabaseError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) return False, None, None - + finally: if db and db.con: db.con.close() - + def loadSettings(self): settings = QSettings() idx = self.dock.comboConnections.findText(Utils.getStringValue(settings, '/pgRoutingLayer/Database', '')) @@ -1216,7 +1214,7 @@ def loadSettings(self): idx = self.dock.comboBoxFunction.findText(Utils.getStringValue(settings, '/pgRoutingLayer/Function', 'dijkstra')) if idx >= 0: self.dock.comboBoxFunction.setCurrentIndex(idx) - + self.dock.lineEditTable.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/edge_table', 'roads')) self.dock.lineEditGeometry.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/geometry', 'the_geom')) self.dock.lineEditId.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/id', 'id')) @@ -1230,7 +1228,7 @@ def loadSettings(self): self.dock.lineEditY2.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/y2', 'y2')) self.dock.lineEditRule.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/rule', 'rule')) self.dock.lineEditToCost.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/to_cost', 'to_cost')) - + self.dock.lineEditIds.setText(Utils.getStringValue(settings, '/pgRoutingLayer/ids', '')) self.dock.lineEditPcts.setText(Utils.getStringValue(settings, '/pgRoutingLayer/pcts', '')) @@ -1250,12 +1248,12 @@ def loadSettings(self): self.dock.checkBoxHeapPaths.setChecked(Utils.getBoolValue(settings, '/pgRoutingLayer/heap_paths', False)) self.dock.checkBoxHasReverseCost.setChecked(Utils.getBoolValue(settings, '/pgRoutingLayer/has_reverse_cost', False)) self.dock.plainTextEditTurnRestrictSql.setPlainText(Utils.getStringValue(settings, '/pgRoutingLayer/turn_restrict_sql', 'null')) - + def saveSettings(self): settings = QSettings() settings.setValue('/pgRoutingLayer/Database', self.dock.comboConnections.currentText()) settings.setValue('/pgRoutingLayer/Function', self.dock.comboBoxFunction.currentText()) - + settings.setValue('/pgRoutingLayer/sql/edge_table', self.dock.lineEditTable.text()) settings.setValue('/pgRoutingLayer/sql/geometry', self.dock.lineEditGeometry.text()) @@ -1272,7 +1270,7 @@ def saveSettings(self): settings.setValue('/pgRoutingLayer/sql/rule', self.dock.lineEditRule.text()) settings.setValue('/pgRoutingLayer/sql/to_cost', self.dock.lineEditToCost.text()) - + settings.setValue('/pgRoutingLayer/ids', self.dock.lineEditIds.text()) settings.setValue('/pgRoutingLayer/pcts', self.dock.lineEditPcts.text()) settings.setValue('/pgRoutingLayer/source_pos', self.dock.lineEditSourcePos.text()) @@ -1291,4 +1289,3 @@ def saveSettings(self): settings.setValue('/pgRoutingLayer/heap_paths', self.dock.checkBoxHeapPaths.isChecked()) settings.setValue('/pgRoutingLayer/has_reverse_cost', self.dock.checkBoxHasReverseCost.isChecked()) settings.setValue('/pgRoutingLayer/turn_restrict_sql', self.dock.plainTextEditTurnRestrictSql.toPlainText()) - From a6655c5b7f8ff1fa54763a445ec3beb70a1032dd Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Sun, 24 Jun 2018 21:17:23 +0530 Subject: [PATCH 06/32] Removed Test_functionBase from functions folder --- functions/Test_FunctionBase.py | 82 ---------------------------------- 1 file changed, 82 deletions(-) delete mode 100644 functions/Test_FunctionBase.py diff --git a/functions/Test_FunctionBase.py b/functions/Test_FunctionBase.py deleted file mode 100644 index 8efbbc0..0000000 --- a/functions/Test_FunctionBase.py +++ /dev/null @@ -1,82 +0,0 @@ -import unittest -import FunctionBase as FB - - - -class TestStringMethods(unittest.TestCase): - def test_getJoinResultWithEdgeTable(self): - args = {'geometry' : 'test_geom','source': 'test_source','startpoint' : 10,'id':'test_id','edge_table':'test_Table','target':100,'endpoint':90} - - expected_sql = """ - WITH - result AS ( ) - SELECT - CASE - WHEN result._node = test_Table.test_source - THEN test_Table.test_geom - ELSE ST_Reverse(test_Table.test_geom) - END AS path_geom, - result.*, test_Table.* - FROM test_Table JOIN result - ON test_Table.test_id = result._edge ORDER BY result.seq - """ - self.maxDiff = None - - self.assertEqual(FB.getJoinResultWithEdgeTable(args),expected_sql) - - def test_getExportManySourceManyTargetMergeQuery(self): - - args = {'geometry' : 'test_geom','source': 'test_source','startpoint' : 10,'id':'test_id','edge_table':'test_Table','target':100,'endpoint':90} - expected_sql = """WITH - result AS ( ), - with_geom AS ( - SELECT - seq, result.path_name, - CASE - WHEN result._node = test_Table.test_source - THEN test_Table.test_geom - ELSE ST_Reverse(test_Table.test_geom) - END AS path_geom - FROM test_Table JOIN result - ON test_Table.test_id = result._edge - ), - one_geom AS ( - SELECT path_name, ST_LineMerge(ST_Union(path_geom)) AS path_geom - FROM with_geom - GROUP BY path_name - ORDER BY path_name - ), - aggregates AS ( - SELECT - path_name, _start_vid, _end_vid, - SUM(_cost) AS agg_cost, - array_agg(_node ORDER BY _path_seq) AS _nodes, - array_agg(_edge ORDER BY _path_seq) AS _edges - FROM result - GROUP BY path_name, _start_vid, _end_vid - ORDER BY _start_vid, _end_vid ) - SELECT row_number() over() as seq, - path_name, _start_vid, _end_vid, agg_cost, _nodes, _edges, - path_geom AS path_geom FROM aggregates JOIN one_geom - USING (path_name) - """ - self.maxDiff = None - - self.assertEqual(FB.getExportManySourceManyTargetMergeQuery(args),expected_sql) - - - - - - - - - - - - -if __name__ == '__main__': - - - unittest.main() - From 8ce9e049c8e512269cd0f85ba3a1d4f900b2ef52 Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Wed, 27 Jun 2018 22:57:58 +0530 Subject: [PATCH 07/32] API Changes_3 --- connectors/__init__.py | 0 functions/dijkstraCost.py | 4 ++-- functions/kdijkstra_cost.py | 4 ++-- functions/trsp_edge.py | 4 ++-- functions/trsp_via_edges.py | 4 ++-- functions/trsp_via_vertices.py | 2 +- functions/tsp_euclid.py | 6 +++--- pgRoutingLayer.py | 11 ++++++----- 8 files changed, 18 insertions(+), 17 deletions(-) mode change 100644 => 100755 connectors/__init__.py diff --git a/connectors/__init__.py b/connectors/__init__.py old mode 100644 new mode 100755 diff --git a/functions/dijkstraCost.py b/functions/dijkstraCost.py index 62eb7b1..219a866 100755 --- a/functions/dijkstraCost.py +++ b/functions/dijkstraCost.py @@ -2,8 +2,8 @@ from builtins import str from qgis.PyQt.QtCore import QSizeF, QPointF from qgis.PyQt.QtGui import QColor -from qgis.core import QgsGeometry, Qgis -from qgis.gui import QgsRubberBand, QgsTextAnnotation +from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation +from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase diff --git a/functions/kdijkstra_cost.py b/functions/kdijkstra_cost.py index cb22513..b67b96f 100755 --- a/functions/kdijkstra_cost.py +++ b/functions/kdijkstra_cost.py @@ -2,8 +2,8 @@ from builtins import str from qgis.PyQt.QtCore import QSizeF, QPointF from qgis.PyQt.QtGui import QColor -from qgis.core import Qgis, QgsGeometry -from qgis.gui import QgsRubberBand, QgsTextAnnotation +from qgis.core import Qgis, QgsGeometry, QgsTextAnnotation +from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase diff --git a/functions/trsp_edge.py b/functions/trsp_edge.py index c39d7c6..eed48de 100755 --- a/functions/trsp_edge.py +++ b/functions/trsp_edge.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from qgis.PyQt.QtCore import * -from PyQt.QtGui import * -from PyQt.QtWidgets import * +from qgis.PyQt.QtGui import * +from qgis.PyQt.QtWidgets import * from builtins import str from qgis.core import Qgis, QgsGeometry from qgis.gui import QgsRubberBand diff --git a/functions/trsp_via_edges.py b/functions/trsp_via_edges.py index ee05f4c..f98d789 100755 --- a/functions/trsp_via_edges.py +++ b/functions/trsp_via_edges.py @@ -1,8 +1,8 @@ from __future__ import absolute_import -#from PyQt4.QtCore import * +from qgis.PyQt.QtCore import * from builtins import str from qgis.PyQt.QtGui import QColor -from qgis.core import Qis, QgsGeometry +from qgis.core import Qgis, QgsGeometry from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils diff --git a/functions/trsp_via_vertices.py b/functions/trsp_via_vertices.py index 7e1ea7f..9a4f88e 100755 --- a/functions/trsp_via_vertices.py +++ b/functions/trsp_via_vertices.py @@ -1,5 +1,5 @@ from __future__ import absolute_import -from qgis.PyQt4.QtCore import * +from qgis.PyQt.QtCore import * from builtins import str from qgis.PyQt.QtGui import QColor from qgis.core import Qgis, QgsGeometry diff --git a/functions/tsp_euclid.py b/functions/tsp_euclid.py index ae0597c..b762eb9 100755 --- a/functions/tsp_euclid.py +++ b/functions/tsp_euclid.py @@ -1,9 +1,9 @@ from __future__ import absolute_import from builtins import str -from qgis.PyQt.QtCore import * +from qgis.PyQt.QtCore import QPointF, QSizeF from qgis.PyQt.QtGui import QTextDocument -from qgis.core import QgsGeometry, Qgis -from qgis.gui import QgsTextAnnotation +from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation +from qgis.gui import * import psycopg2 from .. import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index ef3aabd..f0ee3e5 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -25,8 +25,8 @@ from builtins import object from qgis.PyQt import uic from qgis.PyQt.QtCore import Qt, QObject, pyqtSignal, QRegExp, QSettings -from qgis.PyQt.QtGui import QColor, QIcon, QIntValidator, QDoubleValidator -from qgis.PyQt.QtWidgets import QAction,QDockWidget,QApplication,QLabel,QLineEdit,QPushButton,QWidget +from qgis.PyQt.QtGui import QColor, QIcon, QIntValidator, QDoubleValidator,QRegExpValidator +from qgis.PyQt.QtWidgets import QAction, QDockWidget, QApplication, QLabel, QLineEdit, QPushButton, QWidget,QGridLayout,QToolButton,QVBoxLayout,QHBoxLayout,QSplitter,QGroupBox,QScrollArea,QPlainTextEdit from qgis.core import QgsMessageLog,Qgis from qgis.gui import QgsVertexMarker,QgsRubberBand,QgsMapToolEmitPoint from . import dbConnection @@ -47,10 +47,10 @@ class PgRoutingLayer(object): 'astar', 'drivingDistance', 'alphashape', + 'tsp_euclid', 'bdDijkstra', 'bdAstar', 'dijkstraCost', - 'tsp_euclid', 'kdijkstra_cost', 'trsp_edge', 'kdijkstra_path', @@ -92,6 +92,7 @@ class PgRoutingLayer(object): FRACTION_DECIMAL_PLACES = 2 version = 3.0 functions = {} + def __init__(self, iface): # Save reference to the QGIS interface @@ -190,7 +191,6 @@ def initGui(self): # import the function exec("from pgRoutingLayer.functions import %s as function" % funcfname, globals(),globals()) funcname = function.Function.getName() - print(funcname) self.functions[funcname] = function.Function(self.dock) self.dock.comboBoxFunction.addItem(funcname) @@ -303,7 +303,8 @@ def loadFunctionsForVersion(self): def updateFunctionEnabled(self, text): - + # for testing //TODO remove text ='dijkstra' + text='dijkstra' function = self.functions.get(str(text)) self.toggleSelectButton(None) From 242be1c176ad5d7e05f7e9cdb379499197ae4446 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Thu, 28 Jun 2018 01:38:18 +0200 Subject: [PATCH 08/32] fixed several migrations bugs: import statements, indentation, etc. --- connectors/postgis.py | 1606 ++++++++++++++++++++--------------------- dbConnection.py | 262 +++---- pgRoutingLayer.py | 6 +- 3 files changed, 937 insertions(+), 937 deletions(-) diff --git a/connectors/postgis.py b/connectors/postgis.py index 1b9c9e9..c715a4f 100755 --- a/connectors/postgis.py +++ b/connectors/postgis.py @@ -38,831 +38,831 @@ class TableAttribute(DbConn.TableAttribute): - def __init__(self, row): - self.num, self.name, self.data_type, self.char_max_len, self.modifier, self.notnull, self.hasdefault, self.default = row + def __init__(self, row): + self.num, self.name, self.data_type, self.char_max_len, self.modifier, self.notnull, self.hasdefault, self.default = row class TableConstraint(DbConn.TableConstraint): - """ class that represents a constraint of a table (relation) """ - - def __init__(self, row): - self.name, con_type, self.is_defferable, self.is_deffered, keys = row[:5] - self.keys = list(map(int, keys.split(' '))) - self.con_type = TableConstraint.types[con_type] # convert to enum - if self.con_type == TableConstraint.TypeCheck: - self.check_src = row[5] - elif self.con_type == TableConstraint.TypeForeignKey: - self.foreign_table = row[6] - self.foreign_on_update = TableConstraint.on_action[row[7]] - self.foreign_on_delete = TableConstraint.on_action[row[8]] - self.foreign_match_type = TableConstraint.match_types[row[9]] - self.foreign_keys = row[10] + """ class that represents a constraint of a table (relation) """ + + def __init__(self, row): + self.name, con_type, self.is_defferable, self.is_deffered, keys = row[:5] + self.keys = list(map(int, keys.split(' '))) + self.con_type = TableConstraint.types[con_type] # convert to enum + if self.con_type == TableConstraint.TypeCheck: + self.check_src = row[5] + elif self.con_type == TableConstraint.TypeForeignKey: + self.foreign_table = row[6] + self.foreign_on_update = TableConstraint.on_action[row[7]] + self.foreign_on_delete = TableConstraint.on_action[row[8]] + self.foreign_match_type = TableConstraint.match_types[row[9]] + self.foreign_keys = row[10] class TableIndex(DbConn.TableIndex): - def __init__(self, row): - self.name, columns = row - self.columns = list(map(int, columns.split(' '))) + def __init__(self, row): + self.name, columns = row + self.columns = list(map(int, columns.split(' '))) class TableTrigger(DbConn.TableTrigger): - def __init__(self, row): - self.name, self.function, self.type, self.enabled = row + def __init__(self, row): + self.name, self.function, self.type, self.enabled = row class TableRule(DbConn.TableRule): - def __init__(self, row): - self.name, self.definition = row + def __init__(self, row): + self.name, self.definition = row class DbError(DbConn.DbError): - def __init__(self, error, query=None): - # save error. funny that the variables are in utf8, not - msg = str( error.args[0], 'utf-8') - if query == None: - if hasattr(error, "cursor") and hasattr(error.cursor, "query"): - query = str(error.cursor.query, 'utf-8') - else: - query = str(query) - DbConn.DbError.__init__(self, msg, query) - + def __init__(self, error, query=None): + # save error. funny that the variables are in utf8, not + msg = str( error.args[0], 'utf-8') + if query == None: + if hasattr(error, "cursor") and hasattr(error.cursor, "query"): + query = str(error.cursor.query, 'utf-8') + else: + query = str(query) + DbConn.DbError.__init__(self, msg, query) + class TableField(DbConn.TableField): - def __init__(self, name, data_type, is_null=None, default=None, modifier=None): - self.name, self.data_type, self.is_null, self.default, self.modifier = name, data_type, is_null, default, modifier - + def __init__(self, name, data_type, is_null=None, default=None, modifier=None): + self.name, self.data_type, self.is_null, self.default, self.modifier = name, data_type, is_null, default, modifier + class Connection(DbConn.Connection): - @classmethod - def getTypeName(self): - return 'postgis' - - @classmethod - def getTypeNameString(self): - return 'PostgreSQL' - - @classmethod - def getProviderName(self): - return 'postgres' - - @classmethod - def getSettingsKey(self): - return 'PostgreSQL' - - @classmethod - def icon(self): - return QIcon(":/icons/postgis_elephant.png") - - @classmethod - def connect(self, selected, parent=None): - settings = QSettings() - settings.beginGroup( u"/%s/connections/%s" % (self.getSettingsKey(), selected) ) - - if not settings.contains( "database" ): # non-existent entry? - raise DbError( 'there is no defined database connection "%s".' % selected ) - - get_value_str = lambda x: str(settings.value(x) if Utils.isSIPv2() else settings.value(x).toString()) - service, host, port, database, username, password = list(map(get_value_str, ["service", "host", "port", "database", "username", "password"])) - - # qgis1.5 use 'savePassword' instead of 'save' setting - isSave = settings.value("save") if Utils.isSIPv2() else settings.value("save").toBool() - isSavePassword = settings.value("savePassword") if Utils.isSIPv2() else settings.value("savePassword").toBool() - if not ( isSave or isSavePassword ): - (password, ok) = QInputDialog.getText(parent, "Enter password", 'Enter password for connection "%s":' % selected, QLineEdit.Password) - if not ok: return - - settings.endGroup() - - uri = qgis.core.QgsDataSourceUri() - if service: - uri.setConnection(service, database, username, password) - else: - uri.setConnection(host, port, database, username, password) - - return Connection(uri) - - - def __init__(self, uri): - DbConn.Connection.__init__(self, uri) - - self.service = uri.service() - self.host = uri.host() - self.port = uri.port() - self.dbname = uri.database() - self.user = uri.username() - self.passwd = uri.password() - - try: - self.con = psycopg2.connect(self.con_info()) - except psycopg2.OperationalError as e: - raise DbError(e) - - if not self.dbname: - self.dbname = self.get_dbname() - - self.has_spatial = self.check_spatial() - - self.check_geometry_columns_table() - - # a counter to ensure that the cursor will be unique - self.last_cursor_id = 0 - - def con_info(self): - con_str = '' - if self.service: con_str += "service='%s' " % self.service - if self.host: con_str += "host='%s' " % self.host - if self.port: con_str += "port=%s " % self.port - if self.dbname: con_str += "dbname='%s' " % self.dbname - if self.user: con_str += "user='%s' " % self.user - if self.passwd: con_str += "password='%s' " % self.passwd - return con_str - - def get_dbname(self): - c = self.con.cursor() - self._exec_sql(c, "SELECT current_database()") - return c.fetchone()[0] - - def get_info(self): - c = self.con.cursor() - self._exec_sql(c, "SELECT version()") - return c.fetchone()[0] - - def check_spatial(self): - """ check whether postgis_version is present in catalog """ - c = self.con.cursor() - self._exec_sql(c, "SELECT COUNT(*) FROM pg_proc WHERE proname = 'postgis_version'") - return (c.fetchone()[0] > 0) - - def get_spatial_info(self): - """ returns tuple about postgis support: - - lib version - - installed scripts version - - released scripts version - - geos version - - proj version - - whether uses stats - """ - c = self.con.cursor() - self._exec_sql(c, "SELECT postgis_lib_version(), postgis_scripts_installed(), postgis_scripts_released(), postgis_geos_version(), postgis_proj_version(), postgis_uses_stats()") - return c.fetchone() - - def check_geometry_columns_table(self): - - c = self.con.cursor() - self._exec_sql(c, "SELECT relname FROM pg_class WHERE relname = 'geometry_columns' AND pg_class.relkind IN ('v', 'r')") - self.has_geometry_columns = (len(c.fetchall()) != 0) - - if not self.has_geometry_columns: - self.has_geometry_columns_access = False - return - - # find out whether has privileges to access geometry_columns table - self.has_geometry_columns_access = self.get_table_privileges('geometry_columns')[0] - - - def list_schemas(self): - """ - get list of schemas in tuples: (oid, name, owner, perms) - """ - c = self.con.cursor() - sql = "SELECT oid, nspname, pg_get_userbyid(nspowner), nspacl FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname != 'information_schema'" - self._exec_sql(c, sql) - - schema_cmp = lambda x,y: -1 if x[1] < y[1] else 1 - - return sorted(c.fetchall(), cmp=schema_cmp) - - def list_geotables(self, schema=None): - """ - get list of tables with schemas, whether user has privileges, whether table has geometry column(s) etc. - - geometry_columns: - - f_table_schema - - f_table_name - - f_geometry_column - - coord_dimension - - srid - - type - """ - c = self.con.cursor() - - if schema: - schema_where = " AND nspname = '%s' " % self._quote_str(schema) - else: - schema_where = " AND (nspname != 'information_schema' AND nspname !~ 'pg_') " - - # LEFT OUTER JOIN: like LEFT JOIN but if there are more matches, for join, all are used (not only one) - - # first find out whether postgis is enabled - if not self.has_spatial: - # get all tables and views - sql = """SELECT pg_class.relname, pg_namespace.nspname, pg_class.relkind, pg_get_userbyid(relowner), reltuples, relpages, NULL, NULL, NULL, NULL - FROM pg_class - JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace - WHERE pg_class.relkind IN ('v', 'r')""" + schema_where + "ORDER BY nspname, relname" - else: - # discovery of all tables and whether they contain a geometry column - sql = """SELECT pg_class.relname, pg_namespace.nspname, pg_class.relkind, pg_get_userbyid(relowner), reltuples, relpages, pg_attribute.attname, pg_attribute.atttypid::regtype, NULL, NULL - FROM pg_class - JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace - LEFT OUTER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid AND - ( pg_attribute.atttypid = 'geometry'::regtype - OR pg_attribute.atttypid IN (SELECT oid FROM pg_type WHERE typbasetype='geometry'::regtype ) ) - WHERE pg_class.relkind IN ('v', 'r')""" + schema_where + "ORDER BY nspname, relname, attname" - - self._exec_sql(c, sql) - items = c.fetchall() - - # get geometry info from geometry_columns if exists - if self.has_spatial and self.has_geometry_columns and self.has_geometry_columns_access: - sql = """SELECT relname, nspname, relkind, pg_get_userbyid(relowner), reltuples, relpages, - geometry_columns.f_geometry_column, geometry_columns.type, geometry_columns.coord_dimension, geometry_columns.srid - FROM pg_class - JOIN pg_namespace ON relnamespace=pg_namespace.oid - LEFT OUTER JOIN geometry_columns ON relname=f_table_name AND nspname=f_table_schema - WHERE (relkind = 'r' or relkind='v') """ + schema_where + "ORDER BY nspname, relname, f_geometry_column" - self._exec_sql(c, sql) - - # merge geometry info to "items" - for i, geo_item in enumerate(c.fetchall()): - if geo_item[7]: - items[i] = geo_item - - return items - - - def get_table_rows(self, table, schema=None): - c = self.con.cursor() - self._exec_sql(c, "SELECT COUNT(*) FROM %s" % self._table_name(schema, table)) - return c.fetchone()[0] - - - def get_table_fields(self, table, schema=None): - """ return list of columns in table """ - c = self.con.cursor() - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """SELECT a.attnum AS ordinal_position, - a.attname AS column_name, - t.typname AS data_type, - a.attlen AS char_max_len, - a.atttypmod AS modifier, - a.attnotnull AS notnull, - a.atthasdef AS hasdefault, - adef.adsrc AS default_value - FROM pg_class c - JOIN pg_attribute a ON a.attrelid = c.oid - JOIN pg_type t ON a.atttypid = t.oid - JOIN pg_namespace nsp ON c.relnamespace = nsp.oid - LEFT JOIN pg_attrdef adef ON adef.adrelid = a.attrelid AND adef.adnum = a.attnum - WHERE - c.relname = '%s' %s AND - a.attnum > 0 - ORDER BY a.attnum""" % (self._quote_str(table), schema_where) - - self._exec_sql(c, sql) - attrs = [] - for row in c.fetchall(): - attrs.append(TableAttribute(row)) - return attrs - - - def get_table_indexes(self, table, schema=None): - """ get info about table's indexes. ignore primary key and unique constraint index, they get listed in constaints """ - c = self.con.cursor() - - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """SELECT relname, indkey FROM pg_class, pg_index - WHERE pg_class.oid = pg_index.indexrelid AND pg_class.oid IN ( - SELECT indexrelid FROM pg_index, pg_class - JOIN pg_namespace nsp ON pg_class.relnamespace = nsp.oid - WHERE pg_class.relname='%s' %s AND pg_class.oid=pg_index.indrelid - AND indisprimary != 't' )""" % (self._quote_str(table), schema_where) # AND indisunique != 't' - self._exec_sql(c, sql) - indexes = [] - for row in c.fetchall(): - indexes.append(TableIndex(row)) - return indexes - - - def get_table_unique_indexes(self, table, schema=None): - """ get all the unique indexes """ - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """SELECT relname, indkey - FROM pg_index JOIN pg_class ON pg_index.indrelid=pg_class.oid - JOIN pg_namespace nsp ON pg_class.relnamespace = nsp.oid - WHERE pg_class.relname='%s' %s - AND indisprimary != 't' AND indisunique = 't'""" % (self._quote_str(table), schema_where) - c = self.con.cursor() - self._exec_sql(c, sql) - uniqueIndexes = [] - for row in c.fetchall(): - uniqueIndexes.append(TableIndex(row)) - return uniqueIndexes - - - def get_table_constraints(self, table, schema=None): - c = self.con.cursor() - - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """SELECT c.conname, c.contype, c.condeferrable, c.condeferred, array_to_string(c.conkey, ' '), c.consrc, - t2.relname, c.confupdtype, c.confdeltype, c.confmatchtype, array_to_string(c.confkey, ' ') FROM pg_constraint c - LEFT JOIN pg_class t ON c.conrelid = t.oid - LEFT JOIN pg_class t2 ON c.confrelid = t2.oid - JOIN pg_namespace nsp ON t.relnamespace = nsp.oid - WHERE t.relname = '%s' %s """ % (self._quote_str(table), schema_where) - - self._exec_sql(c, sql) - - constrs = [] - for row in c.fetchall(): - constrs.append(TableConstraint(row)) - return constrs - - - def get_table_triggers(self, table, schema=None): - c = self.con.cursor() - - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """ SELECT tgname, proname, tgtype, tgenabled FROM pg_trigger trig - LEFT JOIN pg_class t ON trig.tgrelid = t.oid - LEFT JOIN pg_proc p ON trig.tgfoid = p.oid - JOIN pg_namespace nsp ON t.relnamespace = nsp.oid - WHERE t.relname ='%s' %s """ % (self._quote_str(table), schema_where) - - self._exec_sql(c, sql) - - triggers = [] - for row in c.fetchall(): - triggers.append(TableTrigger(row)) - return triggers - - - def get_table_rules(self, table, schema=None): - c = self.con.cursor() - - schema_where = " AND schemaname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """ SELECT rulename, definition FROM pg_rules - WHERE tablename='%s' %s """ % (self._quote_str(table), schema_where) - - self._exec_sql(c, sql) - - rules = [] - for row in c.fetchall(): - rules.append(TableRule(row)) - - return rules - - def get_table_estimated_extent(self, geom, table, schema=None): - """ find out estimated extent (from the statistics) """ - c = self.con.cursor() - - extent = "estimated_extent('%s','%s','%s')" % (self._quote_str(schema), self._quote_str(table), self._quote_str(geom)) - sql = """ SELECT xmin(%(ext)s), ymin(%(ext)s), xmax(%(ext)s), ymax(%(ext)s) """ % { 'ext' : extent } - self._exec_sql(c, sql) - - row = c.fetchone() - return row - - def get_view_definition(self, view, schema=None): - """ returns definition of the view """ - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """SELECT pg_get_viewdef(c.oid) FROM pg_class c - JOIN pg_namespace nsp ON c.relnamespace = nsp.oid - WHERE relname='%s' %s AND relkind='v'""" % (self._quote_str(view), schema_where) - c = self.con.cursor() - self._exec_sql(c, sql) - return c.fetchone()[0] - - """ - def list_tables(self): - c = self.con.cursor() - c.execute("SELECT relname FROM pg_class WHERE relname !~ '^(pg_|sql_)' AND relkind = 'r'") - return c.fetchall() - """ - - def add_geometry_column(self, table, geom_type, schema=None, geom_column='the_geom', srid=-1, dim=2): - - # use schema if explicitly specified - if schema: - schema_part = "'%s', " % self._quote_str(schema) - else: - schema_part = "" - sql = "SELECT AddGeometryColumn(%s'%s', '%s', %d, '%s', %d)" % (schema_part, self._quote_str(table), self._quote_str(geom_column), srid, self._quote_str(geom_type), dim) - self._exec_sql_and_commit(sql) - - def delete_geometry_column(self, table, geom_column, schema=None): - """ use postgis function to delete geometry column correctly """ - if schema: - schema_part = "'%s', " % self._quote_str(schema) - else: - schema_part = "" - sql = "SELECT DropGeometryColumn(%s'%s', '%s')" % (schema_part, self._quote_str(table), self._quote_str(geom_column)) - self._exec_sql_and_commit(sql) - - def delete_geometry_table(self, table, schema=None): - """ delete table with one or more geometries using postgis function """ - if schema: - schema_part = "'%s', " % self._quote_str(schema) - else: - schema_part = "" - sql = "SELECT DropGeometryTable(%s'%s')" % (schema_part, self._quote_str(table)) - self._exec_sql_and_commit(sql) - - def create_table(self, table, fields, pkey=None, schema=None): - """ create ordinary table - 'fields' is array containing instances of TableField - 'pkey' contains name of column to be used as primary key - """ - - if len(fields) == 0: - return False - - table_name = self._table_name(schema, table) - - sql = "CREATE TABLE %s (%s" % (table_name, fields[0].field_def(self)) - for field in fields[1:]: - sql += ", %s" % field.field_def(self) - if pkey: - sql += ", PRIMARY KEY (%s)" % self._quote(pkey) - sql += ")" - self._exec_sql_and_commit(sql) - return True - - def delete_table(self, table, schema=None): - """ delete table from the database """ - table_name = self._table_name(schema, table) - sql = "DROP TABLE %s" % table_name - self._exec_sql_and_commit(sql) - - def empty_table(self, table, schema=None): - """ delete all rows from table """ - table_name = self._table_name(schema, table) - sql = "TRUNCATE %s" % table_name - self._exec_sql_and_commit(sql) - - def rename_table(self, table, new_table, schema=None): - """ rename a table in database """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s RENAME TO %s" % (table_name, self._quote(new_table)) - self._exec_sql_and_commit(sql) - - # update geometry_columns if postgis is enabled - if self.has_spatial and self.has_geometry_columns and self.has_geometry_columns_access: - sql = "UPDATE geometry_columns SET f_table_name='%s' WHERE f_table_name='%s'" % (self._quote_str(new_table), self._quote_str(table)) - if schema is not None: - sql += " AND f_table_schema='%s'" % self._quote_str(schema) - self._exec_sql_and_commit(sql) - - def create_view(self, name, query, schema=None): - view_name = self._table_name(schema, name) - sql = "CREATE VIEW %s AS %s" % (view_name, query) - self._exec_sql_and_commit(sql) - - def delete_view(self, name, schema=None): - view_name = self._table_name(schema, name) - sql = "DROP VIEW %s" % view_name - self._exec_sql_and_commit(sql) - - def rename_view(self, name, new_name, schema=None): - """ rename view in database """ - self.rename_table(name, new_name, schema) - - def create_schema(self, schema): - """ create a new empty schema in database """ - sql = "CREATE SCHEMA %s" % self._quote(schema) - self._exec_sql_and_commit(sql) - - def delete_schema(self, schema): - """ drop (empty) schema from database """ - sql = "DROP SCHEMA %s" % self._quote(schema) - self._exec_sql_and_commit(sql) - - def rename_schema(self, schema, new_schema): - """ rename a schema in database """ - sql = "ALTER SCHEMA %s RENAME TO %s" % (self._quote(schema), self._quote(new_schema)) - self._exec_sql_and_commit(sql) - - # update geometry_columns if postgis is enabled - if self.has_spatial: - sql = "UPDATE geometry_columns SET f_table_schema='%s' WHERE f_table_schema='%s'" % (self._quote_str(new_schema), self._quote_str(schema)) - self._exec_sql_and_commit(sql) - - def table_add_column(self, table, field, schema=None): - """ add a column to table (passed as TableField instance) """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s ADD %s" % (table_name, field.field_def(self)) - self._exec_sql_and_commit(sql) - - def table_delete_column(self, table, field, schema=None): - """ delete column from a table """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s DROP %s" % (table_name, self._quote(field)) - self._exec_sql_and_commit(sql) - - def table_column_rename(self, table, name, new_name, schema=None): - """ rename column in a table """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s RENAME %s TO %s" % (table_name, self._quote(name), self._quote(new_name)) - self._exec_sql_and_commit(sql) - - # update geometry_columns if postgis is enabled - if self.has_spatial: - sql = "UPDATE geometry_columns SET f_geometry_column='%s' WHERE f_geometry_column='%s' AND f_table_name='%s'" % (self._quote_str(new_name), self._quote_str(name), self._quote_str(table)) - if schema is not None: - sql += " AND f_table_schema='%s'" % self._quote(schema) - self._exec_sql_and_commit(sql) - - def table_column_set_type(self, table, column, data_type, schema=None): - """ change column type """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s ALTER %s TYPE %s" % (table_name, self._quote(column), data_type) - self._exec_sql_and_commit(sql) - - def table_column_set_default(self, table, column, default, schema=None): - """ change column's default value. If default=None drop default value """ - table_name = self._table_name(schema, table) - if default: - sql = "ALTER TABLE %s ALTER %s SET DEFAULT %s" % (table_name, self._quote(column), default) - else: - sql = "ALTER TABLE %s ALTER %s DROP DEFAULT" % (table_name, self._quote(column)) - self._exec_sql_and_commit(sql) - - def table_column_set_null(self, table, column, is_null, schema=None): - """ change whether column can contain null values """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s ALTER %s " % (table_name, self._quote(column)) - if is_null: - sql += "DROP NOT NULL" - else: - sql += "SET NOT NULL" - self._exec_sql_and_commit(sql) - - def table_add_primary_key(self, table, column, schema=None): - """ add a primery key (with one column) to a table """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s ADD PRIMARY KEY (%s)" % (table_name, self._quote(column)) - self._exec_sql_and_commit(sql) - - def table_add_unique_constraint(self, table, column, schema=None): - """ add a unique constraint to a table """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s ADD UNIQUE (%s)" % (table_name, self._quote(column)) - self._exec_sql_and_commit(sql) - - def table_delete_constraint(self, table, constraint, schema=None): - """ delete constraint in a table """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s DROP CONSTRAINT %s" % (table_name, self._quote(constraint)) - self._exec_sql_and_commit(sql) - - def table_move_to_schema(self, table, new_schema, schema=None): - if new_schema == schema: - return - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s SET SCHEMA %s" % (table_name, self._quote(new_schema)) - self._exec_sql_and_commit(sql) - - # update geometry_columns if postgis is enabled - if self.has_spatial: - sql = "UPDATE geometry_columns SET f_table_schema='%s' WHERE f_table_name='%s'" % (self._quote_str(new_schema), self._quote_str(table)) - if schema is not None: - sql += " AND f_table_schema='%s'" % self._quote_str(schema) - self._exec_sql_and_commit(sql) - - def table_apply_function(self, schema, table, res_column, fct, param): - """ apply a function to a column and save the result in other column """ - table = self._table_name(schema, table) - sql = "UPDATE %s SET %s = %s(%s)" % (table, self._quote(res_column), fct, self._quote(param)) - self._exec_sql_and_commit(sql) - - def table_enable_triggers(self, table, schema, enable=True): - """ enable or disable all triggers on table """ - table = self._table_name(schema, table) - sql = "ALTER TABLE %s %s TRIGGER ALL" % (table, "ENABLE" if enable else "DISABLE") - self._exec_sql_and_commit(sql) - - def table_enable_trigger(self, table, schema, trigger, enable=True): - """ enable or disable one trigger on table """ - table = self._table_name(schema, table) - sql = "ALTER TABLE %s %s TRIGGER %s" % (table, "ENABLE" if enable else "DISABLE", self._quote(trigger)) - self._exec_sql_and_commit(sql) - - def table_delete_trigger(self, table, schema, trigger): - """ delete trigger on table """ - table = self._table_name(schema, table) - sql = "DROP TRIGGER %s ON %s" % (self._quote(trigger), table) - self._exec_sql_and_commit(sql) - - def table_delete_rule(self, table, schema, rule): - """ delete rule on table """ - table = self._table_name(schema, table) - sql = "DROP RULE %s ON %s" % (self._quote(rule), table) - self._exec_sql_and_commit(sql) - - def create_index(self, table, name, column, schema=None): - """ create index on one column using default options """ - table_name = self._table_name(schema, table) - idx_name = self._quote(name) - sql = "CREATE INDEX %s ON %s (%s)" % (idx_name, table_name, self._quote(column)) - self._exec_sql_and_commit(sql) - - def create_spatial_index(self, table, schema=None, geom_column='the_geom'): - table_name = self._table_name(schema, table) - idx_name = self._quote("sidx_"+table) - sql = "CREATE INDEX %s ON %s USING GIST(%s GIST_GEOMETRY_OPS)" % (idx_name, table_name, self._quote(geom_column)) - self._exec_sql_and_commit(sql) - - def delete_index(self, name, schema=None): - index_name = self._table_name(schema, name) - sql = "DROP INDEX %s" % index_name - self._exec_sql_and_commit(sql) - - def get_database_privileges(self): - """ db privileges: (can create schemas, can create temp. tables) """ - sql = "SELECT has_database_privilege('%(d)s', 'CREATE'), has_database_privilege('%(d)s', 'TEMP')" % { 'd' : self._quote_str(self.dbname) } - c = self.con.cursor() - self._exec_sql(c, sql) - return c.fetchone() - - def get_schema_privileges(self, schema): - """ schema privileges: (can create new objects, can access objects in schema) """ - sql = "SELECT has_schema_privilege('%(s)s', 'CREATE'), has_schema_privilege('%(s)s', 'USAGE')" % { 's' : self._quote_str(schema) } - c = self.con.cursor() - self._exec_sql(c, sql) - return c.fetchone() - - def get_table_privileges(self, table, schema=None): - """ table privileges: (select, insert, update, delete) """ - t = self._table_name(schema, table) - sql = """SELECT has_table_privilege('%(t)s', 'SELECT'), has_table_privilege('%(t)s', 'INSERT'), - has_table_privilege('%(t)s', 'UPDATE'), has_table_privilege('%(t)s', 'DELETE')""" % { 't': self._quote_str(t) } - c = self.con.cursor() - self._exec_sql(c, sql) - return c.fetchone() - - def vacuum_analyze(self, table, schema=None): - """ run vacuum analyze on a table """ - t = self._table_name(schema, table) - # vacuum analyze must be run outside transaction block - we have to change isolation level - self.con.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) - c = self.con.cursor() - self._exec_sql(c, "VACUUM ANALYZE %s" % t) - self.con.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) - - def sr_info_for_srid(self, srid): - if not self.has_spatial: - return "Unknown" - - try: - c = self.con.cursor() - self._exec_sql(c, "SELECT srtext FROM spatial_ref_sys WHERE srid = '%d'" % srid) - sr = c.fetchone() - if sr is None: - return "Unknown" - srtext = sr[0] - # try to extract just SR name (should be qouted in double quotes) - x = re.search('"([^"]+)"', srtext) - if x is not None: - srtext = x.group() - return srtext - except DbError as e: - return "Unknown" - - def insert_table_row(self, table, values, schema=None, cursor=None): - """ insert a row with specified values to a table. - if a cursor is specified, it doesn't commit (expecting that there will be more inserts) - otherwise it commits immediately """ - t = self._table_name(schema, table) - sql = "" - for value in values: - # TODO: quote values? - if sql: sql += ", " - sql += value - sql = "INSERT INTO %s VALUES (%s)" % (t, sql) - if cursor: - self._exec_sql(cursor, sql) - else: - self._exec_sql_and_commit(sql) - - - def table_add_function_trigger(self, schema, table, resColumn, fct, geomColumn): - """ add a trigger on insert and update that recalculates the value from geometry column """ - - trig_f_name = "%s_calc_%s" % (table, fct) - trig_name = "calc_%s" % fct - ctx = { 'fname' : trig_f_name, 'tname' : trig_name, - 'res' : resColumn, 'geom' : geomColumn, - 'f' : fct, 'table' : self._table_name(schema, table) } - sql = """ - CREATE OR REPLACE FUNCTION %(fname)s() RETURNS TRIGGER AS - $$ - BEGIN - IF (TG_OP = 'INSERT') THEN - NEW.%(res)s := %(f)s(NEW.%(geom)s); - ELSIF (TG_OP = 'UPDATE') THEN - IF NOT (NEW.%(geom)s ~= OLD.%(geom)s) THEN - NEW.%(res)s := %(f)s(NEW.%(geom)s); - END IF; - END IF; - RETURN NEW; - END; - $$ - LANGUAGE 'plpgsql'; - - CREATE TRIGGER %(tname)s BEFORE INSERT OR UPDATE ON %(table)s FOR EACH ROW - EXECUTE PROCEDURE %(fname)s(); - """ % ctx - - self._exec_sql_and_commit(sql) - - - def get_named_cursor(self, table=None): - """ return an unique named cursor, optionally including a table name """ - self.last_cursor_id += 1 - if table is not None: - table2 = re.sub(r'\W', '_', table.encode('ascii','replace')) # all non-alphanum characters to underscore - cur_name = "cursor_%d_table_%s" % (self.last_cursor_id, table2) - else: - cur_name = "cursor_%d" % self.last_cursor_id - #cur_name = ("\"db_table_"+self.table+"\"").replace(' ', '_') - #cur_name = cur_name.encode('ascii','replace').replace('?', '_') - return self.con.cursor(cur_name) - - def _exec_sql(self, cursor, sql): - try: - cursor.execute(sql) - except psycopg2.Error as e: - # do the rollback to avoid a "current transaction aborted, commands ignored" errors - self.con.rollback() - raise DbError(e) - - def _exec_sql_and_commit(self, sql): - """ tries to execute and commit some action, on error it rolls back the change """ - #try: - c = self.con.cursor() - self._exec_sql(c, sql) - self.con.commit() - #except DbError, e: - # self.con.rollback() - # raise - - def _quote(self, identifier): - identifier = str(identifier) # make sure it's python unicode string - return u'"%s"' % identifier.replace('"', '""') - - def _quote_str(self, txt): - """ make the string safe - replace ' with '' """ - txt = str(txt) # make sure it's python unicode string - return txt.replace("'", "''") - - def _table_name(self, schema, table): - if not schema: - return self._quote(table) - else: - return u"%s.%s" % (self._quote(schema), self._quote(table)) - + @classmethod + def getTypeName(self): + return 'postgis' + + @classmethod + def getTypeNameString(self): + return 'PostgreSQL' + + @classmethod + def getProviderName(self): + return 'postgres' + + @classmethod + def getSettingsKey(self): + return 'PostgreSQL' + + @classmethod + def icon(self): + return QIcon(":/icons/postgis_elephant.png") + + @classmethod + def connect(self, selected, parent=None): + settings = QSettings() + settings.beginGroup( u"/%s/connections/%s" % (self.getSettingsKey(), selected) ) + + if not settings.contains( "database" ): # non-existent entry? + raise DbError( 'there is no defined database connection "%s".' % selected ) + + get_value_str = lambda x: str(settings.value(x) if Utils.isSIPv2() else settings.value(x).toString()) + service, host, port, database, username, password = list(map(get_value_str, ["service", "host", "port", "database", "username", "password"])) + + # qgis1.5 use 'savePassword' instead of 'save' setting + isSave = settings.value("save") if Utils.isSIPv2() else settings.value("save").toBool() + isSavePassword = settings.value("savePassword") if Utils.isSIPv2() else settings.value("savePassword").toBool() + if not ( isSave or isSavePassword ): + (password, ok) = QInputDialog.getText(parent, "Enter password", 'Enter password for connection "%s":' % selected, QLineEdit.Password) + if not ok: return + + settings.endGroup() + + uri = QgsDataSourceUri() + if service: + uri.setConnection(service, database, username, password) + else: + uri.setConnection(host, port, database, username, password) + + return Connection(uri) + + + def __init__(self, uri): + DbConn.Connection.__init__(self, uri) + + self.service = uri.service() + self.host = uri.host() + self.port = uri.port() + self.dbname = uri.database() + self.user = uri.username() + self.passwd = uri.password() + + try: + self.con = psycopg2.connect(self.con_info()) + except psycopg2.OperationalError as e: + raise DbError(e) + + if not self.dbname: + self.dbname = self.get_dbname() + + self.has_spatial = self.check_spatial() + + self.check_geometry_columns_table() + + # a counter to ensure that the cursor will be unique + self.last_cursor_id = 0 + + def con_info(self): + con_str = '' + if self.service: con_str += "service='%s' " % self.service + if self.host: con_str += "host='%s' " % self.host + if self.port: con_str += "port=%s " % self.port + if self.dbname: con_str += "dbname='%s' " % self.dbname + if self.user: con_str += "user='%s' " % self.user + if self.passwd: con_str += "password='%s' " % self.passwd + return con_str + + def get_dbname(self): + c = self.con.cursor() + self._exec_sql(c, "SELECT current_database()") + return c.fetchone()[0] + + def get_info(self): + c = self.con.cursor() + self._exec_sql(c, "SELECT version()") + return c.fetchone()[0] + + def check_spatial(self): + """ check whether postgis_version is present in catalog """ + c = self.con.cursor() + self._exec_sql(c, "SELECT COUNT(*) FROM pg_proc WHERE proname = 'postgis_version'") + return (c.fetchone()[0] > 0) + + def get_spatial_info(self): + """ returns tuple about postgis support: + - lib version + - installed scripts version + - released scripts version + - geos version + - proj version + - whether uses stats + """ + c = self.con.cursor() + self._exec_sql(c, "SELECT postgis_lib_version(), postgis_scripts_installed(), postgis_scripts_released(), postgis_geos_version(), postgis_proj_version(), postgis_uses_stats()") + return c.fetchone() + + def check_geometry_columns_table(self): + + c = self.con.cursor() + self._exec_sql(c, "SELECT relname FROM pg_class WHERE relname = 'geometry_columns' AND pg_class.relkind IN ('v', 'r')") + self.has_geometry_columns = (len(c.fetchall()) != 0) + + if not self.has_geometry_columns: + self.has_geometry_columns_access = False + return + + # find out whether has privileges to access geometry_columns table + self.has_geometry_columns_access = self.get_table_privileges('geometry_columns')[0] + + + def list_schemas(self): + """ + get list of schemas in tuples: (oid, name, owner, perms) + """ + c = self.con.cursor() + sql = "SELECT oid, nspname, pg_get_userbyid(nspowner), nspacl FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname != 'information_schema'" + self._exec_sql(c, sql) + + schema_cmp = lambda x,y: -1 if x[1] < y[1] else 1 + + return sorted(c.fetchall(), cmp=schema_cmp) + + def list_geotables(self, schema=None): + """ + get list of tables with schemas, whether user has privileges, whether table has geometry column(s) etc. + + geometry_columns: + - f_table_schema + - f_table_name + - f_geometry_column + - coord_dimension + - srid + - type + """ + c = self.con.cursor() + + if schema: + schema_where = " AND nspname = '%s' " % self._quote_str(schema) + else: + schema_where = " AND (nspname != 'information_schema' AND nspname !~ 'pg_') " + + # LEFT OUTER JOIN: like LEFT JOIN but if there are more matches, for join, all are used (not only one) + + # first find out whether postgis is enabled + if not self.has_spatial: + # get all tables and views + sql = """SELECT pg_class.relname, pg_namespace.nspname, pg_class.relkind, pg_get_userbyid(relowner), reltuples, relpages, NULL, NULL, NULL, NULL + FROM pg_class + JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace + WHERE pg_class.relkind IN ('v', 'r')""" + schema_where + "ORDER BY nspname, relname" + else: + # discovery of all tables and whether they contain a geometry column + sql = """SELECT pg_class.relname, pg_namespace.nspname, pg_class.relkind, pg_get_userbyid(relowner), reltuples, relpages, pg_attribute.attname, pg_attribute.atttypid::regtype, NULL, NULL + FROM pg_class + JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace + LEFT OUTER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid AND + ( pg_attribute.atttypid = 'geometry'::regtype + OR pg_attribute.atttypid IN (SELECT oid FROM pg_type WHERE typbasetype='geometry'::regtype ) ) + WHERE pg_class.relkind IN ('v', 'r')""" + schema_where + "ORDER BY nspname, relname, attname" + + self._exec_sql(c, sql) + items = c.fetchall() + + # get geometry info from geometry_columns if exists + if self.has_spatial and self.has_geometry_columns and self.has_geometry_columns_access: + sql = """SELECT relname, nspname, relkind, pg_get_userbyid(relowner), reltuples, relpages, + geometry_columns.f_geometry_column, geometry_columns.type, geometry_columns.coord_dimension, geometry_columns.srid + FROM pg_class + JOIN pg_namespace ON relnamespace=pg_namespace.oid + LEFT OUTER JOIN geometry_columns ON relname=f_table_name AND nspname=f_table_schema + WHERE (relkind = 'r' or relkind='v') """ + schema_where + "ORDER BY nspname, relname, f_geometry_column" + self._exec_sql(c, sql) + + # merge geometry info to "items" + for i, geo_item in enumerate(c.fetchall()): + if geo_item[7]: + items[i] = geo_item + + return items + + + def get_table_rows(self, table, schema=None): + c = self.con.cursor() + self._exec_sql(c, "SELECT COUNT(*) FROM %s" % self._table_name(schema, table)) + return c.fetchone()[0] + + + def get_table_fields(self, table, schema=None): + """ return list of columns in table """ + c = self.con.cursor() + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """SELECT a.attnum AS ordinal_position, + a.attname AS column_name, + t.typname AS data_type, + a.attlen AS char_max_len, + a.atttypmod AS modifier, + a.attnotnull AS notnull, + a.atthasdef AS hasdefault, + adef.adsrc AS default_value + FROM pg_class c + JOIN pg_attribute a ON a.attrelid = c.oid + JOIN pg_type t ON a.atttypid = t.oid + JOIN pg_namespace nsp ON c.relnamespace = nsp.oid + LEFT JOIN pg_attrdef adef ON adef.adrelid = a.attrelid AND adef.adnum = a.attnum + WHERE + c.relname = '%s' %s AND + a.attnum > 0 + ORDER BY a.attnum""" % (self._quote_str(table), schema_where) + + self._exec_sql(c, sql) + attrs = [] + for row in c.fetchall(): + attrs.append(TableAttribute(row)) + return attrs + + + def get_table_indexes(self, table, schema=None): + """ get info about table's indexes. ignore primary key and unique constraint index, they get listed in constaints """ + c = self.con.cursor() + + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """SELECT relname, indkey FROM pg_class, pg_index + WHERE pg_class.oid = pg_index.indexrelid AND pg_class.oid IN ( + SELECT indexrelid FROM pg_index, pg_class + JOIN pg_namespace nsp ON pg_class.relnamespace = nsp.oid + WHERE pg_class.relname='%s' %s AND pg_class.oid=pg_index.indrelid + AND indisprimary != 't' )""" % (self._quote_str(table), schema_where) # AND indisunique != 't' + self._exec_sql(c, sql) + indexes = [] + for row in c.fetchall(): + indexes.append(TableIndex(row)) + return indexes + + + def get_table_unique_indexes(self, table, schema=None): + """ get all the unique indexes """ + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """SELECT relname, indkey + FROM pg_index JOIN pg_class ON pg_index.indrelid=pg_class.oid + JOIN pg_namespace nsp ON pg_class.relnamespace = nsp.oid + WHERE pg_class.relname='%s' %s + AND indisprimary != 't' AND indisunique = 't'""" % (self._quote_str(table), schema_where) + c = self.con.cursor() + self._exec_sql(c, sql) + uniqueIndexes = [] + for row in c.fetchall(): + uniqueIndexes.append(TableIndex(row)) + return uniqueIndexes + + + def get_table_constraints(self, table, schema=None): + c = self.con.cursor() + + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """SELECT c.conname, c.contype, c.condeferrable, c.condeferred, array_to_string(c.conkey, ' '), c.consrc, + t2.relname, c.confupdtype, c.confdeltype, c.confmatchtype, array_to_string(c.confkey, ' ') FROM pg_constraint c + LEFT JOIN pg_class t ON c.conrelid = t.oid + LEFT JOIN pg_class t2 ON c.confrelid = t2.oid + JOIN pg_namespace nsp ON t.relnamespace = nsp.oid + WHERE t.relname = '%s' %s """ % (self._quote_str(table), schema_where) + + self._exec_sql(c, sql) + + constrs = [] + for row in c.fetchall(): + constrs.append(TableConstraint(row)) + return constrs + + + def get_table_triggers(self, table, schema=None): + c = self.con.cursor() + + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """ SELECT tgname, proname, tgtype, tgenabled FROM pg_trigger trig + LEFT JOIN pg_class t ON trig.tgrelid = t.oid + LEFT JOIN pg_proc p ON trig.tgfoid = p.oid + JOIN pg_namespace nsp ON t.relnamespace = nsp.oid + WHERE t.relname ='%s' %s """ % (self._quote_str(table), schema_where) + + self._exec_sql(c, sql) + + triggers = [] + for row in c.fetchall(): + triggers.append(TableTrigger(row)) + return triggers + + + def get_table_rules(self, table, schema=None): + c = self.con.cursor() + + schema_where = " AND schemaname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """ SELECT rulename, definition FROM pg_rules + WHERE tablename='%s' %s """ % (self._quote_str(table), schema_where) + + self._exec_sql(c, sql) + + rules = [] + for row in c.fetchall(): + rules.append(TableRule(row)) + + return rules + + def get_table_estimated_extent(self, geom, table, schema=None): + """ find out estimated extent (from the statistics) """ + c = self.con.cursor() + + extent = "estimated_extent('%s','%s','%s')" % (self._quote_str(schema), self._quote_str(table), self._quote_str(geom)) + sql = """ SELECT xmin(%(ext)s), ymin(%(ext)s), xmax(%(ext)s), ymax(%(ext)s) """ % { 'ext' : extent } + self._exec_sql(c, sql) + + row = c.fetchone() + return row + + def get_view_definition(self, view, schema=None): + """ returns definition of the view """ + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """SELECT pg_get_viewdef(c.oid) FROM pg_class c + JOIN pg_namespace nsp ON c.relnamespace = nsp.oid + WHERE relname='%s' %s AND relkind='v'""" % (self._quote_str(view), schema_where) + c = self.con.cursor() + self._exec_sql(c, sql) + return c.fetchone()[0] + + """ + def list_tables(self): + c = self.con.cursor() + c.execute("SELECT relname FROM pg_class WHERE relname !~ '^(pg_|sql_)' AND relkind = 'r'") + return c.fetchall() + """ + + def add_geometry_column(self, table, geom_type, schema=None, geom_column='the_geom', srid=-1, dim=2): + + # use schema if explicitly specified + if schema: + schema_part = "'%s', " % self._quote_str(schema) + else: + schema_part = "" + sql = "SELECT AddGeometryColumn(%s'%s', '%s', %d, '%s', %d)" % (schema_part, self._quote_str(table), self._quote_str(geom_column), srid, self._quote_str(geom_type), dim) + self._exec_sql_and_commit(sql) + + def delete_geometry_column(self, table, geom_column, schema=None): + """ use postgis function to delete geometry column correctly """ + if schema: + schema_part = "'%s', " % self._quote_str(schema) + else: + schema_part = "" + sql = "SELECT DropGeometryColumn(%s'%s', '%s')" % (schema_part, self._quote_str(table), self._quote_str(geom_column)) + self._exec_sql_and_commit(sql) + + def delete_geometry_table(self, table, schema=None): + """ delete table with one or more geometries using postgis function """ + if schema: + schema_part = "'%s', " % self._quote_str(schema) + else: + schema_part = "" + sql = "SELECT DropGeometryTable(%s'%s')" % (schema_part, self._quote_str(table)) + self._exec_sql_and_commit(sql) + + def create_table(self, table, fields, pkey=None, schema=None): + """ create ordinary table + 'fields' is array containing instances of TableField + 'pkey' contains name of column to be used as primary key + """ + + if len(fields) == 0: + return False + + table_name = self._table_name(schema, table) + + sql = "CREATE TABLE %s (%s" % (table_name, fields[0].field_def(self)) + for field in fields[1:]: + sql += ", %s" % field.field_def(self) + if pkey: + sql += ", PRIMARY KEY (%s)" % self._quote(pkey) + sql += ")" + self._exec_sql_and_commit(sql) + return True + + def delete_table(self, table, schema=None): + """ delete table from the database """ + table_name = self._table_name(schema, table) + sql = "DROP TABLE %s" % table_name + self._exec_sql_and_commit(sql) + + def empty_table(self, table, schema=None): + """ delete all rows from table """ + table_name = self._table_name(schema, table) + sql = "TRUNCATE %s" % table_name + self._exec_sql_and_commit(sql) + + def rename_table(self, table, new_table, schema=None): + """ rename a table in database """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s RENAME TO %s" % (table_name, self._quote(new_table)) + self._exec_sql_and_commit(sql) + + # update geometry_columns if postgis is enabled + if self.has_spatial and self.has_geometry_columns and self.has_geometry_columns_access: + sql = "UPDATE geometry_columns SET f_table_name='%s' WHERE f_table_name='%s'" % (self._quote_str(new_table), self._quote_str(table)) + if schema is not None: + sql += " AND f_table_schema='%s'" % self._quote_str(schema) + self._exec_sql_and_commit(sql) + + def create_view(self, name, query, schema=None): + view_name = self._table_name(schema, name) + sql = "CREATE VIEW %s AS %s" % (view_name, query) + self._exec_sql_and_commit(sql) + + def delete_view(self, name, schema=None): + view_name = self._table_name(schema, name) + sql = "DROP VIEW %s" % view_name + self._exec_sql_and_commit(sql) + + def rename_view(self, name, new_name, schema=None): + """ rename view in database """ + self.rename_table(name, new_name, schema) + + def create_schema(self, schema): + """ create a new empty schema in database """ + sql = "CREATE SCHEMA %s" % self._quote(schema) + self._exec_sql_and_commit(sql) + + def delete_schema(self, schema): + """ drop (empty) schema from database """ + sql = "DROP SCHEMA %s" % self._quote(schema) + self._exec_sql_and_commit(sql) + + def rename_schema(self, schema, new_schema): + """ rename a schema in database """ + sql = "ALTER SCHEMA %s RENAME TO %s" % (self._quote(schema), self._quote(new_schema)) + self._exec_sql_and_commit(sql) + + # update geometry_columns if postgis is enabled + if self.has_spatial: + sql = "UPDATE geometry_columns SET f_table_schema='%s' WHERE f_table_schema='%s'" % (self._quote_str(new_schema), self._quote_str(schema)) + self._exec_sql_and_commit(sql) + + def table_add_column(self, table, field, schema=None): + """ add a column to table (passed as TableField instance) """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s ADD %s" % (table_name, field.field_def(self)) + self._exec_sql_and_commit(sql) + + def table_delete_column(self, table, field, schema=None): + """ delete column from a table """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s DROP %s" % (table_name, self._quote(field)) + self._exec_sql_and_commit(sql) + + def table_column_rename(self, table, name, new_name, schema=None): + """ rename column in a table """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s RENAME %s TO %s" % (table_name, self._quote(name), self._quote(new_name)) + self._exec_sql_and_commit(sql) + + # update geometry_columns if postgis is enabled + if self.has_spatial: + sql = "UPDATE geometry_columns SET f_geometry_column='%s' WHERE f_geometry_column='%s' AND f_table_name='%s'" % (self._quote_str(new_name), self._quote_str(name), self._quote_str(table)) + if schema is not None: + sql += " AND f_table_schema='%s'" % self._quote(schema) + self._exec_sql_and_commit(sql) + + def table_column_set_type(self, table, column, data_type, schema=None): + """ change column type """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s ALTER %s TYPE %s" % (table_name, self._quote(column), data_type) + self._exec_sql_and_commit(sql) + + def table_column_set_default(self, table, column, default, schema=None): + """ change column's default value. If default=None drop default value """ + table_name = self._table_name(schema, table) + if default: + sql = "ALTER TABLE %s ALTER %s SET DEFAULT %s" % (table_name, self._quote(column), default) + else: + sql = "ALTER TABLE %s ALTER %s DROP DEFAULT" % (table_name, self._quote(column)) + self._exec_sql_and_commit(sql) + + def table_column_set_null(self, table, column, is_null, schema=None): + """ change whether column can contain null values """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s ALTER %s " % (table_name, self._quote(column)) + if is_null: + sql += "DROP NOT NULL" + else: + sql += "SET NOT NULL" + self._exec_sql_and_commit(sql) + + def table_add_primary_key(self, table, column, schema=None): + """ add a primery key (with one column) to a table """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s ADD PRIMARY KEY (%s)" % (table_name, self._quote(column)) + self._exec_sql_and_commit(sql) + + def table_add_unique_constraint(self, table, column, schema=None): + """ add a unique constraint to a table """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s ADD UNIQUE (%s)" % (table_name, self._quote(column)) + self._exec_sql_and_commit(sql) + + def table_delete_constraint(self, table, constraint, schema=None): + """ delete constraint in a table """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s DROP CONSTRAINT %s" % (table_name, self._quote(constraint)) + self._exec_sql_and_commit(sql) + + def table_move_to_schema(self, table, new_schema, schema=None): + if new_schema == schema: + return + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s SET SCHEMA %s" % (table_name, self._quote(new_schema)) + self._exec_sql_and_commit(sql) + + # update geometry_columns if postgis is enabled + if self.has_spatial: + sql = "UPDATE geometry_columns SET f_table_schema='%s' WHERE f_table_name='%s'" % (self._quote_str(new_schema), self._quote_str(table)) + if schema is not None: + sql += " AND f_table_schema='%s'" % self._quote_str(schema) + self._exec_sql_and_commit(sql) + + def table_apply_function(self, schema, table, res_column, fct, param): + """ apply a function to a column and save the result in other column """ + table = self._table_name(schema, table) + sql = "UPDATE %s SET %s = %s(%s)" % (table, self._quote(res_column), fct, self._quote(param)) + self._exec_sql_and_commit(sql) + + def table_enable_triggers(self, table, schema, enable=True): + """ enable or disable all triggers on table """ + table = self._table_name(schema, table) + sql = "ALTER TABLE %s %s TRIGGER ALL" % (table, "ENABLE" if enable else "DISABLE") + self._exec_sql_and_commit(sql) + + def table_enable_trigger(self, table, schema, trigger, enable=True): + """ enable or disable one trigger on table """ + table = self._table_name(schema, table) + sql = "ALTER TABLE %s %s TRIGGER %s" % (table, "ENABLE" if enable else "DISABLE", self._quote(trigger)) + self._exec_sql_and_commit(sql) + + def table_delete_trigger(self, table, schema, trigger): + """ delete trigger on table """ + table = self._table_name(schema, table) + sql = "DROP TRIGGER %s ON %s" % (self._quote(trigger), table) + self._exec_sql_and_commit(sql) + + def table_delete_rule(self, table, schema, rule): + """ delete rule on table """ + table = self._table_name(schema, table) + sql = "DROP RULE %s ON %s" % (self._quote(rule), table) + self._exec_sql_and_commit(sql) + + def create_index(self, table, name, column, schema=None): + """ create index on one column using default options """ + table_name = self._table_name(schema, table) + idx_name = self._quote(name) + sql = "CREATE INDEX %s ON %s (%s)" % (idx_name, table_name, self._quote(column)) + self._exec_sql_and_commit(sql) + + def create_spatial_index(self, table, schema=None, geom_column='the_geom'): + table_name = self._table_name(schema, table) + idx_name = self._quote("sidx_"+table) + sql = "CREATE INDEX %s ON %s USING GIST(%s GIST_GEOMETRY_OPS)" % (idx_name, table_name, self._quote(geom_column)) + self._exec_sql_and_commit(sql) + + def delete_index(self, name, schema=None): + index_name = self._table_name(schema, name) + sql = "DROP INDEX %s" % index_name + self._exec_sql_and_commit(sql) + + def get_database_privileges(self): + """ db privileges: (can create schemas, can create temp. tables) """ + sql = "SELECT has_database_privilege('%(d)s', 'CREATE'), has_database_privilege('%(d)s', 'TEMP')" % { 'd' : self._quote_str(self.dbname) } + c = self.con.cursor() + self._exec_sql(c, sql) + return c.fetchone() + + def get_schema_privileges(self, schema): + """ schema privileges: (can create new objects, can access objects in schema) """ + sql = "SELECT has_schema_privilege('%(s)s', 'CREATE'), has_schema_privilege('%(s)s', 'USAGE')" % { 's' : self._quote_str(schema) } + c = self.con.cursor() + self._exec_sql(c, sql) + return c.fetchone() + + def get_table_privileges(self, table, schema=None): + """ table privileges: (select, insert, update, delete) """ + t = self._table_name(schema, table) + sql = """SELECT has_table_privilege('%(t)s', 'SELECT'), has_table_privilege('%(t)s', 'INSERT'), + has_table_privilege('%(t)s', 'UPDATE'), has_table_privilege('%(t)s', 'DELETE')""" % { 't': self._quote_str(t) } + c = self.con.cursor() + self._exec_sql(c, sql) + return c.fetchone() + + def vacuum_analyze(self, table, schema=None): + """ run vacuum analyze on a table """ + t = self._table_name(schema, table) + # vacuum analyze must be run outside transaction block - we have to change isolation level + self.con.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + c = self.con.cursor() + self._exec_sql(c, "VACUUM ANALYZE %s" % t) + self.con.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) + + def sr_info_for_srid(self, srid): + if not self.has_spatial: + return "Unknown" + + try: + c = self.con.cursor() + self._exec_sql(c, "SELECT srtext FROM spatial_ref_sys WHERE srid = '%d'" % srid) + sr = c.fetchone() + if sr is None: + return "Unknown" + srtext = sr[0] + # try to extract just SR name (should be qouted in double quotes) + x = re.search('"([^"]+)"', srtext) + if x is not None: + srtext = x.group() + return srtext + except DbError as e: + return "Unknown" + + def insert_table_row(self, table, values, schema=None, cursor=None): + """ insert a row with specified values to a table. + if a cursor is specified, it doesn't commit (expecting that there will be more inserts) + otherwise it commits immediately """ + t = self._table_name(schema, table) + sql = "" + for value in values: + # TODO: quote values? + if sql: sql += ", " + sql += value + sql = "INSERT INTO %s VALUES (%s)" % (t, sql) + if cursor: + self._exec_sql(cursor, sql) + else: + self._exec_sql_and_commit(sql) + + + def table_add_function_trigger(self, schema, table, resColumn, fct, geomColumn): + """ add a trigger on insert and update that recalculates the value from geometry column """ + + trig_f_name = "%s_calc_%s" % (table, fct) + trig_name = "calc_%s" % fct + ctx = { 'fname' : trig_f_name, 'tname' : trig_name, + 'res' : resColumn, 'geom' : geomColumn, + 'f' : fct, 'table' : self._table_name(schema, table) } + sql = """ + CREATE OR REPLACE FUNCTION %(fname)s() RETURNS TRIGGER AS + $$ + BEGIN + IF (TG_OP = 'INSERT') THEN + NEW.%(res)s := %(f)s(NEW.%(geom)s); + ELSIF (TG_OP = 'UPDATE') THEN + IF NOT (NEW.%(geom)s ~= OLD.%(geom)s) THEN + NEW.%(res)s := %(f)s(NEW.%(geom)s); + END IF; + END IF; + RETURN NEW; + END; + $$ + LANGUAGE 'plpgsql'; + + CREATE TRIGGER %(tname)s BEFORE INSERT OR UPDATE ON %(table)s FOR EACH ROW + EXECUTE PROCEDURE %(fname)s(); + """ % ctx + + self._exec_sql_and_commit(sql) + + + def get_named_cursor(self, table=None): + """ return an unique named cursor, optionally including a table name """ + self.last_cursor_id += 1 + if table is not None: + table2 = re.sub(r'\W', '_', table.encode('ascii','replace')) # all non-alphanum characters to underscore + cur_name = "cursor_%d_table_%s" % (self.last_cursor_id, table2) + else: + cur_name = "cursor_%d" % self.last_cursor_id + #cur_name = ("\"db_table_"+self.table+"\"").replace(' ', '_') + #cur_name = cur_name.encode('ascii','replace').replace('?', '_') + return self.con.cursor(cur_name) + + def _exec_sql(self, cursor, sql): + try: + cursor.execute(sql) + except psycopg2.Error as e: + # do the rollback to avoid a "current transaction aborted, commands ignored" errors + self.con.rollback() + raise DbError(e) + + def _exec_sql_and_commit(self, sql): + """ tries to execute and commit some action, on error it rolls back the change """ + #try: + c = self.con.cursor() + self._exec_sql(c, sql) + self.con.commit() + #except DbError, e: + # self.con.rollback() + # raise + + def _quote(self, identifier): + identifier = str(identifier) # make sure it's python unicode string + return u'"%s"' % identifier.replace('"', '""') + + def _quote_str(self, txt): + """ make the string safe - replace ' with '' """ + txt = str(txt) # make sure it's python unicode string + return txt.replace("'", "''") + + def _table_name(self, schema, table): + if not schema: + return self._quote(table) + else: + return u"%s.%s" % (self._quote(schema), self._quote(table)) + # for debugging / testing if __name__ == '__main__': - db = GeoDB(host='localhost',dbname='gis',user='gisak',passwd='g') - - # fix_print_with_import - print(db.list_schemas()) - # fix_print_with_import - print('==========') - - for row in db.list_geotables(): - # fix_print_with_import - print(row) - - # fix_print_with_import - print('==========') - - for row in db.get_table_indexes('trencin'): - # fix_print_with_import - print(row) - - # fix_print_with_import - print('==========') - - for row in db.get_table_constraints('trencin'): - # fix_print_with_import - print(row) - - # fix_print_with_import - print('==========') - - # fix_print_with_import - print(db.get_table_rows('trencin')) - - #for fld in db.get_table_metadata('trencin'): - # print fld - - #try: - # db.create_table('trrrr', [('id','serial'), ('test','text')]) - #except DbError, e: - # print e.message, e.query - + db = GeoDB(host='localhost',dbname='gis',user='gisak',passwd='g') + + # fix_print_with_import + print(db.list_schemas()) + # fix_print_with_import + print('==========') + + for row in db.list_geotables(): + # fix_print_with_import + print(row) + + # fix_print_with_import + print('==========') + + for row in db.get_table_indexes('trencin'): + # fix_print_with_import + print(row) + + # fix_print_with_import + print('==========') + + for row in db.get_table_constraints('trencin'): + # fix_print_with_import + print(row) + + # fix_print_with_import + print('==========') + + # fix_print_with_import + print(db.get_table_rows('trencin')) + + #for fld in db.get_table_metadata('trencin'): + # print fld + + #try: + # db.create_table('trrrr', [('id','serial'), ('test','text')]) + #except DbError, e: + # print e.message, e.query + # vim: noet ts=8 : diff --git a/dbConnection.py b/dbConnection.py index ec9d12a..d1f7af2 100755 --- a/dbConnection.py +++ b/dbConnection.py @@ -9,180 +9,180 @@ from qgis.PyQt.QtWidgets import QAction -from . import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils class ConnectionManager(object): - SUPPORTED_CONNECTORS = ['postgis'] - MISSED_CONNECTORS = [] - - @classmethod - def initConnectionSupport(self): - conntypes = ConnectionManager.SUPPORTED_CONNECTORS - for c in conntypes: - try: - connector = self.getConnection( c ) - except ImportError as e: - module = e.args[0][ len("No module named "): ] - ConnectionManager.SUPPORTED_CONNECTORS.remove( c ) - ConnectionManager.MISSED_CONNECTORS.append( (c, module) ) - - return len(ConnectionManager.SUPPORTED_CONNECTORS) > 0 - - @classmethod - def getConnection(self, conntype, uri=None): - if not self.isSupported(conntype): - raise NotSupportedConnTypeException(conntype) - - # import the connector - exec( "from connectors import %s as connector" % conntype) - return connector.Connection(uri) if uri else connector.Connection - - @classmethod - def isSupported(self, conntype): - return conntype in ConnectionManager.SUPPORTED_CONNECTORS - - @classmethod - def getAvailableConnections(self, conntypes=None): - if conntypes == None: - conntypes = ConnectionManager.SUPPORTED_CONNECTORS - if not hasattr(conntypes, '__iter__'): - conntypes = [conntypes] - - connections = [] - for c in conntypes: - connection = self.getConnection( c ) - connections.extend( connection.getAvailableConnections() ) - return connections + SUPPORTED_CONNECTORS = ['postgis'] + MISSED_CONNECTORS = [] + + @classmethod + def initConnectionSupport(self): + conntypes = ConnectionManager.SUPPORTED_CONNECTORS + for c in conntypes: + try: + connector = self.getConnection( c ) + except ImportError as e: + module = e.args[0][ len("No module named "): ] + ConnectionManager.SUPPORTED_CONNECTORS.remove( c ) + ConnectionManager.MISSED_CONNECTORS.append( (c, module) ) + + return len(ConnectionManager.SUPPORTED_CONNECTORS) > 0 + + @classmethod + def getConnection(self, conntype, uri=None): + if not self.isSupported(conntype): + raise NotSupportedConnTypeException(conntype) + + # import the connector + exec( "from pgRoutingLayer.connectors import %s as connector" % conntype, globals(),globals()) + return connector.Connection(uri) if uri else connector.Connection + + @classmethod + def isSupported(self, conntype): + return conntype in ConnectionManager.SUPPORTED_CONNECTORS + + @classmethod + def getAvailableConnections(self, conntypes=None): + if conntypes == None: + conntypes = ConnectionManager.SUPPORTED_CONNECTORS + if not hasattr(conntypes, '__iter__'): + conntypes = [conntypes] + + connections = [] + for c in conntypes: + connection = self.getConnection( c ) + connections.extend( connection.getAvailableConnections() ) + return connections class NotSupportedConnTypeException(Exception): - def __init__(self, conntype): - self.msg = u"%s is not supported yet" % conntype + def __init__(self, conntype): + self.msg = u"%s is not supported yet" % conntype - def __str__(self): - return self.msg.encode('utf-8') + def __str__(self): + return self.msg.encode('utf-8') class DbError(Exception): - def __init__(self, errormsg, query=None): - self.msg = str( errormsg ) - self.query = str( query ) if query else None + def __init__(self, errormsg, query=None): + self.msg = str( errormsg ) + self.query = str( query ) if query else None - def __str__(self): - msg = self.msg.encode('utf-8') - if self.query != None: - msg += "\nQuery:\n" + self.query.encode('utf-8') - return msg + def __str__(self): + msg = self.msg.encode('utf-8') + if self.query != None: + msg += "\nQuery:\n" + self.query.encode('utf-8') + return msg class Connection(object): - def __init__(self, uri): - self.uri = uri + def __init__(self, uri): + self.uri = uri - @classmethod - def getTypeName(self): - pass + @classmethod + def getTypeName(self): + pass - @classmethod - def getTypeNameString(self): - pass + @classmethod + def getTypeNameString(self): + pass - @classmethod - def getProviderName(self): - pass + @classmethod + def getProviderName(self): + pass - @classmethod - def getSettingsKey(self): - pass + @classmethod + def getSettingsKey(self): + pass - @classmethod - def icon(self): - pass + @classmethod + def icon(self): + pass - @classmethod - def getAvailableConnections(self): - connections = [] + @classmethod + def getAvailableConnections(self): + connections = [] - settings = QSettings() - settings.beginGroup( "/%s/connections" % self.getSettingsKey() ) - keys = settings.childGroups() - for name in keys: - connections.append( Connection.ConnectionAction(name, self.getTypeName()) ) - settings.endGroup() + settings = QSettings() + settings.beginGroup( "/%s/connections" % self.getSettingsKey() ) + keys = settings.childGroups() + for name in keys: + connections.append( Connection.ConnectionAction(name, self.getTypeName()) ) + settings.endGroup() - return connections + return connections - def getURI(self): - # returns a new QgsDataSourceUri instance - return qgis.core.QgsDataSourceUri( self.uri.connectionInfo() ) + def getURI(self): + # returns a new QgsDataSourceUri instance + return qgis.core.QgsDataSourceUri( self.uri.connectionInfo() ) - def getAction(self, parent=None): - return Connection.ConnectionAction(self.uri.database(), self.getTypeName(), parent) + def getAction(self, parent=None): + return Connection.ConnectionAction(self.uri.database(), self.getTypeName(), parent) - class ConnectionAction(QAction): - def __init__(self, text, conntype, parent=None): - self.type = conntype - icon = ConnectionManager.getConnection(self.type).icon() - QAction.__init__(self, icon, text, parent) + class ConnectionAction(QAction): + def __init__(self, text, conntype, parent=None): + self.type = conntype + icon = ConnectionManager.getConnection(self.type).icon() + QAction.__init__(self, icon, text, parent) - def connect(self): - selected = self.text() - conn = ConnectionManager.getConnection( self.type ).connect( selected, self.parent() ) + def connect(self): + selected = self.text() + conn = ConnectionManager.getConnection( self.type ).connect( selected, self.parent() ) - # set as default in QSettings - settings = QSettings() - settings.setValue( "/%s/connections/selected" % conn.getSettingsKey(), selected ) + # set as default in QSettings + settings = QSettings() + settings.setValue( "/%s/connections/selected" % conn.getSettingsKey(), selected ) - return conn + return conn class TableAttribute(object): - pass + pass class TableConstraint(object): - """ class that represents a constraint of a table (relation) """ - - TypeCheck, TypeForeignKey, TypePrimaryKey, TypeUnique = list(range(4)) - types = { "c" : TypeCheck, "f" : TypeForeignKey, "p" : TypePrimaryKey, "u" : TypeUnique } - - on_action = { "a" : "NO ACTION", "r" : "RESTRICT", "c" : "CASCADE", "n" : "SET NULL", "d" : "SET DEFAULT" } - match_types = { "u" : "UNSPECIFIED", "f" : "FULL", "p" : "PARTIAL" } + """ class that represents a constraint of a table (relation) """ - pass + TypeCheck, TypeForeignKey, TypePrimaryKey, TypeUnique = list(range(4)) + types = { "c" : TypeCheck, "f" : TypeForeignKey, "p" : TypePrimaryKey, "u" : TypeUnique } + + on_action = { "a" : "NO ACTION", "r" : "RESTRICT", "c" : "CASCADE", "n" : "SET NULL", "d" : "SET DEFAULT" } + match_types = { "u" : "UNSPECIFIED", "f" : "FULL", "p" : "PARTIAL" } + + pass class TableIndex(object): - pass + pass class TableTrigger(object): - # Bits within tgtype (pg_trigger.h) - TypeRow = (1 << 0) # row or statement - TypeBefore = (1 << 1) # before or after - # events: one or more - TypeInsert = (1 << 2) - TypeDelete = (1 << 3) - TypeUpdate = (1 << 4) - TypeTruncate = (1 << 5) + # Bits within tgtype (pg_trigger.h) + TypeRow = (1 << 0) # row or statement + TypeBefore = (1 << 1) # before or after + # events: one or more + TypeInsert = (1 << 2) + TypeDelete = (1 << 3) + TypeUpdate = (1 << 4) + TypeTruncate = (1 << 5) - pass + pass class TableRule(object): - pass + pass class TableField(object): - def is_null_txt(self): - if self.is_null: - return "NULL" - else: - return "NOT NULL" - - def field_def(self, db): - """ return field definition as used for CREATE TABLE or ALTER TABLE command """ - data_type = self.data_type if (not self.modifier or self.modifier < 0) else "%s(%d)" % (self.data_type, self.modifier) - txt = "%s %s %s" % (db._quote(self.name), data_type, self.is_null_txt()) - if self.default and len(self.default) > 0: - txt += " DEFAULT %s" % self.default - return txt + def is_null_txt(self): + if self.is_null: + return "NULL" + else: + return "NOT NULL" + + def field_def(self, db): + """ return field definition as used for CREATE TABLE or ALTER TABLE command """ + data_type = self.data_type if (not self.modifier or self.modifier < 0) else "%s(%d)" % (self.data_type, self.modifier) + txt = "%s %s %s" % (db._quote(self.name), data_type, self.is_null_txt()) + if self.default and len(self.default) > 0: + txt += " DEFAULT %s" % self.default + return txt diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index f0ee3e5..f354ecd 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -29,9 +29,9 @@ from qgis.PyQt.QtWidgets import QAction, QDockWidget, QApplication, QLabel, QLineEdit, QPushButton, QWidget,QGridLayout,QToolButton,QVBoxLayout,QHBoxLayout,QSplitter,QGroupBox,QScrollArea,QPlainTextEdit from qgis.core import QgsMessageLog,Qgis from qgis.gui import QgsVertexMarker,QgsRubberBand,QgsMapToolEmitPoint -from . import dbConnection +from pgRoutingLayer import dbConnection from qgis.utils import iface -from . import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils #import highlighter as hl import os import psycopg2 @@ -92,7 +92,7 @@ class PgRoutingLayer(object): FRACTION_DECIMAL_PLACES = 2 version = 3.0 functions = {} - + def __init__(self, iface): # Save reference to the QGIS interface From dc1068d6a9d9f8a3ab3f1d4bba37ad8681a9645e Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Thu, 28 Jun 2018 02:19:04 +0200 Subject: [PATCH 09/32] fixed str decoding problem --- connectors/postgis.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/connectors/postgis.py b/connectors/postgis.py index c715a4f..47c7313 100755 --- a/connectors/postgis.py +++ b/connectors/postgis.py @@ -77,11 +77,10 @@ def __init__(self, row): class DbError(DbConn.DbError): def __init__(self, error, query=None): - # save error. funny that the variables are in utf8, not - msg = str( error.args[0], 'utf-8') + msg = str(error.args[0]) if query == None: if hasattr(error, "cursor") and hasattr(error.cursor, "query"): - query = str(error.cursor.query, 'utf-8') + query = str(error.cursor.query) else: query = str(query) DbConn.DbError.__init__(self, msg, query) From d1766c93be22e54de64b5ede1b5568f07fae6632 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Sat, 30 Jun 2018 02:12:57 +0200 Subject: [PATCH 10/32] rmeoved from metadata- qgis minimum version --- metadata.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/metadata.txt b/metadata.txt index 638dbf8..4fc9a96 100644 --- a/metadata.txt +++ b/metadata.txt @@ -4,7 +4,6 @@ description=Dockable widget that adds pgRouting layers about=Dockable widget that adds pgRouting layers version=2.2.2 qgisMinimumVersion=3.0 -qgisMaximumVersion=3.1 author=Anita Graser, Ko Nagase, Vicky Vergara email=project@pgrouting.org changelog=2.2.2 From c01f1acccdcfd3556f1b9fb7a1f0335d319b6012 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Sat, 30 Jun 2018 02:12:57 +0200 Subject: [PATCH 11/32] removed from metadata- qgis minimum version --- metadata.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/metadata.txt b/metadata.txt index 638dbf8..4fc9a96 100644 --- a/metadata.txt +++ b/metadata.txt @@ -4,7 +4,6 @@ description=Dockable widget that adds pgRouting layers about=Dockable widget that adds pgRouting layers version=2.2.2 qgisMinimumVersion=3.0 -qgisMaximumVersion=3.1 author=Anita Graser, Ko Nagase, Vicky Vergara email=project@pgrouting.org changelog=2.2.2 From 588a0ae011c6e6fe71311442fa9c7bd31c0a5ab0 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Sat, 30 Jun 2018 02:26:12 +0200 Subject: [PATCH 12/32] restaured qgismaximum version with value 4.0 --- metadata.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/metadata.txt b/metadata.txt index 4fc9a96..4832c8d 100644 --- a/metadata.txt +++ b/metadata.txt @@ -4,6 +4,7 @@ description=Dockable widget that adds pgRouting layers about=Dockable widget that adds pgRouting layers version=2.2.2 qgisMinimumVersion=3.0 +qgisMaximumVersion=4.0 author=Anita Graser, Ko Nagase, Vicky Vergara email=project@pgrouting.org changelog=2.2.2 From 8a0bc5ddb2a248d7dc79814bc867dd30eab51c4a Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Sun, 1 Jul 2018 18:19:14 +0530 Subject: [PATCH 13/32] API_changes_5 --- dbConnection.py | 262 +++++++++++++++++++------------------- functions/FunctionBase.py | 10 +- pgRoutingLayer.py | 31 +++-- pgRoutingLayer_utils.py | 0 4 files changed, 151 insertions(+), 152 deletions(-) mode change 100644 => 100755 pgRoutingLayer_utils.py diff --git a/dbConnection.py b/dbConnection.py index ec9d12a..d1f7af2 100755 --- a/dbConnection.py +++ b/dbConnection.py @@ -9,180 +9,180 @@ from qgis.PyQt.QtWidgets import QAction -from . import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils class ConnectionManager(object): - SUPPORTED_CONNECTORS = ['postgis'] - MISSED_CONNECTORS = [] - - @classmethod - def initConnectionSupport(self): - conntypes = ConnectionManager.SUPPORTED_CONNECTORS - for c in conntypes: - try: - connector = self.getConnection( c ) - except ImportError as e: - module = e.args[0][ len("No module named "): ] - ConnectionManager.SUPPORTED_CONNECTORS.remove( c ) - ConnectionManager.MISSED_CONNECTORS.append( (c, module) ) - - return len(ConnectionManager.SUPPORTED_CONNECTORS) > 0 - - @classmethod - def getConnection(self, conntype, uri=None): - if not self.isSupported(conntype): - raise NotSupportedConnTypeException(conntype) - - # import the connector - exec( "from connectors import %s as connector" % conntype) - return connector.Connection(uri) if uri else connector.Connection - - @classmethod - def isSupported(self, conntype): - return conntype in ConnectionManager.SUPPORTED_CONNECTORS - - @classmethod - def getAvailableConnections(self, conntypes=None): - if conntypes == None: - conntypes = ConnectionManager.SUPPORTED_CONNECTORS - if not hasattr(conntypes, '__iter__'): - conntypes = [conntypes] - - connections = [] - for c in conntypes: - connection = self.getConnection( c ) - connections.extend( connection.getAvailableConnections() ) - return connections + SUPPORTED_CONNECTORS = ['postgis'] + MISSED_CONNECTORS = [] + + @classmethod + def initConnectionSupport(self): + conntypes = ConnectionManager.SUPPORTED_CONNECTORS + for c in conntypes: + try: + connector = self.getConnection( c ) + except ImportError as e: + module = e.args[0][ len("No module named "): ] + ConnectionManager.SUPPORTED_CONNECTORS.remove( c ) + ConnectionManager.MISSED_CONNECTORS.append( (c, module) ) + + return len(ConnectionManager.SUPPORTED_CONNECTORS) > 0 + + @classmethod + def getConnection(self, conntype, uri=None): + if not self.isSupported(conntype): + raise NotSupportedConnTypeException(conntype) + + # import the connector + exec( "from pgRoutingLayer.connectors import %s as connector" % conntype, globals(),globals()) + return connector.Connection(uri) if uri else connector.Connection + + @classmethod + def isSupported(self, conntype): + return conntype in ConnectionManager.SUPPORTED_CONNECTORS + + @classmethod + def getAvailableConnections(self, conntypes=None): + if conntypes == None: + conntypes = ConnectionManager.SUPPORTED_CONNECTORS + if not hasattr(conntypes, '__iter__'): + conntypes = [conntypes] + + connections = [] + for c in conntypes: + connection = self.getConnection( c ) + connections.extend( connection.getAvailableConnections() ) + return connections class NotSupportedConnTypeException(Exception): - def __init__(self, conntype): - self.msg = u"%s is not supported yet" % conntype + def __init__(self, conntype): + self.msg = u"%s is not supported yet" % conntype - def __str__(self): - return self.msg.encode('utf-8') + def __str__(self): + return self.msg.encode('utf-8') class DbError(Exception): - def __init__(self, errormsg, query=None): - self.msg = str( errormsg ) - self.query = str( query ) if query else None + def __init__(self, errormsg, query=None): + self.msg = str( errormsg ) + self.query = str( query ) if query else None - def __str__(self): - msg = self.msg.encode('utf-8') - if self.query != None: - msg += "\nQuery:\n" + self.query.encode('utf-8') - return msg + def __str__(self): + msg = self.msg.encode('utf-8') + if self.query != None: + msg += "\nQuery:\n" + self.query.encode('utf-8') + return msg class Connection(object): - def __init__(self, uri): - self.uri = uri + def __init__(self, uri): + self.uri = uri - @classmethod - def getTypeName(self): - pass + @classmethod + def getTypeName(self): + pass - @classmethod - def getTypeNameString(self): - pass + @classmethod + def getTypeNameString(self): + pass - @classmethod - def getProviderName(self): - pass + @classmethod + def getProviderName(self): + pass - @classmethod - def getSettingsKey(self): - pass + @classmethod + def getSettingsKey(self): + pass - @classmethod - def icon(self): - pass + @classmethod + def icon(self): + pass - @classmethod - def getAvailableConnections(self): - connections = [] + @classmethod + def getAvailableConnections(self): + connections = [] - settings = QSettings() - settings.beginGroup( "/%s/connections" % self.getSettingsKey() ) - keys = settings.childGroups() - for name in keys: - connections.append( Connection.ConnectionAction(name, self.getTypeName()) ) - settings.endGroup() + settings = QSettings() + settings.beginGroup( "/%s/connections" % self.getSettingsKey() ) + keys = settings.childGroups() + for name in keys: + connections.append( Connection.ConnectionAction(name, self.getTypeName()) ) + settings.endGroup() - return connections + return connections - def getURI(self): - # returns a new QgsDataSourceUri instance - return qgis.core.QgsDataSourceUri( self.uri.connectionInfo() ) + def getURI(self): + # returns a new QgsDataSourceUri instance + return qgis.core.QgsDataSourceUri( self.uri.connectionInfo() ) - def getAction(self, parent=None): - return Connection.ConnectionAction(self.uri.database(), self.getTypeName(), parent) + def getAction(self, parent=None): + return Connection.ConnectionAction(self.uri.database(), self.getTypeName(), parent) - class ConnectionAction(QAction): - def __init__(self, text, conntype, parent=None): - self.type = conntype - icon = ConnectionManager.getConnection(self.type).icon() - QAction.__init__(self, icon, text, parent) + class ConnectionAction(QAction): + def __init__(self, text, conntype, parent=None): + self.type = conntype + icon = ConnectionManager.getConnection(self.type).icon() + QAction.__init__(self, icon, text, parent) - def connect(self): - selected = self.text() - conn = ConnectionManager.getConnection( self.type ).connect( selected, self.parent() ) + def connect(self): + selected = self.text() + conn = ConnectionManager.getConnection( self.type ).connect( selected, self.parent() ) - # set as default in QSettings - settings = QSettings() - settings.setValue( "/%s/connections/selected" % conn.getSettingsKey(), selected ) + # set as default in QSettings + settings = QSettings() + settings.setValue( "/%s/connections/selected" % conn.getSettingsKey(), selected ) - return conn + return conn class TableAttribute(object): - pass + pass class TableConstraint(object): - """ class that represents a constraint of a table (relation) """ - - TypeCheck, TypeForeignKey, TypePrimaryKey, TypeUnique = list(range(4)) - types = { "c" : TypeCheck, "f" : TypeForeignKey, "p" : TypePrimaryKey, "u" : TypeUnique } - - on_action = { "a" : "NO ACTION", "r" : "RESTRICT", "c" : "CASCADE", "n" : "SET NULL", "d" : "SET DEFAULT" } - match_types = { "u" : "UNSPECIFIED", "f" : "FULL", "p" : "PARTIAL" } + """ class that represents a constraint of a table (relation) """ - pass + TypeCheck, TypeForeignKey, TypePrimaryKey, TypeUnique = list(range(4)) + types = { "c" : TypeCheck, "f" : TypeForeignKey, "p" : TypePrimaryKey, "u" : TypeUnique } + + on_action = { "a" : "NO ACTION", "r" : "RESTRICT", "c" : "CASCADE", "n" : "SET NULL", "d" : "SET DEFAULT" } + match_types = { "u" : "UNSPECIFIED", "f" : "FULL", "p" : "PARTIAL" } + + pass class TableIndex(object): - pass + pass class TableTrigger(object): - # Bits within tgtype (pg_trigger.h) - TypeRow = (1 << 0) # row or statement - TypeBefore = (1 << 1) # before or after - # events: one or more - TypeInsert = (1 << 2) - TypeDelete = (1 << 3) - TypeUpdate = (1 << 4) - TypeTruncate = (1 << 5) + # Bits within tgtype (pg_trigger.h) + TypeRow = (1 << 0) # row or statement + TypeBefore = (1 << 1) # before or after + # events: one or more + TypeInsert = (1 << 2) + TypeDelete = (1 << 3) + TypeUpdate = (1 << 4) + TypeTruncate = (1 << 5) - pass + pass class TableRule(object): - pass + pass class TableField(object): - def is_null_txt(self): - if self.is_null: - return "NULL" - else: - return "NOT NULL" - - def field_def(self, db): - """ return field definition as used for CREATE TABLE or ALTER TABLE command """ - data_type = self.data_type if (not self.modifier or self.modifier < 0) else "%s(%d)" % (self.data_type, self.modifier) - txt = "%s %s %s" % (db._quote(self.name), data_type, self.is_null_txt()) - if self.default and len(self.default) > 0: - txt += " DEFAULT %s" % self.default - return txt + def is_null_txt(self): + if self.is_null: + return "NULL" + else: + return "NOT NULL" + + def field_def(self, db): + """ return field definition as used for CREATE TABLE or ALTER TABLE command """ + data_type = self.data_type if (not self.modifier or self.modifier < 0) else "%s(%d)" % (self.data_type, self.modifier) + txt = "%s %s %s" % (db._quote(self.name), data_type, self.is_null_txt()) + if self.default and len(self.default) > 0: + txt += " DEFAULT %s" % self.default + return txt diff --git a/functions/FunctionBase.py b/functions/FunctionBase.py index d20671d..7439cec 100755 --- a/functions/FunctionBase.py +++ b/functions/FunctionBase.py @@ -1,6 +1,6 @@ from builtins import str from builtins import object -from qgis.core import (QgsMessageLog, Qgis, QgsGeometry) +from qgis.core import (QgsMessageLog, Qgis, QgsGeometry,QgsWkbTypes) from qgis.gui import QgsRubberBand from qgis.PyQt.QtGui import QColor @@ -218,11 +218,11 @@ def drawManyPaths(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)s, node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) @@ -253,11 +253,11 @@ def drawOnePath(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: resultPathRubberBand.addPoint(pt) - elif geom.wkbType() == QGis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): resultPathRubberBand.addPoint(pt) diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index f0ee3e5..5103f6b 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -25,13 +25,13 @@ from builtins import object from qgis.PyQt import uic from qgis.PyQt.QtCore import Qt, QObject, pyqtSignal, QRegExp, QSettings -from qgis.PyQt.QtGui import QColor, QIcon, QIntValidator, QDoubleValidator,QRegExpValidator -from qgis.PyQt.QtWidgets import QAction, QDockWidget, QApplication, QLabel, QLineEdit, QPushButton, QWidget,QGridLayout,QToolButton,QVBoxLayout,QHBoxLayout,QSplitter,QGroupBox,QScrollArea,QPlainTextEdit -from qgis.core import QgsMessageLog,Qgis +from qgis.PyQt.QtGui import QColor, QIcon, QIntValidator, QDoubleValidator,QRegExpValidator, QCursor +from qgis.PyQt.QtWidgets import QAction, QDockWidget, QApplication, QLabel, QLineEdit, QPushButton, QWidget,QGridLayout,QToolButton,QVBoxLayout,QHBoxLayout,QSplitter,QGroupBox,QScrollArea,QPlainTextEdit, QMessageBox +from qgis.core import QgsMessageLog,Qgis,QgsRectangle, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject, QgsGeometry from qgis.gui import QgsVertexMarker,QgsRubberBand,QgsMapToolEmitPoint -from . import dbConnection +from pgRoutingLayer import dbConnection from qgis.utils import iface -from . import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils #import highlighter as hl import os import psycopg2 @@ -92,7 +92,7 @@ class PgRoutingLayer(object): FRACTION_DECIMAL_PLACES = 2 version = 3.0 functions = {} - + def __init__(self, iface): # Save reference to the QGIS interface @@ -279,8 +279,8 @@ def updateConnectionEnabled(self): 'Selected database: ' + dbname + '\npgRouting version: ' + str(self.version)) - currentFunction = self.dock.comboBoxFunction.currentText() - if currentFunction =='': + currentFunction = str (self.dock.comboBoxFunction.currentText()) + if currentFunction ==' ': return self.loadFunctionsForVersion() @@ -303,8 +303,8 @@ def loadFunctionsForVersion(self): def updateFunctionEnabled(self, text): - # for testing //TODO remove text ='dijkstra' - text='dijkstra' + # for testing only. Remove text = dikstra + text = 'dijkstra' function = self.functions.get(str(text)) self.toggleSelectButton(None) @@ -1027,12 +1027,11 @@ def findNearestNode(self, args, pt): #srid, geomType = self.getSridAndGeomType(con, args) #srid, geomType = Utils.getSridAndGeomType(con, args['edge_table'], args['geometry']) srid, geomType = Utils.getSridAndGeomType(con, '%(edge_table)s' % args, '%(geometry)s' % args) - if self.iface.mapCanvas().hasCrsTransformEnabled(): - layerCrs = QgsCoordinateReferenceSystem() - Utils.createFromSrid(layerCrs, srid) - trans = QgsCoordinateTransform(canvasCrs, layerCrs) - pt = trans.transform(pt) - rect = trans.transform(rect) + layerCrs = QgsCoordinateReferenceSystem() + Utils.createFromSrid(layerCrs, srid) + trans = QgsCoordinateTransform(canvasCrs, layerCrs, QgsProject.instance()) + pt = trans.transform(pt) + rect = trans.transform(rect) args['canvas_srid'] = Utils.getCanvasSrid(canvasCrs) args['srid'] = srid diff --git a/pgRoutingLayer_utils.py b/pgRoutingLayer_utils.py old mode 100644 new mode 100755 From 5246b3a0a977c44182534b5b998e4721b6579174 Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Sun, 1 Jul 2018 18:24:43 +0530 Subject: [PATCH 14/32] Indent_correction --- connectors/postgis.py | 1605 ++++++++++++++++++++--------------------- 1 file changed, 802 insertions(+), 803 deletions(-) diff --git a/connectors/postgis.py b/connectors/postgis.py index 1b9c9e9..47c7313 100755 --- a/connectors/postgis.py +++ b/connectors/postgis.py @@ -38,831 +38,830 @@ class TableAttribute(DbConn.TableAttribute): - def __init__(self, row): - self.num, self.name, self.data_type, self.char_max_len, self.modifier, self.notnull, self.hasdefault, self.default = row + def __init__(self, row): + self.num, self.name, self.data_type, self.char_max_len, self.modifier, self.notnull, self.hasdefault, self.default = row class TableConstraint(DbConn.TableConstraint): - """ class that represents a constraint of a table (relation) """ - - def __init__(self, row): - self.name, con_type, self.is_defferable, self.is_deffered, keys = row[:5] - self.keys = list(map(int, keys.split(' '))) - self.con_type = TableConstraint.types[con_type] # convert to enum - if self.con_type == TableConstraint.TypeCheck: - self.check_src = row[5] - elif self.con_type == TableConstraint.TypeForeignKey: - self.foreign_table = row[6] - self.foreign_on_update = TableConstraint.on_action[row[7]] - self.foreign_on_delete = TableConstraint.on_action[row[8]] - self.foreign_match_type = TableConstraint.match_types[row[9]] - self.foreign_keys = row[10] + """ class that represents a constraint of a table (relation) """ + + def __init__(self, row): + self.name, con_type, self.is_defferable, self.is_deffered, keys = row[:5] + self.keys = list(map(int, keys.split(' '))) + self.con_type = TableConstraint.types[con_type] # convert to enum + if self.con_type == TableConstraint.TypeCheck: + self.check_src = row[5] + elif self.con_type == TableConstraint.TypeForeignKey: + self.foreign_table = row[6] + self.foreign_on_update = TableConstraint.on_action[row[7]] + self.foreign_on_delete = TableConstraint.on_action[row[8]] + self.foreign_match_type = TableConstraint.match_types[row[9]] + self.foreign_keys = row[10] class TableIndex(DbConn.TableIndex): - def __init__(self, row): - self.name, columns = row - self.columns = list(map(int, columns.split(' '))) + def __init__(self, row): + self.name, columns = row + self.columns = list(map(int, columns.split(' '))) class TableTrigger(DbConn.TableTrigger): - def __init__(self, row): - self.name, self.function, self.type, self.enabled = row + def __init__(self, row): + self.name, self.function, self.type, self.enabled = row class TableRule(DbConn.TableRule): - def __init__(self, row): - self.name, self.definition = row + def __init__(self, row): + self.name, self.definition = row class DbError(DbConn.DbError): - def __init__(self, error, query=None): - # save error. funny that the variables are in utf8, not - msg = str( error.args[0], 'utf-8') - if query == None: - if hasattr(error, "cursor") and hasattr(error.cursor, "query"): - query = str(error.cursor.query, 'utf-8') - else: - query = str(query) - DbConn.DbError.__init__(self, msg, query) - + def __init__(self, error, query=None): + msg = str(error.args[0]) + if query == None: + if hasattr(error, "cursor") and hasattr(error.cursor, "query"): + query = str(error.cursor.query) + else: + query = str(query) + DbConn.DbError.__init__(self, msg, query) + class TableField(DbConn.TableField): - def __init__(self, name, data_type, is_null=None, default=None, modifier=None): - self.name, self.data_type, self.is_null, self.default, self.modifier = name, data_type, is_null, default, modifier - + def __init__(self, name, data_type, is_null=None, default=None, modifier=None): + self.name, self.data_type, self.is_null, self.default, self.modifier = name, data_type, is_null, default, modifier + class Connection(DbConn.Connection): - @classmethod - def getTypeName(self): - return 'postgis' - - @classmethod - def getTypeNameString(self): - return 'PostgreSQL' - - @classmethod - def getProviderName(self): - return 'postgres' - - @classmethod - def getSettingsKey(self): - return 'PostgreSQL' - - @classmethod - def icon(self): - return QIcon(":/icons/postgis_elephant.png") - - @classmethod - def connect(self, selected, parent=None): - settings = QSettings() - settings.beginGroup( u"/%s/connections/%s" % (self.getSettingsKey(), selected) ) - - if not settings.contains( "database" ): # non-existent entry? - raise DbError( 'there is no defined database connection "%s".' % selected ) - - get_value_str = lambda x: str(settings.value(x) if Utils.isSIPv2() else settings.value(x).toString()) - service, host, port, database, username, password = list(map(get_value_str, ["service", "host", "port", "database", "username", "password"])) - - # qgis1.5 use 'savePassword' instead of 'save' setting - isSave = settings.value("save") if Utils.isSIPv2() else settings.value("save").toBool() - isSavePassword = settings.value("savePassword") if Utils.isSIPv2() else settings.value("savePassword").toBool() - if not ( isSave or isSavePassword ): - (password, ok) = QInputDialog.getText(parent, "Enter password", 'Enter password for connection "%s":' % selected, QLineEdit.Password) - if not ok: return - - settings.endGroup() - - uri = qgis.core.QgsDataSourceUri() - if service: - uri.setConnection(service, database, username, password) - else: - uri.setConnection(host, port, database, username, password) - - return Connection(uri) - - - def __init__(self, uri): - DbConn.Connection.__init__(self, uri) - - self.service = uri.service() - self.host = uri.host() - self.port = uri.port() - self.dbname = uri.database() - self.user = uri.username() - self.passwd = uri.password() - - try: - self.con = psycopg2.connect(self.con_info()) - except psycopg2.OperationalError as e: - raise DbError(e) - - if not self.dbname: - self.dbname = self.get_dbname() - - self.has_spatial = self.check_spatial() - - self.check_geometry_columns_table() - - # a counter to ensure that the cursor will be unique - self.last_cursor_id = 0 - - def con_info(self): - con_str = '' - if self.service: con_str += "service='%s' " % self.service - if self.host: con_str += "host='%s' " % self.host - if self.port: con_str += "port=%s " % self.port - if self.dbname: con_str += "dbname='%s' " % self.dbname - if self.user: con_str += "user='%s' " % self.user - if self.passwd: con_str += "password='%s' " % self.passwd - return con_str - - def get_dbname(self): - c = self.con.cursor() - self._exec_sql(c, "SELECT current_database()") - return c.fetchone()[0] - - def get_info(self): - c = self.con.cursor() - self._exec_sql(c, "SELECT version()") - return c.fetchone()[0] - - def check_spatial(self): - """ check whether postgis_version is present in catalog """ - c = self.con.cursor() - self._exec_sql(c, "SELECT COUNT(*) FROM pg_proc WHERE proname = 'postgis_version'") - return (c.fetchone()[0] > 0) - - def get_spatial_info(self): - """ returns tuple about postgis support: - - lib version - - installed scripts version - - released scripts version - - geos version - - proj version - - whether uses stats - """ - c = self.con.cursor() - self._exec_sql(c, "SELECT postgis_lib_version(), postgis_scripts_installed(), postgis_scripts_released(), postgis_geos_version(), postgis_proj_version(), postgis_uses_stats()") - return c.fetchone() - - def check_geometry_columns_table(self): - - c = self.con.cursor() - self._exec_sql(c, "SELECT relname FROM pg_class WHERE relname = 'geometry_columns' AND pg_class.relkind IN ('v', 'r')") - self.has_geometry_columns = (len(c.fetchall()) != 0) - - if not self.has_geometry_columns: - self.has_geometry_columns_access = False - return - - # find out whether has privileges to access geometry_columns table - self.has_geometry_columns_access = self.get_table_privileges('geometry_columns')[0] - - - def list_schemas(self): - """ - get list of schemas in tuples: (oid, name, owner, perms) - """ - c = self.con.cursor() - sql = "SELECT oid, nspname, pg_get_userbyid(nspowner), nspacl FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname != 'information_schema'" - self._exec_sql(c, sql) - - schema_cmp = lambda x,y: -1 if x[1] < y[1] else 1 - - return sorted(c.fetchall(), cmp=schema_cmp) - - def list_geotables(self, schema=None): - """ - get list of tables with schemas, whether user has privileges, whether table has geometry column(s) etc. - - geometry_columns: - - f_table_schema - - f_table_name - - f_geometry_column - - coord_dimension - - srid - - type - """ - c = self.con.cursor() - - if schema: - schema_where = " AND nspname = '%s' " % self._quote_str(schema) - else: - schema_where = " AND (nspname != 'information_schema' AND nspname !~ 'pg_') " - - # LEFT OUTER JOIN: like LEFT JOIN but if there are more matches, for join, all are used (not only one) - - # first find out whether postgis is enabled - if not self.has_spatial: - # get all tables and views - sql = """SELECT pg_class.relname, pg_namespace.nspname, pg_class.relkind, pg_get_userbyid(relowner), reltuples, relpages, NULL, NULL, NULL, NULL - FROM pg_class - JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace - WHERE pg_class.relkind IN ('v', 'r')""" + schema_where + "ORDER BY nspname, relname" - else: - # discovery of all tables and whether they contain a geometry column - sql = """SELECT pg_class.relname, pg_namespace.nspname, pg_class.relkind, pg_get_userbyid(relowner), reltuples, relpages, pg_attribute.attname, pg_attribute.atttypid::regtype, NULL, NULL - FROM pg_class - JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace - LEFT OUTER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid AND - ( pg_attribute.atttypid = 'geometry'::regtype - OR pg_attribute.atttypid IN (SELECT oid FROM pg_type WHERE typbasetype='geometry'::regtype ) ) - WHERE pg_class.relkind IN ('v', 'r')""" + schema_where + "ORDER BY nspname, relname, attname" - - self._exec_sql(c, sql) - items = c.fetchall() - - # get geometry info from geometry_columns if exists - if self.has_spatial and self.has_geometry_columns and self.has_geometry_columns_access: - sql = """SELECT relname, nspname, relkind, pg_get_userbyid(relowner), reltuples, relpages, - geometry_columns.f_geometry_column, geometry_columns.type, geometry_columns.coord_dimension, geometry_columns.srid - FROM pg_class - JOIN pg_namespace ON relnamespace=pg_namespace.oid - LEFT OUTER JOIN geometry_columns ON relname=f_table_name AND nspname=f_table_schema - WHERE (relkind = 'r' or relkind='v') """ + schema_where + "ORDER BY nspname, relname, f_geometry_column" - self._exec_sql(c, sql) - - # merge geometry info to "items" - for i, geo_item in enumerate(c.fetchall()): - if geo_item[7]: - items[i] = geo_item - - return items - - - def get_table_rows(self, table, schema=None): - c = self.con.cursor() - self._exec_sql(c, "SELECT COUNT(*) FROM %s" % self._table_name(schema, table)) - return c.fetchone()[0] - - - def get_table_fields(self, table, schema=None): - """ return list of columns in table """ - c = self.con.cursor() - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """SELECT a.attnum AS ordinal_position, - a.attname AS column_name, - t.typname AS data_type, - a.attlen AS char_max_len, - a.atttypmod AS modifier, - a.attnotnull AS notnull, - a.atthasdef AS hasdefault, - adef.adsrc AS default_value - FROM pg_class c - JOIN pg_attribute a ON a.attrelid = c.oid - JOIN pg_type t ON a.atttypid = t.oid - JOIN pg_namespace nsp ON c.relnamespace = nsp.oid - LEFT JOIN pg_attrdef adef ON adef.adrelid = a.attrelid AND adef.adnum = a.attnum - WHERE - c.relname = '%s' %s AND - a.attnum > 0 - ORDER BY a.attnum""" % (self._quote_str(table), schema_where) - - self._exec_sql(c, sql) - attrs = [] - for row in c.fetchall(): - attrs.append(TableAttribute(row)) - return attrs - - - def get_table_indexes(self, table, schema=None): - """ get info about table's indexes. ignore primary key and unique constraint index, they get listed in constaints """ - c = self.con.cursor() - - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """SELECT relname, indkey FROM pg_class, pg_index - WHERE pg_class.oid = pg_index.indexrelid AND pg_class.oid IN ( - SELECT indexrelid FROM pg_index, pg_class - JOIN pg_namespace nsp ON pg_class.relnamespace = nsp.oid - WHERE pg_class.relname='%s' %s AND pg_class.oid=pg_index.indrelid - AND indisprimary != 't' )""" % (self._quote_str(table), schema_where) # AND indisunique != 't' - self._exec_sql(c, sql) - indexes = [] - for row in c.fetchall(): - indexes.append(TableIndex(row)) - return indexes - - - def get_table_unique_indexes(self, table, schema=None): - """ get all the unique indexes """ - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """SELECT relname, indkey - FROM pg_index JOIN pg_class ON pg_index.indrelid=pg_class.oid - JOIN pg_namespace nsp ON pg_class.relnamespace = nsp.oid - WHERE pg_class.relname='%s' %s - AND indisprimary != 't' AND indisunique = 't'""" % (self._quote_str(table), schema_where) - c = self.con.cursor() - self._exec_sql(c, sql) - uniqueIndexes = [] - for row in c.fetchall(): - uniqueIndexes.append(TableIndex(row)) - return uniqueIndexes - - - def get_table_constraints(self, table, schema=None): - c = self.con.cursor() - - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """SELECT c.conname, c.contype, c.condeferrable, c.condeferred, array_to_string(c.conkey, ' '), c.consrc, - t2.relname, c.confupdtype, c.confdeltype, c.confmatchtype, array_to_string(c.confkey, ' ') FROM pg_constraint c - LEFT JOIN pg_class t ON c.conrelid = t.oid - LEFT JOIN pg_class t2 ON c.confrelid = t2.oid - JOIN pg_namespace nsp ON t.relnamespace = nsp.oid - WHERE t.relname = '%s' %s """ % (self._quote_str(table), schema_where) - - self._exec_sql(c, sql) - - constrs = [] - for row in c.fetchall(): - constrs.append(TableConstraint(row)) - return constrs - - - def get_table_triggers(self, table, schema=None): - c = self.con.cursor() - - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """ SELECT tgname, proname, tgtype, tgenabled FROM pg_trigger trig - LEFT JOIN pg_class t ON trig.tgrelid = t.oid - LEFT JOIN pg_proc p ON trig.tgfoid = p.oid - JOIN pg_namespace nsp ON t.relnamespace = nsp.oid - WHERE t.relname ='%s' %s """ % (self._quote_str(table), schema_where) - - self._exec_sql(c, sql) - - triggers = [] - for row in c.fetchall(): - triggers.append(TableTrigger(row)) - return triggers - - - def get_table_rules(self, table, schema=None): - c = self.con.cursor() - - schema_where = " AND schemaname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """ SELECT rulename, definition FROM pg_rules - WHERE tablename='%s' %s """ % (self._quote_str(table), schema_where) - - self._exec_sql(c, sql) - - rules = [] - for row in c.fetchall(): - rules.append(TableRule(row)) - - return rules - - def get_table_estimated_extent(self, geom, table, schema=None): - """ find out estimated extent (from the statistics) """ - c = self.con.cursor() - - extent = "estimated_extent('%s','%s','%s')" % (self._quote_str(schema), self._quote_str(table), self._quote_str(geom)) - sql = """ SELECT xmin(%(ext)s), ymin(%(ext)s), xmax(%(ext)s), ymax(%(ext)s) """ % { 'ext' : extent } - self._exec_sql(c, sql) - - row = c.fetchone() - return row - - def get_view_definition(self, view, schema=None): - """ returns definition of the view """ - schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" - sql = """SELECT pg_get_viewdef(c.oid) FROM pg_class c - JOIN pg_namespace nsp ON c.relnamespace = nsp.oid - WHERE relname='%s' %s AND relkind='v'""" % (self._quote_str(view), schema_where) - c = self.con.cursor() - self._exec_sql(c, sql) - return c.fetchone()[0] - - """ - def list_tables(self): - c = self.con.cursor() - c.execute("SELECT relname FROM pg_class WHERE relname !~ '^(pg_|sql_)' AND relkind = 'r'") - return c.fetchall() - """ - - def add_geometry_column(self, table, geom_type, schema=None, geom_column='the_geom', srid=-1, dim=2): - - # use schema if explicitly specified - if schema: - schema_part = "'%s', " % self._quote_str(schema) - else: - schema_part = "" - sql = "SELECT AddGeometryColumn(%s'%s', '%s', %d, '%s', %d)" % (schema_part, self._quote_str(table), self._quote_str(geom_column), srid, self._quote_str(geom_type), dim) - self._exec_sql_and_commit(sql) - - def delete_geometry_column(self, table, geom_column, schema=None): - """ use postgis function to delete geometry column correctly """ - if schema: - schema_part = "'%s', " % self._quote_str(schema) - else: - schema_part = "" - sql = "SELECT DropGeometryColumn(%s'%s', '%s')" % (schema_part, self._quote_str(table), self._quote_str(geom_column)) - self._exec_sql_and_commit(sql) - - def delete_geometry_table(self, table, schema=None): - """ delete table with one or more geometries using postgis function """ - if schema: - schema_part = "'%s', " % self._quote_str(schema) - else: - schema_part = "" - sql = "SELECT DropGeometryTable(%s'%s')" % (schema_part, self._quote_str(table)) - self._exec_sql_and_commit(sql) - - def create_table(self, table, fields, pkey=None, schema=None): - """ create ordinary table - 'fields' is array containing instances of TableField - 'pkey' contains name of column to be used as primary key - """ - - if len(fields) == 0: - return False - - table_name = self._table_name(schema, table) - - sql = "CREATE TABLE %s (%s" % (table_name, fields[0].field_def(self)) - for field in fields[1:]: - sql += ", %s" % field.field_def(self) - if pkey: - sql += ", PRIMARY KEY (%s)" % self._quote(pkey) - sql += ")" - self._exec_sql_and_commit(sql) - return True - - def delete_table(self, table, schema=None): - """ delete table from the database """ - table_name = self._table_name(schema, table) - sql = "DROP TABLE %s" % table_name - self._exec_sql_and_commit(sql) - - def empty_table(self, table, schema=None): - """ delete all rows from table """ - table_name = self._table_name(schema, table) - sql = "TRUNCATE %s" % table_name - self._exec_sql_and_commit(sql) - - def rename_table(self, table, new_table, schema=None): - """ rename a table in database """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s RENAME TO %s" % (table_name, self._quote(new_table)) - self._exec_sql_and_commit(sql) - - # update geometry_columns if postgis is enabled - if self.has_spatial and self.has_geometry_columns and self.has_geometry_columns_access: - sql = "UPDATE geometry_columns SET f_table_name='%s' WHERE f_table_name='%s'" % (self._quote_str(new_table), self._quote_str(table)) - if schema is not None: - sql += " AND f_table_schema='%s'" % self._quote_str(schema) - self._exec_sql_and_commit(sql) - - def create_view(self, name, query, schema=None): - view_name = self._table_name(schema, name) - sql = "CREATE VIEW %s AS %s" % (view_name, query) - self._exec_sql_and_commit(sql) - - def delete_view(self, name, schema=None): - view_name = self._table_name(schema, name) - sql = "DROP VIEW %s" % view_name - self._exec_sql_and_commit(sql) - - def rename_view(self, name, new_name, schema=None): - """ rename view in database """ - self.rename_table(name, new_name, schema) - - def create_schema(self, schema): - """ create a new empty schema in database """ - sql = "CREATE SCHEMA %s" % self._quote(schema) - self._exec_sql_and_commit(sql) - - def delete_schema(self, schema): - """ drop (empty) schema from database """ - sql = "DROP SCHEMA %s" % self._quote(schema) - self._exec_sql_and_commit(sql) - - def rename_schema(self, schema, new_schema): - """ rename a schema in database """ - sql = "ALTER SCHEMA %s RENAME TO %s" % (self._quote(schema), self._quote(new_schema)) - self._exec_sql_and_commit(sql) - - # update geometry_columns if postgis is enabled - if self.has_spatial: - sql = "UPDATE geometry_columns SET f_table_schema='%s' WHERE f_table_schema='%s'" % (self._quote_str(new_schema), self._quote_str(schema)) - self._exec_sql_and_commit(sql) - - def table_add_column(self, table, field, schema=None): - """ add a column to table (passed as TableField instance) """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s ADD %s" % (table_name, field.field_def(self)) - self._exec_sql_and_commit(sql) - - def table_delete_column(self, table, field, schema=None): - """ delete column from a table """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s DROP %s" % (table_name, self._quote(field)) - self._exec_sql_and_commit(sql) - - def table_column_rename(self, table, name, new_name, schema=None): - """ rename column in a table """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s RENAME %s TO %s" % (table_name, self._quote(name), self._quote(new_name)) - self._exec_sql_and_commit(sql) - - # update geometry_columns if postgis is enabled - if self.has_spatial: - sql = "UPDATE geometry_columns SET f_geometry_column='%s' WHERE f_geometry_column='%s' AND f_table_name='%s'" % (self._quote_str(new_name), self._quote_str(name), self._quote_str(table)) - if schema is not None: - sql += " AND f_table_schema='%s'" % self._quote(schema) - self._exec_sql_and_commit(sql) - - def table_column_set_type(self, table, column, data_type, schema=None): - """ change column type """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s ALTER %s TYPE %s" % (table_name, self._quote(column), data_type) - self._exec_sql_and_commit(sql) - - def table_column_set_default(self, table, column, default, schema=None): - """ change column's default value. If default=None drop default value """ - table_name = self._table_name(schema, table) - if default: - sql = "ALTER TABLE %s ALTER %s SET DEFAULT %s" % (table_name, self._quote(column), default) - else: - sql = "ALTER TABLE %s ALTER %s DROP DEFAULT" % (table_name, self._quote(column)) - self._exec_sql_and_commit(sql) - - def table_column_set_null(self, table, column, is_null, schema=None): - """ change whether column can contain null values """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s ALTER %s " % (table_name, self._quote(column)) - if is_null: - sql += "DROP NOT NULL" - else: - sql += "SET NOT NULL" - self._exec_sql_and_commit(sql) - - def table_add_primary_key(self, table, column, schema=None): - """ add a primery key (with one column) to a table """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s ADD PRIMARY KEY (%s)" % (table_name, self._quote(column)) - self._exec_sql_and_commit(sql) - - def table_add_unique_constraint(self, table, column, schema=None): - """ add a unique constraint to a table """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s ADD UNIQUE (%s)" % (table_name, self._quote(column)) - self._exec_sql_and_commit(sql) - - def table_delete_constraint(self, table, constraint, schema=None): - """ delete constraint in a table """ - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s DROP CONSTRAINT %s" % (table_name, self._quote(constraint)) - self._exec_sql_and_commit(sql) - - def table_move_to_schema(self, table, new_schema, schema=None): - if new_schema == schema: - return - table_name = self._table_name(schema, table) - sql = "ALTER TABLE %s SET SCHEMA %s" % (table_name, self._quote(new_schema)) - self._exec_sql_and_commit(sql) - - # update geometry_columns if postgis is enabled - if self.has_spatial: - sql = "UPDATE geometry_columns SET f_table_schema='%s' WHERE f_table_name='%s'" % (self._quote_str(new_schema), self._quote_str(table)) - if schema is not None: - sql += " AND f_table_schema='%s'" % self._quote_str(schema) - self._exec_sql_and_commit(sql) - - def table_apply_function(self, schema, table, res_column, fct, param): - """ apply a function to a column and save the result in other column """ - table = self._table_name(schema, table) - sql = "UPDATE %s SET %s = %s(%s)" % (table, self._quote(res_column), fct, self._quote(param)) - self._exec_sql_and_commit(sql) - - def table_enable_triggers(self, table, schema, enable=True): - """ enable or disable all triggers on table """ - table = self._table_name(schema, table) - sql = "ALTER TABLE %s %s TRIGGER ALL" % (table, "ENABLE" if enable else "DISABLE") - self._exec_sql_and_commit(sql) - - def table_enable_trigger(self, table, schema, trigger, enable=True): - """ enable or disable one trigger on table """ - table = self._table_name(schema, table) - sql = "ALTER TABLE %s %s TRIGGER %s" % (table, "ENABLE" if enable else "DISABLE", self._quote(trigger)) - self._exec_sql_and_commit(sql) - - def table_delete_trigger(self, table, schema, trigger): - """ delete trigger on table """ - table = self._table_name(schema, table) - sql = "DROP TRIGGER %s ON %s" % (self._quote(trigger), table) - self._exec_sql_and_commit(sql) - - def table_delete_rule(self, table, schema, rule): - """ delete rule on table """ - table = self._table_name(schema, table) - sql = "DROP RULE %s ON %s" % (self._quote(rule), table) - self._exec_sql_and_commit(sql) - - def create_index(self, table, name, column, schema=None): - """ create index on one column using default options """ - table_name = self._table_name(schema, table) - idx_name = self._quote(name) - sql = "CREATE INDEX %s ON %s (%s)" % (idx_name, table_name, self._quote(column)) - self._exec_sql_and_commit(sql) - - def create_spatial_index(self, table, schema=None, geom_column='the_geom'): - table_name = self._table_name(schema, table) - idx_name = self._quote("sidx_"+table) - sql = "CREATE INDEX %s ON %s USING GIST(%s GIST_GEOMETRY_OPS)" % (idx_name, table_name, self._quote(geom_column)) - self._exec_sql_and_commit(sql) - - def delete_index(self, name, schema=None): - index_name = self._table_name(schema, name) - sql = "DROP INDEX %s" % index_name - self._exec_sql_and_commit(sql) - - def get_database_privileges(self): - """ db privileges: (can create schemas, can create temp. tables) """ - sql = "SELECT has_database_privilege('%(d)s', 'CREATE'), has_database_privilege('%(d)s', 'TEMP')" % { 'd' : self._quote_str(self.dbname) } - c = self.con.cursor() - self._exec_sql(c, sql) - return c.fetchone() - - def get_schema_privileges(self, schema): - """ schema privileges: (can create new objects, can access objects in schema) """ - sql = "SELECT has_schema_privilege('%(s)s', 'CREATE'), has_schema_privilege('%(s)s', 'USAGE')" % { 's' : self._quote_str(schema) } - c = self.con.cursor() - self._exec_sql(c, sql) - return c.fetchone() - - def get_table_privileges(self, table, schema=None): - """ table privileges: (select, insert, update, delete) """ - t = self._table_name(schema, table) - sql = """SELECT has_table_privilege('%(t)s', 'SELECT'), has_table_privilege('%(t)s', 'INSERT'), - has_table_privilege('%(t)s', 'UPDATE'), has_table_privilege('%(t)s', 'DELETE')""" % { 't': self._quote_str(t) } - c = self.con.cursor() - self._exec_sql(c, sql) - return c.fetchone() - - def vacuum_analyze(self, table, schema=None): - """ run vacuum analyze on a table """ - t = self._table_name(schema, table) - # vacuum analyze must be run outside transaction block - we have to change isolation level - self.con.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) - c = self.con.cursor() - self._exec_sql(c, "VACUUM ANALYZE %s" % t) - self.con.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) - - def sr_info_for_srid(self, srid): - if not self.has_spatial: - return "Unknown" - - try: - c = self.con.cursor() - self._exec_sql(c, "SELECT srtext FROM spatial_ref_sys WHERE srid = '%d'" % srid) - sr = c.fetchone() - if sr is None: - return "Unknown" - srtext = sr[0] - # try to extract just SR name (should be qouted in double quotes) - x = re.search('"([^"]+)"', srtext) - if x is not None: - srtext = x.group() - return srtext - except DbError as e: - return "Unknown" - - def insert_table_row(self, table, values, schema=None, cursor=None): - """ insert a row with specified values to a table. - if a cursor is specified, it doesn't commit (expecting that there will be more inserts) - otherwise it commits immediately """ - t = self._table_name(schema, table) - sql = "" - for value in values: - # TODO: quote values? - if sql: sql += ", " - sql += value - sql = "INSERT INTO %s VALUES (%s)" % (t, sql) - if cursor: - self._exec_sql(cursor, sql) - else: - self._exec_sql_and_commit(sql) - - - def table_add_function_trigger(self, schema, table, resColumn, fct, geomColumn): - """ add a trigger on insert and update that recalculates the value from geometry column """ - - trig_f_name = "%s_calc_%s" % (table, fct) - trig_name = "calc_%s" % fct - ctx = { 'fname' : trig_f_name, 'tname' : trig_name, - 'res' : resColumn, 'geom' : geomColumn, - 'f' : fct, 'table' : self._table_name(schema, table) } - sql = """ - CREATE OR REPLACE FUNCTION %(fname)s() RETURNS TRIGGER AS - $$ - BEGIN - IF (TG_OP = 'INSERT') THEN - NEW.%(res)s := %(f)s(NEW.%(geom)s); - ELSIF (TG_OP = 'UPDATE') THEN - IF NOT (NEW.%(geom)s ~= OLD.%(geom)s) THEN - NEW.%(res)s := %(f)s(NEW.%(geom)s); - END IF; - END IF; - RETURN NEW; - END; - $$ - LANGUAGE 'plpgsql'; - - CREATE TRIGGER %(tname)s BEFORE INSERT OR UPDATE ON %(table)s FOR EACH ROW - EXECUTE PROCEDURE %(fname)s(); - """ % ctx - - self._exec_sql_and_commit(sql) - - - def get_named_cursor(self, table=None): - """ return an unique named cursor, optionally including a table name """ - self.last_cursor_id += 1 - if table is not None: - table2 = re.sub(r'\W', '_', table.encode('ascii','replace')) # all non-alphanum characters to underscore - cur_name = "cursor_%d_table_%s" % (self.last_cursor_id, table2) - else: - cur_name = "cursor_%d" % self.last_cursor_id - #cur_name = ("\"db_table_"+self.table+"\"").replace(' ', '_') - #cur_name = cur_name.encode('ascii','replace').replace('?', '_') - return self.con.cursor(cur_name) - - def _exec_sql(self, cursor, sql): - try: - cursor.execute(sql) - except psycopg2.Error as e: - # do the rollback to avoid a "current transaction aborted, commands ignored" errors - self.con.rollback() - raise DbError(e) - - def _exec_sql_and_commit(self, sql): - """ tries to execute and commit some action, on error it rolls back the change """ - #try: - c = self.con.cursor() - self._exec_sql(c, sql) - self.con.commit() - #except DbError, e: - # self.con.rollback() - # raise - - def _quote(self, identifier): - identifier = str(identifier) # make sure it's python unicode string - return u'"%s"' % identifier.replace('"', '""') - - def _quote_str(self, txt): - """ make the string safe - replace ' with '' """ - txt = str(txt) # make sure it's python unicode string - return txt.replace("'", "''") - - def _table_name(self, schema, table): - if not schema: - return self._quote(table) - else: - return u"%s.%s" % (self._quote(schema), self._quote(table)) - + @classmethod + def getTypeName(self): + return 'postgis' + + @classmethod + def getTypeNameString(self): + return 'PostgreSQL' + + @classmethod + def getProviderName(self): + return 'postgres' + + @classmethod + def getSettingsKey(self): + return 'PostgreSQL' + + @classmethod + def icon(self): + return QIcon(":/icons/postgis_elephant.png") + + @classmethod + def connect(self, selected, parent=None): + settings = QSettings() + settings.beginGroup( u"/%s/connections/%s" % (self.getSettingsKey(), selected) ) + + if not settings.contains( "database" ): # non-existent entry? + raise DbError( 'there is no defined database connection "%s".' % selected ) + + get_value_str = lambda x: str(settings.value(x) if Utils.isSIPv2() else settings.value(x).toString()) + service, host, port, database, username, password = list(map(get_value_str, ["service", "host", "port", "database", "username", "password"])) + + # qgis1.5 use 'savePassword' instead of 'save' setting + isSave = settings.value("save") if Utils.isSIPv2() else settings.value("save").toBool() + isSavePassword = settings.value("savePassword") if Utils.isSIPv2() else settings.value("savePassword").toBool() + if not ( isSave or isSavePassword ): + (password, ok) = QInputDialog.getText(parent, "Enter password", 'Enter password for connection "%s":' % selected, QLineEdit.Password) + if not ok: return + + settings.endGroup() + + uri = QgsDataSourceUri() + if service: + uri.setConnection(service, database, username, password) + else: + uri.setConnection(host, port, database, username, password) + + return Connection(uri) + + + def __init__(self, uri): + DbConn.Connection.__init__(self, uri) + + self.service = uri.service() + self.host = uri.host() + self.port = uri.port() + self.dbname = uri.database() + self.user = uri.username() + self.passwd = uri.password() + + try: + self.con = psycopg2.connect(self.con_info()) + except psycopg2.OperationalError as e: + raise DbError(e) + + if not self.dbname: + self.dbname = self.get_dbname() + + self.has_spatial = self.check_spatial() + + self.check_geometry_columns_table() + + # a counter to ensure that the cursor will be unique + self.last_cursor_id = 0 + + def con_info(self): + con_str = '' + if self.service: con_str += "service='%s' " % self.service + if self.host: con_str += "host='%s' " % self.host + if self.port: con_str += "port=%s " % self.port + if self.dbname: con_str += "dbname='%s' " % self.dbname + if self.user: con_str += "user='%s' " % self.user + if self.passwd: con_str += "password='%s' " % self.passwd + return con_str + + def get_dbname(self): + c = self.con.cursor() + self._exec_sql(c, "SELECT current_database()") + return c.fetchone()[0] + + def get_info(self): + c = self.con.cursor() + self._exec_sql(c, "SELECT version()") + return c.fetchone()[0] + + def check_spatial(self): + """ check whether postgis_version is present in catalog """ + c = self.con.cursor() + self._exec_sql(c, "SELECT COUNT(*) FROM pg_proc WHERE proname = 'postgis_version'") + return (c.fetchone()[0] > 0) + + def get_spatial_info(self): + """ returns tuple about postgis support: + - lib version + - installed scripts version + - released scripts version + - geos version + - proj version + - whether uses stats + """ + c = self.con.cursor() + self._exec_sql(c, "SELECT postgis_lib_version(), postgis_scripts_installed(), postgis_scripts_released(), postgis_geos_version(), postgis_proj_version(), postgis_uses_stats()") + return c.fetchone() + + def check_geometry_columns_table(self): + + c = self.con.cursor() + self._exec_sql(c, "SELECT relname FROM pg_class WHERE relname = 'geometry_columns' AND pg_class.relkind IN ('v', 'r')") + self.has_geometry_columns = (len(c.fetchall()) != 0) + + if not self.has_geometry_columns: + self.has_geometry_columns_access = False + return + + # find out whether has privileges to access geometry_columns table + self.has_geometry_columns_access = self.get_table_privileges('geometry_columns')[0] + + + def list_schemas(self): + """ + get list of schemas in tuples: (oid, name, owner, perms) + """ + c = self.con.cursor() + sql = "SELECT oid, nspname, pg_get_userbyid(nspowner), nspacl FROM pg_namespace WHERE nspname !~ '^pg_' AND nspname != 'information_schema'" + self._exec_sql(c, sql) + + schema_cmp = lambda x,y: -1 if x[1] < y[1] else 1 + + return sorted(c.fetchall(), cmp=schema_cmp) + + def list_geotables(self, schema=None): + """ + get list of tables with schemas, whether user has privileges, whether table has geometry column(s) etc. + + geometry_columns: + - f_table_schema + - f_table_name + - f_geometry_column + - coord_dimension + - srid + - type + """ + c = self.con.cursor() + + if schema: + schema_where = " AND nspname = '%s' " % self._quote_str(schema) + else: + schema_where = " AND (nspname != 'information_schema' AND nspname !~ 'pg_') " + + # LEFT OUTER JOIN: like LEFT JOIN but if there are more matches, for join, all are used (not only one) + + # first find out whether postgis is enabled + if not self.has_spatial: + # get all tables and views + sql = """SELECT pg_class.relname, pg_namespace.nspname, pg_class.relkind, pg_get_userbyid(relowner), reltuples, relpages, NULL, NULL, NULL, NULL + FROM pg_class + JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace + WHERE pg_class.relkind IN ('v', 'r')""" + schema_where + "ORDER BY nspname, relname" + else: + # discovery of all tables and whether they contain a geometry column + sql = """SELECT pg_class.relname, pg_namespace.nspname, pg_class.relkind, pg_get_userbyid(relowner), reltuples, relpages, pg_attribute.attname, pg_attribute.atttypid::regtype, NULL, NULL + FROM pg_class + JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace + LEFT OUTER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid AND + ( pg_attribute.atttypid = 'geometry'::regtype + OR pg_attribute.atttypid IN (SELECT oid FROM pg_type WHERE typbasetype='geometry'::regtype ) ) + WHERE pg_class.relkind IN ('v', 'r')""" + schema_where + "ORDER BY nspname, relname, attname" + + self._exec_sql(c, sql) + items = c.fetchall() + + # get geometry info from geometry_columns if exists + if self.has_spatial and self.has_geometry_columns and self.has_geometry_columns_access: + sql = """SELECT relname, nspname, relkind, pg_get_userbyid(relowner), reltuples, relpages, + geometry_columns.f_geometry_column, geometry_columns.type, geometry_columns.coord_dimension, geometry_columns.srid + FROM pg_class + JOIN pg_namespace ON relnamespace=pg_namespace.oid + LEFT OUTER JOIN geometry_columns ON relname=f_table_name AND nspname=f_table_schema + WHERE (relkind = 'r' or relkind='v') """ + schema_where + "ORDER BY nspname, relname, f_geometry_column" + self._exec_sql(c, sql) + + # merge geometry info to "items" + for i, geo_item in enumerate(c.fetchall()): + if geo_item[7]: + items[i] = geo_item + + return items + + + def get_table_rows(self, table, schema=None): + c = self.con.cursor() + self._exec_sql(c, "SELECT COUNT(*) FROM %s" % self._table_name(schema, table)) + return c.fetchone()[0] + + + def get_table_fields(self, table, schema=None): + """ return list of columns in table """ + c = self.con.cursor() + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """SELECT a.attnum AS ordinal_position, + a.attname AS column_name, + t.typname AS data_type, + a.attlen AS char_max_len, + a.atttypmod AS modifier, + a.attnotnull AS notnull, + a.atthasdef AS hasdefault, + adef.adsrc AS default_value + FROM pg_class c + JOIN pg_attribute a ON a.attrelid = c.oid + JOIN pg_type t ON a.atttypid = t.oid + JOIN pg_namespace nsp ON c.relnamespace = nsp.oid + LEFT JOIN pg_attrdef adef ON adef.adrelid = a.attrelid AND adef.adnum = a.attnum + WHERE + c.relname = '%s' %s AND + a.attnum > 0 + ORDER BY a.attnum""" % (self._quote_str(table), schema_where) + + self._exec_sql(c, sql) + attrs = [] + for row in c.fetchall(): + attrs.append(TableAttribute(row)) + return attrs + + + def get_table_indexes(self, table, schema=None): + """ get info about table's indexes. ignore primary key and unique constraint index, they get listed in constaints """ + c = self.con.cursor() + + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """SELECT relname, indkey FROM pg_class, pg_index + WHERE pg_class.oid = pg_index.indexrelid AND pg_class.oid IN ( + SELECT indexrelid FROM pg_index, pg_class + JOIN pg_namespace nsp ON pg_class.relnamespace = nsp.oid + WHERE pg_class.relname='%s' %s AND pg_class.oid=pg_index.indrelid + AND indisprimary != 't' )""" % (self._quote_str(table), schema_where) # AND indisunique != 't' + self._exec_sql(c, sql) + indexes = [] + for row in c.fetchall(): + indexes.append(TableIndex(row)) + return indexes + + + def get_table_unique_indexes(self, table, schema=None): + """ get all the unique indexes """ + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """SELECT relname, indkey + FROM pg_index JOIN pg_class ON pg_index.indrelid=pg_class.oid + JOIN pg_namespace nsp ON pg_class.relnamespace = nsp.oid + WHERE pg_class.relname='%s' %s + AND indisprimary != 't' AND indisunique = 't'""" % (self._quote_str(table), schema_where) + c = self.con.cursor() + self._exec_sql(c, sql) + uniqueIndexes = [] + for row in c.fetchall(): + uniqueIndexes.append(TableIndex(row)) + return uniqueIndexes + + + def get_table_constraints(self, table, schema=None): + c = self.con.cursor() + + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """SELECT c.conname, c.contype, c.condeferrable, c.condeferred, array_to_string(c.conkey, ' '), c.consrc, + t2.relname, c.confupdtype, c.confdeltype, c.confmatchtype, array_to_string(c.confkey, ' ') FROM pg_constraint c + LEFT JOIN pg_class t ON c.conrelid = t.oid + LEFT JOIN pg_class t2 ON c.confrelid = t2.oid + JOIN pg_namespace nsp ON t.relnamespace = nsp.oid + WHERE t.relname = '%s' %s """ % (self._quote_str(table), schema_where) + + self._exec_sql(c, sql) + + constrs = [] + for row in c.fetchall(): + constrs.append(TableConstraint(row)) + return constrs + + + def get_table_triggers(self, table, schema=None): + c = self.con.cursor() + + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """ SELECT tgname, proname, tgtype, tgenabled FROM pg_trigger trig + LEFT JOIN pg_class t ON trig.tgrelid = t.oid + LEFT JOIN pg_proc p ON trig.tgfoid = p.oid + JOIN pg_namespace nsp ON t.relnamespace = nsp.oid + WHERE t.relname ='%s' %s """ % (self._quote_str(table), schema_where) + + self._exec_sql(c, sql) + + triggers = [] + for row in c.fetchall(): + triggers.append(TableTrigger(row)) + return triggers + + + def get_table_rules(self, table, schema=None): + c = self.con.cursor() + + schema_where = " AND schemaname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """ SELECT rulename, definition FROM pg_rules + WHERE tablename='%s' %s """ % (self._quote_str(table), schema_where) + + self._exec_sql(c, sql) + + rules = [] + for row in c.fetchall(): + rules.append(TableRule(row)) + + return rules + + def get_table_estimated_extent(self, geom, table, schema=None): + """ find out estimated extent (from the statistics) """ + c = self.con.cursor() + + extent = "estimated_extent('%s','%s','%s')" % (self._quote_str(schema), self._quote_str(table), self._quote_str(geom)) + sql = """ SELECT xmin(%(ext)s), ymin(%(ext)s), xmax(%(ext)s), ymax(%(ext)s) """ % { 'ext' : extent } + self._exec_sql(c, sql) + + row = c.fetchone() + return row + + def get_view_definition(self, view, schema=None): + """ returns definition of the view """ + schema_where = " AND nspname='%s' " % self._quote_str(schema) if schema is not None else "" + sql = """SELECT pg_get_viewdef(c.oid) FROM pg_class c + JOIN pg_namespace nsp ON c.relnamespace = nsp.oid + WHERE relname='%s' %s AND relkind='v'""" % (self._quote_str(view), schema_where) + c = self.con.cursor() + self._exec_sql(c, sql) + return c.fetchone()[0] + + """ + def list_tables(self): + c = self.con.cursor() + c.execute("SELECT relname FROM pg_class WHERE relname !~ '^(pg_|sql_)' AND relkind = 'r'") + return c.fetchall() + """ + + def add_geometry_column(self, table, geom_type, schema=None, geom_column='the_geom', srid=-1, dim=2): + + # use schema if explicitly specified + if schema: + schema_part = "'%s', " % self._quote_str(schema) + else: + schema_part = "" + sql = "SELECT AddGeometryColumn(%s'%s', '%s', %d, '%s', %d)" % (schema_part, self._quote_str(table), self._quote_str(geom_column), srid, self._quote_str(geom_type), dim) + self._exec_sql_and_commit(sql) + + def delete_geometry_column(self, table, geom_column, schema=None): + """ use postgis function to delete geometry column correctly """ + if schema: + schema_part = "'%s', " % self._quote_str(schema) + else: + schema_part = "" + sql = "SELECT DropGeometryColumn(%s'%s', '%s')" % (schema_part, self._quote_str(table), self._quote_str(geom_column)) + self._exec_sql_and_commit(sql) + + def delete_geometry_table(self, table, schema=None): + """ delete table with one or more geometries using postgis function """ + if schema: + schema_part = "'%s', " % self._quote_str(schema) + else: + schema_part = "" + sql = "SELECT DropGeometryTable(%s'%s')" % (schema_part, self._quote_str(table)) + self._exec_sql_and_commit(sql) + + def create_table(self, table, fields, pkey=None, schema=None): + """ create ordinary table + 'fields' is array containing instances of TableField + 'pkey' contains name of column to be used as primary key + """ + + if len(fields) == 0: + return False + + table_name = self._table_name(schema, table) + + sql = "CREATE TABLE %s (%s" % (table_name, fields[0].field_def(self)) + for field in fields[1:]: + sql += ", %s" % field.field_def(self) + if pkey: + sql += ", PRIMARY KEY (%s)" % self._quote(pkey) + sql += ")" + self._exec_sql_and_commit(sql) + return True + + def delete_table(self, table, schema=None): + """ delete table from the database """ + table_name = self._table_name(schema, table) + sql = "DROP TABLE %s" % table_name + self._exec_sql_and_commit(sql) + + def empty_table(self, table, schema=None): + """ delete all rows from table """ + table_name = self._table_name(schema, table) + sql = "TRUNCATE %s" % table_name + self._exec_sql_and_commit(sql) + + def rename_table(self, table, new_table, schema=None): + """ rename a table in database """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s RENAME TO %s" % (table_name, self._quote(new_table)) + self._exec_sql_and_commit(sql) + + # update geometry_columns if postgis is enabled + if self.has_spatial and self.has_geometry_columns and self.has_geometry_columns_access: + sql = "UPDATE geometry_columns SET f_table_name='%s' WHERE f_table_name='%s'" % (self._quote_str(new_table), self._quote_str(table)) + if schema is not None: + sql += " AND f_table_schema='%s'" % self._quote_str(schema) + self._exec_sql_and_commit(sql) + + def create_view(self, name, query, schema=None): + view_name = self._table_name(schema, name) + sql = "CREATE VIEW %s AS %s" % (view_name, query) + self._exec_sql_and_commit(sql) + + def delete_view(self, name, schema=None): + view_name = self._table_name(schema, name) + sql = "DROP VIEW %s" % view_name + self._exec_sql_and_commit(sql) + + def rename_view(self, name, new_name, schema=None): + """ rename view in database """ + self.rename_table(name, new_name, schema) + + def create_schema(self, schema): + """ create a new empty schema in database """ + sql = "CREATE SCHEMA %s" % self._quote(schema) + self._exec_sql_and_commit(sql) + + def delete_schema(self, schema): + """ drop (empty) schema from database """ + sql = "DROP SCHEMA %s" % self._quote(schema) + self._exec_sql_and_commit(sql) + + def rename_schema(self, schema, new_schema): + """ rename a schema in database """ + sql = "ALTER SCHEMA %s RENAME TO %s" % (self._quote(schema), self._quote(new_schema)) + self._exec_sql_and_commit(sql) + + # update geometry_columns if postgis is enabled + if self.has_spatial: + sql = "UPDATE geometry_columns SET f_table_schema='%s' WHERE f_table_schema='%s'" % (self._quote_str(new_schema), self._quote_str(schema)) + self._exec_sql_and_commit(sql) + + def table_add_column(self, table, field, schema=None): + """ add a column to table (passed as TableField instance) """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s ADD %s" % (table_name, field.field_def(self)) + self._exec_sql_and_commit(sql) + + def table_delete_column(self, table, field, schema=None): + """ delete column from a table """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s DROP %s" % (table_name, self._quote(field)) + self._exec_sql_and_commit(sql) + + def table_column_rename(self, table, name, new_name, schema=None): + """ rename column in a table """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s RENAME %s TO %s" % (table_name, self._quote(name), self._quote(new_name)) + self._exec_sql_and_commit(sql) + + # update geometry_columns if postgis is enabled + if self.has_spatial: + sql = "UPDATE geometry_columns SET f_geometry_column='%s' WHERE f_geometry_column='%s' AND f_table_name='%s'" % (self._quote_str(new_name), self._quote_str(name), self._quote_str(table)) + if schema is not None: + sql += " AND f_table_schema='%s'" % self._quote(schema) + self._exec_sql_and_commit(sql) + + def table_column_set_type(self, table, column, data_type, schema=None): + """ change column type """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s ALTER %s TYPE %s" % (table_name, self._quote(column), data_type) + self._exec_sql_and_commit(sql) + + def table_column_set_default(self, table, column, default, schema=None): + """ change column's default value. If default=None drop default value """ + table_name = self._table_name(schema, table) + if default: + sql = "ALTER TABLE %s ALTER %s SET DEFAULT %s" % (table_name, self._quote(column), default) + else: + sql = "ALTER TABLE %s ALTER %s DROP DEFAULT" % (table_name, self._quote(column)) + self._exec_sql_and_commit(sql) + + def table_column_set_null(self, table, column, is_null, schema=None): + """ change whether column can contain null values """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s ALTER %s " % (table_name, self._quote(column)) + if is_null: + sql += "DROP NOT NULL" + else: + sql += "SET NOT NULL" + self._exec_sql_and_commit(sql) + + def table_add_primary_key(self, table, column, schema=None): + """ add a primery key (with one column) to a table """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s ADD PRIMARY KEY (%s)" % (table_name, self._quote(column)) + self._exec_sql_and_commit(sql) + + def table_add_unique_constraint(self, table, column, schema=None): + """ add a unique constraint to a table """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s ADD UNIQUE (%s)" % (table_name, self._quote(column)) + self._exec_sql_and_commit(sql) + + def table_delete_constraint(self, table, constraint, schema=None): + """ delete constraint in a table """ + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s DROP CONSTRAINT %s" % (table_name, self._quote(constraint)) + self._exec_sql_and_commit(sql) + + def table_move_to_schema(self, table, new_schema, schema=None): + if new_schema == schema: + return + table_name = self._table_name(schema, table) + sql = "ALTER TABLE %s SET SCHEMA %s" % (table_name, self._quote(new_schema)) + self._exec_sql_and_commit(sql) + + # update geometry_columns if postgis is enabled + if self.has_spatial: + sql = "UPDATE geometry_columns SET f_table_schema='%s' WHERE f_table_name='%s'" % (self._quote_str(new_schema), self._quote_str(table)) + if schema is not None: + sql += " AND f_table_schema='%s'" % self._quote_str(schema) + self._exec_sql_and_commit(sql) + + def table_apply_function(self, schema, table, res_column, fct, param): + """ apply a function to a column and save the result in other column """ + table = self._table_name(schema, table) + sql = "UPDATE %s SET %s = %s(%s)" % (table, self._quote(res_column), fct, self._quote(param)) + self._exec_sql_and_commit(sql) + + def table_enable_triggers(self, table, schema, enable=True): + """ enable or disable all triggers on table """ + table = self._table_name(schema, table) + sql = "ALTER TABLE %s %s TRIGGER ALL" % (table, "ENABLE" if enable else "DISABLE") + self._exec_sql_and_commit(sql) + + def table_enable_trigger(self, table, schema, trigger, enable=True): + """ enable or disable one trigger on table """ + table = self._table_name(schema, table) + sql = "ALTER TABLE %s %s TRIGGER %s" % (table, "ENABLE" if enable else "DISABLE", self._quote(trigger)) + self._exec_sql_and_commit(sql) + + def table_delete_trigger(self, table, schema, trigger): + """ delete trigger on table """ + table = self._table_name(schema, table) + sql = "DROP TRIGGER %s ON %s" % (self._quote(trigger), table) + self._exec_sql_and_commit(sql) + + def table_delete_rule(self, table, schema, rule): + """ delete rule on table """ + table = self._table_name(schema, table) + sql = "DROP RULE %s ON %s" % (self._quote(rule), table) + self._exec_sql_and_commit(sql) + + def create_index(self, table, name, column, schema=None): + """ create index on one column using default options """ + table_name = self._table_name(schema, table) + idx_name = self._quote(name) + sql = "CREATE INDEX %s ON %s (%s)" % (idx_name, table_name, self._quote(column)) + self._exec_sql_and_commit(sql) + + def create_spatial_index(self, table, schema=None, geom_column='the_geom'): + table_name = self._table_name(schema, table) + idx_name = self._quote("sidx_"+table) + sql = "CREATE INDEX %s ON %s USING GIST(%s GIST_GEOMETRY_OPS)" % (idx_name, table_name, self._quote(geom_column)) + self._exec_sql_and_commit(sql) + + def delete_index(self, name, schema=None): + index_name = self._table_name(schema, name) + sql = "DROP INDEX %s" % index_name + self._exec_sql_and_commit(sql) + + def get_database_privileges(self): + """ db privileges: (can create schemas, can create temp. tables) """ + sql = "SELECT has_database_privilege('%(d)s', 'CREATE'), has_database_privilege('%(d)s', 'TEMP')" % { 'd' : self._quote_str(self.dbname) } + c = self.con.cursor() + self._exec_sql(c, sql) + return c.fetchone() + + def get_schema_privileges(self, schema): + """ schema privileges: (can create new objects, can access objects in schema) """ + sql = "SELECT has_schema_privilege('%(s)s', 'CREATE'), has_schema_privilege('%(s)s', 'USAGE')" % { 's' : self._quote_str(schema) } + c = self.con.cursor() + self._exec_sql(c, sql) + return c.fetchone() + + def get_table_privileges(self, table, schema=None): + """ table privileges: (select, insert, update, delete) """ + t = self._table_name(schema, table) + sql = """SELECT has_table_privilege('%(t)s', 'SELECT'), has_table_privilege('%(t)s', 'INSERT'), + has_table_privilege('%(t)s', 'UPDATE'), has_table_privilege('%(t)s', 'DELETE')""" % { 't': self._quote_str(t) } + c = self.con.cursor() + self._exec_sql(c, sql) + return c.fetchone() + + def vacuum_analyze(self, table, schema=None): + """ run vacuum analyze on a table """ + t = self._table_name(schema, table) + # vacuum analyze must be run outside transaction block - we have to change isolation level + self.con.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + c = self.con.cursor() + self._exec_sql(c, "VACUUM ANALYZE %s" % t) + self.con.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED) + + def sr_info_for_srid(self, srid): + if not self.has_spatial: + return "Unknown" + + try: + c = self.con.cursor() + self._exec_sql(c, "SELECT srtext FROM spatial_ref_sys WHERE srid = '%d'" % srid) + sr = c.fetchone() + if sr is None: + return "Unknown" + srtext = sr[0] + # try to extract just SR name (should be qouted in double quotes) + x = re.search('"([^"]+)"', srtext) + if x is not None: + srtext = x.group() + return srtext + except DbError as e: + return "Unknown" + + def insert_table_row(self, table, values, schema=None, cursor=None): + """ insert a row with specified values to a table. + if a cursor is specified, it doesn't commit (expecting that there will be more inserts) + otherwise it commits immediately """ + t = self._table_name(schema, table) + sql = "" + for value in values: + # TODO: quote values? + if sql: sql += ", " + sql += value + sql = "INSERT INTO %s VALUES (%s)" % (t, sql) + if cursor: + self._exec_sql(cursor, sql) + else: + self._exec_sql_and_commit(sql) + + + def table_add_function_trigger(self, schema, table, resColumn, fct, geomColumn): + """ add a trigger on insert and update that recalculates the value from geometry column """ + + trig_f_name = "%s_calc_%s" % (table, fct) + trig_name = "calc_%s" % fct + ctx = { 'fname' : trig_f_name, 'tname' : trig_name, + 'res' : resColumn, 'geom' : geomColumn, + 'f' : fct, 'table' : self._table_name(schema, table) } + sql = """ + CREATE OR REPLACE FUNCTION %(fname)s() RETURNS TRIGGER AS + $$ + BEGIN + IF (TG_OP = 'INSERT') THEN + NEW.%(res)s := %(f)s(NEW.%(geom)s); + ELSIF (TG_OP = 'UPDATE') THEN + IF NOT (NEW.%(geom)s ~= OLD.%(geom)s) THEN + NEW.%(res)s := %(f)s(NEW.%(geom)s); + END IF; + END IF; + RETURN NEW; + END; + $$ + LANGUAGE 'plpgsql'; + + CREATE TRIGGER %(tname)s BEFORE INSERT OR UPDATE ON %(table)s FOR EACH ROW + EXECUTE PROCEDURE %(fname)s(); + """ % ctx + + self._exec_sql_and_commit(sql) + + + def get_named_cursor(self, table=None): + """ return an unique named cursor, optionally including a table name """ + self.last_cursor_id += 1 + if table is not None: + table2 = re.sub(r'\W', '_', table.encode('ascii','replace')) # all non-alphanum characters to underscore + cur_name = "cursor_%d_table_%s" % (self.last_cursor_id, table2) + else: + cur_name = "cursor_%d" % self.last_cursor_id + #cur_name = ("\"db_table_"+self.table+"\"").replace(' ', '_') + #cur_name = cur_name.encode('ascii','replace').replace('?', '_') + return self.con.cursor(cur_name) + + def _exec_sql(self, cursor, sql): + try: + cursor.execute(sql) + except psycopg2.Error as e: + # do the rollback to avoid a "current transaction aborted, commands ignored" errors + self.con.rollback() + raise DbError(e) + + def _exec_sql_and_commit(self, sql): + """ tries to execute and commit some action, on error it rolls back the change """ + #try: + c = self.con.cursor() + self._exec_sql(c, sql) + self.con.commit() + #except DbError, e: + # self.con.rollback() + # raise + + def _quote(self, identifier): + identifier = str(identifier) # make sure it's python unicode string + return u'"%s"' % identifier.replace('"', '""') + + def _quote_str(self, txt): + """ make the string safe - replace ' with '' """ + txt = str(txt) # make sure it's python unicode string + return txt.replace("'", "''") + + def _table_name(self, schema, table): + if not schema: + return self._quote(table) + else: + return u"%s.%s" % (self._quote(schema), self._quote(table)) + # for debugging / testing if __name__ == '__main__': - db = GeoDB(host='localhost',dbname='gis',user='gisak',passwd='g') - - # fix_print_with_import - print(db.list_schemas()) - # fix_print_with_import - print('==========') - - for row in db.list_geotables(): - # fix_print_with_import - print(row) - - # fix_print_with_import - print('==========') - - for row in db.get_table_indexes('trencin'): - # fix_print_with_import - print(row) - - # fix_print_with_import - print('==========') - - for row in db.get_table_constraints('trencin'): - # fix_print_with_import - print(row) - - # fix_print_with_import - print('==========') - - # fix_print_with_import - print(db.get_table_rows('trencin')) - - #for fld in db.get_table_metadata('trencin'): - # print fld - - #try: - # db.create_table('trrrr', [('id','serial'), ('test','text')]) - #except DbError, e: - # print e.message, e.query - + db = GeoDB(host='localhost',dbname='gis',user='gisak',passwd='g') + + # fix_print_with_import + print(db.list_schemas()) + # fix_print_with_import + print('==========') + + for row in db.list_geotables(): + # fix_print_with_import + print(row) + + # fix_print_with_import + print('==========') + + for row in db.get_table_indexes('trencin'): + # fix_print_with_import + print(row) + + # fix_print_with_import + print('==========') + + for row in db.get_table_constraints('trencin'): + # fix_print_with_import + print(row) + + # fix_print_with_import + print('==========') + + # fix_print_with_import + print(db.get_table_rows('trencin')) + + #for fld in db.get_table_metadata('trencin'): + # print fld + + #try: + # db.create_table('trrrr', [('id','serial'), ('test','text')]) + #except DbError, e: + # print e.message, e.query + # vim: noet ts=8 : From 3bf0d6927a587c32968413a3987a5300dcf7842e Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 2 Jul 2018 02:31:24 +0200 Subject: [PATCH 15/32] editorconfig --- .editorconfig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b1e512c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +charset = utf-8 + +# Docstrings and comments use max_line_length = 79 +[*.py] +max_line_length = 119 + +# Makefiles always use tabs for indentation +[Makefile] +indent_style = tab From 8104e4eb260cf983bd41a264bfc7b4e1a111cfbd Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Sun, 8 Jul 2018 19:07:15 +0530 Subject: [PATCH 16/32] API_Changes_8 --- functions/dijkstraCost.py | 12 ++++++------ functions/kdijkstra_cost.py | 10 +++++----- functions/kdijkstra_path.py | 6 +++--- functions/ksp.py | 6 +++--- functions/trsp_edge.py | 6 +++--- functions/trsp_vertex.py | 4 ++-- functions/trsp_via_edges.py | 6 +++--- functions/trsp_via_vertices.py | 6 +++--- functions/tsp_euclid.py | 12 ++++++------ pgRoutingLayer.py | 14 +++++++------- 10 files changed, 41 insertions(+), 41 deletions(-) diff --git a/functions/dijkstraCost.py b/functions/dijkstraCost.py index 219a866..d6490e4 100755 --- a/functions/dijkstraCost.py +++ b/functions/dijkstraCost.py @@ -1,8 +1,8 @@ from __future__ import absolute_import from builtins import str from qgis.PyQt.QtCore import QSizeF, QPointF -from qgis.PyQt.QtGui import QColor -from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation +from qgis.PyQt.QtGui import QColor, QTextDocument +from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation, QgsWkbTypes, QgsAnnotation from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -108,11 +108,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) @@ -145,10 +145,10 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): textAnnotation = QgsTextAnnotation(mapCanvas) textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) - textAnnotation.setOffsetFromReferencePoint(QPointF(20, -40)) + textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) textAnnotation.setDocument(textDocument) - textAnnotation.update() + #textAnnotation.update() resultNodesTextAnnotations.append(textAnnotation) diff --git a/functions/kdijkstra_cost.py b/functions/kdijkstra_cost.py index b67b96f..6e7f0e4 100755 --- a/functions/kdijkstra_cost.py +++ b/functions/kdijkstra_cost.py @@ -2,7 +2,7 @@ from builtins import str from qgis.PyQt.QtCore import QSizeF, QPointF from qgis.PyQt.QtGui import QColor -from qgis.core import Qgis, QgsGeometry, QgsTextAnnotation +from qgis.core import Qgis, QgsGeometry, QgsTextAnnotation, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -105,11 +105,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) @@ -144,10 +144,10 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): textAnnotation = QgsTextAnnotation(mapCanvas) textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) - textAnnotation.setOffsetFromReferencePoint(QPointF(20, -40)) + textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) textAnnotation.setDocument(textDocument) - textAnnotation.update() + #textAnnotation.update() resultNodesTextAnnotations.append(textAnnotation) canvasItemList['annotations'] = resultNodesTextAnnotations diff --git a/functions/kdijkstra_path.py b/functions/kdijkstra_path.py index 614c030..4e60a08 100755 --- a/functions/kdijkstra_path.py +++ b/functions/kdijkstra_path.py @@ -2,7 +2,7 @@ from qgis.PyQt.QtCore import * from builtins import str from qgis.PyQt.QtGui import QColor -from qgis.core import QgsGeometry, Qgis +from qgis.core import QgsGeometry, Qgis, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -138,11 +138,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) diff --git a/functions/ksp.py b/functions/ksp.py index 9dc0872..6471822 100755 --- a/functions/ksp.py +++ b/functions/ksp.py @@ -2,7 +2,7 @@ from qgis.PyQt.QtCore import * from builtins import str from qgis.PyQt.QtGui import QColor -from qgis.core import Qgis, QgsGeometry +from qgis.core import Qgis, QgsGeometry, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -232,11 +232,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (route_id:%(result_route_id)d, node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) diff --git a/functions/trsp_edge.py b/functions/trsp_edge.py index eed48de..281e4cb 100755 --- a/functions/trsp_edge.py +++ b/functions/trsp_edge.py @@ -3,7 +3,7 @@ from qgis.PyQt.QtGui import * from qgis.PyQt.QtWidgets import * from builtins import str -from qgis.core import Qgis, QgsGeometry +from qgis.core import Qgis, QgsGeometry, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -174,11 +174,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: resultPathRubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): resultPathRubberBand.addPoint(pt) diff --git a/functions/trsp_vertex.py b/functions/trsp_vertex.py index 3415a4f..dd59ed0 100755 --- a/functions/trsp_vertex.py +++ b/functions/trsp_vertex.py @@ -76,11 +76,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: resultPathRubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): resultPathRubberBand.addPoint(pt) diff --git a/functions/trsp_via_edges.py b/functions/trsp_via_edges.py index f98d789..93bd1f4 100755 --- a/functions/trsp_via_edges.py +++ b/functions/trsp_via_edges.py @@ -2,7 +2,7 @@ from qgis.PyQt.QtCore import * from builtins import str from qgis.PyQt.QtGui import QColor -from qgis.core import Qgis, QgsGeometry +from qgis.core import Qgis, QgsGeometry, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -257,11 +257,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) diff --git a/functions/trsp_via_vertices.py b/functions/trsp_via_vertices.py index 9a4f88e..eba3a70 100755 --- a/functions/trsp_via_vertices.py +++ b/functions/trsp_via_vertices.py @@ -2,7 +2,7 @@ from qgis.PyQt.QtCore import * from builtins import str from qgis.PyQt.QtGui import QColor -from qgis.core import Qgis, QgsGeometry +from qgis.core import Qgis, QgsGeometry, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -105,11 +105,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: rubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) diff --git a/functions/tsp_euclid.py b/functions/tsp_euclid.py index b762eb9..30f281c 100755 --- a/functions/tsp_euclid.py +++ b/functions/tsp_euclid.py @@ -97,11 +97,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: resultPathsRubberBands.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): resultPathsRubberBands.addPoint(pt) prevrow = row @@ -123,11 +123,11 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: resultPathsRubberBands.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): resultPathsRubberBands.addPoint(pt) @@ -163,9 +163,9 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): textAnnotation = QgsTextAnnotation(mapCanvas) textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) - textAnnotation.setOffsetFromReferencePoint(QPointF(20, -40)) + textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) textAnnotation.setDocument(textDocument) - textAnnotation.update() + #textAnnotation.update() resultNodesTextAnnotations.append(textAnnotation) def __init__(self, ui): diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index 5103f6b..142e28e 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -27,7 +27,7 @@ from qgis.PyQt.QtCore import Qt, QObject, pyqtSignal, QRegExp, QSettings from qgis.PyQt.QtGui import QColor, QIcon, QIntValidator, QDoubleValidator,QRegExpValidator, QCursor from qgis.PyQt.QtWidgets import QAction, QDockWidget, QApplication, QLabel, QLineEdit, QPushButton, QWidget,QGridLayout,QToolButton,QVBoxLayout,QHBoxLayout,QSplitter,QGroupBox,QScrollArea,QPlainTextEdit, QMessageBox -from qgis.core import QgsMessageLog,Qgis,QgsRectangle, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject, QgsGeometry +from qgis.core import QgsMessageLog,Qgis,QgsRectangle, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject, QgsGeometry,QgsWkbTypes from qgis.gui import QgsVertexMarker,QgsRubberBand,QgsMapToolEmitPoint from pgRoutingLayer import dbConnection from qgis.utils import iface @@ -386,11 +386,11 @@ def setIds(self, pt): idRubberBand = QgsRubberBand(mapCanvas, Utils.getRubberBandType(False)) idRubberBand.setColor(Qt.yellow) idRubberBand.setWidth(4) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: idRubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): idRubberBand.addPoint(pt) self.idsRubberBands.append(idRubberBand) @@ -433,11 +433,11 @@ def setSourceId(self, pt): if result: self.dock.lineEditSourceId.setText(str(id)) geom = QgsGeometry().fromWkt(wkt) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: self.sourceIdRubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): self.sourceIdRubberBand.addPoint(pt) self.dock.lineEditSourcePos.setText(str(pos)) @@ -505,11 +505,11 @@ def setTargetId(self, pt): if result: self.dock.lineEditTargetId.setText(str(id)) geom = QgsGeometry().fromWkt(wkt) - if geom.wkbType() == Qgis.WKBMultiLineString: + if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): for pt in line: self.targetIdRubberBand.addPoint(pt) - elif geom.wkbType() == Qgis.WKBLineString: + elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): self.targetIdRubberBand.addPoint(pt) self.dock.lineEditTargetPos.setText(str(pos)) From da154ff07f7d88c3fe5856976fe70ac3fe9d2e3d Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Wed, 11 Jul 2018 00:36:57 +0200 Subject: [PATCH 17/32] fixed bug in QgsDataSourceUri handling --- dbConnection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbConnection.py b/dbConnection.py index d1f7af2..11ad58a 100755 --- a/dbConnection.py +++ b/dbConnection.py @@ -117,7 +117,7 @@ def getAvailableConnections(self): def getURI(self): # returns a new QgsDataSourceUri instance - return qgis.core.QgsDataSourceUri( self.uri.connectionInfo() ) + return QgsDataSourceUri( self.uri.connectionInfo() ) def getAction(self, parent=None): return Connection.ConnectionAction(self.uri.database(), self.getTypeName(), parent) From 64874883c3cdccf35e3f39937e6e0881376b600d Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Wed, 11 Jul 2018 00:55:36 +0200 Subject: [PATCH 18/32] fixed bdDijkstra function --- functions/bdDijkstra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/bdDijkstra.py b/functions/bdDijkstra.py index 05ddd2c..8605e3c 100755 --- a/functions/bdDijkstra.py +++ b/functions/bdDijkstra.py @@ -73,7 +73,7 @@ def getQuery(self, args): %(source)s AS source, %(target)s AS target, %(cost)s AS cost - %(reverse_cost)s, + %(reverse_cost)s FROM %(edge_table)s %(where_clause)s', array[%(source_ids)s]::BIGINT[], array[%(target_ids)s]::BIGINT[], %(directed)s) From 5277f71c420c1fad7d978d8be0180d142f291b5f Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Mon, 16 Jul 2018 04:13:26 +0530 Subject: [PATCH 19/32] API_changes_9 --- functions/alphashape.py | 14 +++++++------- functions/astar.py | 2 +- functions/bdDijkstra.py | 2 +- functions/drivingDistance.py | 6 +++--- functions/kdijkstra_cost.py | 4 ++-- functions/kdijkstra_path.py | 2 +- functions/trsp_vertex.py | 4 ++-- functions/trsp_via_edges.py | 2 +- functions/trsp_via_vertices.py | 2 +- functions/tsp_euclid.py | 4 ++-- pgRoutingLayer.py | 23 +++++++++++++---------- 11 files changed, 34 insertions(+), 31 deletions(-) diff --git a/functions/alphashape.py b/functions/alphashape.py index 5fb954c..518d630 100755 --- a/functions/alphashape.py +++ b/functions/alphashape.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from builtins import str -from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsPoint, QgsMessageLog +from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsPoint, QgsMessageLog, QgsProject from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -143,11 +143,11 @@ def getExportQuery(self, args): def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): resultAreaRubberBand = canvasItemList['area'] trans = None - if mapCanvas.hasCrsTransformEnabled(): - canvasCrs = Utils.getDestinationCrs(mapCanvas) - layerCrs = QgsCoordinateReferenceSystem() - Utils.createFromSrid(layerCrs, args['srid']) - trans = QgsCoordinateTransform(layerCrs, canvasCrs) + + canvasCrs = Utils.getDestinationCrs(mapCanvas) + layerCrs = QgsCoordinateReferenceSystem() + Utils.createFromSrid(layerCrs, args['srid']) + trans = QgsCoordinateTransform(layerCrs, canvasCrs,QgsProject.instance()) # return columns are 'x', 'y' for row in rows: @@ -158,7 +158,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): return pt = QgsPoint(x, y) if trans: - pt = trans.transform(pt) + pt = trans.transform(pt.x(),pt.y()) resultAreaRubberBand.addPoint(pt) diff --git a/functions/astar.py b/functions/astar.py index 101b797..abceef6 100755 --- a/functions/astar.py +++ b/functions/astar.py @@ -4,7 +4,7 @@ class Function(FunctionBase): - version = 2.0 + version = 3.0 @classmethod def getName(self): diff --git a/functions/bdDijkstra.py b/functions/bdDijkstra.py index 05ddd2c..8605e3c 100755 --- a/functions/bdDijkstra.py +++ b/functions/bdDijkstra.py @@ -73,7 +73,7 @@ def getQuery(self, args): %(source)s AS source, %(target)s AS target, %(cost)s AS cost - %(reverse_cost)s, + %(reverse_cost)s FROM %(edge_table)s %(where_clause)s', array[%(source_ids)s]::BIGINT[], array[%(target_ids)s]::BIGINT[], %(directed)s) diff --git a/functions/drivingDistance.py b/functions/drivingDistance.py index 5b331f4..2e2c1fc 100755 --- a/functions/drivingDistance.py +++ b/functions/drivingDistance.py @@ -1,8 +1,8 @@ from __future__ import absolute_import from builtins import str from qgis.PyQt.QtCore import Qt -#from PyQt.QtGui import * -from qgis.core import QgsGeometry, QgsPoint +from qgis.PyQt.QtGui import * +from qgis.core import QgsGeometry, QgsPointXY from qgis.gui import QgsVertexMarker import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -123,7 +123,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): vertexMarker.setColor(Qt.red) vertexMarker.setPenWidth(2) vertexMarker.setIconSize(5) - vertexMarker.setCenter(QgsPoint(pt)) + vertexMarker.setCenter(QgsPointXY(pt)) resultNodesVertexMarkers.append(vertexMarker) def __init__(self, ui): diff --git a/functions/kdijkstra_cost.py b/functions/kdijkstra_cost.py index 6e7f0e4..61c544d 100755 --- a/functions/kdijkstra_cost.py +++ b/functions/kdijkstra_cost.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from builtins import str from qgis.PyQt.QtCore import QSizeF, QPointF -from qgis.PyQt.QtGui import QColor +from qgis.PyQt.QtGui import QColor, QTextDocument from qgis.core import Qgis, QgsGeometry, QgsTextAnnotation, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 @@ -17,7 +17,7 @@ def getName(self): @classmethod def isSupportedVersion(self, version): # Deprecated on version 2.2 - return version >= 2.0 and version < 2.2 + return version >= 2.0 and version > 2.2 @classmethod def getControlNames(self, version): diff --git a/functions/kdijkstra_path.py b/functions/kdijkstra_path.py index 4e60a08..3ddbdac 100755 --- a/functions/kdijkstra_path.py +++ b/functions/kdijkstra_path.py @@ -17,7 +17,7 @@ def getName(self): @classmethod def isSupportedVersion(self, version): # Deprecated on version 2.2 - return version >= 2.0 and version < 2.2 + return version >= 2.0 and version > 2.2 @classmethod diff --git a/functions/trsp_vertex.py b/functions/trsp_vertex.py index dd59ed0..9ab9cee 100755 --- a/functions/trsp_vertex.py +++ b/functions/trsp_vertex.py @@ -2,7 +2,7 @@ from qgis.PyQt.QtCore import * from qgis.PyQt.QtGui import * from builtins import str -from qgis.core import Qgis, QgsGeometry +from qgis.core import Qgis, QgsGeometry, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -26,7 +26,7 @@ def getControlNames(self, version): ] def isSupportedVersion(self, version): - return version >= 2.0 and version < 3.0 + return version >= 2.0 def prepare(self, canvasItemList): resultPathRubberBand = canvasItemList['path'] diff --git a/functions/trsp_via_edges.py b/functions/trsp_via_edges.py index 93bd1f4..c8f5c34 100755 --- a/functions/trsp_via_edges.py +++ b/functions/trsp_via_edges.py @@ -41,7 +41,7 @@ def canExportMerged(self): return True def isSupportedVersion(self, version): - return version >= 2.1 and version < 3.0 + return version >= 2.1 def prepare(self, canvasItemList): resultPathsRubberBands = canvasItemList['paths'] diff --git a/functions/trsp_via_vertices.py b/functions/trsp_via_vertices.py index eba3a70..d937e4e 100755 --- a/functions/trsp_via_vertices.py +++ b/functions/trsp_via_vertices.py @@ -23,7 +23,7 @@ def getControlNames(self, version): def isSupportedVersion(self, version): - return version >= 2.1 and version < 3.0 + return version >= 2.1 def prepare(self, canvasItemList): resultPathsRubberBands = canvasItemList['paths'] diff --git a/functions/tsp_euclid.py b/functions/tsp_euclid.py index 30f281c..c6f4a53 100755 --- a/functions/tsp_euclid.py +++ b/functions/tsp_euclid.py @@ -2,7 +2,7 @@ from builtins import str from qgis.PyQt.QtCore import QPointF, QSizeF from qgis.PyQt.QtGui import QTextDocument -from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation +from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation, QgsWkbTypes from qgis.gui import * import psycopg2 from .. import pgRoutingLayer_utils as Utils @@ -34,7 +34,7 @@ def canExportMerged(self): return False def isSupportedVersion(self, version): - return version >= 2.0 and version < 3.0 + return version >= 2.0 def prepare(self, canvasItemList): resultNodesTextAnnotations = canvasItemList['annotations'] diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index 142e28e..79204e5 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -42,6 +42,7 @@ class PgRoutingLayer(object): SUPPORTED_FUNCTIONS = [ + 'tsp_euclid', 'dijkstra', 'trsp_vertex', 'astar', @@ -90,7 +91,7 @@ class PgRoutingLayer(object): ] FIND_RADIUS = 10 FRACTION_DECIMAL_PLACES = 2 - version = 3.0 + version = 2.6 functions = {} @@ -228,7 +229,7 @@ def unload(self): self.iface.removeDockWidget(self.dock) def reloadConnections(self): - oldReloadMessage = False + oldReloadMessage = self.reloadMessage self.reloadMessage = False database = str(self.dock.comboConnections.currentText()) @@ -303,8 +304,10 @@ def loadFunctionsForVersion(self): def updateFunctionEnabled(self, text): - # for testing only. Remove text = dikstra - text = 'dijkstra' + text = str (self.dock.comboBoxFunction.currentText()) + if text== '': + return + self.clear() function = self.functions.get(str(text)) self.toggleSelectButton(None) @@ -1149,12 +1152,12 @@ def findNearestLink(self, args, pt): srid, geomType = Utils.getSridAndGeomType(con, '%(edge_table)s' % args, '%(geometry)s' % args) - if self.iface.mapCanvas().hasCrsTransformEnabled(): - layerCrs = QgsCoordinateReferenceSystem() - Utils.createFromSrid(layerCrs, srid) - trans = QgsCoordinateTransform(canvasCrs, layerCrs) - pt = trans.transform(pt) - rect = trans.transform(rect) + + layerCrs = QgsCoordinateReferenceSystem() + Utils.createFromSrid(layerCrs, srid) + trans = QgsCoordinateTransform(canvasCrs, layerCrs, QgsProject.instance()) + pt = trans.transform(pt) + rect = trans.transform(rect) args['canvas_srid'] = Utils.getCanvasSrid(canvasCrs) args['srid'] = srid From 42c0ecfe7dce723c67fb435d366f96e22c3335de Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Sat, 28 Jul 2018 01:41:01 +0200 Subject: [PATCH 20/32] removing deprecated signatures: kdijkstra_cost and kdijkstra_path --- functions/dijkstraCost.py | 12 +-- functions/kdijkstra_cost.py | 156 ------------------------------------ functions/kdijkstra_path.py | 154 ----------------------------------- pgRoutingLayer.py | 4 +- pgRoutingLayer_utils.py | 2 +- readme.md | 3 +- 6 files changed, 9 insertions(+), 322 deletions(-) delete mode 100755 functions/kdijkstra_cost.py delete mode 100755 functions/kdijkstra_path.py diff --git a/functions/dijkstraCost.py b/functions/dijkstraCost.py index d6490e4..76ee0ce 100755 --- a/functions/dijkstraCost.py +++ b/functions/dijkstraCost.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from builtins import str from qgis.PyQt.QtCore import QSizeF, QPointF -from qgis.PyQt.QtGui import QColor, QTextDocument +from qgis.PyQt.QtGui import QColor, QTextDocument from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation, QgsWkbTypes, QgsAnnotation from qgis.gui import QgsRubberBand import psycopg2 @@ -12,7 +12,7 @@ class Function(FunctionBase): @classmethod def getName(self): - return 'pgr_dijkstraCost' + return 'dijkstraCost' @classmethod def isSupportedVersion(self, version): @@ -29,14 +29,14 @@ def canExportMerged(self): return False - + def prepare(self, canvasItemList): resultNodesTextAnnotations = canvasItemList['annotations'] for anno in resultNodesTextAnnotations: anno.setVisible(False) canvasItemList['annotations'] = [] - + def getQuery(self, args): args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) return """ @@ -58,7 +58,7 @@ def getQuery(self, args): def getExportQuery(self, args): args['result_query'] = self.getQuery(args) - args['vertex_table'] = """ + args['vertex_table'] = """ %(edge_table)s_vertices_pgr """ % args @@ -96,7 +96,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): rubberBand.setWidth(4) if args['result_cost'] != -1: query2 = """ - SELECT ST_AsText( ST_MakeLine( + SELECT ST_AsText( ST_MakeLine( (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_source_id)d), (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_target_id)d) )) diff --git a/functions/kdijkstra_cost.py b/functions/kdijkstra_cost.py deleted file mode 100755 index 61c544d..0000000 --- a/functions/kdijkstra_cost.py +++ /dev/null @@ -1,156 +0,0 @@ -from __future__ import absolute_import -from builtins import str -from qgis.PyQt.QtCore import QSizeF, QPointF -from qgis.PyQt.QtGui import QColor, QTextDocument -from qgis.core import Qgis, QgsGeometry, QgsTextAnnotation, QgsWkbTypes -from qgis.gui import QgsRubberBand -import psycopg2 -from .. import pgRoutingLayer_utils as Utils -from .FunctionBase import FunctionBase - -class Function(FunctionBase): - - @classmethod - def getName(self): - return 'kdijkstra(cost)' - - @classmethod - def isSupportedVersion(self, version): - # Deprecated on version 2.2 - return version >= 2.0 and version > 2.2 - - @classmethod - def getControlNames(self, version): - # version 2.0 has only one to many - return self.commonControls + self.commonBoxes + [ - 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', - 'labelTargetIds', 'lineEditTargetIds', 'buttonSelectTargetIds', - ] - - - @classmethod - def canExport(self): - return True - - @classmethod - def canExportMerged(self): - return False - - def prepare(self, canvasItemList): - resultNodesTextAnnotations = canvasItemList['annotations'] - for anno in resultNodesTextAnnotations: - anno.setVisible(False) - self.iface.mapCanvas().scene().removeItem(anno) - canvasItemList['annotations'] = [] - - def getQuery(self, args): - args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) - return """ - SELECT seq, id1 AS source, id2 AS target, cost FROM pgr_kdijkstraCost(' - SELECT %(id)s::int4 AS id, - %(source)s::int4 AS source, - %(target)s::int4 AS target, - %(cost)s::float8 AS cost%(reverse_cost)s - FROM %(edge_table)s - %(where_clause)s', - %(source_id)s, array[%(target_ids)s], %(directed)s, %(has_reverse_cost)s)""" % args - - def getExportQuery(self, args): - args['result_query'] = self.getQuery(args) - args['vertex_table'] = """ - %(edge_table)s_vertices_pgr - """ % args - - return """ - WITH - result AS ( %(result_query)s ) - SELECT result.*, ST_MakeLine(a.the_geom, b.the_geom) AS path_geom - - FROM result - JOIN %(vertex_table)s AS a ON (source = a.id) - JOIN %(vertex_table)s AS b ON (target = b.id) - """ % args - - - def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): - resultPathsRubberBands = canvasItemList['paths'] - rubberBand = None - cur_path_id = -1 - for row in rows: - cur2 = con.cursor() - args['result_path_id'] = row[0] - args['result_source_id'] = row[1] - args['result_target_id'] = row[2] - args['result_cost'] = row[3] - if args['result_path_id'] != cur_path_id: - cur_path_id = args['result_path_id'] - if rubberBand: - resultPathsRubberBands.append(rubberBand) - rubberBand = None - - rubberBand = QgsRubberBand(mapCanvas, Utils.getRubberBandType(False)) - rubberBand.setColor(QColor(255, 0, 0, 128)) - rubberBand.setWidth(4) - if args['result_cost'] != -1: - query2 = """ - SELECT ST_AsText( ST_MakeLine( - (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_source_id)d), - (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_target_id)d) - )) - """ % args - ##Utils.logMessage(query2) - cur2.execute(query2) - row2 = cur2.fetchone() - ##Utils.logMessage(str(row2[0])) - assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args - - geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QgsWkbTypes.MultiLineString: - for line in geom.asMultiPolyline(): - for pt in line: - rubberBand.addPoint(pt) - elif geom.wkbType() == QgsWkbTypes.LineString: - for pt in geom.asPolyline(): - rubberBand.addPoint(pt) - - if rubberBand: - resultPathsRubberBands.append(rubberBand) - rubberBand = None - - - resultNodesTextAnnotations = canvasItemList['annotations'] - Utils.setStartPoint(geomType, args) - Utils.setEndPoint(geomType, args) - for row in rows: - cur2 = con.cursor() - args['result_seq'] = row[0] - args['result_source_id'] = row[1] - args['result_target_id'] = row[2] - args['result_cost'] = row[3] - query2 = """ - SELECT ST_AsText(%(transform_s)s%(startpoint)s%(transform_e)s) FROM %(edge_table)s - WHERE %(source)s = %(result_target_id)d - UNION - SELECT ST_AsText(%(transform_s)s%(endpoint)s%(transform_e)s) FROM %(edge_table)s - WHERE %(target)s = %(result_target_id)d - """ % args - cur2.execute(query2) - row2 = cur2.fetchone() - assert row2, "Invalid result geometry. (target_id:%(result_target_id)d)" % args - - geom = QgsGeometry().fromWkt(str(row2[0])) - pt = geom.asPoint() - textDocument = QTextDocument("%(result_target_id)d:%(result_cost)f" % args) - textAnnotation = QgsTextAnnotation(mapCanvas) - textAnnotation.setMapPosition(geom.asPoint()) - textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) - textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) - textAnnotation.setDocument(textDocument) - - #textAnnotation.update() - resultNodesTextAnnotations.append(textAnnotation) - canvasItemList['annotations'] = resultNodesTextAnnotations - - - def __init__(self, ui): - FunctionBase.__init__(self, ui) diff --git a/functions/kdijkstra_path.py b/functions/kdijkstra_path.py deleted file mode 100755 index 3ddbdac..0000000 --- a/functions/kdijkstra_path.py +++ /dev/null @@ -1,154 +0,0 @@ -from __future__ import absolute_import -from qgis.PyQt.QtCore import * -from builtins import str -from qgis.PyQt.QtGui import QColor -from qgis.core import QgsGeometry, Qgis, QgsWkbTypes -from qgis.gui import QgsRubberBand -import psycopg2 -from .. import pgRoutingLayer_utils as Utils -from .FunctionBase import FunctionBase - -class Function(FunctionBase): - - @classmethod - def getName(self): - return 'kdijkstra(path)' - - @classmethod - def isSupportedVersion(self, version): - # Deprecated on version 2.2 - return version >= 2.0 and version > 2.2 - - - @classmethod - def getControlNames(self, version): - # version 2.0 has only one to many - return self.commonControls + self.commonBoxes + [ - 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', - 'labelTargetIds', 'lineEditTargetIds', 'buttonSelectTargetIds', - ] - - - def prepare(self, canvasItemList): - resultPathsRubberBands = canvasItemList['paths'] - for path in resultPathsRubberBands: - path.reset(Utils.getRubberBandType(False)) - canvasItemList['paths'] = [] - - def getQuery(self, args): - args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) - return """ - SELECT seq, - id1 AS _path, id2 AS _node, id3 AS _edge, cost AS _cost FROM pgr_kdijkstraPath(' - SELECT %(id)s::int4 AS id, - %(source)s::int4 AS source, - %(target)s::int4 AS target, - %(cost)s::float8 AS cost%(reverse_cost)s - FROM %(edge_table)s - %(where_clause)s', - %(source_id)s, array[%(target_ids)s], %(directed)s, %(has_reverse_cost)s)""" % args - - def getExportQuery(self, args): - return self.getJoinResultWithEdgeTable(args) - - def getExportMergeQuery(self, args): - args['result_query'] = self.getQuery(args) - - args['with_geom_query'] = """ - SELECT - seq, _path, - CASE - WHEN result._node = %(edge_table)s.%(source)s - THEN %(edge_table)s.%(geometry)s - ELSE ST_Reverse(%(edge_table)s.%(geometry)s) - END AS path_geom - FROM %(edge_table)s JOIN result - ON %(edge_table)s.%(id)s = result._edge - """ % args - - args['one_geom_query'] = """ - SELECT _path, ST_LineMerge(ST_Union(path_geom)) AS path_geom - FROM with_geom - GROUP BY _path - ORDER BY _path - """ % args - - args['aggregates_query'] = """SELECT - _path, - SUM(_cost) AS agg_cost, - array_agg(_node ORDER BY seq) AS _nodes, - array_agg(_edge ORDER BY seq) AS _edges - FROM result - GROUP BY _path - """ - - query = """ - WITH - result AS ( %(result_query)s ), - with_geom AS ( %(with_geom_query)s ), - one_geom AS ( %(one_geom_query)s ), - aggregates AS ( %(aggregates_query)s ) - SELECT row_number() over() as seq, - _path, _nodes, _edges, agg_cost, - path_geom FROM aggregates JOIN one_geom - USING (_path) - """ % args - return query - - def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): - resultPathsRubberBands = canvasItemList['paths'] - rubberBand = None - cur_path_id = -1 - for row in rows: - cur2 = con.cursor() - args['result_path_id'] = row[1] - args['result_node_id'] = row[2] - args['result_edge_id'] = row[3] - args['result_cost'] = row[4] - if args['result_path_id'] != cur_path_id: - cur_path_id = args['result_path_id'] - if rubberBand: - resultPathsRubberBands.append(rubberBand) - rubberBand = None - - rubberBand = QgsRubberBand(mapCanvas, Utils.getRubberBandType(False)) - rubberBand.setColor(QColor(255, 0, 0, 128)) - rubberBand.setWidth(4) - - if args['result_edge_id'] != -1: - query2 = """ - SELECT ST_AsText(%(transform_s)s%(geometry)s%(transform_e)s) FROM %(edge_table)s - WHERE %(source)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d - UNION - SELECT ST_AsText(%(transform_s)sST_Reverse(%(geometry)s)%(transform_e)s) FROM %(edge_table)s - WHERE %(target)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d; - """ % args - ## pgRouting <= 2.0.0rc1 - #query2 = """ - # SELECT ST_AsText(%(transform_s)sST_Reverse(%(geometry)s)%(transform_e)s) FROM %(edge_table)s - # WHERE %(source)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d - # UNION - # SELECT ST_AsText(%(transform_s)s%(geometry)s%(transform_e)s) FROM %(edge_table)s - # WHERE %(target)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d; - #""" % args - ##Utils.logMessage(query2) - cur2.execute(query2) - row2 = cur2.fetchone() - ##Utils.logMessage(str(row2[0])) - assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args - - geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QgsWkbTypes.MultiLineString: - for line in geom.asMultiPolyline(): - for pt in line: - rubberBand.addPoint(pt) - elif geom.wkbType() == QgsWkbTypes.LineString: - for pt in geom.asPolyline(): - rubberBand.addPoint(pt) - - if rubberBand: - resultPathsRubberBands.append(rubberBand) - rubberBand = None - - def __init__(self, ui): - FunctionBase.__init__(self, ui) diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index 79204e5..271cf36 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -52,9 +52,7 @@ class PgRoutingLayer(object): 'bdDijkstra', 'bdAstar', 'dijkstraCost', - 'kdijkstra_cost', 'trsp_edge', - 'kdijkstra_path', 'ksp', 'trsp_via_vertices', 'trsp_via_edges' @@ -1152,7 +1150,7 @@ def findNearestLink(self, args, pt): srid, geomType = Utils.getSridAndGeomType(con, '%(edge_table)s' % args, '%(geometry)s' % args) - + layerCrs = QgsCoordinateReferenceSystem() Utils.createFromSrid(layerCrs, srid) trans = QgsCoordinateTransform(canvasCrs, layerCrs, QgsProject.instance()) diff --git a/pgRoutingLayer_utils.py b/pgRoutingLayer_utils.py index f3480a0..2deccfb 100755 --- a/pgRoutingLayer_utils.py +++ b/pgRoutingLayer_utils.py @@ -129,6 +129,6 @@ def getPgrVersion(con): return float(version) except psycopg2.DatabaseError as e: #database didn't have pgrouting - return 0; + return 0 except SystemError as e: return 0 diff --git a/readme.md b/readme.md index 6683b07..378244b 100644 --- a/readme.md +++ b/readme.md @@ -21,9 +21,8 @@ PgRoutingLayer currently supports the following functions: * bdAstar * bdDijkstra * dijkstra +* dijkstraCost * drivingDistance -* kdijkstra_cost -* kdijkstra_path * ksp * shootingStar * trsp_edge From 93fb33d73cf409eadf4ffda80e20076fa175f3c8 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Sat, 28 Jul 2018 03:04:38 +0200 Subject: [PATCH 21/32] fixings for textAnnotations --- functions/dijkstraCost.py | 6 ++---- functions/tsp_euclid.py | 37 +++++++++++++++++++------------------ pgRoutingLayer.py | 1 - 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/functions/dijkstraCost.py b/functions/dijkstraCost.py index 76ee0ce..f0adaef 100755 --- a/functions/dijkstraCost.py +++ b/functions/dijkstraCost.py @@ -142,16 +142,14 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): geom = QgsGeometry().fromWkt(str(row2[0])) pt = geom.asPoint() textDocument = QTextDocument("%(result_target_id)d:%(result_cost)f" % args) - textAnnotation = QgsTextAnnotation(mapCanvas) + textAnnotation = QgsTextAnnotation() textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) textAnnotation.setDocument(textDocument) - #textAnnotation.update() + QgsMapCanvasAnnotationItem(textAnnotation, mapCanvas) resultNodesTextAnnotations.append(textAnnotation) - - def __init__(self, ui): FunctionBase.__init__(self, ui) diff --git a/functions/tsp_euclid.py b/functions/tsp_euclid.py index c6f4a53..dea82ac 100755 --- a/functions/tsp_euclid.py +++ b/functions/tsp_euclid.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from builtins import str from qgis.PyQt.QtCore import QPointF, QSizeF -from qgis.PyQt.QtGui import QTextDocument +from qgis.PyQt.QtGui import QTextDocument from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation, QgsWkbTypes from qgis.gui import * import psycopg2 @@ -9,11 +9,11 @@ from .FunctionBase import FunctionBase class Function(FunctionBase): - + @classmethod def getName(self): return 'tsp(euclid)' - + @classmethod def getControlNames(self, version): return [ @@ -24,7 +24,7 @@ def getControlNames(self, version): 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', 'labelTargetId', 'lineEditTargetId', 'buttonSelectTargetId' ] - + @classmethod def canExport(self): return False @@ -34,14 +34,14 @@ def canExportMerged(self): return False def isSupportedVersion(self, version): - return version >= 2.0 + return version >= 2.0 def prepare(self, canvasItemList): resultNodesTextAnnotations = canvasItemList['annotations'] for anno in resultNodesTextAnnotations: anno.setVisible(False) canvasItemList['annotations'] = [] - + def getQuery(self, args): return """ SELECT seq, id1 AS internal, id2 AS node, cost FROM pgr_tsp(' @@ -52,14 +52,14 @@ def getQuery(self, args): FROM %(edge_table)s_vertices_pgr WHERE id IN (%(ids)s)', %(source_id)s::int4, %(target_id)s::int4) """ % args - + def getExportQuery(self, args): args['result_query'] = self.getQuery(args) query = """ WITH result AS ( %(result_query)s ) - SELECT + SELECT CASE WHEN result._node = %(edge_table)s.%(source)s THEN %(edge_table)s.%(geometry)s @@ -78,14 +78,14 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): if i == 0: prevrow = row firstrow = row - i += 1 + i += 1 cur2 = con.cursor() args['result_seq'] = row[0] args['result_source_id'] = prevrow[2] args['result_target_id'] = row[2] args['result_cost'] = row[3] query2 = """ - SELECT ST_AsText( ST_MakeLine( + SELECT ST_AsText( ST_MakeLine( (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_source_id)d), (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_target_id)d) )) @@ -106,20 +106,20 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): resultPathsRubberBands.addPoint(pt) prevrow = row lastrow = row - + args['result_source_id'] = lastrow[2] args['result_target_id'] = firstrow[2] args['result_cost'] = row[3] query2 = """ - SELECT ST_AsText( ST_MakeLine( + SELECT ST_AsText( ST_MakeLine( (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_source_id)d), (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_target_id)d) )) """ % args - ##Utils.logMessage(query2) + # Utils.logMessage(query2) cur2.execute(query2) row2 = cur2.fetchone() - ##Utils.logMessage(str(row2[0])) + # Utils.logMessage(str(row2[0])) assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) @@ -156,17 +156,18 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): cur2.execute(query2) row2 = cur2.fetchone() assert row2, "Invalid result geometry. (node_id:%(result_node_id)d)" % args - + geom = QgsGeometry().fromWkt(str(row2[0])) pt = geom.asPoint() textDocument = QTextDocument("%(result_seq)d:%(result_node_id)d" % args) - textAnnotation = QgsTextAnnotation(mapCanvas) + textAnnotation = QgsTextAnnotation() textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) textAnnotation.setDocument(textDocument) - #textAnnotation.update() + + QgsMapCanvasAnnotationItem(textAnnotation, mapCanvas) resultNodesTextAnnotations.append(textAnnotation) - + def __init__(self, ui): FunctionBase.__init__(self, ui) diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index 271cf36..f1925a1 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -875,7 +875,6 @@ def clear(self): for anno in self.canvasItemList['annotations']: try: anno.setVisible(False) - self.iface.mapCanvas().scene().removeItem(anno) except RuntimeError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) From 57324b93a6c377262f53e99a013c1bcf0cdf364d Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Tue, 31 Jul 2018 02:18:04 +0530 Subject: [PATCH 22/32] Documentation and styling. --- Test_utils.py | 0 __init__.py | 0 dbConnection.py | 2 +- functions/FunctionBase.py | 16 +++- functions/alphashape.py | 7 +- functions/astar.py | 6 +- functions/bdAstar.py | 6 +- functions/bdDijkstra.py | 6 +- functions/dijkstra.py | 6 +- functions/dijkstraCost.py | 24 ++--- functions/drivingDistance.py | 6 +- functions/kdijkstra_cost.py | 156 --------------------------------- functions/kdijkstra_path.py | 154 -------------------------------- functions/ksp.py | 6 +- functions/trsp_edge.py | 6 +- functions/trsp_vertex.py | 7 +- functions/trsp_via_edges.py | 6 +- functions/trsp_via_vertices.py | 6 +- functions/tsp_euclid.py | 43 +++++---- icon.png | Bin icons/add.png | Bin metadata.txt | 2 +- pgRoutingLayer.py | 31 ++++++- pgRoutingLayer_utils.py | 20 ++++- readme.md | 3 +- tests/Test_FunctionBase.py | 0 tests/Test_utils.py | 0 tests/__init__.py | 0 tests/test_data/poly.PRJ | 0 tests/test_data/poly.dbf | Bin tests/test_data/poly.shp | Bin tests/test_data/poly.shx | Bin tests/test_init.py | 0 ui_pgRoutingLayer.ui | 0 34 files changed, 156 insertions(+), 363 deletions(-) mode change 100644 => 100755 Test_utils.py mode change 100644 => 100755 __init__.py delete mode 100755 functions/kdijkstra_cost.py delete mode 100755 functions/kdijkstra_path.py mode change 100644 => 100755 icon.png mode change 100644 => 100755 icons/add.png mode change 100644 => 100755 metadata.txt mode change 100644 => 100755 readme.md mode change 100644 => 100755 tests/Test_FunctionBase.py mode change 100644 => 100755 tests/Test_utils.py mode change 100644 => 100755 tests/__init__.py mode change 100644 => 100755 tests/test_data/poly.PRJ mode change 100644 => 100755 tests/test_data/poly.dbf mode change 100644 => 100755 tests/test_data/poly.shp mode change 100644 => 100755 tests/test_data/poly.shx mode change 100644 => 100755 tests/test_init.py mode change 100644 => 100755 ui_pgRoutingLayer.ui diff --git a/Test_utils.py b/Test_utils.py old mode 100644 new mode 100755 diff --git a/__init__.py b/__init__.py old mode 100644 new mode 100755 diff --git a/dbConnection.py b/dbConnection.py index d1f7af2..11ad58a 100755 --- a/dbConnection.py +++ b/dbConnection.py @@ -117,7 +117,7 @@ def getAvailableConnections(self): def getURI(self): # returns a new QgsDataSourceUri instance - return qgis.core.QgsDataSourceUri( self.uri.connectionInfo() ) + return QgsDataSourceUri( self.uri.connectionInfo() ) def getAction(self, parent=None): return Connection.ConnectionAction(self.uri.database(), self.getTypeName(), parent) diff --git a/functions/FunctionBase.py b/functions/FunctionBase.py index 7439cec..e1b2ecc 100755 --- a/functions/FunctionBase.py +++ b/functions/FunctionBase.py @@ -4,7 +4,7 @@ from qgis.gui import QgsRubberBand from qgis.PyQt.QtGui import QColor -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils class FunctionBase(object): @@ -45,22 +45,27 @@ def getControlNames(self, version): @classmethod def isEdgeBase(self): + ''' checks if EdgeBase is set. ''' return self.exportEdgeBase @classmethod def canExport(self): + ''' checks if exportButton is set ''' return self.exportButton @classmethod def canExportMerged(self): + ''' checks if exportMergeButton is set. ''' return self.exportMergeButton @classmethod def isSupportedVersion(self, version): + ''' returns true if version is between 2 and 3.0 ''' return version >= 2.0 and version < 3.0 @classmethod def whereClause(self, table, geometry, bbox): + ''' returns where clause for sql parameterising ''' if bbox == ' ': return ' ' else: @@ -82,6 +87,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): pass def getJoinResultWithEdgeTable(self, args): + '''returns a query which joins edge_table with result based on edge.id''' args['result_query'] = self.getQuery(args) query = """ @@ -101,6 +107,7 @@ def getJoinResultWithEdgeTable(self, args): def getExportOneSourceOneTargetMergeQuery(self, args): + ''' returns merge query for one source and one target ''' args['result_query'] = self.getQuery(args) args['with_geom_query'] = """ @@ -139,6 +146,7 @@ def getExportOneSourceOneTargetMergeQuery(self, args): def getExportManySourceManyTargetMergeQuery(self, args): + ''' returns merge query for many source and many target ''' args['result_query'] = self.getQuery(args) args['with_geom_query'] = """ @@ -184,6 +192,7 @@ def getExportManySourceManyTargetMergeQuery(self, args): def drawManyPaths(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draws multi line string on the mapCanvas. ''' resultPathsRubberBands = canvasItemList['paths'] rubberBand = None cur_path_id = str(-1) + "," + str(-1) @@ -232,8 +241,9 @@ def drawManyPaths(self, rows, con, args, geomType, canvasItemList, mapCanvas): def drawOnePath(self, rows, con, args, geomType, canvasItemList, mapCanvas): - resultPathRubberBand = canvasItemList['path'] - for row in rows: + ''' draws line string on the mapCanvas. ''' + resultPathRubberBand = canvasItemList['path'] + for row in rows: cur2 = con.cursor() args['result_node_id'] = row[1] args['result_edge_id'] = row[2] diff --git a/functions/alphashape.py b/functions/alphashape.py index 518d630..88290af 100755 --- a/functions/alphashape.py +++ b/functions/alphashape.py @@ -4,17 +4,19 @@ from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsPoint, QgsMessageLog, QgsProject from qgis.gui import QgsRubberBand import psycopg2 -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'alphashape' @classmethod def getControlNames(self, version): + ''' returns control names for this function. ''' self.version = version return self.commonControls + self.commonBoxes + [ 'labelId', 'lineEditId', @@ -37,6 +39,7 @@ def prepare(self, canvasItemList): resultAreaRubberBand.reset(Utils.getRubberBandType(True)) def getQuery(self, args): + ''' returns the sql query for pgr_alphashape ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) if args['version'] < 2.1: return """ @@ -141,6 +144,8 @@ def getExportQuery(self, args): def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the resulting polygon. ''' + resultAreaRubberBand = canvasItemList['area'] trans = None diff --git a/functions/astar.py b/functions/astar.py index abceef6..231495a 100755 --- a/functions/astar.py +++ b/functions/astar.py @@ -1,5 +1,5 @@ from __future__ import absolute_import -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @@ -8,10 +8,12 @@ class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'astar' @classmethod def getControlNames(self, version): + ''' returns control names for this function. ''' self.version = version if self.version < 2.4: return self.commonControls + self.commonBoxes + self.astarControls + [ @@ -36,6 +38,7 @@ def prepare(self, canvasItemList): def getQuery(self, args): + ''' returns the sql query in required signature format of pgr_astar ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) if self.version < 2.3: return """ @@ -102,6 +105,7 @@ def getExportMergeQuery(self, args): def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result. ''' if self.version < 2.4: self.drawOnePath(rows, con, args, geomType, canvasItemList, mapCanvas) else: diff --git a/functions/bdAstar.py b/functions/bdAstar.py index f3c7af6..9b90d1b 100755 --- a/functions/bdAstar.py +++ b/functions/bdAstar.py @@ -1,5 +1,5 @@ from __future__ import absolute_import -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @@ -8,10 +8,12 @@ class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'bdAstar' @classmethod def getControlNames(self, version): + ''' returns control names. ''' self.version = version if self.version < 2.5: return self.commonControls + self.commonBoxes + self.astarControls + [ @@ -36,6 +38,7 @@ def prepare(self, canvasItemList): def getQuery(self, args): + ''' returns the sql query in required signature format of pgr_bdAstar ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) if self.version < 2.5: return """ @@ -85,6 +88,7 @@ def getExportMergeQuery(self, args): def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draws the result ''' if self.version < 2.5: self.drawOnePath(rows, con, args, geomType, canvasItemList, mapCanvas) else: diff --git a/functions/bdDijkstra.py b/functions/bdDijkstra.py index 8605e3c..2ca5e9d 100755 --- a/functions/bdDijkstra.py +++ b/functions/bdDijkstra.py @@ -1,5 +1,5 @@ from __future__ import absolute_import -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @@ -8,10 +8,12 @@ class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'bdDijkstra' @classmethod def getControlNames(self, version): + ''' returns control names. ''' self.version = version if self.version < 2.5: return self.commonControls + self.commonBoxes + [ @@ -36,6 +38,7 @@ def prepare(self, canvasItemList): def getQuery(self, args): + ''' returns the sql query in required signature format of pgr_bdDijkstra ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) if self.version < 2.4: return """ @@ -90,6 +93,7 @@ def getExportMergeQuery(self, args): def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' if self.version < 2.5: self.drawOnePath(rows, con, args, geomType, canvasItemList, mapCanvas) else: diff --git a/functions/dijkstra.py b/functions/dijkstra.py index 2f42d3a..b377d91 100755 --- a/functions/dijkstra.py +++ b/functions/dijkstra.py @@ -1,5 +1,5 @@ from __future__ import absolute_import -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @@ -8,11 +8,13 @@ class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'dijkstra' @classmethod def getControlNames(self, version): + ''' returns control names. ''' self.version = version if self.version < 2.1: # version 2.0 has only one to one @@ -39,6 +41,7 @@ def prepare(self, canvasItemList): def getQuery(self, args): + ''' returns the sql query in required signature format of pgr_bdDijkstra ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) if self.version < 2.1: return """ @@ -82,6 +85,7 @@ def getExportMergeQuery(self, args): def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' if self.version < 2.1: self.drawOnePath(rows, con, args, geomType, canvasItemList, mapCanvas) else: diff --git a/functions/dijkstraCost.py b/functions/dijkstraCost.py index d6490e4..0eb6c43 100755 --- a/functions/dijkstraCost.py +++ b/functions/dijkstraCost.py @@ -1,21 +1,23 @@ from __future__ import absolute_import from builtins import str from qgis.PyQt.QtCore import QSizeF, QPointF -from qgis.PyQt.QtGui import QColor, QTextDocument +from qgis.PyQt.QtGui import QColor, QTextDocument from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation, QgsWkbTypes, QgsAnnotation from qgis.gui import QgsRubberBand import psycopg2 -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @classmethod def getName(self): - return 'pgr_dijkstraCost' + ''' returns Function name. ''' + return 'dijkstraCost' @classmethod def isSupportedVersion(self, version): + ''' Checks supported version ''' # valid starting pgr v2.1 return version >= 2.1 @@ -29,15 +31,16 @@ def canExportMerged(self): return False - + def prepare(self, canvasItemList): resultNodesTextAnnotations = canvasItemList['annotations'] for anno in resultNodesTextAnnotations: anno.setVisible(False) canvasItemList['annotations'] = [] - + def getQuery(self, args): + ''' returns the sql query in required signature format of pgr_dijkstra ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) return """ SELECT row_number() over() AS seq, @@ -58,7 +61,7 @@ def getQuery(self, args): def getExportQuery(self, args): args['result_query'] = self.getQuery(args) - args['vertex_table'] = """ + args['vertex_table'] = """ %(edge_table)s_vertices_pgr """ % args @@ -76,6 +79,7 @@ def getExportQuery(self, args): def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' resultPathsRubberBands = canvasItemList['paths'] rubberBand = None cur_path_id = -1 @@ -96,7 +100,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): rubberBand.setWidth(4) if args['result_cost'] != -1: query2 = """ - SELECT ST_AsText( ST_MakeLine( + SELECT ST_AsText( ST_MakeLine( (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_source_id)d), (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_target_id)d) )) @@ -142,16 +146,14 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): geom = QgsGeometry().fromWkt(str(row2[0])) pt = geom.asPoint() textDocument = QTextDocument("%(result_target_id)d:%(result_cost)f" % args) - textAnnotation = QgsTextAnnotation(mapCanvas) + textAnnotation = QgsTextAnnotation() textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) textAnnotation.setDocument(textDocument) - #textAnnotation.update() + QgsMapCanvasAnnotationItem(textAnnotation, mapCanvas) resultNodesTextAnnotations.append(textAnnotation) - - def __init__(self, ui): FunctionBase.__init__(self, ui) diff --git a/functions/drivingDistance.py b/functions/drivingDistance.py index 2e2c1fc..4ec02aa 100755 --- a/functions/drivingDistance.py +++ b/functions/drivingDistance.py @@ -5,17 +5,19 @@ from qgis.core import QgsGeometry, QgsPointXY from qgis.gui import QgsVertexMarker import psycopg2 -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'drivingDistance' @classmethod def getControlNames(self, version): + ''' returns control names. ''' if version < 2.1: # version 2.0 has only one to one return self.commonControls + self.commonBoxes + [ @@ -35,6 +37,7 @@ def prepare(self, canvasItemList): canvasItemList['markers'] = [] def getQuery(self, args): + ''' returns the sql query in required signature format of pgr_drivingDistance ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) if (args['version'] < 2.1): return """ @@ -93,6 +96,7 @@ def getExportMergeQuery(self, args): return self.getJoinResultWithEdgeTable(args) def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' resultNodesVertexMarkers = canvasItemList['markers'] table = """%(edge_table)s_vertices_pgr""" % args srid, geomType = Utils.getSridAndGeomType(con, table, 'the_geom') diff --git a/functions/kdijkstra_cost.py b/functions/kdijkstra_cost.py deleted file mode 100755 index 61c544d..0000000 --- a/functions/kdijkstra_cost.py +++ /dev/null @@ -1,156 +0,0 @@ -from __future__ import absolute_import -from builtins import str -from qgis.PyQt.QtCore import QSizeF, QPointF -from qgis.PyQt.QtGui import QColor, QTextDocument -from qgis.core import Qgis, QgsGeometry, QgsTextAnnotation, QgsWkbTypes -from qgis.gui import QgsRubberBand -import psycopg2 -from .. import pgRoutingLayer_utils as Utils -from .FunctionBase import FunctionBase - -class Function(FunctionBase): - - @classmethod - def getName(self): - return 'kdijkstra(cost)' - - @classmethod - def isSupportedVersion(self, version): - # Deprecated on version 2.2 - return version >= 2.0 and version > 2.2 - - @classmethod - def getControlNames(self, version): - # version 2.0 has only one to many - return self.commonControls + self.commonBoxes + [ - 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', - 'labelTargetIds', 'lineEditTargetIds', 'buttonSelectTargetIds', - ] - - - @classmethod - def canExport(self): - return True - - @classmethod - def canExportMerged(self): - return False - - def prepare(self, canvasItemList): - resultNodesTextAnnotations = canvasItemList['annotations'] - for anno in resultNodesTextAnnotations: - anno.setVisible(False) - self.iface.mapCanvas().scene().removeItem(anno) - canvasItemList['annotations'] = [] - - def getQuery(self, args): - args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) - return """ - SELECT seq, id1 AS source, id2 AS target, cost FROM pgr_kdijkstraCost(' - SELECT %(id)s::int4 AS id, - %(source)s::int4 AS source, - %(target)s::int4 AS target, - %(cost)s::float8 AS cost%(reverse_cost)s - FROM %(edge_table)s - %(where_clause)s', - %(source_id)s, array[%(target_ids)s], %(directed)s, %(has_reverse_cost)s)""" % args - - def getExportQuery(self, args): - args['result_query'] = self.getQuery(args) - args['vertex_table'] = """ - %(edge_table)s_vertices_pgr - """ % args - - return """ - WITH - result AS ( %(result_query)s ) - SELECT result.*, ST_MakeLine(a.the_geom, b.the_geom) AS path_geom - - FROM result - JOIN %(vertex_table)s AS a ON (source = a.id) - JOIN %(vertex_table)s AS b ON (target = b.id) - """ % args - - - def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): - resultPathsRubberBands = canvasItemList['paths'] - rubberBand = None - cur_path_id = -1 - for row in rows: - cur2 = con.cursor() - args['result_path_id'] = row[0] - args['result_source_id'] = row[1] - args['result_target_id'] = row[2] - args['result_cost'] = row[3] - if args['result_path_id'] != cur_path_id: - cur_path_id = args['result_path_id'] - if rubberBand: - resultPathsRubberBands.append(rubberBand) - rubberBand = None - - rubberBand = QgsRubberBand(mapCanvas, Utils.getRubberBandType(False)) - rubberBand.setColor(QColor(255, 0, 0, 128)) - rubberBand.setWidth(4) - if args['result_cost'] != -1: - query2 = """ - SELECT ST_AsText( ST_MakeLine( - (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_source_id)d), - (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_target_id)d) - )) - """ % args - ##Utils.logMessage(query2) - cur2.execute(query2) - row2 = cur2.fetchone() - ##Utils.logMessage(str(row2[0])) - assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args - - geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QgsWkbTypes.MultiLineString: - for line in geom.asMultiPolyline(): - for pt in line: - rubberBand.addPoint(pt) - elif geom.wkbType() == QgsWkbTypes.LineString: - for pt in geom.asPolyline(): - rubberBand.addPoint(pt) - - if rubberBand: - resultPathsRubberBands.append(rubberBand) - rubberBand = None - - - resultNodesTextAnnotations = canvasItemList['annotations'] - Utils.setStartPoint(geomType, args) - Utils.setEndPoint(geomType, args) - for row in rows: - cur2 = con.cursor() - args['result_seq'] = row[0] - args['result_source_id'] = row[1] - args['result_target_id'] = row[2] - args['result_cost'] = row[3] - query2 = """ - SELECT ST_AsText(%(transform_s)s%(startpoint)s%(transform_e)s) FROM %(edge_table)s - WHERE %(source)s = %(result_target_id)d - UNION - SELECT ST_AsText(%(transform_s)s%(endpoint)s%(transform_e)s) FROM %(edge_table)s - WHERE %(target)s = %(result_target_id)d - """ % args - cur2.execute(query2) - row2 = cur2.fetchone() - assert row2, "Invalid result geometry. (target_id:%(result_target_id)d)" % args - - geom = QgsGeometry().fromWkt(str(row2[0])) - pt = geom.asPoint() - textDocument = QTextDocument("%(result_target_id)d:%(result_cost)f" % args) - textAnnotation = QgsTextAnnotation(mapCanvas) - textAnnotation.setMapPosition(geom.asPoint()) - textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) - textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) - textAnnotation.setDocument(textDocument) - - #textAnnotation.update() - resultNodesTextAnnotations.append(textAnnotation) - canvasItemList['annotations'] = resultNodesTextAnnotations - - - def __init__(self, ui): - FunctionBase.__init__(self, ui) diff --git a/functions/kdijkstra_path.py b/functions/kdijkstra_path.py deleted file mode 100755 index 3ddbdac..0000000 --- a/functions/kdijkstra_path.py +++ /dev/null @@ -1,154 +0,0 @@ -from __future__ import absolute_import -from qgis.PyQt.QtCore import * -from builtins import str -from qgis.PyQt.QtGui import QColor -from qgis.core import QgsGeometry, Qgis, QgsWkbTypes -from qgis.gui import QgsRubberBand -import psycopg2 -from .. import pgRoutingLayer_utils as Utils -from .FunctionBase import FunctionBase - -class Function(FunctionBase): - - @classmethod - def getName(self): - return 'kdijkstra(path)' - - @classmethod - def isSupportedVersion(self, version): - # Deprecated on version 2.2 - return version >= 2.0 and version > 2.2 - - - @classmethod - def getControlNames(self, version): - # version 2.0 has only one to many - return self.commonControls + self.commonBoxes + [ - 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', - 'labelTargetIds', 'lineEditTargetIds', 'buttonSelectTargetIds', - ] - - - def prepare(self, canvasItemList): - resultPathsRubberBands = canvasItemList['paths'] - for path in resultPathsRubberBands: - path.reset(Utils.getRubberBandType(False)) - canvasItemList['paths'] = [] - - def getQuery(self, args): - args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) - return """ - SELECT seq, - id1 AS _path, id2 AS _node, id3 AS _edge, cost AS _cost FROM pgr_kdijkstraPath(' - SELECT %(id)s::int4 AS id, - %(source)s::int4 AS source, - %(target)s::int4 AS target, - %(cost)s::float8 AS cost%(reverse_cost)s - FROM %(edge_table)s - %(where_clause)s', - %(source_id)s, array[%(target_ids)s], %(directed)s, %(has_reverse_cost)s)""" % args - - def getExportQuery(self, args): - return self.getJoinResultWithEdgeTable(args) - - def getExportMergeQuery(self, args): - args['result_query'] = self.getQuery(args) - - args['with_geom_query'] = """ - SELECT - seq, _path, - CASE - WHEN result._node = %(edge_table)s.%(source)s - THEN %(edge_table)s.%(geometry)s - ELSE ST_Reverse(%(edge_table)s.%(geometry)s) - END AS path_geom - FROM %(edge_table)s JOIN result - ON %(edge_table)s.%(id)s = result._edge - """ % args - - args['one_geom_query'] = """ - SELECT _path, ST_LineMerge(ST_Union(path_geom)) AS path_geom - FROM with_geom - GROUP BY _path - ORDER BY _path - """ % args - - args['aggregates_query'] = """SELECT - _path, - SUM(_cost) AS agg_cost, - array_agg(_node ORDER BY seq) AS _nodes, - array_agg(_edge ORDER BY seq) AS _edges - FROM result - GROUP BY _path - """ - - query = """ - WITH - result AS ( %(result_query)s ), - with_geom AS ( %(with_geom_query)s ), - one_geom AS ( %(one_geom_query)s ), - aggregates AS ( %(aggregates_query)s ) - SELECT row_number() over() as seq, - _path, _nodes, _edges, agg_cost, - path_geom FROM aggregates JOIN one_geom - USING (_path) - """ % args - return query - - def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): - resultPathsRubberBands = canvasItemList['paths'] - rubberBand = None - cur_path_id = -1 - for row in rows: - cur2 = con.cursor() - args['result_path_id'] = row[1] - args['result_node_id'] = row[2] - args['result_edge_id'] = row[3] - args['result_cost'] = row[4] - if args['result_path_id'] != cur_path_id: - cur_path_id = args['result_path_id'] - if rubberBand: - resultPathsRubberBands.append(rubberBand) - rubberBand = None - - rubberBand = QgsRubberBand(mapCanvas, Utils.getRubberBandType(False)) - rubberBand.setColor(QColor(255, 0, 0, 128)) - rubberBand.setWidth(4) - - if args['result_edge_id'] != -1: - query2 = """ - SELECT ST_AsText(%(transform_s)s%(geometry)s%(transform_e)s) FROM %(edge_table)s - WHERE %(source)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d - UNION - SELECT ST_AsText(%(transform_s)sST_Reverse(%(geometry)s)%(transform_e)s) FROM %(edge_table)s - WHERE %(target)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d; - """ % args - ## pgRouting <= 2.0.0rc1 - #query2 = """ - # SELECT ST_AsText(%(transform_s)sST_Reverse(%(geometry)s)%(transform_e)s) FROM %(edge_table)s - # WHERE %(source)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d - # UNION - # SELECT ST_AsText(%(transform_s)s%(geometry)s%(transform_e)s) FROM %(edge_table)s - # WHERE %(target)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d; - #""" % args - ##Utils.logMessage(query2) - cur2.execute(query2) - row2 = cur2.fetchone() - ##Utils.logMessage(str(row2[0])) - assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args - - geom = QgsGeometry().fromWkt(str(row2[0])) - if geom.wkbType() == QgsWkbTypes.MultiLineString: - for line in geom.asMultiPolyline(): - for pt in line: - rubberBand.addPoint(pt) - elif geom.wkbType() == QgsWkbTypes.LineString: - for pt in geom.asPolyline(): - rubberBand.addPoint(pt) - - if rubberBand: - resultPathsRubberBands.append(rubberBand) - rubberBand = None - - def __init__(self, ui): - FunctionBase.__init__(self, ui) diff --git a/functions/ksp.py b/functions/ksp.py index 6471822..8ae1bf8 100755 --- a/functions/ksp.py +++ b/functions/ksp.py @@ -5,7 +5,7 @@ from qgis.core import Qgis, QgsGeometry, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @@ -15,10 +15,12 @@ class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'ksp' @classmethod def getControlNames(self, version): + ''' returns control names. ''' # function pgr_ksp(text,integer,integer,integer, boolean) # boolean is has_rcost # only works for directed graph @@ -58,6 +60,7 @@ def prepare(self, canvasItemList): canvasItemList['paths'] = [] def getQuery(self, args): + ''' returns the sql query in required signature format of pgr_bdDijkstra ''' if (self.version < 2.1): return """ SELECT @@ -189,6 +192,7 @@ def getExportMergeQuery(self, args): def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' resultPathsRubberBands = canvasItemList['paths'] rubberBand = None cur_route_id = -1 diff --git a/functions/trsp_edge.py b/functions/trsp_edge.py index 281e4cb..8d26b06 100755 --- a/functions/trsp_edge.py +++ b/functions/trsp_edge.py @@ -6,17 +6,19 @@ from qgis.core import Qgis, QgsGeometry, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'trsp(edge)' @classmethod def getControlNames(self, version): + ''' returns control names. ''' return self.commonControls + self.commonBoxes + [ 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', 'labelSourcePos', 'lineEditSourcePos', @@ -34,6 +36,7 @@ def prepare(self, canvasItemList): resultPathRubberBand.reset(Utils.getRubberBandType(False)) def getQuery(self, args): + ''' returns the sql query in required signature format of pgr_trsp_edge ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) return """ SELECT seq, id1 AS _node, id2 AS _edge, cost AS _cost FROM pgr_trsp(' @@ -128,6 +131,7 @@ def getExportMergeQuery(self, args): """ % args def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' resultPathRubberBand = canvasItemList['path'] i = 0 count = len(rows) diff --git a/functions/trsp_vertex.py b/functions/trsp_vertex.py index 9ab9cee..02067e5 100755 --- a/functions/trsp_vertex.py +++ b/functions/trsp_vertex.py @@ -5,17 +5,19 @@ from qgis.core import Qgis, QgsGeometry, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'trsp(vertex)' @classmethod def getControlNames(self, version): + ''' returns control names. ''' return self.commonControls + self.commonBoxes + [ 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', 'labelTargetId', 'lineEditTargetId', 'buttonSelectTargetId', @@ -26,6 +28,7 @@ def getControlNames(self, version): ] def isSupportedVersion(self, version): + ''' checks supported version. ''' return version >= 2.0 def prepare(self, canvasItemList): @@ -33,6 +36,7 @@ def prepare(self, canvasItemList): resultPathRubberBand.reset(Utils.getRubberBandType(False)) def getQuery(self, args): + ''' returns the sql query in required signature format of trsp_vertex ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) return """ SELECT seq, id1 AS _node, id2 AS _edge, cost AS _cost FROM pgr_trsp(' @@ -55,6 +59,7 @@ def getExportMergeQuery(self, args): def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' resultPathRubberBand = canvasItemList['path'] for row in rows: cur2 = con.cursor() diff --git a/functions/trsp_via_edges.py b/functions/trsp_via_edges.py index c8f5c34..7eabaa2 100755 --- a/functions/trsp_via_edges.py +++ b/functions/trsp_via_edges.py @@ -5,17 +5,19 @@ from qgis.core import Qgis, QgsGeometry, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'trsp(via edges)' @classmethod def getControlNames(self, version): + ''' returns control names. ''' return [ 'labelId', 'lineEditId', 'labelSource', 'lineEditSource', @@ -50,6 +52,7 @@ def prepare(self, canvasItemList): canvasItemList['paths'] = [] def getQuery(self, args): + ''' returns the sql query in required signature format of trsp_via_edges ''' return """ SELECT seq, id1 AS _path, id2 AS _node, id3 AS _edge, cost as _cost FROM pgr_trspViaEdges(' SELECT %(id)s::int4 AS id, @@ -166,6 +169,7 @@ def getExportMergeQuery(self, args): return query def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' resultPathsRubberBands = canvasItemList['paths'] rubberBand = None cur_path_id = -1 diff --git a/functions/trsp_via_vertices.py b/functions/trsp_via_vertices.py index d937e4e..8523b04 100755 --- a/functions/trsp_via_vertices.py +++ b/functions/trsp_via_vertices.py @@ -5,17 +5,19 @@ from qgis.core import Qgis, QgsGeometry, QgsWkbTypes from qgis.gui import QgsRubberBand import psycopg2 -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): @classmethod def getName(self): + ''' returns Function name. ''' return 'trsp(via vertices)' @classmethod def getControlNames(self, version): + ''' returns control names. ''' return self.commonControls + self.commonBoxes + [ 'labelIds', 'lineEditIds', 'buttonSelectIds', 'labelTurnRestrictSql', 'plainTextEditTurnRestrictSql' @@ -23,6 +25,7 @@ def getControlNames(self, version): def isSupportedVersion(self, version): + ''' checks the supported version ''' return version >= 2.1 def prepare(self, canvasItemList): @@ -32,6 +35,7 @@ def prepare(self, canvasItemList): canvasItemList['paths'] = [] def getQuery(self, args): + ''' returns the sql query in required signature format of trsp_via_vertices ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) return """ SELECT seq, id1 AS _path, id2 AS _node, id3 AS _edge, cost AS _cost FROM pgr_trspViaVertices(' diff --git a/functions/tsp_euclid.py b/functions/tsp_euclid.py index c6f4a53..7693033 100755 --- a/functions/tsp_euclid.py +++ b/functions/tsp_euclid.py @@ -1,21 +1,23 @@ from __future__ import absolute_import from builtins import str from qgis.PyQt.QtCore import QPointF, QSizeF -from qgis.PyQt.QtGui import QTextDocument +from qgis.PyQt.QtGui import QTextDocument from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation, QgsWkbTypes from qgis.gui import * import psycopg2 -from .. import pgRoutingLayer_utils as Utils +from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): - + @classmethod def getName(self): + ''' returns Function name. ''' return 'tsp(euclid)' - + @classmethod def getControlNames(self, version): + ''' returns control names. ''' return [ 'labelId', 'lineEditId', 'labelSource', 'lineEditSource', @@ -24,7 +26,7 @@ def getControlNames(self, version): 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', 'labelTargetId', 'lineEditTargetId', 'buttonSelectTargetId' ] - + @classmethod def canExport(self): return False @@ -34,15 +36,16 @@ def canExportMerged(self): return False def isSupportedVersion(self, version): - return version >= 2.0 + return version >= 2.0 def prepare(self, canvasItemList): resultNodesTextAnnotations = canvasItemList['annotations'] for anno in resultNodesTextAnnotations: anno.setVisible(False) canvasItemList['annotations'] = [] - + def getQuery(self, args): + ''' returns the sql query in required signature format of tsp_euclid ''' return """ SELECT seq, id1 AS internal, id2 AS node, cost FROM pgr_tsp(' SELECT id::int4, @@ -52,14 +55,14 @@ def getQuery(self, args): FROM %(edge_table)s_vertices_pgr WHERE id IN (%(ids)s)', %(source_id)s::int4, %(target_id)s::int4) """ % args - + def getExportQuery(self, args): args['result_query'] = self.getQuery(args) query = """ WITH result AS ( %(result_query)s ) - SELECT + SELECT CASE WHEN result._node = %(edge_table)s.%(source)s THEN %(edge_table)s.%(geometry)s @@ -72,20 +75,21 @@ def getExportQuery(self, args): return query def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' resultPathsRubberBands = canvasItemList['path'] i = 0 for row in rows: if i == 0: prevrow = row firstrow = row - i += 1 + i += 1 cur2 = con.cursor() args['result_seq'] = row[0] args['result_source_id'] = prevrow[2] args['result_target_id'] = row[2] args['result_cost'] = row[3] query2 = """ - SELECT ST_AsText( ST_MakeLine( + SELECT ST_AsText( ST_MakeLine( (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_source_id)d), (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_target_id)d) )) @@ -106,20 +110,20 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): resultPathsRubberBands.addPoint(pt) prevrow = row lastrow = row - + args['result_source_id'] = lastrow[2] args['result_target_id'] = firstrow[2] args['result_cost'] = row[3] query2 = """ - SELECT ST_AsText( ST_MakeLine( + SELECT ST_AsText( ST_MakeLine( (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_source_id)d), (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_target_id)d) )) """ % args - ##Utils.logMessage(query2) + # Utils.logMessage(query2) cur2.execute(query2) row2 = cur2.fetchone() - ##Utils.logMessage(str(row2[0])) + # Utils.logMessage(str(row2[0])) assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args geom = QgsGeometry().fromWkt(str(row2[0])) @@ -156,17 +160,18 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): cur2.execute(query2) row2 = cur2.fetchone() assert row2, "Invalid result geometry. (node_id:%(result_node_id)d)" % args - + geom = QgsGeometry().fromWkt(str(row2[0])) pt = geom.asPoint() textDocument = QTextDocument("%(result_seq)d:%(result_node_id)d" % args) - textAnnotation = QgsTextAnnotation(mapCanvas) + textAnnotation = QgsTextAnnotation() textAnnotation.setMapPosition(geom.asPoint()) textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) textAnnotation.setDocument(textDocument) - #textAnnotation.update() + + QgsMapCanvasAnnotationItem(textAnnotation, mapCanvas) resultNodesTextAnnotations.append(textAnnotation) - + def __init__(self, ui): FunctionBase.__init__(self, ui) diff --git a/icon.png b/icon.png old mode 100644 new mode 100755 diff --git a/icons/add.png b/icons/add.png old mode 100644 new mode 100755 diff --git a/metadata.txt b/metadata.txt old mode 100644 new mode 100755 index 638dbf8..4832c8d --- a/metadata.txt +++ b/metadata.txt @@ -4,7 +4,7 @@ description=Dockable widget that adds pgRouting layers about=Dockable widget that adds pgRouting layers version=2.2.2 qgisMinimumVersion=3.0 -qgisMaximumVersion=3.1 +qgisMaximumVersion=4.0 author=Anita Graser, Ko Nagase, Vicky Vergara email=project@pgrouting.org changelog=2.2.2 diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index 79204e5..13cc00b 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -52,9 +52,7 @@ class PgRoutingLayer(object): 'bdDijkstra', 'bdAstar', 'dijkstraCost', - 'kdijkstra_cost', 'trsp_edge', - 'kdijkstra_path', 'ksp', 'trsp_via_vertices', 'trsp_via_edges' @@ -222,6 +220,7 @@ def show(self): self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dock) def unload(self): + ''' Removes the plugin menu item and icon''' self.clear() self.saveSettings() # Remove the plugin menu item and icon @@ -229,6 +228,7 @@ def unload(self): self.iface.removeDockWidget(self.dock) def reloadConnections(self): + ''' Reloads the connection with database. ''' oldReloadMessage = self.reloadMessage self.reloadMessage = False database = str(self.dock.comboConnections.currentText()) @@ -268,6 +268,7 @@ def reloadConnections(self): def updateConnectionEnabled(self): + ''' Updates the database connection name and function ''' dbname = str(self.dock.comboConnections.currentText()) if dbname =='': return @@ -288,6 +289,7 @@ def updateConnectionEnabled(self): self.updateFunctionEnabled(currentFunction) def loadFunctionsForVersion(self): + ''' Loads function names based on pgr version. ''' currentText = str(self.dock.comboBoxFunction.currentText()) self.dock.comboBoxFunction.clear() @@ -304,6 +306,7 @@ def loadFunctionsForVersion(self): def updateFunctionEnabled(self, text): + ''' Updates the GUI fields of the selected function. ''' text = str (self.dock.comboBoxFunction.currentText()) if text== '': return @@ -343,6 +346,7 @@ def updateFunctionEnabled(self, text): self.dock.buttonExportMerged.setEnabled(canExportMerged) def selectIds(self, checked): + ''' Selects the ids and dispaly on lineEdit. ''' if checked: self.toggleSelectButton(self.dock.buttonSelectIds) self.dock.lineEditIds.setText("") @@ -360,6 +364,7 @@ def selectIds(self, checked): self.iface.mapCanvas().unsetMapTool(self.idsEmitPoint) def setIds(self, pt): + ''' Sets the ids on mapCanvas with color ''' function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getBaseArguments() mapCanvas = self.iface.mapCanvas() @@ -411,6 +416,7 @@ def setIds(self, pt): Utils.refreshMapCanvas(mapCanvas) def selectSourceId(self, checked): + ''' Selects the source id and dispaly its value on lineEdit. ''' if checked: self.toggleSelectButton(self.dock.buttonSelectSourceId) self.dock.lineEditSourceId.setText("") @@ -421,6 +427,7 @@ def selectSourceId(self, checked): self.iface.mapCanvas().unsetMapTool(self.sourceIdEmitPoint) def setSourceId(self, pt): + ''' Sets the source id by finding nearest node and displays in mapCanvas with color ''' function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getBaseArguments() if not function.isEdgeBase(): @@ -452,6 +459,7 @@ def setSourceId(self, pt): def selectSourceIds(self, checked): + ''' Selects the source ids and dispaly its value on lineEdit. ''' if checked: self.toggleSelectButton(self.dock.buttonSelectSourceIds) self.dock.lineEditSourceIds.setText("") @@ -464,6 +472,7 @@ def selectSourceIds(self, checked): self.iface.mapCanvas().unsetMapTool(self.sourceIdsEmitPoint) def setSourceIds(self, pt): + ''' Sets the source id by finding nearest node and displays in mapCanvas with color ''' args = self.getBaseArguments() result, id, wkt = self.findNearestNode(args, pt) if result: @@ -483,6 +492,7 @@ def setSourceIds(self, pt): def selectTargetId(self, checked): + ''' Selects the target id and dispaly its value on lineEdit. ''' if checked: self.toggleSelectButton(self.dock.buttonSelectTargetId) self.dock.lineEditTargetId.setText("") @@ -493,6 +503,7 @@ def selectTargetId(self, checked): self.iface.mapCanvas().unsetMapTool(self.targetIdEmitPoint) def setTargetId(self, pt): + ''' Sets the target id by finding nearest node and displays in mapCanvas with color ''' function = self.functions[str(self.dock.comboBoxFunction.currentText())] args = self.getBaseArguments() if not function.isEdgeBase(): @@ -523,6 +534,7 @@ def setTargetId(self, pt): Utils.refreshMapCanvas(self.iface.mapCanvas()) def selectTargetIds(self, checked): + ''' Selects the target ids and dispaly its value on lineEdit. ''' if checked: self.toggleSelectButton(self.dock.buttonSelectTargetIds) self.dock.lineEditTargetIds.setText("") @@ -535,6 +547,7 @@ def selectTargetIds(self, checked): self.iface.mapCanvas().unsetMapTool(self.targetIdsEmitPoint) def setTargetIds(self, pt): + ''' Sets the target ids by finding nearest node and displays in mapCanvas with color ''' args = self.getBaseArguments() result, id, wkt = self.findNearestNode(args, pt) if result: @@ -553,6 +566,7 @@ def setTargetIds(self, pt): Utils.refreshMapCanvas(mapCanvas) def updateReverseCostEnabled(self, state): + ''' Updates the reverse cost checkBox ''' if state == Qt.Checked: self.dock.lineEditReverseCost.setEnabled(True) else: @@ -629,6 +643,7 @@ def run(self): 'server closed the connection unexpectedly') def export(self): + ''' Exports the result layer ''' QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) function = self.functions[str(self.dock.comboBoxFunction.currentText())] @@ -699,6 +714,7 @@ def export(self): 'server closed the connection unexpectedly') def cleanQuery(self, msgQuery): + ''' Cleans the query ''' query = msgQuery.replace('\n', ' ') query = re.sub(r'\s+', ' ', query) query = query.replace('( ', '(') @@ -729,6 +745,7 @@ def getBBOX(self, srid, use_bbox): def exportMerged(self): + ''' exports the result layer with input layer ''' QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) function = self.functions[str(self.dock.comboBoxFunction.currentText())] @@ -802,6 +819,7 @@ def exportMerged(self): 'server closed the connection unexpectedly') def getLayerName(self, args, letter=''): + ''' returns the layer Name ''' function = self.functions[str(self.dock.comboBoxFunction.currentText())] layerName = "(" + letter @@ -844,6 +862,7 @@ def getLayerName(self, args, letter=''): def clear(self): + ''' Clears the selected ids ''' #self.dock.lineEditIds.setText("") for marker in self.idsVertexMarkers: marker.setVisible(False) @@ -877,7 +896,6 @@ def clear(self): for anno in self.canvasItemList['annotations']: try: anno.setVisible(False) - self.iface.mapCanvas().scene().removeItem(anno) except RuntimeError as e: QApplication.restoreOverrideCursor() QMessageBox.critical(self.dock, self.dock.windowTitle(), '%s' % e) @@ -900,6 +918,7 @@ def toggleSelectButton(self, button): selectButton.click() def getArguments(self, controls): + ''' updates the GUI field text to args ''' args = {} args['edge_table'] = self.dock.lineEditTable.text() args['geometry'] = self.dock.lineEditGeometry.text() @@ -995,6 +1014,7 @@ def getArguments(self, controls): return args def getBaseArguments(self): + ''' updates base arguments from GUI to args ''' args = {} args['edge_table'] = self.dock.lineEditTable.text() args['geometry'] = self.dock.lineEditGeometry.text() @@ -1018,6 +1038,7 @@ def getBaseArguments(self): # emulate "matching.sql" - "find_nearest_node_within_distance" def findNearestNode(self, args, pt): + ''' finds the nearest node to selected point ''' distance = self.iface.mapCanvas().getCoordinateTransform().mapUnitsPerPixel() * self.FIND_RADIUS rect = QgsRectangle(pt.x() - distance, pt.y() - distance, pt.x() + distance, pt.y() + distance) canvasCrs = Utils.getDestinationCrs(self.iface.mapCanvas()) @@ -1139,6 +1160,7 @@ def findNearestNode(self, args, pt): # emulate "matching.sql" - "find_nearest_link_within_distance" def findNearestLink(self, args, pt): + ''' finds the nearest link to selected point ''' distance = self.iface.mapCanvas().getCoordinateTransform().mapUnitsPerPixel() * self.FIND_RADIUS rect = QgsRectangle(pt.x() - distance, pt.y() - distance, pt.x() + distance, pt.y() + distance) canvasCrs = Utils.getDestinationCrs(self.iface.mapCanvas()) @@ -1152,7 +1174,7 @@ def findNearestLink(self, args, pt): srid, geomType = Utils.getSridAndGeomType(con, '%(edge_table)s' % args, '%(geometry)s' % args) - + layerCrs = QgsCoordinateReferenceSystem() Utils.createFromSrid(layerCrs, srid) trans = QgsCoordinateTransform(canvasCrs, layerCrs, QgsProject.instance()) @@ -1210,6 +1232,7 @@ def findNearestLink(self, args, pt): db.con.close() def loadSettings(self): + ''' loads the default settings ''' settings = QSettings() idx = self.dock.comboConnections.findText(Utils.getStringValue(settings, '/pgRoutingLayer/Database', '')) if idx >= 0: diff --git a/pgRoutingLayer_utils.py b/pgRoutingLayer_utils.py index f3480a0..3af160d 100755 --- a/pgRoutingLayer_utils.py +++ b/pgRoutingLayer_utils.py @@ -7,6 +7,8 @@ def getSridAndGeomType(con, table, geometry): + ''' retrieve Spatial Reference Id and geometry type, example 4326(WGS84) , Point ''' + args = {} args['table'] = table args['geometry'] = geometry @@ -21,18 +23,23 @@ def getSridAndGeomType(con, table, geometry): def setStartPoint(geomType, args): + ''' records startpoint of geometry and stores in args dictionary. ''' + if geomType == 'ST_MultiLineString': args['startpoint'] = "ST_StartPoint(ST_GeometryN(%(geometry)s, 1))" % args elif geomType == 'ST_LineString': args['startpoint'] = "ST_StartPoint(%(geometry)s)" % args def setEndPoint(geomType, args): + ''' records endpoint and stores in args. ''' + if geomType == 'ST_MultiLineString': args['endpoint'] = "ST_EndPoint(ST_GeometryN(%(geometry)s, 1))" % args elif geomType == 'ST_LineString': args['endpoint'] = "ST_EndPoint(%(geometry)s)" % args def setTransformQuotes(args, srid, canvas_srid): + ''' Sets transformQuotes ''' if srid > 0 and canvas_srid > 0: args['transform_s'] = "ST_Transform(" args['transform_e'] = ", %(canvas_srid)d)" % args @@ -41,24 +48,29 @@ def setTransformQuotes(args, srid, canvas_srid): args['transform_e'] = "" def isSIPv2(): + '''Checks the version of SIP ''' return sip.getapi('QVariant') > 1 def getStringValue(settings, key, value): + ''' returns key and its corresponding value. example: ("interval",30). ''' if isSIPv2(): return settings.value(key, value, type=str) else: return settings.value(key, QVariant(value)).toString() def getBoolValue(settings, key, value): + ''' returns True if settings exist otherwise False. ''' if isSIPv2(): return settings.value(key, value, type=bool) else: return settings.value(key, QVariant(value)).toBool() def isQGISv1(): + ''' returns True if QGis has version l.9 or less, otherwise False. ''' return Qgis.QGIS_VERSION_INT < 10900 def getDestinationCrs(mapCanvas): + ''' returns Coordinate Reference ID of map/overlaid layers. ''' if isQGISv1(): return mapCanvas.mapRenderer().destinationSrs() else: @@ -68,18 +80,21 @@ def getDestinationCrs(mapCanvas): return mapCanvas.mapSettings().destinationCrs() def getCanvasSrid(crs): + ''' Returns SRID based on QGIS version. ''' if isQGISv1(): return crs.epsg() else: return crs.postgisSrid() def createFromSrid(crs, srid): + ''' Creates EPSG crs for QGIS version 1 or Creates Spatial reference system based of SRID for QGIS version 2. ''' if isQGISv1(): return crs.createFromEpsg(srid) else: return crs.createFromSrid(srid) def getRubberBandType(isPolygon): + ''' returns RubberBandType as polygon or lineString ''' if isQGISv1(): return isPolygon else: @@ -89,6 +104,7 @@ def getRubberBandType(isPolygon): return QgsWkbTypes.LineGeometry def refreshMapCanvas(mapCanvas): + ''' refreshes the mapCanvas , RubberBand is cleared. ''' if Qgis.QGIS_VERSION_INT < 20400: return mapCanvas.clear() else: @@ -98,6 +114,7 @@ def logMessage(message, level=Qgis.Info): QgsMessageLog.logMessage(message, 'pgRouting Layer', level) def getNodeQuery(args, geomType): + ''' returns a sql query to get nodes from a geometry. ''' setStartPoint(geomType, args) setEndPoint(geomType, args) return """ @@ -118,6 +135,7 @@ def getNodeQuery(args, geomType): )""" % args def getPgrVersion(con): + ''' returns version of PostgreSQL database. ''' try: cur = con.cursor() cur.execute('SELECT version FROM pgr_version()') @@ -129,6 +147,6 @@ def getPgrVersion(con): return float(version) except psycopg2.DatabaseError as e: #database didn't have pgrouting - return 0; + return 0 except SystemError as e: return 0 diff --git a/readme.md b/readme.md old mode 100644 new mode 100755 index 6683b07..378244b --- a/readme.md +++ b/readme.md @@ -21,9 +21,8 @@ PgRoutingLayer currently supports the following functions: * bdAstar * bdDijkstra * dijkstra +* dijkstraCost * drivingDistance -* kdijkstra_cost -* kdijkstra_path * ksp * shootingStar * trsp_edge diff --git a/tests/Test_FunctionBase.py b/tests/Test_FunctionBase.py old mode 100644 new mode 100755 diff --git a/tests/Test_utils.py b/tests/Test_utils.py old mode 100644 new mode 100755 diff --git a/tests/__init__.py b/tests/__init__.py old mode 100644 new mode 100755 diff --git a/tests/test_data/poly.PRJ b/tests/test_data/poly.PRJ old mode 100644 new mode 100755 diff --git a/tests/test_data/poly.dbf b/tests/test_data/poly.dbf old mode 100644 new mode 100755 diff --git a/tests/test_data/poly.shp b/tests/test_data/poly.shp old mode 100644 new mode 100755 diff --git a/tests/test_data/poly.shx b/tests/test_data/poly.shx old mode 100644 new mode 100755 diff --git a/tests/test_init.py b/tests/test_init.py old mode 100644 new mode 100755 diff --git a/ui_pgRoutingLayer.ui b/ui_pgRoutingLayer.ui old mode 100644 new mode 100755 From 785258c493c97284ee526c6d23a65a2b8da2f58a Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Mon, 6 Aug 2018 04:43:40 +0530 Subject: [PATCH 23/32] withPoint Family Functionality added for : 1. pgr_withPoints 2. pgr_withPointsCost --- pgRoutingLayer.py | 43 +++++++++-- with_Points.py | 110 +++++++++++++++++++++++++++ with_PointsCost.py | 186 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 334 insertions(+), 5 deletions(-) create mode 100644 with_Points.py create mode 100644 with_PointsCost.py diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index 13cc00b..b3b0648 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -43,6 +43,8 @@ class PgRoutingLayer(object): SUPPORTED_FUNCTIONS = [ 'tsp_euclid', + 'with_Points', + 'with_PointsCost', 'dijkstra', 'trsp_vertex', 'astar', @@ -80,12 +82,14 @@ class PgRoutingLayer(object): 'labelTargetPos', 'lineEditTargetPos', 'labelDistance', 'lineEditDistance', 'labelAlpha', 'lineEditAlpha', - 'labelPaths', 'lineEditPaths', + 'labelPaths', 'lineEditPaths','label_pointsTable','lineEditPointsTable', 'checkBoxDirected', 'checkBoxHasReverseCost', 'checkBoxHeapPaths', - 'checkBoxUseBBOX', + 'checkBoxUseBBOX', 'checkBoxDetails', 'labelTurnRestrictSql', 'plainTextEditTurnRestrictSql', + 'labelPid', 'lineEditPid', 'labelEdge_id', 'lineEditEdge_id', + 'labelFraction', 'lineEditFraction', 'labelSide', 'lineEditSide','labelDrivingSide','checkBoxLeft','checkBoxRight' ] FIND_RADIUS = 10 FRACTION_DECIMAL_PLACES = 2 @@ -930,6 +934,22 @@ def getArguments(self, controls): if 'lineEditTarget' in controls: args['target'] = self.dock.lineEditTarget.text() + + if 'lineEditPointsTable' in controls: + args['points_table'] = self.dock.lineEditPointsTable.text() + + if 'lineEditPid' in controls: + args['pid'] = self.dock.lineEditPid.text() + + if 'lineEditEdge_id' in controls: + args['edge_id'] = self.dock.lineEditEdge_id.text() + + if 'lineEditFraction' in controls: + args['fraction'] = self.dock.lineEditFraction.text() + + if 'lineEditSide' in controls: + args['side'] = self.dock.lineEditSide.text() + if 'lineEditCost' in controls: args['cost'] = self.dock.lineEditCost.text() @@ -991,6 +1011,12 @@ def getArguments(self, controls): if 'checkBoxDirected' in controls: args['directed'] = str(self.dock.checkBoxDirected.isChecked()).lower() + if 'checkBoxDetails' in controls: + args['details'] = str(self.dock.checkBoxDirected.isChecked()).lower() + + + + if 'checkBoxHeapPaths' in controls: args['heap_paths'] = str(self.dock.checkBoxHeapPaths.isChecked()).lower() @@ -998,9 +1024,14 @@ def getArguments(self, controls): args['use_bbox'] = str(self.dock.checkBoxUseBBOX.isChecked()).lower() else: args['use_bbox'] = 'false' - - - + + if 'labelDrivingSide' in controls: + args['driving_side'] = str('b') + if (self.dock.checkBoxLeft.isChecked() == True and self.dock.checkBoxRight.isChecked() == False): + args['driving_side'] = str('l') + elif (self.dock.checkBoxLeft.isChecked() == False and self.dock.checkBoxRight.isChecked() == True): + args['driving_side'] = str('r') + if 'checkBoxHasReverseCost' in controls: args['has_reverse_cost'] = str(self.dock.checkBoxHasReverseCost.isChecked()).lower() if args['has_reverse_cost'] == 'false': @@ -1242,6 +1273,7 @@ def loadSettings(self): self.dock.comboBoxFunction.setCurrentIndex(idx) self.dock.lineEditTable.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/edge_table', 'roads')) + self.dock.lineEditPointsTable.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/pointsOfInterest', 'pointsOfInterest')) self.dock.lineEditGeometry.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/geometry', 'the_geom')) self.dock.lineEditId.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/id', 'id')) self.dock.lineEditSource.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/source', 'source')) @@ -1281,6 +1313,7 @@ def saveSettings(self): settings.setValue('/pgRoutingLayer/Function', self.dock.comboBoxFunction.currentText()) settings.setValue('/pgRoutingLayer/sql/edge_table', self.dock.lineEditTable.text()) + settings.setValue('/pgRoutingLayer/sql/pointsOfInterest', self.dock.lineEditPointsTable.text()) settings.setValue('/pgRoutingLayer/sql/geometry', self.dock.lineEditGeometry.text()) settings.setValue('/pgRoutingLayer/sql/id', self.dock.lineEditId.text()) diff --git a/with_Points.py b/with_Points.py new file mode 100644 index 0000000..fd6ef53 --- /dev/null +++ b/with_Points.py @@ -0,0 +1,110 @@ +from __future__ import absolute_import +from builtins import str +from qgis.PyQt.QtCore import QSizeF, QPointF +from qgis.PyQt.QtGui import QColor, QTextDocument +from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation, QgsWkbTypes, QgsAnnotation +from qgis.gui import QgsRubberBand +import psycopg2 +from pgRoutingLayer import pgRoutingLayer_utils as Utils +from .FunctionBase import FunctionBase + +class Function(FunctionBase): + + @classmethod + def getName(self): + ''' returns Function name. ''' + return 'with_Points' + + @classmethod + def getControlNames(self, version): + ''' returns control names. ''' + self.version = version + if self.version < 2.1: + # version 2.0 has only one to one + return self.commonControls + self.commonBoxes + [ + 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', + 'labelTargetId','labelSide','lineEditSide','lineEditEdge_id','labelFraction','lineEditFraction', 'lineEditPointsTable', 'lineEditTargetId','labelPid','lineEditPid','labelEdge_id', 'buttonSelectTargetId', + 'labelEdge_id','labelSide' + + ] + else: + return self.commonControls + self.commonBoxes + [ + 'labelSourceIds', 'lineEditSourceIds', 'buttonSelectSourceIds', + 'labelTargetIds', 'lineEditPointsTable', 'label_pointsTable', 'lineEditTargetIds', 'buttonSelectTargetIds', + 'labelSide','lineEditSide','lineEditEdge_id','labelEdge_id','labelFraction','lineEditFraction','labelPid','lineEditPid', + 'labelDrivingSide','checkBoxLeft','checkBoxRight','checkBoxDetails' + ] + + + @classmethod + def isSupportedVersion(self, version): + ''' Checks supported version ''' + # valid starting pgr v2.1 + return version >= 2.1 + + + @classmethod + def canExportQuery(self): + return False + + @classmethod + def canExportMerged(self): + return False + + + + def prepare(self, canvasItemList): + resultNodesTextAnnotations = canvasItemList['annotations'] + for anno in resultNodesTextAnnotations: + anno.setVisible(False) + canvasItemList['annotations'] = [] + + + def getQuery(self, args): + ''' returns the sql query in required signature format of pgr_withPoints ''' + args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) + args['where_clause_with'] = self.whereClause(args['points_table'], args['geometry'], args['BBOX']) + return """ + SELECT seq, path_seq AS path_seq, + start_vid AS _start_vid , end_vid AS _end_vid, node, cost AS _cost, lead(agg_cost) over() AS agg_cost + FROM pgr_withPoints(' + SELECT %(id)s AS id, + %(source)s AS source, + %(target)s AS target, + %(cost)s AS cost + %(reverse_cost)s + FROM %(edge_table)s + %(where_clause)s + ', + 'SELECT %(pid)s AS pid, + %(edge_id)s AS edge_id, + %(fraction)s AS fraction, + %(side)s AS side + FROM %(points_table)s + %(where_clause_with)s', + array[%(source_ids)s]::BIGINT[], array[%(target_ids)s]::BIGINT[], %(directed)s, '%(driving_side)s', %(details)s) + """ % args + + def getExportQuery(self, args): + return self.getJoinResultWithEdgeTable(args) + + + def getExportMergeQuery(self, args): + if self.version < 2.1: + return self.getExportOneSourceOneTargetMergeQuery(args) + else: + return self.getExportManySourceManyTargetMergeQuery(args) + + + + def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' + if self.version < 2.1: + self.drawOnePath(rows, con, args, geomType, canvasItemList, mapCanvas) + else: + self.drawManyPaths(rows, con, args, geomType, canvasItemList, mapCanvas) + + + + def __init__(self, ui): + FunctionBase.__init__(self, ui) diff --git a/with_PointsCost.py b/with_PointsCost.py new file mode 100644 index 0000000..f306a82 --- /dev/null +++ b/with_PointsCost.py @@ -0,0 +1,186 @@ +from __future__ import absolute_import +from builtins import str +from qgis.PyQt.QtCore import QSizeF, QPointF +from qgis.PyQt.QtGui import QColor, QTextDocument +from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation, QgsWkbTypes, QgsAnnotation +from qgis.gui import QgsRubberBand +import psycopg2 +from pgRoutingLayer import pgRoutingLayer_utils as Utils +from .FunctionBase import FunctionBase + +class Function(FunctionBase): + + @classmethod + def getName(self): + ''' returns Function name. ''' + return 'with_PointsCost' + + @classmethod + def getControlNames(self, version): + ''' returns control names. ''' + self.version = version + if self.version < 2.1: + # version 2.0 has only one to one + return self.commonControls + self.commonBoxes + [ + 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', + 'labelTargetId','labelSide','lineEditSide','lineEditEdge_id','labelFraction','lineEditFraction', 'lineEditPointsTable', 'lineEditTargetId','labelPid','lineEditPid','labelEdge_id', 'buttonSelectTargetId', + 'labelEdge_id','labelSide' + + ] + else: + return self.commonControls + self.commonBoxes + [ + 'labelSourceIds', 'lineEditSourceIds', 'buttonSelectSourceIds', + 'labelTargetIds', 'lineEditPointsTable', 'label_pointsTable', 'lineEditTargetIds', 'buttonSelectTargetIds', + 'labelSide','lineEditSide','lineEditEdge_id','labelEdge_id','labelFraction','lineEditFraction','labelPid','lineEditPid', + 'labelDrivingSide','checkBoxLeft','checkBoxRight' + ] + + + @classmethod + def isSupportedVersion(self, version): + ''' Checks supported version ''' + # valid starting pgr v2.1 + return version >= 2.1 + + + @classmethod + def canExportQuery(self): + return False + + @classmethod + def canExportMerged(self): + return False + + + + def prepare(self, canvasItemList): + resultNodesTextAnnotations = canvasItemList['annotations'] + for anno in resultNodesTextAnnotations: + anno.setVisible(False) + canvasItemList['annotations'] = [] + + + def getQuery(self, args): + ''' returns the sql query in required signature format of pgr_withPointsCost ''' + args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) + args['where_clause_with'] = self.whereClause(args['points_table'], args['geometry'], args['BBOX']) + return """ + SELECT start_vid AS start_vid , end_vid AS end_vid, cost AS cost + FROM pgr_withPointsCost(' + SELECT %(id)s AS id, + %(source)s AS source, + %(target)s AS target, + %(cost)s AS cost + %(reverse_cost)s + FROM %(edge_table)s + %(where_clause)s + ', + 'SELECT %(pid)s AS pid, + %(edge_id)s AS edge_id, + %(fraction)s AS fraction, + %(side)s AS side + FROM %(points_table)s + %(where_clause_with)s', + array[%(source_ids)s]::BIGINT[], array[%(target_ids)s]::BIGINT[], %(directed)s, '%(driving_side)s') + """ % args + + def getExportQuery(self, args): + args['result_query'] = self.getQuery(args) + args['vertex_table'] = """ + %(edge_table)s_vertices_pgr + """ % args + + return """ + WITH + result AS ( %(result_query)s ) + SELECT result.*, ST_MakeLine(a.the_geom, b.the_geom) AS path_geom + + FROM result + JOIN %(vertex_table)s AS a ON (start_vid = a.id) + JOIN %(vertex_table)s AS b ON (end_vid = b.id) + """ % args + + + + + def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): + ''' draw the result ''' + resultPathsRubberBands = canvasItemList['paths'] + rubberBand = None + cur_path_id = -1 + for row in rows: + cur2 = con.cursor() + args['result_path_id'] = row[0] + args['result_source_id'] = row[1] + args['result_target_id'] = row[2] + args['result_cost'] = row[3] + if args['result_path_id'] != cur_path_id: + cur_path_id = args['result_path_id'] + if rubberBand: + resultPathsRubberBands.append(rubberBand) + rubberBand = None + + rubberBand = QgsRubberBand(mapCanvas, Utils.getRubberBandType(False)) + rubberBand.setColor(QColor(255, 0, 0, 128)) + rubberBand.setWidth(4) + if args['result_cost'] != -1: + query2 = """ + SELECT ST_AsText( ST_MakeLine( + (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_source_id)d), + (SELECT the_geom FROM %(edge_table)s_vertices_pgr WHERE id = %(result_target_id)d) + )) + """ % args + ##Utils.logMessage(query2) + cur2.execute(query2) + row2 = cur2.fetchone() + ##Utils.logMessage(str(row2[0])) + assert row2, "Invalid result geometry. (path_id:%(result_path_id)d, saource_id:%(result_source_id)d, target_id:%(result_target_id)d)" % args + + geom = QgsGeometry().fromWkt(str(row2[0])) + if geom.wkbType() == QgsWkbTypes.MultiLineString: + for line in geom.asMultiPolyline(): + for pt in line: + rubberBand.addPoint(pt) + elif geom.wkbType() == QgsWkbTypes.LineString: + for pt in geom.asPolyline(): + rubberBand.addPoint(pt) + + if rubberBand: + resultPathsRubberBands.append(rubberBand) + rubberBand = None + resultNodesTextAnnotations = canvasItemList['annotations'] + Utils.setStartPoint(geomType, args) + Utils.setEndPoint(geomType, args) + for row in rows: + cur2 = con.cursor() + args['result_seq'] = row[0] + args['result_source_id'] = row[1] + args['result_target_id'] = row[2] + args['result_cost'] = row[3] + query2 = """ + SELECT ST_AsText(%(transform_s)s%(startpoint)s%(transform_e)s) FROM %(edge_table)s + WHERE %(source)s = %(result_target_id)d + UNION + SELECT ST_AsText(%(transform_s)s%(endpoint)s%(transform_e)s) FROM %(edge_table)s + WHERE %(target)s = %(result_target_id)d + """ % args + cur2.execute(query2) + row2 = cur2.fetchone() + assert row2, "Invalid result geometry. (target_id:%(result_target_id)d)" % args + + geom = QgsGeometry().fromWkt(str(row2[0])) + pt = geom.asPoint() + textDocument = QTextDocument("%(result_target_id)d:%(result_cost)f" % args) + textAnnotation = QgsTextAnnotation() + textAnnotation.setMapPosition(geom.asPoint()) + textAnnotation.setFrameSize(QSizeF(textDocument.idealWidth(), 20)) + textAnnotation.setFrameOffsetFromReferencePoint(QPointF(20, -40)) + textAnnotation.setDocument(textDocument) + + QgsMapCanvasAnnotationItem(textAnnotation, mapCanvas) + resultNodesTextAnnotations.append(textAnnotation) + + + + def __init__(self, ui): + FunctionBase.__init__(self, ui) From 4e9a7722c307187b3183a6a2efa433a78ad4f6cc Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Mon, 6 Aug 2018 21:22:54 +0530 Subject: [PATCH 24/32] Functionality_revised Moved the functions to functions folder --- with_Points.py => functions/with_Points.py | 0 with_PointsCost.py => functions/with_PointsCost.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename with_Points.py => functions/with_Points.py (100%) rename with_PointsCost.py => functions/with_PointsCost.py (100%) diff --git a/with_Points.py b/functions/with_Points.py similarity index 100% rename from with_Points.py rename to functions/with_Points.py diff --git a/with_PointsCost.py b/functions/with_PointsCost.py similarity index 100% rename from with_PointsCost.py rename to functions/with_PointsCost.py From 4ad89bbeab3a66fac07485a4340ff16bd0077938 Mon Sep 17 00:00:00 2001 From: AasheeshT Date: Tue, 14 Aug 2018 02:50:06 +0530 Subject: [PATCH 25/32] DijkstraCost DijkstraCost now works perfectly --- functions/dijkstraCost.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/dijkstraCost.py b/functions/dijkstraCost.py index 0eb6c43..70e951f 100755 --- a/functions/dijkstraCost.py +++ b/functions/dijkstraCost.py @@ -3,7 +3,7 @@ from qgis.PyQt.QtCore import QSizeF, QPointF from qgis.PyQt.QtGui import QColor, QTextDocument from qgis.core import QgsGeometry, Qgis, QgsTextAnnotation, QgsWkbTypes, QgsAnnotation -from qgis.gui import QgsRubberBand +from qgis.gui import QgsRubberBand, QgsMapCanvasAnnotationItem import psycopg2 from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase From 6b0d20203dcc263063bd87f8c48ee91281200b6f Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 1 Oct 2018 01:07:31 +0200 Subject: [PATCH 26/32] remove repeated code --- Test_utils.py | 140 -------------------------------------------------- 1 file changed, 140 deletions(-) delete mode 100755 Test_utils.py diff --git a/Test_utils.py b/Test_utils.py deleted file mode 100755 index 4943bcf..0000000 --- a/Test_utils.py +++ /dev/null @@ -1,140 +0,0 @@ -import pgRoutingLayer_utils as utils -import unittest -import sys -from qgis.core import * -from qgis.gui import * -from PyQt4.QtCore import * -from PyQt4.QtGui import * -import psycopg2 -import sip - - - -class TestUtils(unittest.TestCase): - - def test_setStartPoint_1(self): - args = { 'geometry': 'test_geom','table' : 'test_table'} - geomType = 'ST_LineString' - utils.setStartPoint(geomType,args) - self.assertEqual(args['startpoint'], 'ST_StartPoint(test_geom)') - - def test_setStartPoint_2(self): - args = { 'geometry': 'test_geom','table' : 'test_table'} - geomType = 'ST_MultiLineString' - utils.setStartPoint(geomType,args) - self.assertEqual(args['startpoint'], 'ST_StartPoint(ST_GeometryN(test_geom, 1))') - - def test_setEndPoint_1(self): - args = { 'geometry': 'test_geom','table' : 'test_table'} - geomType = 'ST_LineString' - utils.setEndPoint(geomType,args) - self.assertEqual(args['endpoint'], 'ST_EndPoint(test_geom)') - - def test_setEndPoint_2(self): - args = { 'geometry': 'test_geom','table' : 'test_table'} - geomType = 'ST_MultiLineString' - utils.setEndPoint(geomType,args) - self.assertEqual(args['endpoint'], 'ST_EndPoint(ST_GeometryN(test_geom, 1))') - - def test_isSIPv2(self): - self.assertTrue(utils.isSIPv2()) - - def test_getStringValue(self): - setting = QSettings() - setting.setValue('/pgRoutingLayer/Database', 99) - self.assertEqual(utils.getStringValue(setting,'/pgRoutingLayer/Database', 99) ,'99') - - def test_getBoolValue(self): - setting = QSettings() - setting.setValue('/pgRoutingLayer/Database', 99) - self.assertTrue(utils.getBoolValue(setting,'/pgRoutingLayer/Database', 99)) - - - def test_getDestinationCrs(self): - app = QApplication(sys.argv) - # create a map canvas widget - canvas = QgsMapCanvas() - canvas.setCanvasColor(QColor('white')) - canvas.enableAntiAliasing(True) - canvas.setMinimumSize(800, 600) - # load a shapefile - layer = QgsVectorLayer('test_data' ,'poly', 'ogr') - # add the layer to the canvas and zoom to it - QgsMapLayerRegistry.instance().addMapLayer(layer) - canvas.setLayerSet([QgsMapCanvasLayer(layer)]) - canvas.setExtent(layer.extent()) - self.assertIsNotNone(utils.getDestinationCrs(canvas)) - - def test_getCanvasSrid(self): - crs = QgsCoordinateReferenceSystem() - crs.createFromProj4("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs") - self.assertEqual(utils.getCanvasSrid(crs) ,0) - - def test_getRubberBandType_1(self): - isPolygon = True - self.assertEqual(utils.getRubberBandType(isPolygon),2) - - def test_getRubberBandType_2(self): - isPolygon = False - self.assertEqual(utils.getRubberBandType(isPolygon),1) - - def test_getNodeQuery(self): - args = {'geometry' : 'test_geom','source': 1,'startpoint' : 10,'edge_table':'test_Table','target':100,'endpoint':90} - expected_sql= """ - WITH node AS ( - SELECT id::int4, - ST_X(test_geom) AS x, - ST_Y(test_geom) AS y, - test_geom - FROM ( - SELECT 1::int4 AS id, - ST_StartPoint(ST_GeometryN(test_geom, 1)) AS test_geom - FROM test_Table - UNION - SELECT 100::int4 AS id, - ST_EndPoint(ST_GeometryN(test_geom, 1)) AS test_geom - FROM test_Table - ) AS node - )""" - self.maxDiff = None - geomType = 'ST_MultiLineString' - self.assertMultiLineEqual(utils.getNodeQuery(args,geomType),expected_sql) - -if __name__ == '__main__': - unittest.main() - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From a8aff80a49cae84d032ab938544f44c8cba5a96d Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 1 Oct 2018 02:47:26 +0200 Subject: [PATCH 27/32] fixed KSP functionality --- functions/ksp.py | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/functions/ksp.py b/functions/ksp.py index 8ae1bf8..004fae9 100755 --- a/functions/ksp.py +++ b/functions/ksp.py @@ -12,12 +12,12 @@ class Function(FunctionBase): version = 2.0 - + @classmethod def getName(self): ''' returns Function name. ''' return 'ksp' - + @classmethod def getControlNames(self, version): ''' returns control names. ''' @@ -26,30 +26,17 @@ def getControlNames(self, version): # only works for directed graph self.version = version if (self.version < 2.1): - return [ - 'labelId', 'lineEditId', - 'labelSource', 'lineEditSource', - 'labelTarget', 'lineEditTarget', - 'labelCost', 'lineEditCost', - 'labelReverseCost', 'lineEditReverseCost', + return self.commonControls + self.commonBoxes + [ 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', 'labelTargetId', 'lineEditTargetId', 'buttonSelectTargetId', - 'labelPaths', 'lineEditPaths', - 'checkBoxHasReverseCost' + 'labelPaths', 'lineEditPaths' ] else: # function pgr_ksp(text,bigint,bigint,integer,boolean,boolean) - return [ - 'labelId', 'lineEditId', - 'labelSource', 'lineEditSource', - 'labelTarget', 'lineEditTarget', - 'labelCost', 'lineEditCost', - 'labelReverseCost', 'lineEditReverseCost', + return self.commonControls + self.commonBoxes + [ 'labelSourceId', 'lineEditSourceId', 'buttonSelectSourceId', 'labelTargetId', 'lineEditTargetId', 'buttonSelectTargetId', 'labelPaths', 'lineEditPaths', - 'checkBoxDirected', - 'checkBoxHasReverseCost', 'checkBoxHeapPaths' ] @@ -61,6 +48,7 @@ def prepare(self, canvasItemList): def getQuery(self, args): ''' returns the sql query in required signature format of pgr_bdDijkstra ''' + args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) if (self.version < 2.1): return """ SELECT @@ -74,8 +62,8 @@ def getQuery(self, args): %(cost)s::float8 AS cost %(reverse_cost)s FROM %(edge_table)s - WHERE %(edge_table)s.%(geometry)s && %(BBOX)s', - %(source_id)s, %(target_id)s, %(paths)s, %(has_reverse_cost)s)""" % args + %(where_clause)s', + %(source_id)s, %(target_id)s, %(paths)s, %(has_reverse_cost)s)""" % args else: return """ SELECT seq, @@ -92,7 +80,7 @@ def getQuery(self, args): %(cost)s AS cost %(reverse_cost)s FROM %(edge_table)s - WHERE %(edge_table)s.%(geometry)s && %(BBOX)s', + %(where_clause)s', %(source_id)s, %(target_id)s, %(paths)s, %(directed)s, %(heap_paths)s)""" % args @@ -106,7 +94,7 @@ def getExportMergeQuery(self, args): args['result_query'] = self.getQuery(args) args['with_geom_query'] = """ - SELECT + SELECT seq, _route, CASE WHEN result._node = %(edge_table)s.%(source)s @@ -114,7 +102,7 @@ def getExportMergeQuery(self, args): ELSE ST_Reverse(%(edge_table)s.%(geometry)s) END AS path_geom FROM %(edge_table)s JOIN result - ON %(edge_table)s.%(id)s = result._edge + ON %(edge_table)s.%(id)s = result._edge """ % args args['one_geom_query'] = """ @@ -149,7 +137,7 @@ def getExportMergeQuery(self, args): args['result_query'] = self.getQuery(args) args['with_geom_query'] = """ - SELECT + SELECT seq, result.path_name, CASE WHEN result._node = %(edge_table)s.%(source)s @@ -157,7 +145,7 @@ def getExportMergeQuery(self, args): ELSE ST_Reverse(%(edge_table)s.%(geometry)s) END AS path_geom FROM %(edge_table)s JOIN result - ON %(edge_table)s.%(id)s = result._edge + ON %(edge_table)s.%(id)s = result._edge """ % args args['one_geom_query'] = """ @@ -183,7 +171,7 @@ def getExportMergeQuery(self, args): aggregates AS ( %(aggregates_query)s ) SELECT row_number() over() as seq, _path_id, path_name, _nodes, _edges, agg_cost, - path_geom FROM aggregates JOIN one_geom + path_geom FROM aggregates JOIN one_geom USING (path_name) ORDER BY _path_id """ % args From 9065f3bbd960cac1831c1acbf6e32e8b75d04b08 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 1 Oct 2018 03:03:27 +0200 Subject: [PATCH 28/32] fixed alphashape functionality --- functions/alphashape.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/functions/alphashape.py b/functions/alphashape.py index 88290af..ccc1db1 100755 --- a/functions/alphashape.py +++ b/functions/alphashape.py @@ -1,19 +1,19 @@ from __future__ import absolute_import from builtins import str -from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsPoint, QgsMessageLog, QgsProject +from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsPoint, Qgis, QgsProject from qgis.gui import QgsRubberBand import psycopg2 from pgRoutingLayer import pgRoutingLayer_utils as Utils from .FunctionBase import FunctionBase class Function(FunctionBase): - + @classmethod def getName(self): ''' returns Function name. ''' return 'alphashape' - + @classmethod def getControlNames(self, version): ''' returns control names for this function. ''' @@ -29,7 +29,7 @@ def getControlNames(self, version): 'labelAlpha', 'lineEditAlpha', 'checkBoxDirected', 'checkBoxHasReverseCost' ] - + @classmethod def canExportMerged(self): return False @@ -64,7 +64,7 @@ def getQuery(self, args): ) SELECT * FROM node$$::text) """ % args - + # V21.+ has pgr_drivingDistance with big int # and pgr_alphaShape has an alpha value args['alpha'] = ', ' + str(args['alpha']) @@ -90,7 +90,7 @@ def getQuery(self, args): ) SELECT * FROM node$$::text%(alpha)s) """ % args - + def getExportQuery(self, args): @@ -148,24 +148,24 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): resultAreaRubberBand = canvasItemList['area'] trans = None - + canvasCrs = Utils.getDestinationCrs(mapCanvas) layerCrs = QgsCoordinateReferenceSystem() Utils.createFromSrid(layerCrs, args['srid']) trans = QgsCoordinateTransform(layerCrs, canvasCrs,QgsProject.instance()) - + # return columns are 'x', 'y' for row in rows: x = row[0] y = row[1] if args['version'] > 2.0 and ((x is None) or (y is None)): - Utils.logMessage(u'Alpha shape result geometry is MultiPolygon or has holes.\nPlease click [Export] button to see complete result.', level=QgsMessageLog.WARNING) + Utils.logMessage(u'Alpha shape result geometry is MultiPolygon or has holes.\nPlease click [Export] button to see complete result.', level=Qgis.Warning) return pt = QgsPoint(x, y) if trans: pt = trans.transform(pt.x(),pt.y()) - + resultAreaRubberBand.addPoint(pt) - + def __init__(self, ui): FunctionBase.__init__(self, ui) From 90ff8b930d2cfd22c2974d353250495b94680cf1 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 1 Oct 2018 03:06:07 +0200 Subject: [PATCH 29/32] turn off withpoints functions --- pgRoutingLayer.py | 84 +++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index b3b0648..dfa3057 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -42,22 +42,21 @@ class PgRoutingLayer(object): SUPPORTED_FUNCTIONS = [ - 'tsp_euclid', - 'with_Points', - 'with_PointsCost', 'dijkstra', - 'trsp_vertex', 'astar', - 'drivingDistance', - 'alphashape', - 'tsp_euclid', 'bdDijkstra', 'bdAstar', 'dijkstraCost', - 'trsp_edge', 'ksp', + 'trsp_vertex', + 'trsp_edge', 'trsp_via_vertices', - 'trsp_via_edges' + 'trsp_via_edges', + 'drivingDistance', + 'alphashape', + 'tsp_euclid', + # 'with_Points', + # 'with_PointsCost' ] TOGGLE_CONTROL_NAMES = [ @@ -82,14 +81,16 @@ class PgRoutingLayer(object): 'labelTargetPos', 'lineEditTargetPos', 'labelDistance', 'lineEditDistance', 'labelAlpha', 'lineEditAlpha', - 'labelPaths', 'lineEditPaths','label_pointsTable','lineEditPointsTable', + 'labelPaths', 'lineEditPaths', 'checkBoxDirected', 'checkBoxHasReverseCost', 'checkBoxHeapPaths', - 'checkBoxUseBBOX', 'checkBoxDetails', + 'checkBoxUseBBOX', 'labelTurnRestrictSql', 'plainTextEditTurnRestrictSql', - 'labelPid', 'lineEditPid', 'labelEdge_id', 'lineEditEdge_id', - 'labelFraction', 'lineEditFraction', 'labelSide', 'lineEditSide','labelDrivingSide','checkBoxLeft','checkBoxRight' + # 'checkBoxDetails', + # 'label_pointsTable','lineEditPointsTable', + # 'labelPid', 'lineEditPid', 'labelEdge_id', 'lineEditEdge_id', + # 'labelFraction', 'lineEditFraction', 'labelSide', 'lineEditSide','labelDrivingSide','checkBoxLeft','checkBoxRight' ] FIND_RADIUS = 10 FRACTION_DECIMAL_PLACES = 2 @@ -272,7 +273,7 @@ def reloadConnections(self): def updateConnectionEnabled(self): - ''' Updates the database connection name and function ''' + ''' Updates the database connection name and function ''' dbname = str(self.dock.comboConnections.currentText()) if dbname =='': return @@ -934,21 +935,21 @@ def getArguments(self, controls): if 'lineEditTarget' in controls: args['target'] = self.dock.lineEditTarget.text() - - if 'lineEditPointsTable' in controls: - args['points_table'] = self.dock.lineEditPointsTable.text() - - if 'lineEditPid' in controls: - args['pid'] = self.dock.lineEditPid.text() - if 'lineEditEdge_id' in controls: - args['edge_id'] = self.dock.lineEditEdge_id.text() - - if 'lineEditFraction' in controls: - args['fraction'] = self.dock.lineEditFraction.text() - - if 'lineEditSide' in controls: - args['side'] = self.dock.lineEditSide.text() + # if 'lineEditPointsTable' in controls: + # args['points_table'] = self.dock.lineEditPointsTable.text() + # + # if 'lineEditPid' in controls: + # args['pid'] = self.dock.lineEditPid.text() + # + # if 'lineEditEdge_id' in controls: + # args['edge_id'] = self.dock.lineEditEdge_id.text() + # + # if 'lineEditFraction' in controls: + # args['fraction'] = self.dock.lineEditFraction.text() + # + # if 'lineEditSide' in controls: + # args['side'] = self.dock.lineEditSide.text() if 'lineEditCost' in controls: @@ -1011,11 +1012,8 @@ def getArguments(self, controls): if 'checkBoxDirected' in controls: args['directed'] = str(self.dock.checkBoxDirected.isChecked()).lower() - if 'checkBoxDetails' in controls: - args['details'] = str(self.dock.checkBoxDirected.isChecked()).lower() - - - + # if 'checkBoxDetails' in controls: + # args['details'] = str(self.dock.checkBoxDirected.isChecked()).lower() if 'checkBoxHeapPaths' in controls: args['heap_paths'] = str(self.dock.checkBoxHeapPaths.isChecked()).lower() @@ -1024,14 +1022,14 @@ def getArguments(self, controls): args['use_bbox'] = str(self.dock.checkBoxUseBBOX.isChecked()).lower() else: args['use_bbox'] = 'false' - - if 'labelDrivingSide' in controls: - args['driving_side'] = str('b') - if (self.dock.checkBoxLeft.isChecked() == True and self.dock.checkBoxRight.isChecked() == False): - args['driving_side'] = str('l') - elif (self.dock.checkBoxLeft.isChecked() == False and self.dock.checkBoxRight.isChecked() == True): - args['driving_side'] = str('r') - + + # if 'labelDrivingSide' in controls: + # args['driving_side'] = str('b') + # if (self.dock.checkBoxLeft.isChecked() == True and self.dock.checkBoxRight.isChecked() == False): + # args['driving_side'] = str('l') + # elif (self.dock.checkBoxLeft.isChecked() == False and self.dock.checkBoxRight.isChecked() == True): + # args['driving_side'] = str('r') + if 'checkBoxHasReverseCost' in controls: args['has_reverse_cost'] = str(self.dock.checkBoxHasReverseCost.isChecked()).lower() if args['has_reverse_cost'] == 'false': @@ -1273,7 +1271,7 @@ def loadSettings(self): self.dock.comboBoxFunction.setCurrentIndex(idx) self.dock.lineEditTable.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/edge_table', 'roads')) - self.dock.lineEditPointsTable.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/pointsOfInterest', 'pointsOfInterest')) + # self.dock.lineEditPointsTable.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/pointsOfInterest', 'pointsOfInterest')) self.dock.lineEditGeometry.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/geometry', 'the_geom')) self.dock.lineEditId.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/id', 'id')) self.dock.lineEditSource.setText(Utils.getStringValue(settings, '/pgRoutingLayer/sql/source', 'source')) @@ -1313,7 +1311,7 @@ def saveSettings(self): settings.setValue('/pgRoutingLayer/Function', self.dock.comboBoxFunction.currentText()) settings.setValue('/pgRoutingLayer/sql/edge_table', self.dock.lineEditTable.text()) - settings.setValue('/pgRoutingLayer/sql/pointsOfInterest', self.dock.lineEditPointsTable.text()) + # settings.setValue('/pgRoutingLayer/sql/pointsOfInterest', self.dock.lineEditPointsTable.text()) settings.setValue('/pgRoutingLayer/sql/geometry', self.dock.lineEditGeometry.text()) settings.setValue('/pgRoutingLayer/sql/id', self.dock.lineEditId.text()) From 73b191c7a7ce0360b44a32accca118ca3761c9d3 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 1 Oct 2018 03:06:46 +0200 Subject: [PATCH 30/32] not sort functions in combobox --- pgRoutingLayer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index dfa3057..63581fa 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -299,7 +299,7 @@ def loadFunctionsForVersion(self): self.dock.comboBoxFunction.clear() #for funcname, function in self.functions.items(): - for funcname in sorted(self.functions): + for funcname in self.functions: function = self.functions[funcname] if (function.isSupportedVersion(self.version)): self.dock.comboBoxFunction.addItem(function.getName()) From 6804bcaa82f4c5d0f9e11c9bb93d9f02df7a6ac4 Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Mon, 1 Oct 2018 03:14:06 +0200 Subject: [PATCH 31/32] turn off DijktraCost and tsp_euclid --- pgRoutingLayer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pgRoutingLayer.py b/pgRoutingLayer.py index 63581fa..8977f60 100755 --- a/pgRoutingLayer.py +++ b/pgRoutingLayer.py @@ -46,7 +46,6 @@ class PgRoutingLayer(object): 'astar', 'bdDijkstra', 'bdAstar', - 'dijkstraCost', 'ksp', 'trsp_vertex', 'trsp_edge', @@ -54,7 +53,8 @@ class PgRoutingLayer(object): 'trsp_via_edges', 'drivingDistance', 'alphashape', - 'tsp_euclid', + # 'dijkstraCost', + # 'tsp_euclid', # 'with_Points', # 'with_PointsCost' ] From 17b02fc2b4414336950e1d0d55188e6ff9cebd2a Mon Sep 17 00:00:00 2001 From: cayetanobv Date: Wed, 3 Oct 2018 00:52:42 +0200 Subject: [PATCH 32/32] fixing bugs in TRSP functions --- functions/trsp_edge.py | 38 +++++++++++++++++----------------- functions/trsp_via_edges.py | 38 +++++++++++++++------------------- functions/trsp_via_vertices.py | 16 +++++++------- 3 files changed, 44 insertions(+), 48 deletions(-) diff --git a/functions/trsp_edge.py b/functions/trsp_edge.py index 8d26b06..1adc589 100755 --- a/functions/trsp_edge.py +++ b/functions/trsp_edge.py @@ -10,12 +10,12 @@ from .FunctionBase import FunctionBase class Function(FunctionBase): - + @classmethod def getName(self): ''' returns Function name. ''' return 'trsp(edge)' - + @classmethod def getControlNames(self, version): ''' returns control names. ''' @@ -24,17 +24,17 @@ def getControlNames(self, version): 'labelSourcePos', 'lineEditSourcePos', 'labelTargetId', 'lineEditTargetId', 'buttonSelectTargetId', 'labelTargetPos', 'lineEditTargetPos', - 'labelTurnRestrictSql', 'plainTextEditTurnRestrictSql' ] + 'labelTurnRestrictSql', 'plainTextEditTurnRestrictSql' ] + - @classmethod def isEdgeBase(self): return True - + def prepare(self, canvasItemList): resultPathRubberBand = canvasItemList['path'] resultPathRubberBand.reset(Utils.getRubberBandType(False)) - + def getQuery(self, args): ''' returns the sql query in required signature format of pgr_trsp_edge ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) @@ -46,7 +46,7 @@ def getQuery(self, args): FROM %(edge_table)s %(where_clause)s', %(source_id)s, %(source_pos)s, %(target_id)s, %(target_pos)s, %(directed)s, %(has_reverse_cost)s, %(turn_restrict_sql)s)""" % args - + def getExportQuery(self, args): args['result_query'] = 'result AS (' + self.getQuery(args) + ')' @@ -54,14 +54,14 @@ def getExportQuery(self, args): args['with_geom'] = """ with_geom AS ( SELECT - lead(_node) over(), result.*, %(edge_table)s.* + lead(_node) over(), result.*, %(edge_table)s.* FROM %(edge_table)s JOIN result - ON edge_table.id = result._edge ORDER BY result.seq)""" % args + ON %(edge_table)s.%(id)s = result._edge ORDER BY result.seq)""" % args args['first_row_split'] = self.getRowSplit(args, 'first') args['last_row_split'] = self.getRowSplit(args, 'last') - args['intermediate_rows'] = """ intermediate_rows AS (SELECT + args['intermediate_rows'] = """ intermediate_rows AS (SELECT CASE WHEN result._node = %(edge_table)s.%(source)s THEN %(edge_table)s.%(geometry)s @@ -92,14 +92,14 @@ def getExportMergeQuery(self, args): args['with_geom'] = """ with_geom AS ( SELECT - lead(_node) over(), result.*, %(edge_table)s.* + lead(_node) over(), result.*, %(edge_table)s.* FROM %(edge_table)s JOIN result - ON edge_table.id = result._edge ORDER BY result.seq)""" % args + ON %(edge_table)s.%(id)s = result._edge ORDER BY result.seq)""" % args args['first_row_split'] = self.getRowSplit(args, 'first') args['last_row_split'] = self.getRowSplit(args, 'last') - args['intermediate_rows'] = """ intermediate_rows AS (SELECT + args['intermediate_rows'] = """ intermediate_rows AS (SELECT CASE WHEN result._node = %(edge_table)s.%(source)s THEN %(edge_table)s.%(geometry)s @@ -141,7 +141,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): args['result_node_id'] = row[1] args['result_edge_id'] = row[2] args['result_cost'] = row[3] - + if i == 0 and args['result_node_id'] == -1: args['result_next_node_id'] = rows[i + 1][1] query2 = """ @@ -170,13 +170,13 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): SELECT ST_AsText(%(transform_s)sST_Reverse(%(geometry)s)%(transform_e)s) FROM %(edge_table)s WHERE %(target)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d; """ % args - + ##Utils.logMessage(query2) cur2.execute(query2) row2 = cur2.fetchone() ##Utils.logMessage(str(row2[0])) assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args - + geom = QgsGeometry().fromWkt(str(row2[0])) if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): @@ -185,9 +185,9 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): resultPathRubberBand.addPoint(pt) - + i = i + 1 - + def getRowSplit(self, args, which): # PRIVATE method # upper case for localy defined string values @@ -214,7 +214,7 @@ def getRowSplit(self, args, which): ST_LineInterpolatePoint(%(geometry)s, %(POSITION)s)) ELSE ST_reverse( ST_split( ST_Snap( %(geometry)s, ST_LineInterpolatePoint(%(geometry)s, %(POSITION)s), 0.00001), - ST_LineInterpolatePoint(%(geometry)s, %(POSITION)s))) + ST_LineInterpolatePoint(%(geometry)s, %(POSITION)s))) END AS line_geom, st_length(%(geometry)s) AS length, _cost diff --git a/functions/trsp_via_edges.py b/functions/trsp_via_edges.py index 7eabaa2..f009d12 100755 --- a/functions/trsp_via_edges.py +++ b/functions/trsp_via_edges.py @@ -9,27 +9,22 @@ from .FunctionBase import FunctionBase class Function(FunctionBase): - + @classmethod def getName(self): ''' returns Function name. ''' return 'trsp(via edges)' - + @classmethod def getControlNames(self, version): ''' returns control names. ''' - return [ - 'labelId', 'lineEditId', - 'labelSource', 'lineEditSource', - 'labelTarget', 'lineEditTarget', - 'labelCost', 'lineEditCost', - 'labelReverseCost', 'lineEditReverseCost', + return self.commonControls + self.commonBoxes + [ 'labelIds', 'lineEditIds', 'buttonSelectIds', 'labelPcts', 'lineEditPcts', 'checkBoxDirected', 'checkBoxHasReverseCost', 'labelTurnRestrictSql', 'plainTextEditTurnRestrictSql' ] - + @classmethod def isEdgeBase(self): return True @@ -41,9 +36,9 @@ def canExport(self): @classmethod def canExportMerged(self): return True - + def isSupportedVersion(self, version): - return version >= 2.1 + return version >= 2.1 def prepare(self, canvasItemList): resultPathsRubberBands = canvasItemList['paths'] @@ -53,16 +48,17 @@ def prepare(self, canvasItemList): def getQuery(self, args): ''' returns the sql query in required signature format of trsp_via_edges ''' + args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) return """ SELECT seq, id1 AS _path, id2 AS _node, id3 AS _edge, cost as _cost FROM pgr_trspViaEdges(' SELECT %(id)s::int4 AS id, %(source)s::int4 AS source, %(target)s::int4 AS target, %(cost)s::float8 AS cost%(reverse_cost)s FROM %(edge_table)s - WHERE %(edge_table)s.%(geometry)s && %(BBOX)s', + %(where_clause)s', ARRAY[%(ids)s]::integer[], ARRAY[%(pcts)s]::float8[], %(directed)s, %(has_reverse_cost)s, %(turn_restrict_sql)s)""" % args - + def getQueries(self, args): args['edge_data_q'] = """ edge_data AS ( @@ -77,7 +73,7 @@ def getQueries(self, args): ST_Reverse(ST_LineSubstring(%(geometry)s, 0, fraction)) as tosource FROM %(edge_table)s JOIN edge_data ON (%(edge_table)s.%(id)s = edge_data.eid) ) - """ % args + """ % args args['result_q'] = """ result AS ( @@ -140,13 +136,13 @@ def getQueries(self, args): def getExportQuery(self, args): self.getQueries(args) - + query = """ WITH %(edge_data_q)s, %(result_q)s, %(the_rest_q)s - SELECT + SELECT all_edges.*, %(edge_table)s.* FROM %(edge_table)s JOIN all_edges ON %(edge_table)s.%(id)s = all_edges._edge ORDER BY all_edges.seq @@ -155,7 +151,7 @@ def getExportQuery(self, args): def getExportMergeQuery(self, args): self.getQueries(args) - + query = """ WITH %(edge_data_q)s, @@ -167,7 +163,7 @@ def getExportMergeQuery(self, args): ST_makeLine(path_geom) AS path_geom from all_edges """ % args return query - + def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): ''' draw the result ''' resultPathsRubberBands = canvasItemList['paths'] @@ -253,13 +249,13 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): SELECT ST_AsText(%(transform_s)sST_Reverse(%(geometry)s)%(transform_e)s) FROM %(edge_table)s WHERE %(target)s = %(result_node_id)d AND %(id)s = %(result_edge_id)d; """ % args - + ##Utils.logMessage(query2) cur2.execute(query2) row2 = cur2.fetchone() ##Utils.logMessage(str(row2[0])) assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args - + geom = QgsGeometry().fromWkt(str(row2[0])) if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline(): @@ -268,7 +264,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): elif geom.wkbType() == QgsWkbTypes.LineString: for pt in geom.asPolyline(): rubberBand.addPoint(pt) - + i = i + 1 if rubberBand: diff --git a/functions/trsp_via_vertices.py b/functions/trsp_via_vertices.py index 8523b04..e7778c2 100755 --- a/functions/trsp_via_vertices.py +++ b/functions/trsp_via_vertices.py @@ -9,12 +9,12 @@ from .FunctionBase import FunctionBase class Function(FunctionBase): - + @classmethod def getName(self): ''' returns Function name. ''' return 'trsp(via vertices)' - + @classmethod def getControlNames(self, version): ''' returns control names. ''' @@ -22,18 +22,18 @@ def getControlNames(self, version): 'labelIds', 'lineEditIds', 'buttonSelectIds', 'labelTurnRestrictSql', 'plainTextEditTurnRestrictSql' ] - + def isSupportedVersion(self, version): ''' checks the supported version ''' - return version >= 2.1 + return version >= 2.1 def prepare(self, canvasItemList): resultPathsRubberBands = canvasItemList['paths'] for path in resultPathsRubberBands: path.reset(Utils.getRubberBandType(False)) canvasItemList['paths'] = [] - + def getQuery(self, args): ''' returns the sql query in required signature format of trsp_via_vertices ''' args['where_clause'] = self.whereClause(args['edge_table'], args['geometry'], args['BBOX']) @@ -48,14 +48,14 @@ def getQuery(self, args): %(directed)s, %(has_reverse_cost)s, %(turn_restrict_sql)s) """ % args - + def getExportQuery(self, args): args['result_query'] = self.getQuery(args) query = """ WITH result AS ( %(result_query)s ) - SELECT + SELECT CASE WHEN result._node = %(edge_table)s.%(source)s THEN %(edge_table)s.%(geometry)s @@ -107,7 +107,7 @@ def draw(self, rows, con, args, geomType, canvasItemList, mapCanvas): row2 = cur2.fetchone() ##Utils.logMessage(str(row2[0])) assert row2, "Invalid result geometry. (node_id:%(result_node_id)d, edge_id:%(result_edge_id)d)" % args - + geom = QgsGeometry().fromWkt(str(row2[0])) if geom.wkbType() == QgsWkbTypes.MultiLineString: for line in geom.asMultiPolyline():