def field_check(self, layer, z_field): errorCheck = False #Check if the vectorlayer is projected if layer.crs().geographicFlag() == True: criticalMessageToBar( self, 'Error', "Layer " + layer.name() + " is not projected. Please choose an projected reference system." ) printLogMessage( self, "Layer " + layer.name() + " is not projected. Please choose an projected reference system.", 'Error_LOG') # cancel execution of the script errorCheck = True #check the z-field for field in layer.fields(): #Take a look for the z Field if str(field.name()) == str(z_field): # if the z value is not a float if field.typeName() != "Real" and field.typeName() != "double": #Give a message criticalMessageToBar( self, 'Error', 'The z-Value needs to be a float. Check the field type of the z-Value' ) printLogMessage( self, 'The z-Value needs to be a float. Check the field type of the z-Value', 'Error_LOG') # cancel execution of the script errorCheck = True return errorCheck
def sectionPoint(self, coord_proc, side): if side == 'East': coord_sort = sorted(coord_proc, key=lambda x: (-x[0], x[1])) elif side == 'West': coord_sort = sorted(coord_proc, key=lambda x: (x[0], -x[1])) coord_sort_xy = [] for i in range(0, 2): coord_sort_xy.append(coord_sort[i]) coords_sort_z = sorted(coord_sort_xy, key=lambda x: (-x[2])) point = QgsPoint(coords_sort_z[0][0], coords_sort_z[0][1]) printLogMessage(self, str(coords_sort_z[0][0]) + ',' + str(coords_sort_z[0][1]), 'koord') return point
def run(self): """Run method that performs all the real work""" #trigger help button #helpButton.clicked.connect(showPluginHelp()) # Create the dialog (after translation) and keep reference self.dlg = profileAARDialog() #initialize the Errorhandler errorhandler = ErrorHandler(self.iface) magicbox = Magic_Box(self.iface) export = Export(self.iface) '''DEFINE OUTPUT PATH''' #Choose file if button is clicked self.dlg.outputPath.clear() self.dlg.outputButton.clicked.connect(self.select_output_file) '''SELECT INPUT IN GUI''' # CHOOSE INPUT LAYER # read layers from qgis layers and filter out the pointlayers to display in the input combobox self.dlg.inputCombo.setFilters(QgsMapLayerProxyModel.PointLayer) # CHOOSE COLUMNS FOR Z-VALUE, VIEW AND PR-NUMBER # CALLS FUNCTION LAYER_FIELD (once on startup on activation, to enable using when only one point fc is present) self.dlg.inputCombo.activated.connect(self.layer_field) self.dlg.inputCombo.currentIndexChanged.connect(self.layer_field) self.dlg.helpButton.clicked.connect(self.show_help) '''SHORT BLOCK OF PLUGIN CODE (runs the dialog and triggers the event after the OK button was pressed)''' # create/show the dialog self.dlg.show() # Run the dialog event loop result = self.dlg.exec_() # See if OK was pressed if result: inputCheck = False fieldCheck = False #Check if input fields are filled correctly an if the layer has correct properties inputCheck = errorhandler.input_check(self.dlg.outputPath.text()) '''GET INPUT FROM GUI TO VARIABLES/PREPARE LIST OF DATA''' #GET TEXT FROM METHOD AND DIRECTION #Read the method that is selected method = unicode(self.dlg.methodCombo.currentText()) #read the direction, that is selected direction = unicode(self.dlg.directionCombo.currentText()) #Get the selected layer selectedLayer = self.dlg.inputCombo.currentLayer() #PREPARE DATA LIST #Go thought all data rows in the selected layer iter = selectedLayer.getFeatures() #list for the data coord = [] #list for the different profile names profile_names = [] #check if the z values have the correct type and if the crs is projected fieldCheck = errorhandler.field_check(selectedLayer, self.dlg.zCombo.currentText()) height = False section = False if fieldCheck == True or inputCheck == True: sys.exitfunc() if self.dlg.hightBox.isChecked(): height = True if self.dlg.sectionBox.isChecked(): section = True point_id = 0 for feature in iter: # retrieve every feature with its geometry and attributes # fetch geometry # TODO: 3Nachkommastellen!! Bisher sind es nur 2..... geom = feature.geometry() #getting x and y coordinate x = round(geom.asPoint().x(), 3) y = round(geom.asPoint().y(), 3) #write coordinates and attributes (view, profile and z) in a list # TODO: Use dictinary or object #add an ID to each point point_id += 1 coord.append([x,y,feature[self.dlg.zCombo.currentText()],feature[self.dlg.viewCombo.currentText()], feature[self.dlg.profileCombo.currentText()], feature[self.dlg.useCombo.currentText()], point_id]) #write a list of profilenames (unique entries) if feature[self.dlg.profileCombo.currentText()] not in profile_names: profile_names.append(feature[self.dlg.profileCombo.currentText()]) '''WORK ON EVERY PROFILE IN LOOP''' # CREATE A LIST OF DATA FOR EVERY PROFILE # select every single profile in a loop coord_trans = [] height_points = [] cutting_line = [] for i in range(len(profile_names)): # instantiate a temporary list for a single profile coord_proc = [] # instantiate list for the view to check if all entries in one profile are the same view_check = [] #CHANGE # instantiate list for the selection to check if all entries in one profile are the same selection_check = [] # iterate through the features in coord, if the profilename matches store the features datalist in templist for x in range(len(coord)): if coord[x][4] == profile_names[i]: coord_proc.append(coord[x]) # write the unique view values in the checklist if coord[x][3] not in view_check: view_check.append(coord[x][3]) # CHANGE write the unique selection values in the checklist if coord[x][4] not in selection_check: selection_check.append(coord[x][5]) #Handle Errors depending on the attributes in the fields #Errorhandling: Checking the single Profiles for inconsestency #Therefore we need the data of the actual profile, the view_check with the view values and actual profile name, selection is 0 or 1 profileCheck = False if fieldCheck == False and inputCheck == False: profileCheck = errorhandler.singleprofile(coord_proc, view_check, str(profile_names[i]), selection_check) if profileCheck == False and fieldCheck == False and inputCheck == False: #Calculating the profile and add it to the list transform_return = magicbox.transformation(coord_proc, method, direction) coord_height_list = transform_return['coord_trans'] coord_trans.append(coord_height_list) #CHANGE If checked, the upper right poitn has to be exportet as point if height == True: height_points.append(magicbox.height_points(coord_height_list)) if section == True: cutting_line.append(sectionCalc(self, coord_proc, transform_return['cutting_start'],transform_return['linegress'], transform_return['ns_error']), ) if profileCheck == False: '''Export the data''' #For exporting we need the data, the path and the crs of the input data export.export(coord_trans, self.dlg.outputPath.text(), selectedLayer.crs()) #If points are checked, export them #CHANGE if height == True: export.export_height(height_points, self.dlg.outputPath.text(), selectedLayer.crs()) if section == True: #if a profile is recommended, we have to export it. To make it easy to display everything, export left point first printLogMessage(self,str(coord_proc[0][4]),'sec111t') export.export_section(cutting_line, coord_proc[0][4],self.dlg.outputPath.text(), selectedLayer.crs()) #Load the file to qgis automaticly layer = self.iface.addVectorLayer(self.dlg.outputPath.text(), "", "ogr") #CHANGE if height == True: filename = self.dlg.outputPath.text().split(".shp")[0] filename = filename + "_height.shp" layer = self.iface.addVectorLayer(filename, "", "ogr") if section == True: filename = self.dlg.outputPath.text().split(".shp")[0] filename = filename + "_section.shp" layer = self.iface.addVectorLayer(filename, "", "ogr") #if the loading of the layer fails, give a message if not layer: criticalMessageToBar(self, 'Error', 'Failed to open '+self.dlg.outputPath.text()) pass
def singleprofile(self, coord_proc, view_check, profile_name, selection_check): # TODO: check for consistency before calculation # TODO: check for spatial consistency (no points should be more than x meters apart) errorCheck = False # check if actual profile has less then 4 points if len(coord_proc) <= 3: #if it is less, print error message criticalMessageToBar( self, 'Error', 'A profile needs min. 4 points. Error on profile: ' + str(profile_name)) printLogMessage( self, 'A profile needs min. 4 points. Error on profile: ' + str(profile_name), 'Error_LOG') #cancel execution of the script errorCheck = True # check if the view value is the same in all features if len(view_check) != 1: # if it is not the same, print error message criticalMessageToBar( self, 'Error', 'The view column of your data is inconsistant (either non or two different views are present). Error on profile: ' + str(profile_name)) printLogMessage( self, 'The view column of your data is inconsistant (either non or two different views are present). Error on profile: ' + str(profile_name), 'Error_LOG') # cancel execution of the script errorCheck = True # check if the view is one of the four cardinal directions if view_check[0].upper() not in ["N", "E", "S", "W"]: # if it is not the same, print error message criticalMessageToBar( self, 'Error', 'The view value is not one of the four cardinal directions. Error on profile: ' + str(profile_name)) printLogMessage( self, 'The view value is not one of the four cardinal directions. Error on profile: ' + str(profile_name), 'Error_LOG') # cancel execution of the script errorCheck = True #CHANGE1 check if the selection/use is 0 or 1 for i in range(len(selection_check)): printLogMessage(self, str(selection_check[i]), 'sele') if str(selection_check[i]) not in ["1", "0"]: # if it is not the same, print error message criticalMessageToBar( self, 'Error', 'Only 0 or 1 are allowed in the selection/use. Error on profile: ' + str(profile_name)) printLogMessage( self, 'Only 0 or 1 are allowed in the selection/use. Error on profile: : ' + str(profile_name), 'Error_LOG') # cancel execution of the script errorCheck = True # check if the coordinates x, y, z fall into 2 sigma range #instance a table like list of lists with i rows and j columns warning_message = [] for i in range(3): xyz = [] xyz_lower = [] xyz_upper = [] xyz = columnreader(coord_proc, i) xyz_lower = mean(xyz) - (2 * std(xyz)) xyz_upper = mean(xyz) + (2 * std(xyz)) for j in range(len(xyz)): if xyz[j] < xyz_lower or xyz[j] > xyz_upper: #warning_message.append("Warning: Profile " )+ str(profile_name) + chr(120+i) + str(j) + 'excedes th 2 std interval of ' + chr(120+i)) criticalMessageToBar( self, 'Warning', 'Warning: Profile ' + str(profile_name) + ': ' + chr(120 + i) + 'Pt ' + str(j + 1) + ' exceeds the 2std interval of ' + chr(120 + i)) printLogMessage( self, 'Warning: Profile ' + str(profile_name) + ': ' + chr(120 + i) + 'Pt ' + str(j + 1) + ' exceeds the 2std interval of ' + chr(120 + i), 'Error_LOG') return errorCheck
def transformation(self, coord_proc, method, direction): #initialize the Errorhandler errorhandler = ErrorHandler(self) profilnr_proc = listToList(self, coord_proc, 4) fehler_check = False ns_fehler_vorhanden = ns_error_determination(self, coord_proc) if ns_fehler_vorhanden: # Profil um 45 Grad drehen rotationresult = rotation(self, coord_proc, 45, False) fehler_check = True for i in range(len(coord_proc)): coord_proc[i][0] = rotationresult['x_trans'][i] coord_proc[i][1] = rotationresult['y_trans'][i] coord_proc[i][2] = rotationresult['z_trans'][i] #write the x and v values in the corresponding lists # instantiate an empty list for the transformed coordinates and other values # instantiate lists for the x and y values x_coord_proc = listToList(self, coord_proc, 0) y_coord_proc = listToList(self, coord_proc, 1) z_coord_proc = listToList(self, coord_proc, 2) selection_proc = listToList(self, coord_proc, 5) id_proc = listToList(self, coord_proc, 6) rangcheck_orginal = [] for i in range(len(coord_proc)): tmplist = [] for k in range(len(coord_proc[i])): tmplist.append(coord_proc[i][k]) rangcheck_orginal.append(tmplist) for coords in range(len(rangcheck_orginal)): del rangcheck_orginal[coords][5] del rangcheck_orginal[coords][4] del rangcheck_orginal[coords][3] #distanz zwischen den beiden Punkten oben CHANGE # create the valuelists that are used #EINFUEGEN WENN Spalte = x verwenden xw = [] yw = [] #CHANGE xw_check = [] yw_check = [] for x in range(len(x_coord_proc)): #CHANGE Nur Auswahl zum berechnen der Steigung verwenden if (selection_proc[x] == 1): xw.append(x_coord_proc[x] - min(x_coord_proc)) yw.append(y_coord_proc[x] - min(y_coord_proc)) xw_check.append(x_coord_proc[x] - min(x_coord_proc)) yw_check.append(y_coord_proc[x] - min(y_coord_proc)) #QgsMessageLog.logMessage(str(xw), 'MyPlugin') #CHANGE #There is a problem with lingress if the points are nearly N-S oriented #To solve this, it is nessecary to change the input values of the regression # Calculate the regression for both directions linegress_x = scipy.stats.linregress(scipy.array(xw), scipy.array(yw)) linegress_y = scipy.stats.linregress(scipy.array(yw), scipy.array(xw)) # get the sum of residuals for both direction #We like to use the regression with less sum of the residuals res_x = self.calculateResidual(linegress_x, scipy.array(xw), scipy.array(yw), profilnr_proc[0]) res_y = self.calculateResidual(linegress_y, scipy.array(yw), scipy.array(xw), profilnr_proc[0]) if isnan(res_y) or res_x >= res_y: linegress = linegress_x slope = linegress[0] elif isnan(res_x) or res_x < res_y: linegress = linegress_y # if the linear regression with the changed values was used, the angle of the slope is rotated by 90° slope = tan((-90 - (((atan(linegress[0]) * 180) / pi))) * pi / 180) else: criticalMessageToBar(self, ' Error', 'Calculation failed! Corrupt data!') sys.exitfunc() #CHANGE Check the distance with all points distance = errorhandler.calculateError(linegress, xw_check, yw_check, coord_proc[0][4]) # calculate the degree of the slope #Defining the starting point for the export of the section slope_deg = 0.0 #Variable for determining the paint direction of the cutting line cutting_start = '' if slope < 0 and coord_proc[0][3] in ["N", "E"]: slope_deg = 180 - fabs((atan(slope) * 180) / pi) * -1 cutting_start = 'E' elif slope < 0 and coord_proc[0][3] in ["S", "W"]: slope_deg = fabs((atan(slope) * 180) / pi) cutting_start = 'W' elif slope > 0 and coord_proc[0][3] in ["S", "E"]: slope_deg = ((atan(slope) * 180) / pi) * -1 cutting_start = 'W' elif slope > 0 and coord_proc[0][3] in ["N", "W"]: slope_deg = 180 - ((atan(slope) * 180) / pi) cutting_start = 'E' elif slope == 0 and coord_proc[0][3] == "N": slope_deg = 180 cutting_start = 'E' # instantiate lists for the transformed coordinates x_trans = [] y_trans = [] z_trans = [] first_rotationresult = rotation(self, coord_proc, slope_deg, True) for i in range(len(coord_proc)): x_trans.append(first_rotationresult['x_trans'][i]) y_trans.append(first_rotationresult['y_trans'][i]) z_trans.append(first_rotationresult['z_trans'][i]) if direction == "absolute height": #To get an export for the absolute height it is necessary to rotate the profile like the horizontal way #and move it on the y-axis x_coord_proc = listToList(self, coord_proc, 0) y_coord_proc = listToList(self, coord_proc, 1) z_coord_proc = listToList(self, coord_proc, 2) # calculate the minimal x mean_x = mean(x_coord_proc) mean_y = mean(y_coord_proc) mean_z = mean(z_coord_proc) for i in range(len(x_trans)): x_trans[i] = x_trans[i] - mean_x z_trans[i] = z_trans[i] - mean_y + mean_z # printLogMessage(self, str(x_coord_proc[i]), 'ttt') # printLogMessage(self, str(x_trans[i]), 'ttt') #printLogMessage(self,str(min_x),'ttt') new_min_x = min(x_trans) for i in range(len(x_trans)): x_trans[i] = x_trans[i] + abs(new_min_x) # instantiate a list for the transformed coordinates coord_trans = [] #CHANGE rangcheck_trans = [] # build the finished list for i in range(len(coord_proc)): coord_trans.append([ x_trans[i], y_trans[i], z_trans[i], coord_proc[i][4], coord_proc[i][2], distance[i], selection_proc[i], id_proc[i] ]) rangcheck_trans.append([x_trans[i], z_trans[i], y_trans[i]]) #If the aim is to get the view of the surface, the x-axis has to be rotated aswell if method == "surface": # calculating the slope, therefore preparing lists z_yw = [] z_zw = [] for i in range(len(coord_proc)): z_yw.append(y_trans[i] - min(y_trans + z_trans)) z_zw.append(z_trans[i] - min(y_trans + z_trans)) # actual calculation of the slope using the linear regression again z_slope = scipy.stats.linregress(scipy.array(z_yw), scipy.array(z_zw))[0] # transform the radians of the slope into degrees z_slope_deg = 0.0 if z_slope < 0: z_slope_deg = -(90 - fabs(((atan(z_slope) * 180) / pi))) elif z_slope > 0: z_slope_deg = 90 - ((atan(z_slope) * 180) / pi) elif z_slope == 0: z_slope_deg = 0.0 # calculate the centerpoint z_center_y = mean(y_trans) z_center_z = mean(z_trans) # rewrite the lists for the y and z values y_trans = [] z_trans = [] for i in range(len(coord_trans)): y_trans.append(z_center_y + (coord_trans[i][1] - z_center_y) * cos(z_slope_deg / 180 * pi) - (coord_trans[i][2] - z_center_z) * sin(z_slope_deg / 180 * pi)) z_trans.append(z_center_z + (coord_trans[i][1] - z_center_y) * sin(z_slope_deg / 180 * pi) + (coord_trans[i][2] - z_center_z) * cos(z_slope_deg / 180 * pi)) # empty and rewrite the output list coord_trans = [] rangcheck_trans = [] for i in range(len(coord_proc)): # CHANGE coord_trans.append([ x_trans[i], y_trans[i], z_trans[i], coord_proc[i][4], coord_proc[i][2], distance[i], selection_proc[i], id_proc[i] ]) rangcheck_trans.append([x_trans[i], z_trans[i], y_trans[i]]) # If the direction is in the "original" setting, the points have to be rotated back to their original orientation if direction == "original": # the rotation angle is the negative angle of the first rotation if fehler_check == True: y_slope_deg = -slope_deg - 45 else: y_slope_deg = -slope_deg # get the centerpoint y_center_x = mean(x_trans) y_center_z = mean(z_trans) #rewrite the lists for the x and z values x_trans = [] z_trans = [] for i in range(len(coord_trans)): x_trans.append(y_center_x + (coord_trans[i][0] - y_center_x) * cos(y_slope_deg / 180 * pi) - (coord_trans[i][2] - y_center_z) * sin(y_slope_deg / 180 * pi)) z_trans.append(y_center_z + (coord_trans[i][0] - y_center_x) * sin(y_slope_deg / 180 * pi) + (coord_trans[i][2] - y_center_z) * cos(y_slope_deg / 180 * pi)) # empty and rewrite the output list coord_trans = [] rangcheck_trans = [] for i in range(len(coord_proc)): # CHANGE coord_trans.append([ x_trans[i], y_trans[i], z_trans[i], coord_proc[i][4], coord_proc[i][2], distance[i], selection_proc[i], id_proc[i] ]) rangcheck_trans.append([x_trans[i], z_trans[i], y_trans[i]]) #change # check the distances of the outter points from the old points and the converted ones original_outer_points = self.outer_profile_points(coord_proc) original_distance = self.calculate_distance_from_outer_profile_points_orgiginal( original_outer_points) new_outer_points = [] for point in coord_trans: if point[7] == original_outer_points[0][6] or point[ 7] == original_outer_points[1][6]: new_outer_points.append(point) new_distance = self.calculate_distance_from_outer_profile_points_proc( new_outer_points) printLogMessage(self, 'PR:' + str(coord_proc[0][4]), 'Distance') printLogMessage(self, 'Original Distance: ' + str(original_distance), 'Distance') printLogMessage(self, 'New Distance: ' + str(new_distance), 'Distance') printLogMessage( self, 'Diff. Distance: ' + str(abs(original_distance - new_distance)), 'Distance') if abs(original_distance - new_distance) > 0.01: criticalMessageToBar( self, 'Error', 'Profile was calculated incorrect (1cm acc.) See Log-Window: ' + str(str(coord_proc[0][4]))) printLogMessage(self, 'DISTANCE WARNING!', 'Distance') return { 'coord_trans': coord_trans, 'cutting_start': cutting_start, 'linegress': linegress, 'ns_error': ns_fehler_vorhanden }