def createProfile(self): """ The create profile transforms the SHP file into a networkX object for future processing :param event: :return: """ debugPrint("Create profile") selectedLayer = self.cmbLayer.itemData(self.cmbLayer.currentIndex()) self.theProfile = None try: if selectedLayer is not None: self.theProfile = Profile(selectedLayer, msgcallback=self.setFromToMsg) except Exception as e: if self.theProfile is not None and self.theProfile.metalogs is not None: detailstxt = "LOG:\n=====================\n {0}\n\nException:\n=====================\n{1}".format( "\n ".join(self.theProfile.metalogs), str(e)) okDlg("ERROR:", infoText=str(e), detailsTxt=detailstxt, icon=QtGui.QMessageBox.Critical) else: detailstxt = "Exception:\n=====================\n{0}".format( str(e)) okDlg("ERROR:", "A critical error occured.", detailsTxt=detailstxt, icon=QtGui.QMessageBox.Critical) return
def setUp(self): """ Runs before each test. It's easier just to load a sample file than to write a bunch of mocking """ uri = path.join(path.dirname(__file__), 'data', 'StressTest.shp') self.vlayer = QgsVectorLayer(uri, "StressTest_Layer", "ogr") self.profile = Profile(self.vlayer, None) self.testchoices = [[ self.profile.findEdgewithID(id) for id in [30, 31, 32] ], [self.profile.findEdgewithID(id) for id in [36, 38]]]
class TestWriteCSV(TestCase): def setUp(self): """Runs before each test.""" uri = path.join(path.dirname(__file__),'data','StressTest.shp') self.vlayer = QgsVectorLayer(uri, "StressTest_Layer", "ogr") self.nxProfile = Profile(self.vlayer, None) self.nxProfile.pathfinder(8, 10) # ['DateTime', 'Date', 'Time', 'RealField', 'IntField', 'StringFiel', 'PathName', 'PathName2'] def tearDown(self): """Runs after each test.""" # self.dialog = None def test_basic_write(self): # Test a single path. This is the simplest case cols = ['DateTime', 'Date', 'Time', 'RealField', 'IntField', 'StringFiel', 'PathName', 'PathName2'] self.nxProfile.generateCSV(cols) self.assertEqual(len(self.nxProfile.results), 3) # Check that our keys have the same elements icols = cols + self.nxProfile.calccols + [self.nxProfile.idField, 'Wkt'] rcols = self.nxProfile.results[0].keys() self.assertTrue(all(c in icols for c in rcols)) self.assertTrue(all(c in rcols for c in icols)) def test_colsubset(self): cols = ['PathName', 'PathName2'] self.nxProfile.generateCSV(cols) self.assertEqual(len(self.nxProfile.results), 3) # Check that our keys have the same elements icols = cols + self.nxProfile.calccols + [self.nxProfile.idField, 'Wkt'] rcols = self.nxProfile.results[0].keys() self.assertTrue(all(c in icols for c in rcols)) self.assertTrue(all(c in rcols for c in icols))
def setUp(self): """Runs before each test.""" uri = path.join(path.dirname(__file__),'data','StressTest.shp') self.vlayer = QgsVectorLayer(uri, "StressTest_Layer", "ogr") self.nxProfile = Profile(self.vlayer, None) self.nxProfile.pathfinder(8, 10)
class NetworkProfilerDialog(QtGui.QDialog, FORM_CLASS): def __init__(self, parent=None): """Constructor.""" super(NetworkProfilerDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.theProfile = None # Keep this window on top so we can keep working on the map self.window().setWindowFlags(self.window().windowFlags() | QtCore.Qt.WindowStaysOnTopHint) # We need to separate the state handling here into: Map states and App States # Map State objects: self.mapCanvas = qgis.utils.iface.mapCanvas() self.mapVectorLayers = [] self.mapSelectedObjects = [] # Map States: self.appSelectedLayer = None self.appSelectedFields = [] self.appFromID = None self.appToID = None self.appFromObj = None self.appToObj = None self.lblFrom.setText("") self.lblTo.setText("") # Set icons # Braid choices are constant self.cmbPathChoose.clear() self.cmbPathChoose.addItem(Profile.CHOICE_SHORTEST) self.cmbPathChoose.addItem(Profile.CHOICE_FIELD_NOT_EMPTY) self.cmbPathChoose.addItem(Profile.CHOICE_FIELD_VALUE) self.cmbPathChoose.addItem(Profile.CHOICE_FIELD_NOT_VALUE) self.cmbPathChoose.currentIndexChanged.connect( self.recalcBraidRuleState) # Hook an event into the selection changed event to tell us if we can grab our object or not self.mapCanvas.layersChanged.connect(self.handleMapLayerChange) self.mapCanvas.selectionChanged.connect(self.handleMapSelectionChange) # Hook in a couple of control events for good measure self.cmbLayer.currentIndexChanged.connect(self.cmbLayerChange) # Set up our button events self.btnCreateProfile.clicked.connect(self.saveProfile) self.btnGrabFrom.clicked.connect(self.grabFrom) self.btnGrabTo.clicked.connect(self.grabTo) self.btnFlipFromTo.clicked.connect(self.flipFromTo) self.btnFromToCalc.clicked.connect(self.runProfile) self.btnFindFrom.clicked.connect(self.findFrom) self.btnFindTo.clicked.connect(self.findTo) self.btnGrabFrom.setIcon(QIcon(qAppIcons.TARGET)) self.btnGrabTo.setIcon(QIcon(qAppIcons.TARGET)) self.btnFindFrom.setIcon(QIcon(qAppIcons.EYE)) self.btnFindTo.setIcon(QIcon(qAppIcons.EYE)) self.cmdButtons.button(QtGui.QDialogButtonBox.Cancel).clicked.connect( self.close) self.cmdButtons.button(QtGui.QDialogButtonBox.Help).clicked.connect( self.actionOpenHelp) self.cmdButtons.button(QtGui.QDialogButtonBox.Reset).clicked.connect( self.resetFromTo) # When to recalc app state. Choose these carefully. They run a lot. self.cmbLayer.currentIndexChanged.connect(self.stateUpdate) self.cmbPathChoose.currentIndexChanged.connect(self.stateUpdate) self.cmbPathChooseField.currentIndexChanged.connect(self.stateUpdate) self.txtPathChooseValue.textChanged.connect(self.stateUpdate) def showEvent(self, event): debugPrint("showEvent") super(NetworkProfilerDialog, self).showEvent(event) self.loadPopulate() """ Direct Actions caused by events like clicks Actions trigger a downward cascade of effects """ def loadPopulate(self, event=None): """ This is the magic function that pulls values from the map selection when the form loads :return: """ # TODO: distinguish between from and to calls and do the right thing. If neither, try to do both debugPrint("loadPopulate") self.appFromID = None self.appToID = None self.handleMapLayerChange(True) self.handleMapSelectionChange() self.setAppVectorLayerFromMap() self.recalcVectorLayerFields() self.resetAppSelectedObjects() self.recalcBraidRuleState() self.stateUpdate() def cmbLayerChange(self): if self.isVisible(): debugPrint("cmbLayerChange") self.recalcVectorLayerFields() self.getMapVectorLayerFields() self.createProfile() self.resetFromTo() self.stateUpdate() def resetFromTo(self): debugPrint("resetFromTo") self.setFromToMsg("") self.appFromID = None self.appToID = None self.appFromObj = None self.appToObj = None self.theProfile = None self.stateUpdate() def grabFrom(self): """ Recall: mapSelectedObjects is a list of tuples: (QgsFeature, QgsVectorLayer) :return: """ debugPrint("grabFrom") # If the To point is on a different layer then reset if self.appToObj is not None and self.mapSelectedObjects[0][ 1] != self.appToObj[1]: self.resetFromTo() if self.mapSelectedObjects[0][1] != self.cmbLayer.itemData( self.cmbLayer.currentIndex()): self.setFromToMsg( 'You must choose features on the same layer as your selection above.', 'red') else: self.appFromID = self.mapSelectedObjects[0][0].id() self.appFromObj = self.mapSelectedObjects[0] self.stateUpdate() def grabTo(self): debugPrint("grabTo") # If the To point is on a different layer then throw an error if self.appFromObj is not None and self.mapSelectedObjects[0][ 1] != self.appFromObj[1]: self.setFromToMsg( 'Could not grab point. It was on a different layer than your "From" point', 'red') else: self.appToID = self.mapSelectedObjects[0][0].id() self.appToObj = self.mapSelectedObjects[0] self.stateUpdate() def findFrom(self): if self.appFromObj is not None: self.appFromObj[1].removeSelection() self.appFromObj[1].selectByIds([self.appFromObj[0].id()]) def findTo(self): if self.appToObj is not None: self.appToObj[1].removeSelection() self.appToObj[1].selectByIds([self.appToObj[0].id()]) def flipFromTo(self): debugPrint("flipFromTo") fromID = self.appFromID fromObj = self.appFromObj toID = self.appToID toObj = self.appToObj self.appFromID = toID self.appToID = fromID self.appFromObj = toObj self.appToObj = fromObj self.runProfile() self.stateUpdate() def actionOpenHelp(self): QtGui.QDesktopServices.openUrl(QUrl(HELP_URL)) def flashFeature(self, feature): # TODO: Might be nice to flash the feature at them # self.mapCanvas.setSelectionColor() print "FLASH" """ Indirect and biproduct actions """ def handleMapSelectionChange(self): """ Triggered when the map selection changes :return: """ if self.isVisible(): debugPrint("handleMapSelectionChange") self.getSelectedMapObjects() self.stateUpdate() def handleMapLayerChange(self, force=False): """ Triggered when new layers are added or removed from the map :return: """ if self.isVisible() or force: debugPrint("handleMapLayerChange") # Get the data from the maps self.getMapVectorLayers() self.getMapVectorLayerFields() # Now repopulate the dropdowns self.recalcVectorLayers() def recalcReachLabels(self): debugPrint("recalcReachLabels") self.lblFrom.setText("ID: {}".format(self.appFromID)) self.lblTo.setText("ID: {}".format(self.appToID)) def recalcVectorLayers(self): """ Rebuild the vector layers combo box and reset the selected item if necessary """ debugPrint("recalcVectorLayers") # Store the current layer so we can look it up later currLayer = self.cmbLayer.itemData(self.cmbLayer.currentIndex()) self.cmbLayer.clear() self.cmbLayer.currentIndexChanged.disconnect(self.cmbLayerChange) for layerObj in self.mapVectorLayers: self.cmbLayer.addItem(layerObj['layer'].name(), layerObj['layer']) self.cmbLayer.currentIndexChanged.connect(self.cmbLayerChange) idx = self.cmbLayer.currentIndex() # If something was already selected see if it's still there. if currLayer is not None: selMapLayerIndex = self.cmbLayer.findData(currLayer) if selMapLayerIndex >= 0: self.cmbLayer.setCurrentIndex(selMapLayerIndex) else: self.setAppVectorLayerFromMap() else: self.setAppVectorLayerFromMap() def recalcBraidRuleState(self): """ The braid choices are a little logic-y so we need to enable/disable some of the controls :return: """ debugPrint("recalcBraidRuleState") currentSelection = self.cmbPathChoose.currentText() txtPathChooseValue = currentSelection == Profile.CHOICE_FIELD_NOT_VALUE or currentSelection == Profile.CHOICE_FIELD_VALUE cmbPathChooseField = currentSelection != Profile.CHOICE_SHORTEST # Only change things if we need to if self.txtPathChooseValue.isHidden() == txtPathChooseValue: self.txtPathChooseValue.setEnabled(txtPathChooseValue) self.lblPathChooseValue.setEnabled(txtPathChooseValue) self.txtPathChooseValue.setHidden(not txtPathChooseValue) self.lblPathChooseValue.setHidden(not txtPathChooseValue) if self.cmbPathChooseField.isHidden() == cmbPathChooseField: self.cmbPathChooseField.setEnabled(cmbPathChooseField) self.lblPathChooseField.setEnabled(cmbPathChooseField) self.cmbPathChooseField.setHidden(not cmbPathChooseField) self.lblPathChooseField.setHidden(not cmbPathChooseField) def recalcVectorLayerFields(self): """ Get current layer index and then repopulate the dropdowns accordingly :return: """ debugPrint("recalcVectorLayerFields") selLayerData = self.cmbLayer.itemData(self.cmbLayer.currentIndex()) selLayerObj = None for obj in self.mapVectorLayers: if selLayerData == obj['layer']: selLayerObj = obj # Now add tree objects for each field # Also populate the field dropdown in the path chooser if selLayerObj is not None: self.treeFields.setEnabled(True) # Populate the list of fields to use self.treeFields.clear() self.cmbPathChooseField.clear() for field in selLayerObj['fields']: # QTreeWidget / View row = [field.name(), "", ""] item = QtGui.QTreeWidgetItem(self.treeFields, row) self.cmbPathChooseField.addItem(field.name()) # Select all by default. if len(self.treeFields.selectedIndexes()) == 0: self.treeFields.selectAll() else: self.treeFields.setEnabled(False) def getMapVectorLayerFields(self): """recalcReachValues each layer and identify the int fields as possible ID fields """ debugPrint("getMapVectorLayerFields") for layerObj in self.mapVectorLayers: allfields = [] intfields = [] for field in layerObj['layer'].fields().toList(): allfields.append(field) if field.type() == QVariant.Int or \ field.type() == QVariant.Double: intfields.append(field) layerObj['fields'] = allfields layerObj['idFields'] = intfields def recalcGoButton(self): """ The ok button shouldn't allow bad behaviour :return: """ debugPrint("recalcGoButton") enabled = True if self.cmbLayer.count() == 0 or self.cmbLayer.currentIndex() < 0: enabled = False if self.appFromID is None or self.appToID is None: enabled = False if self.appToID is None or self.appToID is None: enabled = False if len(self.treeFields.selectedIndexes()) == 0: enabled = False self.btnCreateProfile.setEnabled(enabled) def resetAppSelectedObjects(self): """ Set the reach ID field in the UI """ debugPrint("resetAppSelectedObjects") if self.appFromID is None and len(self.mapSelectedObjects) > 0: self.appFromID = self.mapSelectedObjects[0][0].id() if self.appToID is None and len(self.mapSelectedObjects) > 1: self.appToID = self.mapSelectedObjects[1][0].id() self.runProfile() self.stateUpdate() def recalcFieldOptions(self): debugPrint("recalcFieldOptions") def recalcGrabButtons(self): """ Set the Grab button to be disabled for all but one case :return: """ debugPrint("recalcGrabButtons") self.btnGrabFrom.setEnabled(False) self.btnGrabTo.setEnabled(False) if len(self.mapSelectedObjects) > 1: self.setFromToMsg( "You have {} features selected. To use the grab tool you must select only one." .format(len(self.mapSelectedObjects))) elif len(self.mapSelectedObjects) == 0: self.setFromToMsg( "You have 0 features selected. To use the grab tool you must select at least one." .format(len(self.mapSelectedObjects))) else: if self.appFromID is None or self.mapSelectedObjects[0][0].id( ) != self.appFromID: self.btnGrabFrom.setEnabled(True) if self.appToID is None or self.mapSelectedObjects[0][0].id( ) != self.appToID: self.btnGrabTo.setEnabled(True) self.btnFindFrom.setEnabled(False) self.btnFindTo.setEnabled(False) if self.appFromID is not None: self.btnFindFrom.setEnabled(True) if self.appToID is not None: self.btnFindTo.setEnabled(True) def stateUpdate(self): """ This is the function that ripples through and updates the state of the UI :return: """ debugPrint("stateUpdate") self.recalcGrabButtons() self.recalcFieldOptions() self.recalcReachLabels() self.recalcGoButton() """ Profile functions to do with the profile class """ def createProfile(self): """ The create profile transforms the SHP file into a networkX object for future processing :param event: :return: """ debugPrint("Create profile") selectedLayer = self.cmbLayer.itemData(self.cmbLayer.currentIndex()) self.theProfile = None try: if selectedLayer is not None: self.theProfile = Profile(selectedLayer, msgcallback=self.setFromToMsg) except Exception as e: if self.theProfile is not None and self.theProfile.metalogs is not None: detailstxt = "LOG:\n=====================\n {0}\n\nException:\n=====================\n{1}".format( "\n ".join(self.theProfile.metalogs), str(e)) okDlg("ERROR:", infoText=str(e), detailsTxt=detailstxt, icon=QtGui.QMessageBox.Critical) else: detailstxt = "Exception:\n=====================\n{0}".format( str(e)) okDlg("ERROR:", "A critical error occured.", detailsTxt=detailstxt, icon=QtGui.QMessageBox.Critical) return def runProfile(self): """ Run profile does the pathfinding and reports back We do basic pathfinding a bunch of different times before we actually save the file. We may do this multiple times so it makes sense for this to be as fast as humanly possible :param event: :return: """ try: if self.appFromID is not None: debugPrint("runProfile") if self.theProfile is None: self.createProfile() self.theProfile.pathfinder(self.appFromID, self.appToID) newToID = self.theProfile.getToID() noOutflowStr = "" if self.appToID is None and newToID is not None: self.appToID = newToID noOutflowStr = "'To' point was discovered and " self.appFromObj[1].removeSelection() self.appFromObj[1].selectByIds([self.appToID]) self.appToObj = self.mapSelectedObjects[0] if len(self.theProfile.paths) > 0: msg = "{}at least one path was found between 'from' and 'to' points.".format( noOutflowStr) self.setFromToMsg(msg[0].upper() + msg[1:]) else: if self.theProfile.reversible: self.setFromToMsg( "'From' and 'to' points appear to be backwards. Click 'reverse' to correct.", 'red') else: self.setFromToMsg( "No path found between 'From' and 'To' point.", 'red') self.stateUpdate() except Exception as e: traceback.print_exc() self.setFromToMsg("No path found between 'From' and 'To' point.", 'red') def saveProfile(self): """ Write our file to nice outputs on the hard drive :param event: :return: """ if len(self.treeFields.selectedIndexes()) == 0: self.treeFields.selectAll() cols = [ str(idx.data(0, Qt.DisplayRole)) for idx in self.treeFields.selectedItems() ] # Now write to CSV try: selectedLayer = self.cmbLayer.itemData( self.cmbLayer.currentIndex()) rootdir = QtGui.QFileDialog.getExistingDirectory( self, "Specify output folder", "", QtGui.QFileDialog.ShowDirsOnly | QtGui.QFileDialog.DontResolveSymlinks) if rootdir == "" or not os.path.isdir(rootdir): return # - Details about when this was run. Inputs and outputs # - Details about the traversal. foldername = "Profile-{}-{}to{}".format(selectedLayer.name(), self.appFromID, self.appToID) outputdir = os.path.join(rootdir, foldername) csvpath = os.path.join(outputdir, "profile.csv") logpath = os.path.join(outputdir, "profile.log") plotspath = os.path.join(outputdir, "plots") # The plotspath is the deepest so it's the only mkdir I need to call for now if not os.path.isdir(plotspath): os.makedirs(plotspath) self.theProfile.generateCSV(cols) with open(csvpath, 'wb') as f: writer = csv.DictWriter(f, self.theProfile.csvkeys) writer.writeheader() writer.writerows(self.theProfile.results) debugPrint("Done Writing CSV") plots = Plots(self.theProfile, plotspath) plots.createPlots() # Write a file with some information about what just happened with open(logpath, 'w') as f: f.write( "Inputs:\n==========================================\n\n") f.write(" DateTime: {}\n".format( datetime.now().replace(microsecond=0).isoformat())) f.write("LayerName: {}\n".format(selectedLayer.name())) f.write(" FromID: {}\n".format(self.appFromID)) f.write(" ToID: {}\n".format(self.appToID)) listsep = "\n - " f.write(" Fields: {}{}\n".format(listsep, listsep.join(cols))) f.write( "\n\nPath:\n==========================================\n\n" ) f.writelines(self.theProfile.pathmsgs) f.write( "\n\nProfile:\n==========================================\n\n" ) f.writelines(self.theProfile.metalogs) if self.chkAddToMap.isChecked(): addToMap(csvpath, selectedLayer) okDlg("Completed:", infoText="CSV file written: {}".format(csvpath)) qurl = QUrl.fromLocalFile(outputdir) QDesktopServices.openUrl(qurl) except Exception as e: traceback.print_exc() detailstxt = "LOG:\n=====================\n {0}\n\nException:\n=====================\n{1}".format( "\n ".join(self.theProfile.metalogs), str(e)) okDlg("ERROR:", infoText=str(e), detailsTxt=detailstxt, icon=QtGui.QMessageBox.Critical) """ Helper Functions """ def setFromToMsg(self, text="", color='black'): """ Give us some helpful text about grabbing map objects :param text: :param color: :return: """ debugPrint("Set Label event") self.lblFromToStatus.setText(text) self.lblFromToStatus.setStyleSheet('QLabel { color: ' + color + ' }') def setAppVectorLayerFromMap(self): """ Set the current map layer in the dropdown from whatever layer is selected on the map """ debugPrint("setAppVectorLayerFromMap") currLayer = self.mapCanvas.currentLayer() selMapLayerIndex = self.cmbLayer.findData(currLayer) if selMapLayerIndex > -1: self.cmbLayer.setCurrentIndex(selMapLayerIndex) # Set the selection independent of the control so if the map changes # we'll retain it. self.appSelectedLayer = self.cmbLayer.itemData(selMapLayerIndex) def getSelectedMapObjects(self): """ Get a helpful list of selected objects on the map :return: """ debugPrint("getSelectedMapObjects") self.mapSelectedObjects = [] for layer in self.mapVectorLayers: # Only add items that are on the currrent layer if layer['layer'] == self.mapCanvas.currentLayer(): for feat in layer['layer'].selectedFeatures(): self.mapSelectedObjects.append((feat, layer['layer'])) def getMapVectorLayers(self): debugPrint("getMapVectorLayers") self.mapVectorLayers = [{ 'layer': layer } for layer in self.mapCanvas.layers() if type(layer) is QgsVectorLayer]
def test_linestringZType(self): """ Make sure we can open Linestring Z types :return: """ uri = path.join(path.dirname(__file__), 'data', 'LineString.shp') uriZ = path.join(path.dirname(__file__), 'data', 'LineStringZ.shp') uriM = path.join(path.dirname(__file__), 'data', 'LineStringM.shp') uriZM = path.join(path.dirname(__file__), 'data', 'LineStringZM.shp') vlayer = QgsVectorLayer(uri, "StressTest_Layer", "ogr") vlayerZ = QgsVectorLayer(uri, "StressTest_Layer", "ogr") vlayerM = QgsVectorLayer(uri, "StressTest_Layer", "ogr") vlayerZM = QgsVectorLayer(uri, "StressTest_Layer", "ogr") lszProfile = Profile(vlayer) lszProfileZ = Profile(vlayerZ) lszProfileM = Profile(vlayerM) lszProfileZM = Profile(vlayerZM) self.assertTupleEqual(lszProfile.findEdgewithID(0), lszProfileZ.findEdgewithID(0)) self.assertTupleEqual(lszProfile.findEdgewithID(0), lszProfileM.findEdgewithID(0)) self.assertTupleEqual(lszProfile.findEdgewithID(0), lszProfileZM.findEdgewithID(0))
class TestProfiler(unittest.TestCase): def setUp(self): """Runs before each test.""" uri = path.join(path.dirname(__file__), 'data', 'StressTest.shp') self.vlayer = QgsVectorLayer(uri, "StressTest_Layer", "ogr") self.nxProfile = Profile(self.vlayer, None) def tearDown(self): """Runs after each test.""" # self.dialog = None def test_linestringZType(self): """ Make sure we can open Linestring Z types :return: """ uri = path.join(path.dirname(__file__), 'data', 'LineString.shp') uriZ = path.join(path.dirname(__file__), 'data', 'LineStringZ.shp') uriM = path.join(path.dirname(__file__), 'data', 'LineStringM.shp') uriZM = path.join(path.dirname(__file__), 'data', 'LineStringZM.shp') vlayer = QgsVectorLayer(uri, "StressTest_Layer", "ogr") vlayerZ = QgsVectorLayer(uri, "StressTest_Layer", "ogr") vlayerM = QgsVectorLayer(uri, "StressTest_Layer", "ogr") vlayerZM = QgsVectorLayer(uri, "StressTest_Layer", "ogr") lszProfile = Profile(vlayer) lszProfileZ = Profile(vlayerZ) lszProfileM = Profile(vlayerM) lszProfileZM = Profile(vlayerZM) self.assertTupleEqual(lszProfile.findEdgewithID(0), lszProfileZ.findEdgewithID(0)) self.assertTupleEqual(lszProfile.findEdgewithID(0), lszProfileM.findEdgewithID(0)) self.assertTupleEqual(lszProfile.findEdgewithID(0), lszProfileZM.findEdgewithID(0)) def test_getPathEdgeIds(self): from NetworkProfiler.profiler import EdgeObj self.nxProfile.paths = [[ EdgeObj(((0, 1), (2, 3)), [1]), EdgeObj(((2, 3), (4, 5)), [2]), EdgeObj(((4, 5), (6, 7)), [3]), ]] self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[1, 2, 3]]) def test_findnodewithID(self): ptA = self.nxProfile.findEdgewithID(22) ptNone = self.nxProfile.findEdgewithID(9999) self.assertAlmostEqual(ptA.edge[0][0], -2.53887357711) self.assertAlmostEqual(ptA.edge[0][1], 1.56345777511) self.assertAlmostEqual(ptA.edge[1][0], -2.65314254760) self.assertAlmostEqual(ptA.edge[1][1], 1.02476119995) self.assertIsNone(ptNone) # def test_writeCSV(self): # self.fail() # def test_pathfinder(self): """ We test the positive cases first :return: """ # Test a single path. This is the simplest case self.nxProfile.pathfinder(8, 10) self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[8, 9, 10]]) # Partial Path self.nxProfile.pathfinder(8, 9) self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[8, 9]]) # Double Path self.nxProfile.pathfinder(22, 28) self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[22, 24, 25, 27, 28], [22, 23, 26, 27, 28]]) # Triple Path self.nxProfile.pathfinder(29, 34) self.assertListEqual( self.nxProfile.getPathEdgeIds(), [[29, 30, 33, 34], [29, 31, 33, 34], [29, 32, 34]]) # Second Triple Path self.nxProfile.pathfinder(30, 34) self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[30, 33, 34]]) # Two inlets, one outlet self.nxProfile.pathfinder(4, 7) self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[4, 5, 7]]) # Crazy self-intersecting path self.nxProfile.pathfinder(3, 2) self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[3, 0, 1, 2]]) # Circle self.nxProfile.pathfinder(13, 15) self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[13, 14, 15]]) # Start to outflow point self.nxProfile.pathfinder(8) self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[8, 9, 10]]) # Test "find outflow" with multiple paths self.nxProfile.pathfinder(22) self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[22, 24, 25, 27, 28], [22, 23, 26, 27, 28]]) # Test "find outflow" with multiple outflows self.nxProfile.pathfinder(45) self.assertListEqual(self.nxProfile.getPathEdgeIds(), [[45, 35, 38, 39], [45, 35, 36, 37]]) def test_reverse_pathfinder(self): """ If we pass in a backwards path then reverse it :return: """ # Test a single path. Thisse is the simplest case self.assertFalse(self.nxProfile.reversible) self.nxProfile.pathfinder(8, 10) self.assertFalse(self.nxProfile.reversible) self.nxProfile.pathfinder(10, 8) self.assertTrue(self.nxProfile.reversible) def test_segLength(self): self.assertEqual(self.nxProfile._segLength(30), 0.48666221658771897) def test_pathLength(self): self.nxProfile.pathfinder(8, 10) manual = self.nxProfile._segLength(8) + self.nxProfile._segLength( 9) + self.nxProfile._segLength(10) self.assertEqual(self.nxProfile._pathLength(self.nxProfile.paths[0]), manual) def test_chooseEdges(self): print "yay" def test_choosebylength(self): self.nxProfile.pathfinder(29, 34) shortest = self.nxProfile._choosebylength() self.assertListEqual(self.nxProfile.getPathEdgeIds(shortest), [29, 30, 33, 34])
class TestPathChoice(unittest.TestCase): def setUp(self): """ Runs before each test. It's easier just to load a sample file than to write a bunch of mocking """ uri = path.join(path.dirname(__file__), 'data', 'StressTest.shp') self.vlayer = QgsVectorLayer(uri, "StressTest_Layer", "ogr") self.profile = Profile(self.vlayer, None) self.testchoices = [[ self.profile.findEdgewithID(id) for id in [30, 31, 32] ], [self.profile.findEdgewithID(id) for id in [36, 38]]] def test_choose_shortest(self): """ We test the positive cases first :return: """ self.profile.choice = Profile.CHOICE_SHORTEST self.profile.pathfinder(29, 34) shortest = self.profile._choosebylength() self.assertListEqual(self.profile.getPathEdgeIds(shortest), [29, 30, 33, 34]) def test_choose_field_not_empty(self): """ We test the positive cases first :return: """ self.profile.choice = Profile.CHOICE_FIELD_NOT_EMPTY self.profile.fieldname = "PathName2" self.assertEqual( self.profile._chooseEdges(self.testchoices[1])[0].fids[0], 36) def test_choose_field_value(self): """ We test the positive cases first :return: """ self.profile.choice = Profile.CHOICE_FIELD_VALUE self.profile.fieldname = "PathName" self.profile.fieldval = "A" self.assertEqual( self.profile._chooseEdges(self.testchoices[0])[0].fids[0], 30) self.profile.fieldval = "B" self.assertEqual( self.profile._chooseEdges(self.testchoices[0])[0].fids[0], 32) self.profile.fieldval = "C" self.assertEqual( self.profile._chooseEdges(self.testchoices[0])[0].fids[0], 31) # No Good Choice self.profile.fieldval = "D" self.assertEqual(self.profile._chooseEdges(self.testchoices[0]), self.testchoices[0]) # When you don't pass in a value self.profile.fieldname = "PathName" self.profile.fieldval = None self.assertEqual(self.profile._chooseEdges(self.testchoices[0]), self.testchoices[0]) # When you pass in a field that's not there self.profile.fieldname = "PathName" self.profile.fieldval = "Booya" self.assertEqual(self.profile._chooseEdges(self.testchoices[0]), self.testchoices[0]) def test_choose_field_not_value(self): """ We test the positive cases first :return: """ self.profile.choice = Profile.CHOICE_FIELD_NOT_VALUE self.profile.fieldname = "PathName" self.profile.fieldval = "A" chosenpathA = self.profile._chooseEdges(self.testchoices[0]) self.assertEqual(self.profile.getPathEdgeIds(chosenpathA), [31, 32]) self.profile.fieldval = "B" chosenpathB = self.profile._chooseEdges(self.testchoices[0]) self.assertEqual(self.profile.getPathEdgeIds(chosenpathB), [30, 31]) self.profile.fieldval = "C" chosenpathC = self.profile._chooseEdges(self.testchoices[0]) self.assertEqual(self.profile.getPathEdgeIds(chosenpathC), [30, 32]) def test_wrongpath(self): """ Test what happens when there's a good choice that leads to a bad place :return: """ self.profile.choice = Profile.CHOICE_FIELD_NOT_VALUE self.profile.fieldname = "PathName" self.profile.fieldval = "B" self.profile.pathfinder(45, 37) self.assertEqual(self.profile.getPathEdgeIds(self.profile.paths[0]), [45, 35, 36, 37])