Qt Qml Map-地图绘制点与圆的切线

news2024/10/8 17:48:13

 基于此源码替换 main.qml 文件
https://download.csdn.net/download/qq_38159549/89860109icon-default.png?t=O83Ahttps://download.csdn.net/download/qq_38159549/89860109

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.3
import QtLocation       5.3
import QtPositioning    5.3

Window {
    id: root
    visible: true
    height: 700
    width: 1000

    property var center01: QtPositioning.coordinate(25.00856, 102.68702)
    property real radius01: 20

    property var center02: QtPositioning.coordinate(25.00956, 102.68902)
    property real radius02: 30

    /**
     * @brief 根据两个圆的圆心坐标、半径、顺时针或逆时针方向绘制内外公切线。
     *
     * 此函数通过计算两个圆的圆心距离和相对方向,判断是绘制内公切线还是外公切线,并在地图上绘制对应的切线。切线分为内切线和外切线,方向可以是顺时针或逆时针。
     *
     * @param c1 圆1的圆心坐标(QtPositioning.coordinate类型)
     * @param r1 圆1的半径
     * @param direction1 圆1的方向,true表示顺时针,false表示逆时针
     * @param c2 圆2的圆心坐标(QtPositioning.coordinate类型)
     * @param r2 圆2的半径
     * @param direction2 圆2的方向,true表示顺时针,false表示逆时针
     *
     * @details
     * 1. 首先比较两个圆的半径,确定哪个圆较大并作为 largeCircle,较小的圆作为 smallCircle。
     * 2. 计算两个圆心之间的方位角和距离 (centerDist)。
     * 3. 如果圆心距离小于或等于两圆半径之差,则不会绘制切线,提示 "Error: 圆心距离小于半径。",并清除所有切线。
     * 4. 判断两个圆的方向是否一致 (directionMatch):
     *     - 如果方向一致(顺时针-顺时针或逆时针-逆时针),则绘制外公切线。
     *     - 如果方向不一致(顺时针-逆时针或逆时针-顺时针),则绘制内公切线。
     * 5. 对于外切线:计算两个圆的切点并绘制对应的切线路径。
     * 6. 对于内切线:计算两个圆的切点并绘制对应的切线路径。
     */
    function drawTangentCircleToCircle(c1, r1, direction1, c2, r2, direction2) {
        // 确定较大和较小的圆
        var largeCircle, smallCircle, largeRadius, smallRadius, largeDirection, smallDirection;
        if (r1 >= r2) {
            largeCircle = c1;
            smallCircle = c2;
            largeRadius = r1;
            smallRadius = r2;
            largeDirection = direction1;
            smallDirection = direction2;
        } else {
            largeCircle = c2;
            smallCircle = c1;
            largeRadius = r2;
            smallRadius = r1;
            largeDirection = direction2;
            smallDirection = direction1;
        }

        // 计算两圆心之间的方位角和距离
        var doubleCircle_azimuth = largeCircle.azimuthTo(smallCircle);
        var centerDist = largeCircle.distanceTo(smallCircle);

        // 确保圆心距离大于半径之差
        if (centerDist <= Math.abs(largeRadius - smallRadius)) {
            console.log("Error: 圆心距离小于半径.");
            // 清空外切线
            crosspointLineItem_ccw.path = [];
            crosspointLineItem_cw.path = [];
            // 清空内切线
            innerTangentLineItem1.path = [];
            innerTangentLineItem2.path = [];
            return;
        }

        // 判断圆1和圆2的顺逆方向是否一致
        var directionMatch = (largeDirection === smallDirection);

        // --------------------- 外切线 ---------------------
        if (directionMatch) {
            console.log("[绘制外切线]", direction1, direction2)
            var angleRadians = Math.acos(Math.abs(largeRadius - smallRadius) / centerDist);
            var angleDegrees = angleRadians * (180 / Math.PI);

            // 第二个圆为顺时针
            if(direction2) {
                var _LeftCrosspoint_cw = largeCircle.atDistanceAndAzimuth(largeRadius, angleDegrees + doubleCircle_azimuth);
                var _RightCrosspoint_cw = smallCircle.atDistanceAndAzimuth(smallRadius, angleDegrees + doubleCircle_azimuth);

                // 绘制外公切线
                crosspointLineItem_cw.path = [_LeftCrosspoint_cw, _RightCrosspoint_cw]
            }
            // 第二个圆为逆时针
            else {
                var _leftCrosspoint = largeCircle.atDistanceAndAzimuth(largeRadius, 90 - angleDegrees + (doubleCircle_azimuth - 90) )
                var _RightCrosspoint = smallCircle.atDistanceAndAzimuth(smallRadius, (180 - angleDegrees - 90) + (doubleCircle_azimuth - 90) )

                // 绘制外公切线
                crosspointLineItem_ccw.path = [_leftCrosspoint, _RightCrosspoint]
            }

            // 清空内切线
            innerTangentLineItem1.path = [];
            innerTangentLineItem2.path = [];
        }
        // --------------------- 内切线 ---------------------
        else {
            console.log("[绘制内切线]", direction1, direction2)
            var innerAngleRadians = Math.acos((largeRadius + smallRadius) / centerDist);
            var innerAngleDegrees = innerAngleRadians * (180 / Math.PI);

            // 内公切线角度
            var innerAngle1 = doubleCircle_azimuth + innerAngleDegrees;
            var innerAngle2 = doubleCircle_azimuth - innerAngleDegrees;

            // 第二个圆为顺时针
            if(direction2) {
                var innerTangentPoint1Large = largeCircle.atDistanceAndAzimuth(largeRadius, innerAngle1);
                var innerTangentPoint1Small = smallCircle.atDistanceAndAzimuth(smallRadius, innerAngle1 + 180);

                // 绘制内公切线
                innerTangentLineItem1.path = [innerTangentPoint1Large, innerTangentPoint1Small];
            }
            // 第二个圆为逆时针
            else {
                var innerTangentPoint2Large = largeCircle.atDistanceAndAzimuth(largeRadius, innerAngle2);
                var innerTangentPoint2Small = smallCircle.atDistanceAndAzimuth(smallRadius, innerAngle2 + 180);

                // 绘制内公切线
                innerTangentLineItem2.path = [innerTangentPoint2Large, innerTangentPoint2Small];
            }

            // 清空外切线
            crosspointLineItem_ccw.path = [];
            crosspointLineItem_cw.path = [];
        }
    }

    /**
     * @brief 计算并绘制给定坐标点与圆的两条切线
     * @param point 坐标点
     * @param circleCenter 圆心坐标
     * @param radius 圆半径
     */
    function drawTangentPointToCircle(point, circleCenter, radius) {
        // 计算点到圆心的距离
        var distance = point.distanceTo(circleCenter);

        // 确保点在圆的外部,才能计算切线
        if (distance <= radius) {
            console.log("错误:坐标点位于圆内或圆上,无法计算切线。");
            tangentLineItem1.path = [];
            tangentLineItem2.path = [];
            return;
        }

        // 余弦定理计算角度 α(弧度)
        var alpha = Math.acos(radius / distance);

        // 将 α 转换为角度
        var alphaDegrees = alpha * (180 / Math.PI);

        // 计算从点到圆心的初始方位角(度)
        var bearingPO = point.azimuthTo(circleCenter); // 单位为度

        // 计算从圆心到切点的方位角
        var bearingOT1 = (bearingPO + 180 - alphaDegrees + 360) % 360;
        var bearingOT2 = (bearingPO + 180 + alphaDegrees) % 360;

        // 计算切点的坐标
        var tangentPoint1 = circleCenter.atDistanceAndAzimuth(radius, bearingOT1);
        var tangentPoint2 = circleCenter.atDistanceAndAzimuth(radius, bearingOT2);

        // 绘制从点到切点的两条切线
        tangentLineItem1.path = [point, tangentPoint1];
        tangentLineItem2.path = [point, tangentPoint2];
    }

    Map {
        id: _map
        anchors.fill: parent
        zoomLevel:                  15
        center:                     QtPositioning.coordinate(25.00856, 102.68702);
        gesture.flickDeceleration:  3000
        plugin: Plugin { name: "Gaode" }

        Component.onCompleted: {
            drawTangentCircleToCircle(center01, radius01, true, center02, radius02, true)
        }

        MouseArea {
            id: mouseArea_measure
            anchors.fill: parent
            onClicked: {
                var _coordinate = _map.toCoordinate(Qt.point(mouse.x, mouse.y))
                console.log("更新第二圆坐标", _coordinate.latitude, _coordinate.longitude)

                center02 = _coordinate

                //drawTangentCircleToCircle(center01, radius01, true, center02, radius02, true)


                drawTangentPointToCircle(center01, center02, radius02, true)
            }
        }

        // *********** 外切线 ***********
        // (逆)
        MapPolyline {
            id: crosspointLineItem_ccw
            line.width: 2
            line.color: 'pink'
             visible: false
        }

        // (顺)
        MapPolyline {
            id: crosspointLineItem_cw
            line.width: 2
            line.color: 'pink'
              visible: false
        }

        // *********** 内切线 ***********
        // 第一条内切线
        MapPolyline {
            id: innerTangentLineItem1
            line.width: 3
            line.color: 'blue'
        }

        // 第二条内切线
        MapPolyline {
            id: innerTangentLineItem2
            line.width: 3
            line.color: 'blue'
        }

        // *********** 点与圆的切线 ***********
        // 第一条内切线
        MapPolyline {
            id: tangentLineItem1
            line.width: 3
            line.color: 'blue'
        }

        // 第二条内切线
        MapPolyline {
            id: tangentLineItem2
            line.width: 3
            line.color: 'blue'
        }

        Row {
            anchors.bottom:     parent.bottom
            anchors.bottomMargin: 10
            anchors.horizontalCenter:  parent.horizontalCenter

            Text {
                text:   "圆1(红色)半径:"
                color:  "white"
                anchors.verticalCenter: parent.verticalCenter
            }
            TextField {
                id:     radius01Item
                text:   radius01
            }

            Item {
                width: 20
                height: 10
            }
            Text {
                text:   "圆2(黄色)半径:"
                color:  "white"
                anchors.verticalCenter: parent.verticalCenter
            }
            TextField {
                id:     radius02Item
                text:   radius02
            }

            Item {
                width: 20
                height: 10
            }

            Button {
                text:   "更新"
                onClicked: {
                    radius01 = radius01Item.text
                    radius02 = radius02Item.text
                    drawTangentCircleToCircle(center01, radius01, true, center02, radius02, true)
                }
            }

            Button {
                text:   "顺-顺"
                onClicked: {
                    drawTangentCircleToCircle(center01, radius01, true, center02, radius02, true)
                }
            }
            Button {
                text:   "顺-逆"
                onClicked: {
                    drawTangentCircleToCircle(center01, radius01, true, center02, radius02, false)
                }
            }

            Button {
                text:   "逆-逆"
                onClicked: {
                    drawTangentCircleToCircle(center01, radius01, false, center02, radius02, false)
                }
            }
            Button {
                text:   "逆-顺"
                onClicked: {
                    drawTangentCircleToCircle(center01, radius01, false, center02, radius02, true)
                }
            }
        }

        // 圆1(红色)
        MapCircle {
            center: center01
            radius: radius01
            color: "transparent"
            border.width: 3
            border.color: "red"
            visible: false
        }

        // 圆2(黄色),使用atDistanceAndAzimuth计算其位置
        MapCircle {
            center: center02
            radius: radius02
            color:  "transparent"
            border.width: 3
            border.color: 'yellow'
        }

        // 两圆心连线
        MapPolyline {
            line.width: 1
            line.color: 'red'
            visible: false
            path: [
                center01,
                center02
            ]
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2196876.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】进程间通信——System V消息队列和信号量

一、消息队列 1.1 概念 进程间通信的原理是让不同进程看到同一份资源&#xff0c;资源种类的不同就决定了通信方式的差异。如果用管道通信&#xff0c;则资源是文件缓冲区&#xff1b;如果用共享内存&#xff0c;则资源是内存块 消息队列是由操作系统提供的资源&#xff0c;…

注意!新增一本期刊解除On Hold!仍有37本无法检索慎投!

On Hold期刊 2024年9月29日&#xff0c;小编从WOS数据库查到新增一本ESCI期刊《SOCAR Proceedings》解除On Hold标识 目前共37本期刊被标记&#xff0c;期刊整理如下&#xff0c;请注意避雷&#xff01; 01 解除风险期刊 SOCAR Proceedings • ISSN&#xff1a;2218-6867 •…

RAR解压缩软件的全面指南:压缩、加密、修复功能一应俱全

在日常文件管理中&#xff0c;压缩文件格式与解压缩工具是不可或缺的组成部分。而RAR格式&#xff0c;凭借其高效的压缩率、丰富的功能和灵活的文件管理方式&#xff0c;成为了用户最常使用的压缩格式之一。 作为处理RAR格式的专业工具&#xff0c;RAR解压缩软件具备压缩、加密…

马哥亲讲k8s集群搭建

文章目录 Docker和K8s安装1.docker安装2.安装cri-dockerd3.安装kubelet、kubeadm、kubectl4.整合kubelet和cri-dockerd 集群节点部署1.配置国内镜像站2.方式一&#xff1a;命令初始化1.kubeadm init2.保存初始化token3.拷贝/etc/kubernetes/admin.conf4.部署网络插件5.kubectl …

reactNative本地调试localhost踩坑

本地调试请求localhost的时候 1.要和电脑处在同一局域网下面&#xff08;同一个wifi&#xff09; 2.把baseURL的localhost改成命令行中ipconfig查询到的IPv4 地址 . . . . . . . . . . . . : &#xff08;例如&#xff09;192.168.1.103 如果报错Net Work Error&#xff0c;可…

算法知识点————贪心

贪心&#xff1a;只考虑局部最优解&#xff0c;不考虑全部最优解。有时候得不到最优解。 DP&#xff1a;考虑全局最优解。DP的特点&#xff1a;无后效性&#xff08;正在求解的时候不关心前面的解是怎么求的&#xff09;&#xff1b; 二者都是在求最优解的&#xff0c;都有最优…

如何扫描HTTP代理:步骤与注意事项

HTTP代理是一个复杂的过程&#xff0c;通常用于寻找可用的代理服务器&#xff0c;以便在网络中实现匿名或加速访问。虽然这个过程可以帮助用户找到适合的代理&#xff0c;但也需要注意合法性和道德问题。本文将介绍如何扫描HTTP代理&#xff0c;并提供一些建议和注意事项。 什…

剖析十大经典二叉树题目:C 语言代码实现与深度解读

&#x1f4af;前言 二叉树是数据结构中的重要概念&#xff0c;在算法和编程中有着广泛的应用。以下是十大经典的二叉树题目及其解析与 C 语言代码实现&#xff0c;同时也会说明题目来源。 二叉树的基本概念&#x1f449;【剖析二叉树】 目录 &#x1f4af;二叉树的遍历 ⭐前…

AI大模型真的是大龄程序员的新的出路吗?_大龄程序员ai创业

前言 在IT行业的高速运转中&#xff0c;许多资深程序员到了一定年龄后&#xff0c;会发现自己陷入了职业发展的瓶颈。尤其是在北京这样的大厂&#xff0c;业务波动、部门调整以及裁员风险&#xff0c;都让“40”的程序员们感受到了前所未有的压力。当昔日的技术热情逐渐消退&a…

在C#中使用适配器Adapter模式和扩展方法解决面向对象设计问题

之前有阵子在业余时间拓展自己的一个游戏框架&#xff0c;结果在实现的过程中发现一个设计问题。这个游戏框架基于MonoGame实现&#xff0c;在MonoGame中&#xff0c;所有的材质渲染&#xff08;Texture Rendering&#xff09;都是通过SpriteBatch类来完成的。举个例子&#xf…

新书速览|你好,C++

《你好&#xff0c;C》 本书内容 《你好&#xff0c;C》主要介绍C开发环境的搭建、基础语法知识、面向对象编程思想以及标准模板库的应用&#xff0c;特别针对初学者在学习C过程中可能遇到的难点提供了解决方案。全书共分13章&#xff0c;以一个工资程序的不断优化和完善为线索…

ChatGPT助力文献综述写作:提升效率与写作技巧!

文献综述在论文写作中占有举足轻重的地位。它不仅帮助我们梳理已有的研究成果&#xff0c;还能为自己的研究奠定基础。许多同学在撰写文献综述时常常感到头疼&#xff1a;如何处理海量的信息&#xff1f;如何将不同的观点有条理地整合起来&#xff1f;再加上学术语言的高要求&a…

定时任务。

引入 1.启动类上加上注解 2.新建一个定时任务的管理类&#xff0c;交给Spring管理 案例 案例1&#xff1a;fixedRate //上次任务开始到下次任务开始的时间间隔为5秒 //每隔5秒执行一次,不需要等上个任务执行完 Scheduled(fixedRate 5000) public void mask01() throws Inte…

python:web自动化工具selenium安装和配置(1)

UI自动化测试 UI自动化测试&#xff08;User Interface Automation Testing&#xff09;是一种通过编写脚本或使用自动化测试工具&#xff0c;对界面&#xff08;UI&#xff09;进行自动化测试的方法。原理主要是模拟用户打开客户端或网页的UI界面&#xff0c;自动化执行用户界…

【Java 问题】基础——泛型

接上文 泛型 47.Java 泛型了解么&#xff1f;什么是类型擦除&#xff1f;介绍一下常用的通配符&#xff1f; 47.Java 泛型了解么&#xff1f;什么是类型擦除&#xff1f;介绍一下常用的通配符&#xff1f; 什么是泛型&#xff1f; Java 泛型&#xff08;generics&#xff09;是…

REINFORCEMENT LEARNING THROUGH ACTIVE INFERENCE

摘要 强化学习&#xff08;RL&#xff09;的核心原则是智能体寻求最大化累积奖励之和。相比之下&#xff0c;主动推理&#xff0c;认知和计算神经科学中的一个新兴框架&#xff0c;提出代理人采取行动&#xff0c;以最大限度地提高有偏见的生成模型的证据。在这里&#xff0c;…

上门安装维修系统小程序开发详解及源码示例

随着智能家居和设备的普及&#xff0c;消费者对上门安装和维修服务的需求日益增加。为了满足这一市场需求&#xff0c;开发一款上门安装维修系统小程序成为了一种有效的解决方案。本文将详细介绍上门安装维修系统小程序的开发过程&#xff0c;并提供一个简单的源码示例&#xf…

人工智能的未来

引言 人工智能的未来发展将是科技与人类社会深度融合的过程。随着技术的不断进步&#xff0c;AI将在全球经济、文化、政治及道德伦理等领域产生深远影响。本文将探讨人工智能在未来可能的技术进步、应用领域、社会影响、伦理挑战&#xff0c;以及对全球未来的展望。 一、技术前…

数据结构之——二叉树

一、二叉树的基本概念 二叉树是数据结构中的重要概念&#xff0c;每个节点最多有两个子树&#xff0c;分别为左子树和右子树。这种结构具有明确的层次性和特定的性质。 二叉树有五种基本形态&#xff1a; 空二叉树&#xff1a;没有任何节点。只有一个根结点的二叉树&#xff…

【HTTPS】深入解析 https

我的主页&#xff1a;2的n次方_ 1. 背景介绍 在使用 http 协议的时候是不安全的&#xff0c;可能会出现运营商劫持等安全问题&#xff0c;运营商通过劫持 http 流量&#xff0c;篡改返回的网页内容&#xff0c;例如广告业务&#xff0c;可能会通过 Referer 字段 来统计是…