def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # a reference to our map canvas self.canvas = self.iface.mapCanvas() # a reference to shaDEM with the svfContext set to True self.shaDEM = shaDEM(iface, True) # Create the dialog (after translation) and keep reference self.dlg = SVFdialog() # interactive GUI connections: self.dlg.comboBox.layerChanged.connect( self.getParameters ) # is it possible to call shaDEM class functions here? self.dlg.spinBox_bands.valueChanged.connect(self.getParameters) self.dlg.runButton.clicked.connect(self.initLayer) # debug - activate cProdile # self.dlg.runButton.clicked.connect(self.tmpProfile) self.dlg.spinBox_maxHt.valueChanged.connect(self.checkInput)
class svf: def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # a reference to our map canvas self.canvas = self.iface.mapCanvas() # a reference to shaDEM with the svfContext set to True self.shaDEM = shaDEM(iface, True) # Create the dialog (after translation) and keep reference self.dlg = SVFdialog() # interactive GUI connections: self.dlg.comboBox.layerChanged.connect( self.getParameters ) # is it possible to call shaDEM class functions here? self.dlg.spinBox_bands.valueChanged.connect(self.getParameters) self.dlg.runButton.clicked.connect(self.initLayer) # debug - activate cProdile # self.dlg.runButton.clicked.connect(self.tmpProfile) self.dlg.spinBox_maxHt.valueChanged.connect(self.checkInput) # def tmpProfile(self): # runFunction = self.initLayer # cProfile.runctx('runFunction()', globals(), locals(), 'restats') # stream = StringIO.StringIO() # p = pstats.Stats('restats', stream=stream) # p.strip_dirs().sort_stats('time').print_stats(15) # statString = stream.getvalue() # print stream.getvalue() # stream.close() # run method that performs all the real work def start(self): # setup comboBox options by finding all raster layers # have to access the ui through the dialog - i.e: self.dlg self.dlg.runButton.setEnabled(False) # self.dlg.comboBox.clear() # for item in self.shaDEM.listlayers(1): #Raster = 1, Vector = 0 # self.dlg.comboBox.addItem(item) # setup Raster Settings Menu self.getParameters() self.checkInput() # show the dialog self.dlg.show() def getParameters(self): selectLayer = ( self.dlg.comboBox.currentLayer() ) # QgsMapLayerRegistry.instance().mapLayersByName(self.dlg.comboBox.currentText())[0] #self.getLayerByName(self.dlg.comboBox.currentText()) if selectLayer is None or selectLayer.type() != 1: # QMessageBox.critical( self.iface.mainWindow(),"No Raster Layers", "Plugin requires raster layers to be loaded in the project" ) # self.dlg.close()#quit()#sys.exitfunc() return -1 band = self.dlg.spinBox_bands.value() unitsPerPixel = selectLayer.rasterUnitsPerPixelX() # assumes square pixels bandCount = selectLayer.bandCount() maxVal = selectLayer.dataProvider().bandStatistics(band).maximumValue self.dlg.label_unitsPerPx.setText("%.3f" % unitsPerPixel) self.dlg.label_maxValue.setText("%.2f" % maxVal) # if type(input).__name__ == 'str': ##again, why not refresh on each getParameter call? self.dlg.spinBox_bands.setMaximum(bandCount) self.dlg.spinBox_maxHt.setValue(maxVal) def checkInput(self): if self.dlg.spinBox_maxHt.value() > 0: self.dlg.runButton.setEnabled(True) # Gets selected layer from GUI & preforms initial checks for validity def initLayer(self): ne.set_num_threads(mp.cpu_count()) # 1 thread per core rlayer = ( self.dlg.comboBox.currentLayer() ) # QgsMapLayerRegistry.instance().mapLayersByName(self.dlg.comboBox.currentText())[0]#self.getLayerByName(self.dlg.comboBox.currentText()) sensorHt = self.dlg.spinBox_sensorHt.value() # get list of sun vectors vectors = self.skyVectors() self.dlg.progressBar.setMaximum(len(vectors)) scale = rlayer.rasterUnitsPerPixelX() # assumes square pixels. . . bandNum = self.dlg.spinBox_bands.value() maxVal = rlayer.dataProvider().bandStatistics(bandNum).maximumValue # QgsMessageLog.logMessage("maxVal = %s" % str(maxVal), "Plugins", 0) maxUsrHeight = self.dlg.spinBox_maxHt.value() # QgsMessageLog.logMessage("maxUsrHeight = %s" % str(maxUsrHeight), "Plugins", 0) unitZ = maxVal / maxUsrHeight # QgsMessageLog.logMessage("unitZ = %s" % str(unitZ), "Plugins", 0) bandCnt = rlayer.bandCount() data = self.shaDEM.rasterToArray(rlayer, bandNum) # t = time.time() a = data["array"].copy() adjSensorHt = sensorHt / unitZ a = ne.evaluate("a + adjSensorHt") # QgsMessageLog.logMessage("Adjusted Sensor Height= %s" % str(adjSensorHt), "Plugins", 0) svfArr = np.zeros(a.shape) i = 0 for vector in vectors: # debug - print solar altitude angles # QgsMessageLog.logMessage("Vector[%i] solar alt angle: %.2f" % (i+1, math.degrees(math.atan(vector[2]/math.sqrt(vector[0]**2+vector[1]**2)))), "Profile", 0) result = self.shaDEM.ShadowCalc(data, vector, scale, unitZ, maxVal) b = result[0] dz = result[1] svfArr = ne.evaluate("where((b-a) <= 0, svfArr + 1, svfArr)") self.dlg.progressBar.setValue(i) i += 1 # t = time.time() - t # QgsMessageLog.logMessage("SVF main loop : " + str(t), "Profile", 0) data["array"] = svfArr / self.dlg.spinBox_vectors.value() self.saveToFile(data) def skyVectors(self): # populate sky with cosine weighted distribution of vectors according to Ratti & Richens 1999 vectors = [] number = self.dlg.spinBox_vectors.value() i = 0 while i < number: azimuth = random.vonmisesvariate(math.pi, 0) radius = math.sqrt(random.random()) solarElevation = math.acos(radius) x = math.cos(azimuth) * radius y = math.sin(azimuth) * radius z = math.sin(solarElevation) vector = [x, y, z] vectors.append(vector) i += 1 # with open('vectors_debug.csv', 'wb') as csvfile: # writer = csv.writer(csvfile) # for line in vectors: # writer.writerow(line) return vectors # Select all layers of a given type and return as list def listlayers(self, layertype): layersmap = QgsMapLayerRegistry.instance().mapLayers() layerslist = [] for (name, layer) in layersmap.iteritems(): if layertype == layer.type(): layerslist.append(layer.name()) return layerslist def saveToFile(self, data): # Save array as new file # Check file format for GDAL Create capability warn = QgsMessageViewer() driver = gdal.GetDriverByName(data["fileFormat"]) metadata = driver.GetMetadata() if metadata.has_key(gdal.DCAP_CREATE) and metadata[gdal.DCAP_CREATE] != "YES": # warn.setMessageAsPlainText('Driver %s supports Create() method.' % format) # warn.showMessage() # else: warn.setMessageAsPlainText("Driver %s does NOT support Create() method. Aborting...") sys.exit() # TODO : Give user option of converting to a compatible file type. # Create New Raster file newPath = "_svf.".join(data["filePath"].rsplit(".", 1)) if data["fileFormat"] == "GTiff": dst_ds = driver.Create(newPath, data["width"], data["height"], int(1), data["bandType"], ["TFW=YES"]) dst_ds.SetGeoTransform(data["geotransform"]) dst_ds.SetProjection(data["projection"]) else: dst_ds = driver.Create(newPath, data["width"], data["height"], int(1), data["bandType"]) dst_ds.SetGeoTransform(data["geotransform"]) dst_ds.SetProjection(data["projection"]) # write to array dst_ds.GetRasterBand(1).WriteArray(data["array"]) # Once we're done, close properly the dataset dst_ds = None self.AddAsNewLayer(newPath) # Takes path of raster file and adds new layer def AddAsNewLayer(self, path): # adds the new image as a layer, inverts and sets the contrast name = "SVF" self.iface.addRasterLayer(path, name) rlayer = QgsMapLayerRegistry.instance().mapLayersByName(name)[0] # self.getLayerByName(name) rlayer.setContrastEnhancement(1) # rlayer.setInvertHistogram(True) self.iface.mapCanvas().refresh()