def __init__(self, parent): QtGui.QDockWidget.__init__(self, parent.iface.mainWindow()) self.iface = qgis.utils.iface # Set up the user interface from Designer. self.ui = Ui_NetworkXPath() self.ui.setupUi(self) self.loadmenus() # Counters for selected nodes on canvas which need to persist in the #class. self.selected_nodes = {'source': None, 'target': None} QtCore.QObject.connect(self.ui.btnCancel, QtCore.SIGNAL("clicked()"), self.exit) QtCore.QObject.connect(self.ui.comboBoxInputEdges, QtCore.SIGNAL("activated(const QString&)"), self.attribute_weights) QtCore.QObject.connect(self.ui.btnSourceNode, QtCore.SIGNAL("clicked()"), self.source_point) QtCore.QObject.connect(self.ui.btnTargetNode, QtCore.SIGNAL("clicked()"), self.target_point) QtCore.QObject.connect(self.ui.btnOK, QtCore.SIGNAL("clicked()"), self.shortest_path) QtCore.QObject.connect(self.ui.btnSave, QtCore.SIGNAL("clicked()"), self.output_file) QtCore.QObject.connect(self.ui.btnReload, QtCore.SIGNAL("clicked()"), self.clear_reload)
def __init__(self, parent): QtGui.QDockWidget.__init__(self, parent.iface.mainWindow()) self.iface = qgis.utils.iface # Set up the user interface from Designer. self.ui = Ui_NetworkXPath() self.ui.setupUi(self) self.loadmenus() # Counters for selected nodes on canvas which need to persist in the #class. self.selected_nodes = {'source':None, 'target':None} QtCore.QObject.connect(self.ui.btnCancel, QtCore.SIGNAL("clicked()"), self.exit) QtCore.QObject.connect(self.ui.comboBoxInputEdges, QtCore.SIGNAL("activated(const QString&)"), self.attribute_weights) QtCore.QObject.connect(self.ui.btnSourceNode, QtCore.SIGNAL("clicked()"), self.source_point) QtCore.QObject.connect(self.ui.btnTargetNode, QtCore.SIGNAL("clicked()"), self.target_point) QtCore.QObject.connect(self.ui.btnOK, QtCore.SIGNAL("clicked()"), self.shortest_path) QtCore.QObject.connect(self.ui.btnSave, QtCore.SIGNAL("clicked()"), self.output_file) QtCore.QObject.connect(self.ui.btnReload, QtCore.SIGNAL("clicked()"), self.clear_reload)
class NetworkXDialogPath(QtGui.QDockWidget, Ui_NetworkXPath, ShapeLayersToCombo): '''Dialog for running path analysis over network as QGIS PyQt plugin.''' def __init__(self, parent): QtGui.QDockWidget.__init__(self, parent.iface.mainWindow()) self.iface = qgis.utils.iface # Set up the user interface from Designer. self.ui = Ui_NetworkXPath() self.ui.setupUi(self) self.loadmenus() # Counters for selected nodes on canvas which need to persist in the #class. self.selected_nodes = {'source': None, 'target': None} QtCore.QObject.connect(self.ui.btnCancel, QtCore.SIGNAL("clicked()"), self.exit) QtCore.QObject.connect(self.ui.comboBoxInputEdges, QtCore.SIGNAL("activated(const QString&)"), self.attribute_weights) QtCore.QObject.connect(self.ui.btnSourceNode, QtCore.SIGNAL("clicked()"), self.source_point) QtCore.QObject.connect(self.ui.btnTargetNode, QtCore.SIGNAL("clicked()"), self.target_point) QtCore.QObject.connect(self.ui.btnOK, QtCore.SIGNAL("clicked()"), self.shortest_path) QtCore.QObject.connect(self.ui.btnSave, QtCore.SIGNAL("clicked()"), self.output_file) QtCore.QObject.connect(self.ui.btnReload, QtCore.SIGNAL("clicked()"), self.clear_reload) def loadmenus(self): '''Method to load required menus for the shortest path gui widget.''' # List available algorithms self.algorithms = { 'Shortest Path': 'shortest_path', 'Dijkstra': 'dijkstra_path', 'A*': 'astar_path' } for key in self.algorithms: self.ui.comboBoxAlgorithm.addItem(key) self.ui.comboBoxAlgorithm.setCurrentIndex(0) # Add available layers to the input combo box. self.pointfilelist = ["Shapefile point layers:"] self.linefilelist = ["Shapefile line layers:"] self.ui.comboBoxInputNodes.addItem(self.pointfilelist[0]) self.ui.comboBoxInputEdges.addItem(self.linefilelist[0]) self.layermap = QgsMapLayerRegistry.instance().mapLayers() ShapeLayersToCombo(self.ui.comboBoxInputNodes, self.pointfilelist, 0) ShapeLayersToCombo(self.ui.comboBoxInputEdges, self.linefilelist, 1) # Updated comboBoxEdges internally so updated weights. self.attribute_weights() def clearinputs(self): '''Clear form of existing user data.''' self.ui.checkBoxUndirected.setChecked(False) self.ui.checkBoxOverwrite.setChecked(False) self.ui.comboBoxAlgorithm.clear() self.ui.comboBoxInputEdges.clear() self.ui.comboBoxInputNodes.clear() self.ui.comboBoxInputWeight.clear() self.ui.lineEditSourceNode.clear() self.ui.lineEditTargetNode.clear() self.ui.lineEditSave.clear() def clear_reload(self): '''Clear and then reload GUI with default options.''' # First clear any existing node selection on canvas layers = qgis.utils.iface.mapCanvas().layers() nodes = str(self.ui.comboBoxInputNodes.currentText()) for layer in layers: if layer.name() == nodes: layer.removeSelection() self.selected_nodes = {'source': None, 'target': None} # Clear menus and inputs self.clearinputs() self.loadmenus() def attribute_weights(self): '''Method provides list of weights based on edge feature attributes.''' # Clear the attributeComboBoxList self.ui.comboBoxInputWeight.clear() self.ui.comboBoxInputWeight.addItem('None') # Add new attributes of layer to weights. for layer in self.layermap.itervalues(): edges = str(self.ui.comboBoxInputEdges.currentText()) if layer.name() == edges: provider = layer.dataProvider() try: fields = provider.fields() for name in fields: if (fields[name].typeName() == 'Integer' or fields[name].typeName() == 'Real'): self.ui.comboBoxInputWeight.addItem( fields[name].name()) self.ui.comboBoxInputWeight.setCurrentIndex(0) except TypeError: self.ui.comboBoxInputWeight.setCurrentIndex(0) QtGui.QMessageBox.warning( self.iface.mainWindow(), "NetworkX Plugin Error", "Error reading %s attribute table." % edges) else: self.ui.comboBoxInputWeight.setCurrentIndex(0) def source_point(self): '''Method to setup collection of source node from map canvas.''' self.output = self.ui.lineEditSourceNode self.collect_point() self.nodetype = 'source' def target_point(self): '''Method to setup collection of target node from map canvas.''' self.output = self.ui.lineEditTargetNode self.nodetype = 'target' self.collect_point() def collect_point(self): '''Method to collect point location from the QGIS map canvas.''' self.canvas = qgis.utils.iface.mapCanvas() self.point = QgsMapToolEmitPoint(qgis.utils.iface.mapCanvas()) mapCanvas = qgis.utils.iface.mapCanvas() # Create the appropriate map tool and connect the gotPoint() signal. mapCanvas.setMapTool(self.point) QtCore.QObject.connect( self.point, QtCore.SIGNAL("canvasClicked(const QgsPoint &, Qt::MouseButton)"), self.select_feature) def select_feature(self, point): '''Method to select feature from map canvas based on point location.''' # Select Features function from # http://www.qgisworkshop.org/html/workshop/plugins_tutorial.html # setup the provider select to filter results based on a rectangle pntGeom = QgsGeometry.fromPoint(point) # scale-dependent buffer of 3 pixels-worth of map units pntBuff = pntGeom.buffer((self.canvas.mapUnitsPerPixel() * 3), 0) rect = pntBuff.boundingBox() layers = qgis.utils.iface.mapCanvas().layers() nodes = str(self.ui.comboBoxInputNodes.currentText()) for layer in layers: if layer.name() == nodes: provider = layer.dataProvider() if layer.geometryType() == QGis.Point: feat = QgsFeature() # create the select statement provider.select([], rect) # the arguments mean no attributes returned and do a bbox # filter with our buffered rectangle to limit the amount #of features. while provider.nextFeature(feat): # if the feat geom returned from the selection #intersects our point then put it in selection list. if feat.geometry().intersects(rect): self.selected_nodes[self.nodetype] = feat.id() self.output.clear() self.output.insert( str(feat.geometry().asPoint().x()) + ',' + str(feat.geometry().asPoint().y())) layer.removeSelection() for featid in self.selected_nodes.itervalues(): if featid is not None: layer.select(featid) break # stop here to select one point only. def output_file(self): '''Get output directory from Qt file dialog.''' try: self.fd = str( QtGui.QFileDialog.getExistingDirectory( self, "Select Output Directory")) self.ui.lineEditSave.insert(self.fd) except IOError as e: self.ui.lineEditSave.clear() QtGui.QMessageBox.warning(self.iface.mainWindow(), "NetworkX Plugin Error", "%s" % str(e)) def shortest_path(self): ''' Method to compute shortest path over a graph using NetworkX.''' try: source = str(self.ui.lineEditSourceNode.text()) target = str(self.ui.lineEditTargetNode.text()) if source == '': raise IOError, "Please specify source node." source = source.split(',') if target == '': raise IOError, "Please specify target node." target = target.split(',') edges = str( self.linefilelist[self.ui.comboBoxInputEdges.currentIndex()]) if edges == "Shapefile line layers:": raise IOError, "Please specify input edge layer." DG1 = nx.read_shp( str(self.linefilelist[ self.ui.comboBoxInputEdges.currentIndex()])) if self.ui.checkBoxUndirected.isChecked() == True: DG1 = DG1.to_undirected() for node in DG1.nodes(): if str(node[0]) == source[0] and str(node[1] == source[1]): sourceNode = node elif str(node[0]) == target[0] and str(node[1] == target[1]): targetNode = node try: targetNode except NameError: raise IOError, "Specified target node not found in edge layer." try: sourceNode except NameError: raise IOError, "Specified source node not found in edge layer." key = str(self.ui.comboBoxAlgorithm.currentText()) algorithm = self.algorithms[key] method = getattr(nx, algorithm) weight = str(self.ui.comboBoxInputWeight.currentText()) if weight == 'None': p = method(DG1, sourceNode, targetNode) else: p = method(DG1, sourceNode, targetNode, weight) DG2 = nx.DiGraph() for i in range(0, len(p) - 1): DG2.add_edge(p[i], p[i + 1]) DG2.edge[p[i]][p[i + 1]]['Wkt'] = DG1.edge[p[i]][p[i + 1]]['Wkt'] if self.ui.lineEditSave == '': raise IOError, "No output directory specified." if self.ui.checkBoxOverwrite.isChecked(): WriteNetworkShapefiles(DG2, self.fd, overwrite=True) else: WriteNetworkShapefiles(DG2, self.fd) nodes = self.fd + '/nodes.shp' edges = self.fd + '/edges.shp' qgis.utils.iface.addVectorLayer(edges, "Shortest Route Network Edges", "ogr") qgis.utils.iface.addVectorLayer(nodes, "Shortest Route Network Nodes", "ogr") except (IOError, AttributeError, NameError, nx.NetworkXNoPath) as e: QtGui.QMessageBox.warning(self.iface.mainWindow(), "NetworkX Plugin Error", "%s" % str(e)) def exit(self): '''Method to close plugin window.''' self.close()
class NetworkXDialogPath(QtGui.QDockWidget, Ui_NetworkXPath, ShapeLayersToCombo): '''Dialog for running path analysis over network as QGIS PyQt plugin.''' def __init__(self, parent): QtGui.QDockWidget.__init__(self, parent.iface.mainWindow()) self.iface = qgis.utils.iface # Set up the user interface from Designer. self.ui = Ui_NetworkXPath() self.ui.setupUi(self) self.loadmenus() # Counters for selected nodes on canvas which need to persist in the #class. self.selected_nodes = {'source':None, 'target':None} QtCore.QObject.connect(self.ui.btnCancel, QtCore.SIGNAL("clicked()"), self.exit) QtCore.QObject.connect(self.ui.comboBoxInputEdges, QtCore.SIGNAL("activated(const QString&)"), self.attribute_weights) QtCore.QObject.connect(self.ui.btnSourceNode, QtCore.SIGNAL("clicked()"), self.source_point) QtCore.QObject.connect(self.ui.btnTargetNode, QtCore.SIGNAL("clicked()"), self.target_point) QtCore.QObject.connect(self.ui.btnOK, QtCore.SIGNAL("clicked()"), self.shortest_path) QtCore.QObject.connect(self.ui.btnSave, QtCore.SIGNAL("clicked()"), self.output_file) QtCore.QObject.connect(self.ui.btnReload, QtCore.SIGNAL("clicked()"), self.clear_reload) def loadmenus(self): '''Method to load required menus for the shortest path gui widget.''' # List available algorithms self.algorithms = {'Shortest Path':'shortest_path', 'Dijkstra':'dijkstra_path','A*':'astar_path'} for key in self.algorithms: self.ui.comboBoxAlgorithm.addItem(key) self.ui.comboBoxAlgorithm.setCurrentIndex(0) # Add available layers to the input combo box. self.pointfilelist = ["Shapefile point layers:"] self.linefilelist = ["Shapefile line layers:"] self.ui.comboBoxInputNodes.addItem(self.pointfilelist[0]) self.ui.comboBoxInputEdges.addItem(self.linefilelist[0]) self.layermap = QgsMapLayerRegistry.instance().mapLayers() ShapeLayersToCombo(self.ui.comboBoxInputNodes, self.pointfilelist, 0) ShapeLayersToCombo(self.ui.comboBoxInputEdges, self.linefilelist, 1) # Updated comboBoxEdges internally so updated weights. self.attribute_weights() def clearinputs(self): '''Clear form of existing user data.''' self.ui.checkBoxUndirected.setChecked(False) self.ui.checkBoxOverwrite.setChecked(False) self.ui.comboBoxAlgorithm.clear() self.ui.comboBoxInputEdges.clear() self.ui.comboBoxInputNodes.clear() self.ui.comboBoxInputWeight.clear() self.ui.lineEditSourceNode.clear() self.ui.lineEditTargetNode.clear() self.ui.lineEditSave.clear() def clear_reload(self): '''Clear and then reload GUI with default options.''' # First clear any existing node selection on canvas layers = qgis.utils.iface.mapCanvas().layers() nodes = str(self.ui.comboBoxInputNodes.currentText()) for layer in layers: if layer.name() == nodes: layer.removeSelection() self.selected_nodes = {'source':None, 'target':None} # Clear menus and inputs self.clearinputs() self.loadmenus() def attribute_weights(self): '''Method provides list of weights based on edge feature attributes.''' # Clear the attributeComboBoxList self.ui.comboBoxInputWeight.clear() self.ui.comboBoxInputWeight.addItem('None') # Add new attributes of layer to weights. for layer in self.layermap.itervalues(): edges = str(self.ui.comboBoxInputEdges.currentText()) if layer.name() == edges: provider = layer.dataProvider() try: fields = provider.fields() for name in fields: if (fields[name].typeName() == 'Integer' or fields[name].typeName() == 'Real'): self.ui.comboBoxInputWeight.addItem( fields[name].name()) self.ui.comboBoxInputWeight.setCurrentIndex(0) except TypeError: self.ui.comboBoxInputWeight.setCurrentIndex(0) QtGui.QMessageBox.warning( self.iface.mainWindow(), "NetworkX Plugin Error", "Error reading %s attribute table." % edges) else: self.ui.comboBoxInputWeight.setCurrentIndex(0) def source_point(self): '''Method to setup collection of source node from map canvas.''' self.output = self.ui.lineEditSourceNode self.collect_point() self.nodetype = 'source' def target_point(self): '''Method to setup collection of target node from map canvas.''' self.output = self.ui.lineEditTargetNode self.nodetype = 'target' self.collect_point() def collect_point(self): '''Method to collect point location from the QGIS map canvas.''' self.canvas = qgis.utils.iface.mapCanvas() self.point = QgsMapToolEmitPoint(qgis.utils.iface.mapCanvas()) mapCanvas = qgis.utils.iface.mapCanvas() # Create the appropriate map tool and connect the gotPoint() signal. mapCanvas.setMapTool(self.point) QtCore.QObject.connect(self.point, QtCore.SIGNAL("canvasClicked(const QgsPoint &, Qt::MouseButton)"), self.select_feature) def select_feature(self, point): '''Method to select feature from map canvas based on point location.''' # Select Features function from # http://www.qgisworkshop.org/html/workshop/plugins_tutorial.html # setup the provider select to filter results based on a rectangle pntGeom = QgsGeometry.fromPoint(point) # scale-dependent buffer of 3 pixels-worth of map units pntBuff = pntGeom.buffer( (self.canvas.mapUnitsPerPixel() * 3), 0) rect = pntBuff.boundingBox() layers = qgis.utils.iface.mapCanvas().layers() nodes = str(self.ui.comboBoxInputNodes.currentText()) for layer in layers: if layer.name() == nodes: provider = layer.dataProvider() if layer.geometryType() == QGis.Point: feat = QgsFeature() # create the select statement provider.select([], rect) # the arguments mean no attributes returned and do a bbox # filter with our buffered rectangle to limit the amount #of features. while provider.nextFeature(feat): # if the feat geom returned from the selection #intersects our point then put it in selection list. if feat.geometry().intersects(rect): self.selected_nodes[self.nodetype] = feat.id() self.output.clear() self.output.insert( str(feat.geometry().asPoint().x())+',' +str(feat.geometry().asPoint().y())) layer.removeSelection() for featid in self.selected_nodes.itervalues(): if featid is not None: layer.select(featid) break # stop here to select one point only. def output_file(self): '''Get output directory from Qt file dialog.''' try: self.fd = str(QtGui.QFileDialog.getExistingDirectory(self, "Select Output Directory")) self.ui.lineEditSave.insert(self.fd) except IOError as e: self.ui.lineEditSave.clear() QtGui.QMessageBox.warning( self.iface.mainWindow(), "NetworkX Plugin Error", "%s" % str(e)) def shortest_path(self): ''' Method to compute shortest path over a graph using NetworkX.''' try: source = str(self.ui.lineEditSourceNode.text()) target = str(self.ui.lineEditTargetNode.text()) if source == '': raise IOError, "Please specify source node." source = source.split(',') if target == '': raise IOError, "Please specify target node." target = target.split(',') edges = str(self.linefilelist[ self.ui.comboBoxInputEdges.currentIndex()]) if edges == "Shapefile line layers:": raise IOError, "Please specify input edge layer." DG1 = nx.read_shp(str(self.linefilelist[ self.ui.comboBoxInputEdges.currentIndex()])) if self.ui.checkBoxUndirected.isChecked() == True: DG1 = DG1.to_undirected() for node in DG1.nodes(): if str(node[0]) == source[0] and str(node[1] == source[1]): sourceNode = node elif str(node[0]) == target[0] and str(node[1] == target[1]): targetNode = node try: targetNode except NameError: raise IOError, "Specified target node not found in edge layer." try: sourceNode except NameError: raise IOError, "Specified source node not found in edge layer." key = str(self.ui.comboBoxAlgorithm.currentText()) algorithm = self.algorithms[key] method = getattr(nx, algorithm) weight = str(self.ui.comboBoxInputWeight.currentText()) if weight == 'None': p = method(DG1, sourceNode, targetNode) else: p = method(DG1, sourceNode, targetNode, weight) DG2 = nx.DiGraph() for i in range(0, len(p)-1): DG2.add_edge(p[i], p[i+1]) DG2.edge[p[i]][p[i+1]]['Wkt'] = DG1.edge[p[i]][p[i+1]]['Wkt'] if self.ui.lineEditSave == '': raise IOError, "No output directory specified." if self.ui.checkBoxOverwrite.isChecked(): WriteNetworkShapefiles(DG2, self.fd, overwrite=True) else: WriteNetworkShapefiles(DG2, self.fd) nodes = self.fd+'/nodes.shp' edges = self.fd+'/edges.shp' qgis.utils.iface.addVectorLayer(edges, "Shortest Route Network Edges", "ogr") qgis.utils.iface.addVectorLayer(nodes, "Shortest Route Network Nodes", "ogr") except (IOError, AttributeError, NameError, nx.NetworkXNoPath) as e: QtGui.QMessageBox.warning( self.iface.mainWindow(), "NetworkX Plugin Error", "%s" % str(e)) def exit(self): '''Method to close plugin window.''' self.close()