class SedimentalLayerModel(QtCore.QAbstractListModel): def __init__(self, parent=None): super().__init__(parent) self.file = None def setLayer(self, layer): self.beginResetModel() self.file = PreCourlisFileLine(layer) self.endResetModel() def rowCount(self, parent=QtCore.QModelIndex()): if self.file is None: return 0 if self.file.layer() is None: return 0 return len(self.file.layers()) + 1 def data(self, index, role=QtCore.Qt.DisplayRole): if role == QtCore.Qt.DisplayRole: if index.row() == 0: if index.column() == 0: return "zfond" layers = self.file.layers() if index.row() - 1 < len(layers): layer = self.file.layers()[index.row() - 1] if index.column() == 0: return layer
def test_interpolate_lines_multiple_layers(self): layer = QgsVectorLayer(PROFILE_LINES_PATH, "multiple_layers", "ogr") file = PreCourlisFileLine(layer) file.add_sedimental_layer("Layer2", "Layer1", -1.0) self.check_algorithm( {"SECTIONS": layer}, {"OUTPUT": "interpolate_lines_multiple_layers.gml"}, )
def set_sections( self, layer, feature, previous_section, current_section, next_section ): self.position = None self.file = PreCourlisFileLine(layer) self.feature = feature self.previous_section = previous_section self.current_section = current_section self.next_section = next_section self.refresh()
def processAlgorithm(self, parameters, context, feedback): input_layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) reach_name = (self.parameterAsString(parameters, self.REACH_NAME, context) or input_layer.name()) output_path = self.parameterAsString(parameters, self.OUTPUT, context) precourlis_file = PreCourlisFileLine(input_layer) reach = precourlis_file.get_reach(reach_name) output_file = MascaretGeoFile(output_path, mode="write") output_file.has_ref = "ref" in os.path.splitext(output_path)[1][1:] output_file.add_reach(reach) output_file.save(output_path) return {self.OUTPUT: output_path}
def processAlgorithm(self, parameters, context, feedback): input_path = self.parameterAsString(parameters, self.INPUT, context) crs = self.parameterAsCrs(parameters, self.CRS, context) file = MascaretGeoFile(input_path) fields = PreCourlisFileLine.base_fields() (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, crs, ) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) # Compute the number of steps to display within the progress bar and count = sum([len(r.sections) for r in file.reaches.values()]) total = 100.0 / count if count else 0 current = 0 for reach in file.reaches.values(): for section in reach.sections.values(): # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): break feature = PreCourlisFileLine.feature_from_section( section, fields) # Add a feature in the sink sink.addFeature(feature, QgsFeatureSink.FastInsert) # Update the progress bar feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def prepare_sections(self, parameters, context, feedback): sections = self.parameterAsSource(parameters, self.SECTIONS, context) # Prefix layer fields by "Z" for TatooineMesher file = PreCourlisFileLine(sections) alg_params = { "INPUT": parameters[self.SECTIONS], "FIELDS_MAPPING": [ { "name": "sec_id", "type": QVariant.Int, "expression": '"sec_id"' }, { "name": "p_id", "type": QVariant.Int, "expression": '"p_id"' }, { "name": "Zzfond", "type": QVariant.Double, "expression": '"zfond"' }, ] + [{ "name": "Z{}".format(layer), "type": QVariant.Double, "expression": '"{}"'.format(layer), } for layer in file.layers()], "OUTPUT": QgsProcessingUtils.generateTempFilename("tatooine_input.shp"), } outputs = processing.run( "qgis:refactorfields", alg_params, context=context, feedback=feedback, is_child_algorithm=True, ) return outputs["OUTPUT"]
def line_feature_from_points(self, point_features, sec_id, abs_long): points = [ point_feature.geometry().constGet().clone() for point_feature in point_features ] line = QgsGeometry(QgsLineString(points)) intersection_point = None if self.axis: intersection = line.intersection(self.axis.geometry()) # Take only the first parts (QgsMultiPoint => QgsPoint) intersection_point = next(intersection.constParts()).clone() layers = PreCourlisFileLine(None).layers(point_features[0]) line_feature = QgsFeature() line_feature.setAttributes([ # sec_id point_features[0].attribute("sec_id") or sec_id, # sec_name point_features[0].attribute("sec_name") or "{}_{:04.3f}".format("P" if abs_long >= 0 else "N", abs_long), # abs_long abs_long, # axis_x intersection_point.x( ) if self.axis else point_features[0].attribute("axis_x"), # axis_y intersection_point.y( ) if self.axis else point_features[0].attribute("axis_y"), # layers point_features[0].attribute("layers") or "", # p_id ",".join([str(id_ + 1) for id_ in range(0, len(points))]), # topo_bat ",".join([f.attribute("topo_bat") for f in point_features]), # abs_lat ",".join([ str(line.lineLocatePoint(f.geometry())) for f in point_features ]), # zfond ",".join([ str(self.to_float(f.attribute("zfond"))) for f in point_features ]), ] + [ ",".join([ str(self.to_float(f.attribute(layer))) for f in point_features ]) for layer in layers ]) line_feature.setGeometry(line) return line_feature
def processAlgorithm(self, parameters, context, feedback): input_layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) reach_name = (self.parameterAsString(parameters, self.REACH_NAME, context) or input_layer.name()) output_path = self.parameterAsString(parameters, self.OUTPUT, context) # Processing GUI does not correctly handle uppercase characters in file extensions before # QGIS 3.14 if Qgis.QGIS_VERSION_INT < 31400: output_path = output_path.replace(".georefc", ".georefC") output_path = output_path.replace(".geoc", ".geoC") precourlis_file = PreCourlisFileLine(input_layer) reach = precourlis_file.get_reach(reach_name) output_file = MascaretGeoFile(output_path, mode="write") output_file.has_ref = "ref" in os.path.splitext(output_path)[1][1:] output_file.add_reach(reach) output_file.nlayers = len(precourlis_file.layers()) output_file.save(output_path) return {self.OUTPUT: output_path}
def create_widget(self): request = QgsFeatureRequest() request.setLimit(1) feature = next(self.layer.getFeatures(request)) section = PreCourlisFileLine(self.layer).section_from_feature(feature) section.feature = feature widget = GraphWidget(None) model = PointsTableModel(widget) model.set_section(section) sel_model = QtCore.QItemSelectionModel(model, widget) widget.set_selection_model(sel_model) widget.set_sections( layer=self.layer, feature=feature, previous_section=None, current_section=section, next_section=None, ) return widget
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.TRACKS, context) axis = self.parameterAsSource(parameters, self.AXIS, context) self.name_field = self.parameterAsString(parameters, self.NAME_FIELD, context) self.axis = next(axis.getFeatures()) self.name_field_index = source.fields().indexFromName(self.name_field) (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, PreCourlisFileLine.base_fields(), QgsWkbTypes.LineString, source.sourceCrs(), ) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) total = 100.0 / source.featureCount() if source.featureCount() else 0 features = source.getFeatures() for current, feature in enumerate(features): # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): break intersection = feature.geometry().intersection(self.axis.geometry()) assert not intersection.isNull() abs_long = self.axis.geometry().lineLocatePoint(intersection) # Take only the first parts (QgsMultiLineString => QgsLineString) axis_line = next(self.axis.geometry().constParts()).clone() track_line = next(feature.geometry().constParts()).clone() intersection_point = intersection.constGet() track_angle = qgslinestring_angle(track_line, intersection_point) * ( 180 / math.pi ) axis_angle = qgslinestring_angle(axis_line, intersection_point) * ( 180 / math.pi ) d_angle = (track_angle - axis_angle) % 360 out = QgsFeature() out.setAttributes( [ # sec_id None, # sec_name feature.attribute(self.name_field_index) if self.name_field else None, # abs_long abs_long, # axis_x intersection_point.x(), # axis_y intersection_point.y(), # layers "", # p_id QVariant(), # topo_bat "B", # abs_lat QVariant(), # zfond QVariant(), ] ) if d_angle < 180: out.setGeometry(QgsGeometry(track_line.reversed())) else: out.setGeometry(QgsGeometry(feature.geometry())) # Add a feature in the sink sink.addFeature(out, QgsFeatureSink.FastInsert) # Update the progress bar feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
class GraphWidget(FigureCanvas): editing_finished = QtCore.pyqtSignal() def __init__(self, parent=None): self.figure = plt.figure(figsize=(15, 7), constrained_layout=True) super().__init__(self.figure) self.graph = plt.subplot(111) self.selection_tool = SelectionTool(self, self.graph) self.selection_tool.activate() self.zoom_tool = ZoomTool(self, self.graph) self.zoom_tool.activate() self.file = None self.feature = None self.previous_section = None self.current_section = None self.next_section = None self.position = None self.pointing_line = None self.layers_lines = [] self.layers_fills = [] self.legend = None self.current_layer_name = None def close_figure(self): plt.close(self.figure) def set_selection_model(self, model): self.selection_tool.set_selection_model(model) def set_sections( self, layer, feature, previous_section, current_section, next_section ): self.position = None self.file = PreCourlisFileLine(layer) self.feature = feature self.previous_section = previous_section self.current_section = current_section self.next_section = next_section self.refresh() def set_current_layer(self, layer_name): self.current_layer_name = layer_name self.refresh_current_section() self.refresh_legend() self.draw() def axis_position(self, section): """ Return linear referencing of axis intersection for passed section. """ f = section.feature intersection = QgsGeometry( QgsPoint(f.attribute("axis_x"), f.attribute("axis_y")) ) return f.geometry().lineLocatePoint(intersection) def draw_section(self, section, offset, **kwargs): if section.distances[0] is None: return return self.graph.plot( [d + offset for d in section.distances], section.z, label=section.name, **kwargs, ) def clear(self): self.graph.clear() self.pointing_line = None self.layers_lines = [] self.layers_fills = [] self._legend = None def refresh(self): self.clear() axis_pos = self.axis_position(self.current_section) if self.previous_section: self.draw_section( self.previous_section, axis_pos - self.axis_position(self.previous_section), color="green", linewidth=0.5, ) self.refresh_current_section(False) if self.next_section: self.draw_section( self.next_section, axis_pos - self.axis_position(self.next_section), color="blue", linewidth=0.5, ) # Draw axis position self.graph.axvline(axis_pos, color="black") self.graph.grid(True) self.graph.set_ylabel("Z (m)") self.graph.set_xlabel("Abscisse en travers (m)") self.refresh_legend() self.draw() def refresh_legend(self): if self.legend is not None: self.legend.remove() lines, labels = self.graph.get_legend_handles_labels() self.legend = self.graph.legend( lines, labels, bbox_to_anchor=(1, 0.5), loc="center left", fancybox=True, shadow=True, prop={"size": 10}, ) def refresh_current_section(self, draw=True): [line.remove() for line in self.layers_lines] self.layers_lines = [] [poly.remove() for poly in self.layers_fills] self.layers_fills = [] section = self.current_section if section is None: return if section.distances[0] is None: return previous_values = None current_column = None for i, layer in enumerate(["zfond"] + section.layer_names): if layer == "zfond": values = self.current_section.z else: values = section.layers_elev[i - 1] color = self.file.layer_color(layer) if previous_values is not None: self.layers_fills.append( self.graph.fill_between( section.distances, previous_values, values, where=values <= previous_values, facecolor=color, alpha=0.3, ) ) previous_values = values (line,) = self.graph.plot( section.distances, values, label=layer, color=color, marker="." if self.current_layer_name == layer else None, zorder=10 if self.current_layer_name == layer else 1, ) self.layers_lines.append(line) if self.current_layer_name == layer: current_column = i + 1 self.selection_tool.set_data( section.distances, [section.z] + list(section.layers_elev), current_column, ) if draw: self.draw()
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) fields = PreCourlisFilePoint.base_fields() file = PreCourlisFileLine(source) layers = file.layers() for layer in layers: fields.append(QgsField(layer, QVariant.Double)) (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, fields, QgsWkbTypes.PointZ, source.sourceCrs(), ) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) total = 100.0 / source.featureCount() if source.featureCount() else 0 features = source.getFeatures() for current, line_feature in enumerate(features): # Take only the first parts (QgsMultiLineString => QgsLineString) line = next(line_feature.geometry().constParts()).clone() line_layers_values = [ line_feature.attribute(layer).split(",") for layer in layers ] for point, p_id, topo_bat, abs_lat, zfond, point_layers_values, in zip( line.points(), line_feature.attribute("p_id").split(","), line_feature.attribute("topo_bat").split(","), line_feature.attribute("abs_lat").split(","), line_feature.attribute("zfond").split(","), list(zip(*line_layers_values)) or [[]] * line.numPoints(), ): point_feature = QgsFeature() point_feature.setAttributes([ line_feature.attribute("sec_id"), line_feature.attribute("sec_name"), line_feature.attribute("abs_long"), line_feature.attribute("axis_x"), line_feature.attribute("axis_y"), line_feature.attribute("layers"), int(p_id), topo_bat, self.to_float(abs_lat), self.to_float(point.x()), self.to_float(point.y()), self.to_float(zfond), ] + [self.to_float(v) for v in point_layers_values]) point_feature.setGeometry( QgsGeometry(QgsPoint(point.x(), point.y(), 0.0))) sink.addFeature(point_feature, QgsFeatureSink.FastInsert) # Update the progress bar feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def create_file(self): layer = QgsVectorLayer(PROFILE_LINES_PATH, "profiles", "ogr") assert layer.isValid() return PreCourlisFileLine(layer)
def setLayer(self, layer): self.beginResetModel() self.file = PreCourlisFileLine(layer) self.endResetModel()
def section(self): request = QgsFeatureRequest() request.setLimit(1) feature = next(self.layer.getFeatures(request)) return PreCourlisFileLine(self.layer).section_from_feature(feature)
processing.run( "precourlis:import_tracks", { "TRACKS": TRACKS_PATH, "AXIS": AXIS_PATH, "FIRST_POS": 0.0, "NAME_FIELD": "nom_profil", "DISTANCE": 100.0, "DEM": DEM_PATH, "OUTPUT": PROFILE_LINES_PATH, }, ) layer = QgsVectorLayer(PROFILE_LINES_PATH, "profile_lines", "ogr") assert layer.isValid() copyfile( PROFILE_LINES_PATH, os.path.join(DATA_PATH, "input", "profiles_lines_zero_layers.geojson")) layer.startEditing() PreCourlisFileLine(layer).add_sedimental_layer("Layer1", "zfond", -1) layer.commitChanges() processing.run( "precourlis:lines_to_points", { "INPUT": PROFILE_LINES_PATH, "OUTPUT": PROFILE_POINTS_PATH, }, )
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) axis = self.parameterAsSource(parameters, self.AXIS, context) self.first_section_abs_long = self.parameterAsDouble( parameters, self.FIRST_SECTION_ABS_LONG, context) if parameters.get(self.FIRST_AXIS_POINT_ABS_LONG, None) is None: self.first_axis_point_abs_long = None else: self.first_axis_point_abs_long = self.parameterAsDouble( parameters, self.FIRST_AXIS_POINT_ABS_LONG, context) group_field = self.parameterAsString(parameters, self.GROUP_FIELD, context) order_field = self.parameterAsString(parameters, self.ORDER_FIELD, context) self.axis = next(axis.getFeatures()) if axis else None request = QgsFeatureRequest() request.addOrderBy('"{}"'.format(group_field), True, True) file = PreCourlisFileLine(source) fields = PreCourlisFileLine.base_fields() for layer in file.layers(): fields.append(QgsField(layer, QVariant.String, len=100000)) (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, source.sourceCrs(), ) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) total = 100.0 / source.featureCount() if source.featureCount() else 0 features = source.getFeatures(request) group = None point_features = [] sec_id = 0 abs_long_offset = 0 for current, point_feature in enumerate(features): # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): break if current == 0: if self.first_axis_point_abs_long is not None: abs_long_offset = self.first_axis_point_abs_long else: abs_long_offset = (self.first_section_abs_long - point_feature.attribute("abs_long")) if group is not None and point_feature.attribute( group_field) != group: sec_id += 1 sink.addFeature( self.line_feature_from_points( sorted(point_features, key=lambda f: f.attribute(order_field)), sec_id, point_features[0].attribute("abs_long") + abs_long_offset, ), QgsFeatureSink.FastInsert, ) group = None point_features = [] group = point_feature.attribute(group_field) point_features.append(point_feature) # Update the progress bar feedback.setProgress(int(current * total)) if group is not None: sec_id += 1 sink.addFeature( self.line_feature_from_points( sorted(point_features, key=lambda f: f.attribute(order_field)), sec_id, point_features[0].attribute("abs_long") + abs_long_offset, ), QgsFeatureSink.FastInsert, ) group = None return {self.OUTPUT: dest_id}