QGIS自定义地图工具

2023-11-12

官方示例

首先看一下官方文档中的矩形工具源码:

class RectangleMapTool(QgsMapToolEmitPoint):
    def __init__(self, canvas):
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)
        self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)
        self.rubberBand.setColor(QColor(255, 0, 0, 100))
        self.rubberBand.setWidth(1)
        self.reset()

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(True)

    def canvasPressEvent(self, e):
        self.startPoint = self.toMapCoordinates(e.pos())
        self.endPoint = self.startPoint
        self.isEmittingPoint = True
        self.showRect(self.startPoint, self.endPoint)

    def canvasReleaseEvent(self, e):
        self.isEmittingPoint = False
        r = self.rectangle()
        if r is not None:
            print("Rectangle:", r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum())

    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return

        self.endPoint = self.toMapCoordinates(e.pos())
        self.showRect(self.startPoint, self.endPoint)

    def showRect(self, startPoint, endPoint):
        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
        if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
            return

        point1 = QgsPointXY(startPoint.x(), startPoint.y())
        point2 = QgsPointXY(startPoint.x(), endPoint.y())
        point3 = QgsPointXY(endPoint.x(), endPoint.y())
        point4 = QgsPointXY(endPoint.x(), startPoint.y())

        self.rubberBand.addPoint(point1, False)
        self.rubberBand.addPoint(point2, False)
        self.rubberBand.addPoint(point3, False)
        self.rubberBand.addPoint(point4, True)  # true to update canvas
        self.rubberBand.show()

    def rectangle(self):
        if self.startPoint is None or self.endPoint is None:
            return None
        elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == self.endPoint.y():
            return None

        return QgsRectangle(self.startPoint, self.endPoint)

    def deactivate(self):
        super(RectangleMapTool, self).deactivate()
        self.deactivated.emit()
        self.reset()

文档中的有些语法还是QGIS2的写法,比如QGis.Polygon在QGIS3中应该用QgsWkbTypes.PolygonGeometry、QgsPoint在QGIS3中对应QgsPointXY,这里我已经做了更改。

源码解析

  • 首先继承QgsMapToolEmitPoint类
  • 重写canvasPressEvent、canvasReleaseEvent、canvasMoveEvent
  • 使用QgsRubberBand渲染绘制的图形(支持PolygonGeometry、PointGeometry、LineGeometry)

具体思路是当鼠标按压画布时,触发canvasPressEvent,得到第一个点self.startPoint,鼠标拖动后触发canvasMoveEvent,得到第二个点,使用这两个点绘制一个矩形,鼠标释放时触发canvasReleaseEvent,结束绘制。

注:每次绘制前都要清除之前的图形,否则会出现拖影,使用rubberBand的reset方法:

self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)

实现多边形绘制

QGIS自带的矢量编辑工具中已经有多边形绘制工具,如下:

接下来我们添加一个自动垂直绘制的功能,源码如下:

class PolygonMapTool(QgsMapToolEmitPoint):
    def __init__(self, canvas):
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)
        self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)
        self.rubberBand.setColor(QColor(255, 0, 0, 100))
        self.rubberBand.setWidth(1)
        self.reset()

    def reset(self):
        self.is_start = False  # 开始绘图
        self.is_vertical = False  # 垂直画线
        self.cursor_point = None
        self.points = []
        self.rubberBand.reset(True)

    def canvasPressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.points.append(self.cursor_point)
            self.is_start = True
        elif event.button() == Qt.RightButton:
            # 右键结束绘制
            if self.is_start:
                self.is_start = False
                self.cursor_point = None
                self.show_polygon()
                self.points = []
            else:
                pass
        else:
            pass

    def canvasMoveEvent(self, event):
        self.cursor_point = event.mapPoint()
        if not self.is_start:
            return
        self.show_polygon()

    def show_polygon(self):
        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)  # 防止拖影
        first_point = self.points[0]
        last_point = self.points[-1]
        self.rubberBand.addPoint(first_point, False)
        for point in self.points[1:-1]:
            self.rubberBand.addPoint(point, False)
        if self.cursor_point:
            self.rubberBand.addPoint(QgsPointXY(last_point.x(), last_point.y()), False)
        else:
            self.rubberBand.addPoint(QgsPointXY(last_point.x(), last_point.y()), True)
            self.rubberBand.show()
            return
            # 光标所在点
        if self.is_vertical and len(self.points) >= 2:
            countdown_second_point = self.points[-2]
            cursor_point_x = self.cursor_point.x()
            cursor_point_y = self.cursor_point.y()
            diff_x = int(math.fabs(last_point.x() - countdown_second_point.x()))
            diff_y = int(math.fabs(last_point.y() - countdown_second_point.y()))
            if diff_x > diff_y:
                # 最后一条线的x,y差值比较
                cursor_point_x = equation(countdown_second_point.x(), countdown_second_point.y(), last_point.x(),
                                          last_point.y(), self.cursor_point.y(), solve_type='x')
            else:
                cursor_point_y = equation(countdown_second_point.x(), countdown_second_point.y(), last_point.x(),
                                          last_point.y(), self.cursor_point.x(), solve_type='y')
            _cursor_point = QgsPointXY(cursor_point_x, cursor_point_y)
            self.cursor_point = _cursor_point

        self.rubberBand.addPoint(self.cursor_point, True)
        self.rubberBand.show()

    def deactivate(self):
        super(PolygonMapTool, self).deactivate()
        self.deactivated.emit()
        self.reset()

其中equation算法是自己实现的,原理就是直角三角形直角边平方和等于斜边平方(a^{2}+b^{2}=c^{2}),自行理解,使用到了sympy库(第一次使用,好强大。。。),源码如下:

def equation(x1, y1, x2, y2, f, solve_type='x'):
    '''
    已知两点坐标和第三个点x或y,求对应的y或x
    :param x1:第一个点x坐标
    :param y1:第一个点y坐标
    :param x2:第二个点x坐标
    :param y2:第二个点y坐标
    :param f:x或y的坐标值
    :param solve_type:'x'或'y',默认'x'
    :return:
    '''
    if solve_type == 'x':
        x = symbols('x')
        y = f
        s = solve((x - x1) ** 2 + (y - y1) ** 2 - (x2 - x1) ** 2 - (y2 - y1) ** 2 - (x - x2) ** 2 - (y - y2) ** 2, x)
        return s[0]
    else:
        y = symbols('y')
        x = f
        s = solve((x - x1) ** 2 + (y - y1) ** 2 - (x2 - x1) ** 2 - (y2 - y1) ** 2 - (x - x2) ** 2 - (y - y2) ** 2, y)
        return s[0]

截图

矩形

多边形

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QGIS自定义地图工具 的相关文章

  • openSUSE Leap 15.4 防火墙开启与关闭,端口开启与关闭

    openSUSE Leap 15 4 防火墙开启与关闭 端口开启与关闭 1 查看防火墙状态 开启 关闭 查看防火墙当前状态 rcfirewalld status 示例结果 当前状态为开启中 开启防火墙 rcfirewalld start 关
  • 安卓APP_ 布局(2) —— RelativeLayout相对布局

    摘自 安卓APP 布局 2 RelativeLayout相对布局 作者 丶PURSUING 发布时间 2021 04 05 20 19 54 网址 https blog csdn net weixin 44742824 article de
  • 解决Nginx部署Vue项目第一次访问正常第二次访问404的问题

    location add header Access Control Allow Origin root data static project dist try files uri uri index html index index h
  • DNS资源记录详解

    资源记录 resourse record 就是域名服务器保存的记录 也是解析器请求的内容 资源记录保存在zone文件中 域 domain 以 www baidu com 为例 com是一个域 baidu com是一个域 他是com域的一个子
  • 啥是ddl?

    我们在安装软件的时候 经常出现某一个dll文件丢失 无法安装 让人头痛不已 那么到底啥是ddl 还是有度娘好呀 下面是百度百科的词条 数据库模式定义语言DDL Data Definition Language 是用于描述数据库中要存储的现实
  • NFTScan NFT API 在 NFTFi 开发中的应用

    NFTFi 是 NFT 和 Finance 的缩写 旨在 增加 NFT 流动性 提供现金流 NFTFi 是为 NFT 提供金融实用性的去中心化协议和应用程序的新兴生态系统 及使用 NFT 作为基础层在其上建设经济基础设施 在实践中 NFTF
  • 7.7 SHEIN希音笔试

    投的Java工程师岗位 题型为10道单选 5道多选 1道算法题 1道sql题 时长1h 选择题 1 spring事物的传播特性 错误的是 PROPAGATION REQUIRED 支持当前事务 如果当前没有事务 就抛出异常 应该是 支持当前
  • wireshark流量分析网络安全

    目录 前言 题目1 题目2 题目3 题目4 题目5 题目6 题目7 题目8 前言 这是关于一篇wireshark分析数据包的文章 主要是网络安全里的应用 讲述了wireshark的使用方法 主要使用的事wrieshark里的追踪流 欢迎大家
  • elementUI 下拉框select 为多选 赋值回显问题

    下拉框为多选时 绑定的值为数组状态 所以点击编辑赋值时 需要处理为数组 但我遇到的问题是 可以赋值 但不是想要的结果 我想要显示label 但显示的是value 不想得到的效果 想要的是 在赋值后面加 map Number 就可以对号入座了
  • 【翻译】 使用 SFrame 进行可靠的用户空间堆栈跟踪

    请考虑订阅 LWN订阅是 LWN net 的生命线 如果您喜欢这些内容并希望看到更多 您的订阅将有助于确保 LWN 继续蓬勃发展 请访问此页面加入我们 让 LWN 继续在网络上传播 作者 Jonathan Corbet 2023 年 5 月
  • html页面传list,后端list集合中的数据传递到前台HTML中显示(表格形式)

    关键字 web项目中前后台数据传递问题 在学习web项目的过程中 我们肯定会遇到前后台数据交换问题 这个问题我也思考了很久 今天借此总结一下 由于博主水平有限 如有不当之处 还请大家多多指正 废话不所说进入正题 一 HTML页面通过ajax
  • 《Thinking_in_java_4th》持续输出中.......

    目录标题 一 文章目录 二 源码链接 一 文章目录 Java设计者们说过 设计这门语言的灵感主要来自C Java编程思想 第 2章 一切都是对象 Java编程思想 第 4章 控制执行流程 Java编程思想 第14章 类型信息 Java编程思
  • 排名前 16 的 Java 工具类

    原链接 https mp weixin qq com s s6IfovcE LGlZJxIKfT dw 目录 org apache commons io IOUtils org apache commons io FileUtils org
  • 磁耦隔离与传统隔离的区别

    磁耦隔离与传统隔离的区别 传统隔离技术 传统的隔离方式有哪些 这里有三种通常的隔离技术 光电隔离 变压器隔离 磁耦是芯片级变压器隔离技术 电容隔离 在体积 成本 性能等各方面都有优缺点 传统的隔离方式是光电隔离 什么是光耦 什么是光隔离 光
  • Qt GraphicsView框架中实现多个item之间的层次调整功能

    目的 要实现GraphicsView中多个item之间的层次调整功能 即 选中的item可以实现 移动至顶层 移动至底层 上移一层 下移一层 等功能 之前盲目地认为Qt API会提供 获取与之相邻的sibling item 类似这样的接口
  • 2023全新SF授权系统源码 V3.7全开源无加密版本,亲测可用

    2023全新SF授权系统源码 V3 7全开源无加密版本 网站搭建很简单 大致看来一下应该域名解析后上传源码解压 访问域名 install就能直接安装 程序功能简介 1 盗版入库 26种 2 快捷登录 3 采用layuiadmin框架 4 易

随机推荐

  • ASP.NET core MVC动作过滤器执行顺序

    using Microsoft AspNetCore Mvc Filters using System using System Threading Tasks namespace dotnet core Filter public cla
  • 两片74161实现60进制_74LS161设计60进制计数器-数电课程设计

    计数器是一个用以实现计数功能的时序部件 它不仅可用来及脉冲数 还常用作数子系统的定时 分频和执行数字运算以及其它特定的逻辑功能 计数器种类很多 按构成计数器中的各触发器是否使用一个时钟脉冲源来分 有同步计数器和异步计数器 根据计数制的不同
  • js怎么改变样式中的属性值

    可以使用JavaScript来改变HTML元素的样式属性值 具体方法如下 通过id属性获取要修改的元素对象 var obj document getElementById element id 修改元素的样式属性值 obj style pr
  • 错误: 至少有一个需要的隐性或转发依赖函数没找到。_【翻译】自动柯里化Rust函数...

    原文标题 Auto currying Rust Functions 原文链接 https peppe rs posts auto currying rust functions 公众号 Rust碎碎念 本文包含Rust中过程宏 proced
  • 《疯狂Java讲义》读书笔记(一):面向对象,数据类型和运算符,流程控制与数组

    序言 疯狂Java讲义 这本书深入介绍了Java编程的相关方面 全书内容覆盖了Java的基本语法结构 Java的面向对象特征 Java集合框架体系 Java泛型 异常处理 JavaGUI编程 JDBC数据库编程 Java注释 Java的IO
  • Yii Framework 开发教程(6) CComponent 组件

    在Hangman中定义的GameController使用到一些属性word 可以使用 this gt word 的格式来读写这个属性 但实际上在GameController对应到这个属性的方法为 php view plain copy pr
  • 机器学习之集成学习

    一 介绍 集成学习 Ensemble Learning 是一种机器学习技术 通过结合多个学习器 例如决策树 神经网络 支持向量机等 的预测结果 来达到更好的分类或回归预测性能 集成学习可以通过降低模型的方差 提高模型的稳定性和泛化性能 从而
  • greenDao官网

    http greenrobot org greendao documentation
  • 基于Keras实战项目-猫狗熊猫分类大战

    欢迎来到本博客 本次博客内容将继续讲解关于OpenCV的相关知识 作者简介 目前计算机研究生在读 主要研究方向是人工智能和群智能算法方向 目前熟悉深度学习 keras pytorch yolo python网页爬虫 机器学习 计算机视觉 O
  • 三个月华为od工作感受:关于转正,身份和适合谁

    三个月对Od认识的变化 关于华为Od在网上已经被讨论得很多了 在各大IT求职论坛中Od都成为流量密码了 一旦有人谈起od评论区就会开吵 这几个月中我对Od的认识也是从浅入深 对Od的态度也在变化 今年 2022年 4月份的时候那时候我刚入职
  • Redis实现商品秒杀

    随着互联网的发展和消费者的需求越来越高 商品的销售也变得越来越激烈 而对于商家来说 最直观的解决方式即为促销活动 然而 促销活动也会引发一定的风险 如果处理得不当 可能会出现 抢购 活动中的库存不足等问题 本文将利用Redis实现商品秒杀
  • 离线部署node项目、nuxt项目

    如果你的目标系统不具备互联网访问功能 或者具有严格的防火墙管控 并且你想部署一个node应用 那么以下内容可能对你有些帮助 准备好源代码工程 准备好一个具有相同node环境且具备访问互联网功能的同种系统 以下称NetOS 将源代码工程目录拷
  • 一个简单的登录注册界面流程介绍

    登录页面实现 其他页面的实现可以到github上克隆下来 login interface login server 一 用户登录 1 密码登录 流程 用户输入密码 表单使用正则验证用户名和密码格式 点击登录 对密码进行加密 并发送登录验证请
  • LeetCode每日一练 —— 88. 合并两个有序数组

    前言 Wassup guys 我是Edison 今天是 LeetCode 上的 leetcode 88 合并两个有序数组 Let s get it 文章目录 1 题目分析 2 题目图解 思路一 思路二 3 代码实现 1 题目分析 给你两个按
  • ENU、EPSG、ECEF坐标系科普(三维重建)

    科普一 ENU和EPSG实际上代表了两个不同的概念 这两者并不是直接对比的 1 ENU坐标系 ENU坐标系是一种本地切面坐标系 用于表示与地理位置相关的空间数据 在ENU坐标系中 E代表东 East N代表北 North U代表上 Up 它
  • LeetCode 406. Queue Reconstruction by Height 解题报告

    LeetCode 406 Queue Reconstruction by Height 解题报告 题目描述 Suppose you have a random list of people standing in a queue Each
  • 算法—反转链表

    题目 实现单链表的逆转函数 输入一个链表 反转链表后 返回翻转之后的链表 分析 利用三个指针 head node nodeNext node指向当前结点 head指向当前结点的前一个结点 nodeNext指向当前结点的后一个结点 先将hea
  • 浏览器动态显示服务器日志,基于 websocket 实现远程实时日志 在浏览器中查看设备的运行日志...

    本文介绍一个基于websocket实现的远程实时日志系统 可以通过浏览器查看远程移动设备的实时运行日志 系统由三个部分组成 1 服务器 与移动设备和浏览器建立websocket连接 将移动设备websocket上读取的实时日志转发到对应的浏
  • 每日算法-回文链表

    题目 请判断一个链表是否为回文链表 示例 1 输入 1 gt 2 输出 false 示例 2 输入 1 gt 2 gt 2 gt 1 输出 true 进阶 你能否用 O n 时间复杂度和 O 1 空间复杂度解决此题 解法 思路一 先把链表的
  • QGIS自定义地图工具

    官方示例 首先看一下官方文档中的矩形工具源码 class RectangleMapTool QgsMapToolEmitPoint def init self canvas self canvas canvas QgsMapToolEmit