class NorthArrow(QgsMapCanvasItem): def __init__(self, proj, canvas): super(NorthArrow, self).__init__(canvas) self.proj = proj self.canvas = canvas self.map_pos = QgsPointXY(0.0, 0.0) self.svg = QSvgRenderer(":/resources/arrow.svg") self.size = QSize(42, 64) self.corner = 1 def paint(self, painter, xxx, xxx2): """Paint north arrow on painter""" if self.svg.isValid(): pos = self.set_position(self.corner, painter.device().width(), painter.device().height()) rotation = QgsBearingUtils.bearingTrueNorth( self.canvas.mapSettings().destinationCrs(), self.canvas.mapSettings().transformContext(), self.canvas.extent().center()) rotation += self.canvas.rotation() painter.save() painter.rotate(-rotation) # To translate correctly painter.translate(pos.x(), pos.y()) painter.rotate( rotation ) # To rotate north arrow along with canvas, always pointing north # do the drawing, and draw a smooth north arrow even when rotated rectangle = QRectF(-self.size.width() / 2, -self.size.height() / 2, self.size.width(), self.size.height()) self.svg.render(painter, rectangle) painter.restore() def set_position(self, corner, width, height): """ Returns the position of the specified corner with a concrete width and height :param corner: can be 1, 2, 3 or 4. top left, top right, bot left and bot right respectively :param width: width of the paint space :param height: height of the paint space :return: QgsPointXY of the specified corner """ if corner == 1: # top left corner return QgsPointXY(0 + self.size.height() / 2, 0 + self.size.height() / 2) elif corner == 2: # top right corner return QgsPointXY(width - self.size.height() / 2, 0 + self.size.height() / 2) elif corner == 3: # bottom left corner return QgsPointXY(0 + self.size.height() / 2, height - self.size.height() / 2) elif corner == 4: # bottom right corner return QgsPointXY(width - self.size.height() / 2, height - self.size.height() / 2)
def _draw_plate(self, background_path, font_path, target_template, symbols): if background_path.suffix == '.svg': renderer = QSvgRenderer(str(background_path)) assert renderer.isValid() width = target_template['width'] w, h = renderer.viewBox().width(), renderer.viewBox().height() aspect_ratio = w / h w, h = width, width / aspect_ratio image = QImage(w, h, QImage.Format_ARGB32) image.fill(Qt.transparent) # workaround for clean image painter = QPainter(image) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) renderer.render(painter) else: image = QPixmap(str(background_path)) assert not image.isNull() width = target_template['width'] w, h = image.width(), image.height() aspect_ratio = w / h w, h = width, width / aspect_ratio image.scaled(w, h, Qt.KeepAspectRatio) image = image.toImage() painter = QPainter(image) font = get_font(font_path) pen = QPen(QColor.fromRgb(int(target_template['font_color'], base=16)), 2, Qt.SolidLine) painter.setPen(pen) for _, sym in symbols.items(): rect = QRect(sym.x, sym.y, sym.w, sym.h) font.setPointSizeF(sym.font_size) painter.setFont(font) painter.drawText(rect, Qt.AlignCenter, sym.sym) painter.end() return qimage_to_nparr(image)
def drawGraphSymbo(self, renderContext): painter = renderContext.painter() mapToPixel = renderContext.mapToPixel() crs = QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem.PostgisCrsId) xform = QgsCoordinateTransform(self.crs(), crs, QgsProject.instance()) xml = self.symbo_reader.read(utils.resolve('render.xml')) pen = QPen() brush = QBrush() diameter = 0 nodes = self.readGraph().getListNode() edges = self.readGraph().getListEdge() #Dessin des arcs for edge in edges: start = nodes[edge.getNodeA()] end = nodes[edge.getNodeB()] #Gestion des configurations de propriétés graphiques """ Le fichier xml correspond à un dictionnaire de dictionnaire de rules: graphrenderer = {"node_rules": GraphRules, "edge_rules": GraphRules} """ if 'edge_rules' in xml: #Récupération de la valeur par défaut de la rule du xml #Cas où il n'y a pas de ruleunit pour la rule en question for key, val in xml['edge_rules'].items(): rule_prop = key graphRule = val attribute_name = graphRule.getName() default_prop = graphRule.getDefaultProp().value() list_ruleunit = graphRule.getRule() if (len(list_ruleunit) == 0): if (rule_prop == "fgcolor"): color = default_prop pen.setColor(color) elif (rule_prop == "linewidth"): variant = default_prop pen.setWidth(variant) elif (rule_prop == "linestyle"): variant = default_prop pen.setStyle(Qt.PenStyle(variant)) #Cas où il y a une ou plusieurs ruleunit dans le xml dict_features = edge.getFeatures() for edge_att_name, edge_att_val in dict_features.items(): if 'edge_rules' in xml: for key, val in xml['edge_rules'].items(): rule_prop = key graphRule = val attribute_name = graphRule.getName() default_prop = graphRule.getDefaultProp().value() list_ruleunit = graphRule.getRule() if (attribute_name == edge_att_name): if (len(list_ruleunit) != 0): #Permet d'affecter la prop_val à l'attr_val correspondant du fichier graph flag = 0 for index in range(len(list_ruleunit)): attribute_val = list_ruleunit[ index].attr_val.value() prop_val = list_ruleunit[ index].prop_val.value() if (str(edge_att_val) == str(attribute_val) ): if (rule_prop == "fgcolor"): color = prop_val pen.setColor(color) elif (rule_prop == "linewidth"): variant = prop_val pen.setWidth(variant) elif (rule_prop == "linestyle"): variant = prop_val pen.setStyle(Qt.PenStyle(variant)) flag = 1 break #Affecte la valeur par défaut à toutes les autres attr_val n'étant pas présentent dans la liste des rule_unit if (flag == 0): if (rule_prop == "fgcolor"): color = default_prop pen.setColor(color) elif (rule_prop == "linewidth"): variant = default_prop pen.setWidth(variant) elif (rule_prop == "linestyle"): variant = default_prop pen.setStyle(Qt.PenStyle(variant)) painter.setPen(pen) painter.setBrush(brush) pointA = mapToPixel.transform( xform.transform((QgsPointXY(int(start.getX()), int(start.getY()))))) pointB = mapToPixel.transform( xform.transform((QgsPointXY(int(end.getX()), int(end.getY()))))) painter.drawLine(pointA.x(), pointA.y(), pointB.x(), pointB.y()) #Même chose pour le dessin des noeuds, avec des rule_prop différentes for id in nodes: x = nodes[id].getX() y = nodes[id].getY() if 'node_rules' in xml: #Récupération de la valeur par défaut de la rule du xml #Cas où il n'y a pas de ruleunit pour la rule en question for key, val in xml['node_rules'].items(): rule_prop = key graphRule = val attribute_name = graphRule.getName() default_prop = graphRule.getDefaultProp().value() list_ruleunit = graphRule.getRule() if (len(list_ruleunit) == 0): if (rule_prop == "bgcolor"): color = default_prop brush.setColor(color) brush.setStyle(Qt.SolidPattern) elif (rule_prop == "fgcolor"): color = default_prop pen.setColor(color) elif (rule_prop == "linewidth"): variant = default_prop pen.setWidth(variant) elif (rule_prop == "size"): diameter = default_prop elif (rule_prop == "shape"): shape = default_prop #Cas où il y a une ou plusieurs ruleunit dans le xml dict_features = nodes[id].getFeatures() for node_att_name, node_att_val in dict_features.items(): if 'node_rules' in xml: for key, val in xml['node_rules'].items(): rule_prop = key graphRule = val attribute_name = graphRule.getName() default_prop = graphRule.getDefaultProp().value() list_ruleunit = graphRule.getRule() if (attribute_name == node_att_name): if (len(list_ruleunit) != 0): #Permet d'affecter la prop_val à l'attr_val correspondant du fichier graph flag = 0 for index in range(len(list_ruleunit)): attribute_val = list_ruleunit[ index].attr_val.value() prop_val = list_ruleunit[ index].prop_val.value() if (str(node_att_val) == str(attribute_val) ): if (rule_prop == "bgcolor"): color = prop_val brush.setColor(color) brush.setStyle(Qt.SolidPattern) elif (rule_prop == "fgcolor"): color = prop_val pen.setColor(color) elif (rule_prop == "linewidth"): variant = prop_val pen.setWidth(variant) elif (rule_prop == "size"): diameter = prop_val elif (rule_prop == "shape"): shape = prop_val flag = 1 break #Affecte la valeur par défaut à toutes les autres attr_val n'étant pas présentent dans la liste des rule_unit if (flag == 0): if (rule_prop == "bgcolor"): color = default_prop brush.setColor(color) brush.setStyle(Qt.SolidPattern) elif (rule_prop == "fgcolor"): color = default_prop pen.setColor(color) elif (rule_prop == "linewidth"): variant = default_prop pen.setWidth(variant) elif (rule_prop == "size"): diameter = default_prop elif (rule_prop == "shape"): shape = default_prop painter.setPen(pen) painter.setBrush(brush) point = mapToPixel.transform(xform.transform((QgsPointXY(x, y)))) #Différentes formes possibles pour la rule shape if (shape != ""): if (shape == "circle"): painter.drawEllipse(point.x() - diameter / 2, point.y() - diameter / 2, diameter, diameter) elif (shape == "rectangle"): painter.drawRect(point.x() - diameter / 2, point.y() - diameter / 2, diameter, diameter) elif (shape == "cross"): painter.drawLine(point.x() - diameter / 2, point.y() - diameter / 2, point.x() + diameter / 2, point.y() + diameter / 2) painter.drawLine(point.x() - diameter / 2, point.y() + diameter / 2, point.x() + diameter / 2, point.y() - diameter / 2) else: #Draw SVG Node svgr = QSvgRenderer(shape) if (svgr.isValid()): svgr.render( painter, QRectF(point.x() - diameter / 2, point.y() - diameter / 2, diameter, diameter)) else: painter.drawEllipse(point.x() - diameter / 2, point.y() - diameter / 2, diameter, diameter) else: #default is circle painter.drawEllipse(point.x() - diameter / 2, point.y() - diameter / 2, diameter, diameter)
class CanvasMarker(QgsMapCanvasItem): def __init__(self, canvas, color, svg=None, width=0.0, length=0.0, orientation=True, marker_mode=False, config=None): super(CanvasMarker, self).__init__(canvas) self.canvas = canvas self.config = config self.size = 20 self.changing_scale = 400 self.marker_mode = marker_mode self.color = color self.pointbrush = QBrush(self.color) self.pointpen = QPen(Qt.black) self.pointpen.setWidth(1) self.map_pos = QgsPointXY(0.0, 0.0) self.heading = 0 self.width = width self.length = length self.orientation = orientation if svg is not None: # set crs and ellipsoid crs = self.canvas.mapSettings().destinationCrs() self.distance_calc = QgsDistanceArea() self.distance_calc.setSourceCrs(crs, QgsProject.instance().transformContext()) self.distance_calc.setEllipsoid(crs.ellipsoidAcronym()) self.svg = QSvgRenderer(svg) else: self.svg = None def set_size(self, size): self.size = size def set_color(self, color): self.color = color def set_marker_mode(self, canvas_marker_mode): self.marker_mode = canvas_marker_mode def paint(self, painter, xxx, xxx2): pos = self.toCanvasCoordinates(self.map_pos) self.setPos(pos) if self.marker_mode: mode = self.config.csettings["canvas_marker_mode"] if mode == 'auto': self.set_size(20) transform = self.canvas.getCoordinateTransform() start_point = transform.toMapCoordinates(pos.x(), pos.y()) map_end_point_width = endpoint(start_point, self.width, 90 + math.degrees(self.heading)) map_end_point_length = endpoint(start_point, self.length, math.degrees(self.heading)) # to canvas coordinates canvas_end_point_width = self.toCanvasCoordinates(map_end_point_width) canvas_end_point_length = self.toCanvasCoordinates(map_end_point_length) width = magnitude(self.toCanvasCoordinates(start_point), QgsPointXY(canvas_end_point_width)) height = magnitude(self.toCanvasCoordinates(start_point), QgsPointXY(canvas_end_point_length)) if width < self.size and height < self.size: self.changing_scale = self.canvas.scale() else: self.changing_scale = self.canvas.scale() * 2 elif mode == 'manual': self.changing_scale = self.config.csettings["canvas_marker_scale"] else: self.changing_scale = 400 if self.svg is None or self.canvas.scale() >= self.changing_scale: self.set_size(20) half_size = self.size / 2.0 rect = QRectF(0 - half_size, 0 - half_size, self.size, self.size) painter.setRenderHint(QPainter.Antialiasing) self.pointpen.setColor(Qt.black) self.pointpen.setWidth(2) self.pointbrush.setColor(self.color) painter.setBrush(self.pointbrush) painter.setPen(self.pointpen) y = 0 - half_size x = rect.width() / 2 - half_size line = QLine(x, y, x, rect.height() - half_size) y = rect.height() / 2 - half_size x = 0 - half_size line2 = QLine(x, y, rect.width() - half_size, y) # Arrow p = QPolygonF() p.append(QPoint(0 - half_size, 0)) p.append(QPoint(0, -self.size)) x = rect.width() - half_size p.append(QPoint(x, 0)) p.append(QPoint(0, 0)) offsetp = QPolygonF() offsetp.append(QPoint(0 - half_size, 0)) offsetp.append(QPoint(0, -self.size)) x = rect.width() - half_size offsetp.append(QPoint(x, 0)) offsetp.append(QPoint(0, 0)) painter.save() painter.rotate(math.degrees(self.heading) + self.canvas.rotation()) if self.orientation: path = QPainterPath() path.addPolygon(p) painter.drawPath(path) painter.restore() painter.drawEllipse(rect) painter.drawLine(line) painter.drawLine(line2) # svg valid elif self.svg is not None and self.svg.isValid(): # get rotation rotation = self.canvas.rotation() painter.save() transform = self.canvas.getCoordinateTransform() start_point = transform.toMapCoordinates(pos.x(), pos.y()) map_end_point_width = endpoint(start_point, self.width, 90 + math.degrees(self.heading)) map_end_point_length = endpoint(start_point, self.length, math.degrees(self.heading)) # to canvas coordinates canvas_end_point_width = self.toCanvasCoordinates(map_end_point_width) canvas_end_point_length = self.toCanvasCoordinates(map_end_point_length) width = magnitude(self.toCanvasCoordinates(start_point), QgsPointXY(canvas_end_point_width)) height = magnitude(self.toCanvasCoordinates(start_point), QgsPointXY(canvas_end_point_length)) if width > height: self.set_size(width) else: self.set_size(height) if width != 0 and height != 0: center_x = width / 2.0 center_y = height / 2.0 # work out how to shift the image so that it rotates # properly about its center # ( x cos a + y sin a - x, -x sin a + y cos a - y) myradians = math.radians(rotation + math.degrees(self.heading)) xshift = int(((center_x * math.cos(myradians)) + (center_y * math.sin(myradians))) - center_x) yshift = int(((-center_x * math.sin(myradians)) + (center_y * math.cos(myradians))) - center_y) painter.translate(-width / 2, -height / 2) painter.rotate(math.degrees(self.heading) + self.canvas.rotation()) self.svg.render(painter, QRectF(xshift, yshift, width, height)) painter.restore() def boundingRect(self): size = self.size * 2 return QRectF(-size, -size, 2.0 * size, 2.0 * size) def updatePosition(self): self.set_center(self.map_pos, self.heading) def set_center(self, map_pos, heading): self.heading = heading self.map_pos = map_pos self.setPos(self.toCanvasCoordinates(self.map_pos)) def set_width(self, width): self.width = width def set_length(self, length): self.length = length