OpenLayers根据任意数量控制点绘制贝塞尔曲线

news2025/7/19 13:30:35

以下是使用OpenLayers根据任意数量控制点绘制贝塞尔曲线的完整实现方案。该方案支持三个及以上控制点,使用递归算法计算高阶贝塞尔曲线。

实现思路

  1. 贝塞尔曲线原理:使用德卡斯特里奥算法(De Casteljau’s Algorithm)递归计算任意阶贝塞尔曲线。
  2. 坐标转换:将WGS 84经纬度点转换为Web Mercator投影(EPSG:3857)。
  3. 曲线计算:通过递归算法计算曲线上的多个点,形成平滑曲线。
  4. 地图渲染:使用OpenLayers的LineString几何对象绘制曲线。

代码实现

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>OpenLayers多控制点贝塞尔曲线</title>
    <script src="https://cdn.jsdelivr.net/npm/ol@7.3.0/dist/ol.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/ol@7.3.0/ol.css" rel="stylesheet">
    <style>
        .map {
            width: 100%;
            height: 400px;
        }
    </style>
</head>
<body>
    <div id="map" class="map"></div>

    <script>
        // 1. 定义多个控制点(经纬度坐标)
        const controlPoints = [
            [116.3, 39.9],    // 北京
            [118.0, 37.5],    // 黄海海域
            [121.0, 35.0],    // 东海海域
            [122.0, 32.0],    // 控制点
            [121.4, 31.2]     // 上海
        ];

        // 2. 经纬度转Web Mercator投影
        const transformPoints = (points) => {
            return points.map(point => 
                ol.proj.transform(point, 'EPSG:4326', 'EPSG:3857')
            );
        };

        // 3. 递归计算贝塞尔曲线上的点(德卡斯特里奥算法)
        function deCasteljau(points, t) {
            if (points.length === 1) return points[0];
            
            const newPoints = [];
            for (let i = 0; i < points.length - 1; i++) {
                const x = (1 - t) * points[i][0] + t * points[i + 1][0];
                const y = (1 - t) * points[i][1] + t * points[i + 1][1];
                newPoints.push([x, y]);
            }
            
            return deCasteljau(newPoints, t);
        }

        // 4. 计算完整贝塞尔曲线
        function calculateBezierCurve(points, segments = 50) {
            const curvePoints = [];
            for (let t = 0; t <= 1; t += 1 / segments) {
                curvePoints.push(deCasteljau(points, t));
            }
            return curvePoints;
        }

        // 5. 转换坐标并计算贝塞尔曲线
        const mercatorPoints = transformPoints(controlPoints);
        const bezierPoints = calculateBezierCurve(mercatorPoints, 100);

        // 6. 创建地图
        const map = new ol.Map({
            target: 'map',
            layers: [
                new ol.layer.Tile({
                    source: new ol.source.OSM()
                })
            ],
            view: new ol.View({
                center: ol.proj.transform([119, 34], 'EPSG:4326', 'EPSG:3857'),
                zoom: 5
            })
        });

        // 7. 创建贝塞尔曲线要素
        const bezierCurve = new ol.geom.LineString(bezierPoints);
        
        const feature = new ol.Feature({
            geometry: bezierCurve,
            name: '贝塞尔曲线'
        });

        // 8. 设置曲线样式
        feature.setStyle(new ol.style.Style({
            stroke: new ol.style.Stroke({
                color: 'rgba(255, 0, 0, 0.8)',
                width: 3,
                lineDash: [10, 5]
            })
        }));

        // 9. 添加曲线到地图
        const vectorSource = new ol.source.Vector({
            features: [feature]
        });
        
        const vectorLayer = new ol.layer.Vector({
            source: vectorSource
        });
        
        map.addLayer(vectorLayer);

        // 10. 添加控制点标记
        controlPoints.forEach((point, index) => {
            const mercatorPoint = ol.proj.transform(point, 'EPSG:4326', 'EPSG:3857');
            
            const marker = new ol.Feature({
                geometry: new ol.geom.Point(mercatorPoint),
                name: `控制点${index + 1}`
            });
            
            marker.setStyle(new ol.style.Style({
                image: new ol.style.Circle({
                    radius: 6,
                    fill: new ol.style.Fill({ color: 'blue' }),
                    stroke: new ol.style.Stroke({ color: 'white', width: 2 })
                }),
                text: new ol.style.Text({
                    text: `控制点${index + 1}`,
                    font: '14px Arial',
                    fill: new ol.style.Fill({ color: 'black' }),
                    stroke: new ol.style.Stroke({ color: 'white', width: 2 }),
                    offsetY: -10
                })
            }));
            
            vectorSource.addFeature(marker);
        });
    </script>
</body>
</html>

核心代码解析

  1. 德卡斯特里奥算法

    • 递归计算贝塞尔曲线上的点,支持任意数量的控制点
    • 公式:( B(t) = (1-t)B_{0}^{n-1}(t) + tB_{1}^{n-1}(t) )
    • 时间复杂度:( O(n^2) ),n为控制点数量
  2. 坐标转换

    • 使用ol.proj.transform()确保所有点在同一投影系统下
  3. 曲线精度控制

    • segments参数控制曲线的平滑度,值越大曲线越平滑但性能开销更高

效果展示

在这里插入图片描述

(注:实际运行时会显示OpenStreetMap底图和红色贝塞尔曲线)

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

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

相关文章

STM32--RCC--时钟

教程 系统时钟 RCC RCC&#xff08;Reset and Clock Control&#xff09;是STM32微控制器中管理时钟和复位系统的关键外设模块&#xff0c;负责整个芯片的时钟树配置和复位控制。 RCC主要功能 时钟系统管理&#xff1a; 内部/外部时钟源选择 时钟分频/倍频配置 各外设时钟门…

Linux系统入门第十二章 --Shell编程之正则表达式

一、正则表达式 之前学习了 Shell 脚本的基础用法&#xff0c;已经可以利用条件判断、循环等语句编辑 Shell脚本。接下来我们将开始介绍一个很重要的概念-正则表达式(RegularExpression&#xff0c;RE) 1.正则表达式的定义 正则表达式又称正规表达式、常规表达式。在代码中常…

[架构之美]Spring Boot多环境5种方案实现Dev/Test/Prod环境隔离

[架构之美]Spring Boot多环境5种方案实现Dev/Test/Prod环境隔离&#xff08;十六&#xff09; 摘要&#xff1a;本文深入剖析Spring Boot多环境配置的5种实现方案&#xff0c;涵盖YAML分组配置、Maven Profile集成、Kubernetes适配等企业级实践&#xff0c;并附赠配置加密方案…

C++STL——stack,queue

stack与queue 前言容器适配器deque 前言 本篇主要讲解stack与queue的底层&#xff0c;但并不会进行实现&#xff0c;stack的接口 queue的接口 &#xff0c;关于stack与queue的接口在这里不做讲解&#xff0c;因为通过前面的对STL的学习&#xff0c;这些接口都是大同小异的。 …

解决社区录音应用横屏状态下,录音后无法播放的bug

最近看到社区有小伙伴反映&#xff0c;社区录音应用横屏时&#xff0c;录音后无法播放的问题。现分享解决办法。 社区录音应用的来源&#xff1a;https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-5.0.2-Release/code/SystemFeature/Media/Recorder …

专业级软件卸载工具:免费使用,彻底卸载无残留!

在数字生活节奏日益加快的今天&#xff0c;我们的电脑就像每天都在"吃进"各种软件。但您是否注意到&#xff0c;那些看似消失的程序其实悄悄留下了大量冗余文件&#xff1f;就像厨房角落里积攒的调味瓶空罐&#xff0c;日积月累就会让系统变得"消化不良"。…

JVM对象创建内存分配

对象创建的主要流程&#xff1a; 检查加载类–》分配内存–》初始化–》设置对象头–》实例化&#xff0c;执行init方法。 在内存分配中&#xff0c;虚拟机将为新生对象内存分配 Minor GC : 新生代垃圾收集&#xff0c;特点是频繁&#xff0c;回收速度快&#xff1b; Full GC …

PySide6 GUI 学习笔记——常用类及控件使用方法(常用类边距QMarginsF)

文章目录 类简介方法总览关键说明示例代码 类简介 QMarginsF 用于定义四个浮点型边距&#xff08;左、上、右、下&#xff09;&#xff0c;描述围绕矩形的边框尺寸。所有边距接近零时 isNull() 返回 True&#xff0c;支持运算符重载和数学运算。 方法总览 方法名/运算符参数返…

STM32实现九轴IMU的卡尔曼滤波

在嵌入式系统中&#xff0c;精确的姿态估计对于无人机、机器人和虚拟现实等应用至关重要。九轴惯性测量单元&#xff08;IMU&#xff09;通过三轴加速度计、陀螺仪和磁力计提供全面的运动数据。然而&#xff0c;这些传感器数据常伴随噪声和漂移&#xff0c;单独使用无法满足高精…

机器学习-简要与数据集加载

一.机器学习简要 1.1 概念 机器学习即计算机在数据中总结规律并预测未来结果&#xff0c;这一过程仿照人类的学习过程进行。 深度学习是机器学习中的重要算法的其中之一&#xff0c;是一种偏近现代的算法。 1.2 机器学习发展历史 从上世纪50年代的图灵测试提出、塞缪尔开发…

算法训练营第十三天|226.翻转二叉树、101. 对称二叉树、 104.二叉树的最大深度、111.二叉树的最小深度

递归 递归三部曲&#xff1a; 1.确定参数和返回值2.确定终止条件3.确定单层逻辑 226.翻转二叉树 题目 思路与解法 第一想法&#xff1a; 递归&#xff0c;对每个结点进行反转 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, le…

二叉树的遍历与构造

好想回家&#xff0c;我想回家跟馒头酱玩&#xff0c;想老爸老妈。如果上天再给我一次选择的机会&#xff0c;我会选择当一只小动物&#xff0c;或者当棵大树也好&#xff0c;或者我希望自己不要有那么多多余的情绪&#xff0c;不要太被别人影响&#xff0c;开心点&#xff0c;…

MYSQL服务的使用流程

MYSQL是一个单进程多线程&#xff0c;支持多用户&#xff0c;基于客户机/服务器的关系数据库管理系统。与其他数据库管理系统相比&#xff0c;MYSQL具有体积小&#xff0c;易于安装&#xff0c;运行速度快&#xff0c;功能齐全&#xff0c;成本低廉以及开源等特点。MYSQL可运行…

【java】使用iText实现pdf文件增加水印功能

maven依赖 <dependencies><dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>7.2.5</version><type>pom</type></dependency> </dependencies>实现代码 前…

socket套接字-TCP

上一篇&#xff1a;socket套接字-UDP&#xff08;下&#xff09;https://blog.csdn.net/Small_entreprene/article/details/147569071?fromshareblogdetail&sharetypeblogdetail&sharerId147569071&sharereferPC&sharesourceSmall_entreprene&sharefromfr…

MiM: Mask in Mask Self-SupervisedPre-Training for 3D Medical Image Analysis

Abstract Vision Transformer在3D医学图像分析的自监督学习&#xff08;Self-Supervised Learning&#xff0c;SSL&#xff09;中展现了卓越的性能。掩码自编码器&#xff08;Masked Auto-Encoder&#xff0c;MAE&#xff09;用于特征预训练&#xff0c;可以进一步释放ViT在各…

【STM32 学习笔记】I2C通信协议

注&#xff1a;通信协议的设计背景 3:00~10:13 I2C 通讯协议(Inter&#xff0d;Integrated Circuit)是由Phiilps公司开发的&#xff0c;由于它引脚少&#xff0c;硬件实现简单&#xff0c;可扩展性强&#xff0c; 不需要USART、CAN等通讯协议的外部收发设备&#xff0c;现在被广…

深入理解卷积神经网络的输入层:数据的起点与预处理核心

内容摘要 本文围绕卷积神经网络输入层展开&#xff0c;详细介绍其在网络中的重要作用&#xff0c;包括接收不同领域数据的形式及传递数据的过程。深入解读数据预处理的关键操作&#xff0c;如去均值、归一化和PCA/白化。助力读者透彻理解输入层&#xff0c;为构建高效卷积神经…

redis bitmap数据类型调研

一、bitmap是什么&#xff1f; redis原文&#xff1a; Bitmaps are not an actual data type, but a set of bit-oriented operations defined on the String type . This means that bitmaps can be used with string commands, and most importantly with SET and GET. 翻…