openlayers-16-添加一组轨迹动画

实现一组动画,即根据一组只有起止点坐标的线段,实现点在这些线段上较为平滑的移动,移动速度和平滑程度均可控制。

下面的代码仅作为思路参考,还欠缺很多细节,比如在进行插值计算时,还需要判断经纬度坐标差,选择差值大的作为已知项计算插值,这样会避免一些bug并让计算的插值数据更平滑。还有如何把示例中的圆点改为箭头,并计算箭头的方向与线的走向一致等等一些问题。如果有时间,后期会整理一个更加具体的,可以直接移植使用的demo。

运行结果

在这里插入图片描述

代码如下

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>添加箭头动画</title>
    <link href="ol/ol.css" rel="stylesheet" type="text/css"/>
    <script src="ol/ol.js" type="text/javascript"></script>
    <style type="text/css">
        #mapCon {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
<body>
<!-- 地图容器 -->
<div id="mapCon"></div>
<div style="position: absolute;top: 20px;left: 50%;">
    运动速度:<input id="speed" type="range" min="1" max="10" step="1" value="5"/>
    <button id="start-animation">开始</button>
</div>
<script type="text/javascript">
    var key = "4689fc6b9bc0fdc8c48298f751ebfb41";//天地图密钥

    //ol.layer.Tile:是一个瓦片图层类,用于显示瓦片资源。
    //source是必填项,用于为图层设置来源。

    //ol.source.XYZ:
    //创建天地图矢量图层
    var TiandiMap_vec = new ol.layer.Tile({
        title: "天地图矢量图层",
        source: new ol.source.XYZ({
            url: "http://t{0-7}.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=" + key,
            wrapX: false
        })
    });
    //创建天地图矢量注记图层
    var TiandiMap_cva = new ol.layer.Tile({
        title: "天地图矢量注记图层",
        source: new ol.source.XYZ({
            url: "http://t{0-7}.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=" + key,
        })
    });
    //实例化Map对象加载地图
    var map = new ol.Map({
        //地图容器div的ID
        target: 'mapCon',
        //地图容器中加载的图层
        layers: [TiandiMap_vec, TiandiMap_cva],
        //地图视图设置
        view: new ol.View({
            //地图初始中心点(经纬度)
            center: [118.37, 37.14],
            //地图初始显示级别
            zoom: 12,
            projection: "EPSG:4326"
        })
    });

    /**
     * 线性插值
     * @param start 线段开始点的坐标
     * @param end 结束点坐标
     * @param step 步长
     * @returns {*[]}
     */
    function interpolate(start, end, step) {
        const x = start[0] + step * (end[0] - start[0]);
        let num = Math.ceil(Math.abs(end[0] - start[0]) / step);
        let xArr = [];
        setp = end[0] > start[0] ? step : 0 - step;
        for (let i = 1; i < num; i++) {
            xArr.push(start[0] + setp * i);
        }
        //插入起点
        let lonlat = [start];
        for (let i = 0; i < xArr.length; i++) {
            let y = start[1] + (end[1] - start[1]) / (end[0] - start[0]) * (xArr[i] - start[0]);
            lonlat.push([xArr[i], y]);
        }
        //加入终点
        lonlat.push(end);
        return lonlat;
    }

    //构建一组离散化的点(一组线段的起止点)
    var Coordinates = [
        {
            lonlat: [[118.37, 37.14], [118.48, 37.05]]
        },
        {
            lonlat: [[118.46, 37.04], [118.51, 37.06]]
        }
    ];
    //遍历线段,并根据起止点,计算插值数据,用于动画
    let routes = [];//路径对象数组
    for (let i = 0; i < Coordinates.length; i++) {
        const item = Coordinates[i];
        //获取插值数据
        let lonlatArr = interpolate(item.lonlat[0], item.lonlat[1], 0.01);
        //将插值数据构建为Line对象
        let route = new ol.geom.LineString(lonlatArr);
        //获取直线的坐标
        let routeCoords = route.getCoordinates();
        let routeLength = routeCoords.length;

        let routeFeature = new ol.Feature({
            type: 'route',
            geometry: route
        });
        let geoMarker = new ol.Feature({
            type: 'geoMarker',
            geometry: new ol.geom.Point(routeCoords[0])
        });
        routes.push(
            {
                route: route,
                routeCoords: routeCoords,
                routeLength: routeLength,
                routeFeature: routeFeature,
                geoMarker, geoMarker,
                index: 0
            }
        )
    }


    var styles = {
        'route': new ol.style.Style({
            stroke: new ol.style.Stroke({
                width: 6,
                color: [237, 212, 0, 0.8]
            })
        }),
        'icon': new ol.style.Style({
            image: new ol.style.Icon({
                anchor: [0.5, 1],
                src: "../../images/stationicon.png"
            })
        }),
        'geoMarker': new ol.style.Style({
            image: new ol.style.Circle({
                radius: 7,
                snapToPixel: false,
                fill: new ol.style.Fill({color: 'black'}),
                stroke: new ol.style.Stroke({
                    color: 'white',
                    width: 2
                })
            })
        })
    };

    var animating = false;
    var speed, now;
    var speedInput = document.getElementById('speed');
    var startButton = document.getElementById('start-animation');
    //添加用于展示动画的层
    var vectorSource = new ol.source.Vector();
    var vectorLayer = new ol.layer.Vector({
        source: vectorSource,
        style: function (feature) {
            //如果动画是激活的就隐藏geoMarker
            if (animating && feature.get('type') === 'geoMarker') {
                return null;
            }
            return styles[feature.get('type')];
        }
    });

    map.addLayer(vectorLayer);
    for (let i = 0; i < routes.length; i++) {
        let item = routes[i];
        vectorSource.addFeature(item.routeFeature);
        vectorSource.addFeature(item.geoMarker);
    }
    var moveFeature = function (event) {
        var vectorContext = event.vectorContext;
        var frameState = event.frameState;

        if (animating) {

            //通过增加速度,来获得lineString坐标
            for (let i = 0; i < routes.length; i++) {
                const item = routes[i];
                var elapsedTime = frameState.time - item.now;
                item.index = Math.round(speed * elapsedTime / 1000);
                if (item.index >= item.routeLength) {
                    //重置时间和各个线段的下标,用于循环
                    item.now = new Date().getTime();
                    item.index = 0;
                    //执行下面两行代码 自动结束动画
                    //stopAnimation(true);
                    //return;
                }

                var currentPoint = new ol.geom.Point(item.routeCoords[item.index]);
                var feature = new ol.Feature(currentPoint);
                vectorContext.drawFeature(feature, styles.geoMarker);
            }

        }
        //继续动画效果
        map.render();
    };

    function startAnimation() {
        if (animating) {
            stopAnimation(false);
        } else {
            animating = true;
            speed = speedInput.value;
            startButton.textContent = '结束运动';
            //隐藏geoMarker
            for (let i = 0; i < routes.length; i++) {
                const item = routes[i];
                item.geoMarker.setStyle(null);
                item.now = new Date().getTime();
            }

            //设置显示范围
            // map.getView().setCenter(center);
            map.on('postcompose', moveFeature);
            map.render();
        }
    }


    /**
     * @param {boolean}结束动画
     */
    function stopAnimation(ended) {
        animating = false;
        startButton.textContent = '开始运动';
        for (let i = 0; i < routes.length; i++) {
            const item = routes[i];
            //如果动画取消就开始动画
            var coord = ended ? item.routeCoords[item.routeLength - 1] : item.routeCoords[0];
            //将各个线段上的点重新定位到起始位置
            (item.geoMarker.getGeometry()).setCoordinates(coord);
        }

        //移除监听
        map.un('postcompose', moveFeature);
    }

    startButton.addEventListener('click', startAnimation, false);
</script>
</body>
</html>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

6. vue-element-admin 二次开发避坑指南

vue-element-admin 二次开发避坑指南 1.1 前言1.1.1 切换标签时未保存页面的操作内容1.1.2 markdown 样式乱码1.1.3 修改默认尺寸1.1.4 当后端服务器宕机情况下页面加载层一直转圈无法停止&#xff0c;只能关闭页面1.1.5 隐藏齿轮 1.1 前言 上一篇博文&#xff0c;我们分享了vu…

传送带下料口堵塞识别检测算法 yolov5

传送带下料口堵塞识别检测算法通过python基于yolov5网络深度学习框架模型&#xff0c;下料口堵塞识别检测算法能够准确判断下料口是否出现堵塞现象&#xff0c;一旦发现下料口堵塞&#xff0c;算法会立即抓拍发出告警信号。Python是一种由Guido van Rossum开发的通用编程语言&a…

【VM】保姆级VM算法平台二次开发之-环境配置

VM算法平台二次开发 1.下载Visual Studio 20222.项目的创建 C# 应用3.设置属性&#xff0c;去掉属选型32位4. 进行引用的导入工作5. 重新加载&#xff0c;查看引用6. 工具箱添加Dll的依赖。&#xff08;只需要加载一次就行&#xff09; 1.下载Visual Studio 2022 可以直接在官…

【学习笔记】计算机视觉对比学习综述

计算机视觉对比学习综述 前言百花齐放InstDiscInvaSpreadCPCCMC CV双雄MoCoSimCLRMoCo v2SimCLR v2SwAV 不用负样本BYOLSimSiam TransformerMoCo v3DINO 总结参考链接 前言 本篇对比学习综述内容来自于沐神对比学习串讲视频以及其中所提到的论文和博客&#xff0c;对应的链接详…

ESXi 6.7添加螃蟹2.5g网卡支持

安装了ESXi 6.7&#xff0c;结果机器两块网卡只能识别一块&#xff0c;然后想着不能让另一块浪费啊&#xff0c;开始折腾&#xff0c;看着网上都是找的驱动然后封装进iso&#xff0c;可是我已经装完了&#xff0c;怎么办&#xff0c;然后找到了下面解决方法 1.找驱动 下载RTL81…

对话出海企业:2023亚马逊云科技出海日圆桌论坛

在全球经济亟待复苏的今天&#xff0c;持续对外开放是中国未来经济发展重要的“两条腿”之一。在愈发饱和的国内市场&#xff0c;中国企业需要对外寻找全新机遇才能在未来不确定的市场博弈下生存下去。“出海”&#xff0c;也成为近几年最炙手可热的词汇之一&#xff0c;大量中…

mysql通过.frm和.ibd 文件恢复数据库

问题背景&#xff1a;由于强制在服务关闭mysql导致部分数据表以及数据丢失 如下图只有.frm .ibd的文件为我的问题文件 查找不到表结构和表数据目录D:XXXX\mysql-5.7.24-winx64\data\mydata 从frm文件中恢复表结构 先把原来的数据备份一次 避免过程中出错 先备份之前数据的.fr…

Oracle的学习心得和知识总结(二十九)|Oracle数据库数据库回放功能之论文三翻译及学习

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Gui…

数据结构入门 — 栈

本文属于数据结构专栏文章&#xff0c;适合数据结构入门者学习&#xff0c;涵盖数据结构基础的知识和内容体系&#xff0c;文章在介绍数据结构时会配合上动图演示&#xff0c;方便初学者在学习数据结构时理解和学习&#xff0c;了解数据结构系列专栏点击下方链接。 博客主页&am…

利用逻辑回归判断病人肺部是否发生病变

大家好&#xff0c;我是带我去滑雪&#xff01; 判断肺部是否发生病变可以及早发现疾病、指导治疗和监测疾病进展&#xff0c;以及预防和促进肺部健康&#xff0c;定期进行肺部评估和检查对于保护肺健康、预防疾病和提高生活质量至关重要。本期将利用相关医学临床数据结合逻辑回…

DEAP库文档教程二-----创建类型

本节将展示如何通过creator创建类型以及如何使用toolbox进行初始化。 1、Fitness 已经提供的Fitness类是一个抽象类&#xff0c;它需要weight来使得它成为一个函数。一个最小化的适应度是通过负权重构建的&#xff0c;而一个最大化适应度则需要正权重。 creator.create(&quo…

算法通关村第10关【青铜】| 快速排序各种写法

思路&#xff1a; 指定一个数字&#xff0c;将数组比他小的放到左边&#xff0c;比他大的放到右边&#xff0c;实现归位 然后再指定一个数字递归&#xff0c;一直遍历完数组 最好的情况每次指定的都是中间位置的数字&#xff0c;划分完后两边长度相等&#xff0c;2T(n/2) O…

Ansible之playbooks剧本

文章目录 一.playbooks介绍1.playbooks简述2.playbooks剧本格式3.playbooks组成部分4.运行playbooks及检测文件配置 二.模块实战实例1.playbooks模块实战实例2.vars模块实战实例3.指定远程主机sudo切换用户4.when模块实战实例5.with_items迭代模块实战实例6.Templates 模块实战…

【BUG事务内消息发送】事务内消息发送,事务还未结束,消息发送已被消费,查无数据怎么解决?

问题描述 在一个事务内完成插入操作&#xff0c;通过MQ异步通知其他微服务进行事件处理。 由于是在事务内发送&#xff0c;其他服务消费消息&#xff0c;查询数据时还不存在如何解决呢&#xff1f; 解决方案 通过spring-tx包的TransactionSynchronizationManager事务管理器解…

OpenShift 4 - 用 Prometheus 和 Grafana 监视用户应用定制的观测指标(视频)

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.13 的环境中验证 文章目录 OpenShift 的监控功能构成部署被监控应用用 OpenShift 内置功能监控应用用 Grafana 监控应用安装 Grafana 运行环境配置 Grafana 数据源定制监控 Dashboard 演示视…

LeetCode(力扣)669. 修剪二叉搜索树Python

LeetCode669. 修剪二叉搜索树 题目链接代码 题目链接 https://leetcode.cn/problems/trim-a-binary-search-tree/ 代码 递归 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # …

【MySQL】基础知识(二)

MySQL基础知识(二) 文章目录 MySQL基础知识(二)01 表操作1.1 创建表1.2 查看所有表1.3 查看指定表的结构1.4 删除表练习 02 CURD2.1 新增2.1.1 指定列插入2.1.2 datetime类型插入 2.2 查询2.2.1 全列查询2.2.2 指定列查询2.2.3 查询字段为表达式2.2.4 别名查询2.2.5 去重2.2.6 …

android frida 逆向 自吐加密算法

前言&#xff1a; ♛ frida hook android Android 逆向神器 前几天在学习 Android 逆向的时候发现了一个神器&#xff1a;通过 frida hook 我们可以 “劫持” 一些函数 为我们所用&#xff0c; 今天就和大家上手一个 加密函数的劫持 让打印出&#xff1a; 加密秘钥 …

Docker安装详细步骤

Docker安装详细步骤 1、安装环境准备 主机&#xff1a;192.168.40.5 zch01 设置主机名 # hostnamectl set-hostname zch01 && bash 配置hosts文件 [root ~]# vi /etc/hosts 添加如下内容&#xff1a; 192.168.40.5 zch01 关闭防火墙 [rootzch01 ~]# systemct…

分库分表篇-2.1 Mycat-配置文件篇

文章目录 前言一、Mycat server.xml作用&#xff1a;1.1 server.xml 作用&#xff1a;1.2 定义数据库逻辑模式&#xff1a; 二、Mycat schema.xml作用&#xff1a;2.1 schema 标签&#xff1a;2.1.1 schema 中table 标签&#xff1a; 2.2 dataNode 标签&#xff1a;2.3 dataHos…