PostGIS轨迹分析——横跨180°经线

news2024/12/24 9:34:27

问题描述

在处理AIS数据中,经常会遇到轨迹线横穿180°经线的情况,这种数据绘制到地图上显示的非常乱,如下图所示:
在这里插入图片描述

数据模拟

在geojson.io上模拟一条轨迹线,可以看到轨迹显示的非常好,红框里面的经纬度超过了180°,实际情况不会产生这种数据
在这里插入图片描述
使用真实的经纬度仍然还会出现错乱的情况,如下图所示:
在这里插入图片描述

解决思路

遍历轨迹上的每一个点,计算当前点和下一个点经度的差值(diff_x),当diff_x的值大于340°时代表轨迹线是从左到右穿越,小于-340°时则代表轨迹线是从右到左穿越,这样我们就可以编写一个函数来解决此问题:

CREATE OR REPLACE FUNCTION get_lines_from_points(array_geom GEOMETRY[], array_diff_x FLOAT[]) RETURNS SETOF GEOMETRY AS
$$
DECLARE
    array_line  GEOMETRY[];
    line        GEOMETRY;
    array_point GEOMETRY[];
    diff_x      FLOAT;
    i           INT := 1;
BEGIN
    array_point := ARRAY []::GEOMETRY[];
    array_line := ARRAY []::GEOMETRY[];

    FOR diff_x IN SELECT unnest(array_diff_x)
        LOOP
            IF diff_x >= 340 THEN
                array_point := array_point || array_geom[i];
--                raise notice '%',i;
--                raise notice '%',st_astext(array_geom[i]);
                array_line := array_line || ST_MakeLine(array_point);
                array_point := ARRAY []::GEOMETRY[];
            ELSEIF diff_x <= -340 THEN
                array_point := array_point || array_geom[i];
                array_line := array_line || ST_MakeLine(array_point);
                array_point := ARRAY []::GEOMETRY[];
            ELSE
--                raise notice '%',i;
--                raise notice '%',st_astext(array_geom[i]);
                array_point := array_point || array_geom[i];
            END IF;
            i := i + 1;
        END LOOP;
    array_line := array_line || ST_MakeLine(array_point);

    FOR line IN SELECT unnest(array_line)
        LOOP
            RETURN NEXT line;
        END LOOP;
END;
$$
    LANGUAGE plpgsql;

调用函数的sql语句:

with line
         as (select st_geomfromgeojson('{"coordinates":[[-133.56681607072383,38.048432852702376],[-139.5625400372008,26.7466519608887],[-145.80029322035367,25.652134128092058],[-152.40600243048414,24.25006792947383],[-157.12509403666579,24.83850354437604],[-166.03562138914404,25.730242168027047],[-174.60130137567302,25.931369819197315],[-179.039207598898,25.851521506858006],[173.32935654204437,26.040298758510986],[171.01528349485716,29.02363444666598],[170.28599246631518,32.69415629123982],[172.7003594237434,35.35477927943893],[176.05091333553526,37.73333709776331],[-179.3654560402213,39.46966129070009],[-174.16271433310752,39.97444061559179],[-167.33463416628095,39.79967537154252],[-159.09064182365373,39.21533548155793],[-155.6068721801528,38.105767412202454],[-154.62754038722716,36.10105861232415],[-155.91220041908267,33.7318760829549],[-159.52676846180285,31.289767673068425],[-165.0776674179618,30.430887160278573],[-169.0175317306846,30.095326488395187],[-174.15399221361776,29.77057541216257],[-177.41433600934587,30.729135131803034],[178.7977321368836,32.191313894174456],[177.86505030800765,33.66780018177491]],"type":"LineString"}') as geom),
     origin_points as (select (st_dumppoints(line.geom)).path[1] as id, (st_dumppoints(line.geom)).geom as geom
                       from line),
     diff_points as (select origin_points.id                                               as id,
                            geom,
                            st_x(geom) - st_x(lead(geom) over (order by origin_points.id)) as diff_x
                     from origin_points),
     array_points as (select array_agg(geom) as array_geom, array_agg(diff_x) as array_diff_x
                      from diff_points)
select jsonb_build_object(
               'type', 'Feature',
               'geometry', st_asgeojson(get_lines_from_points(array_geom, array_diff_x))::json,
               'properties', jsonb_build_object()
       )
from array_points
;

结果返回了四个线段,知识点:

  • st_dumppoints将模拟的线段打散为点
  • lead函数获取下一行记录
  • array_agg(geom) 和 array_agg(diff_x) 构建了两个用来存储经纬度和经度差的数组,用于传递给get_lines_from_points函数

在这里插入图片描述
在geojson.io中的展示效果,可以看到虽然说轨迹线没有之前那么乱了,但是在180°经线断开了

在这里插入图片描述

优化函数

当轨迹线穿越180°经线时,要在180°经线上补充一个点,同时下一条轨迹线也要在-180°经线上补充一个点,优化后的函数如下所示:

CREATE OR REPLACE FUNCTION get_lines_from_points(array_geom GEOMETRY[], array_diff_x FLOAT[]) RETURNS SETOF GEOMETRY AS
$$
DECLARE
    array_line  GEOMETRY[];
    line        GEOMETRY;
    array_point GEOMETRY[];
    temp_point  GEOMETRY;
    diff_x      FLOAT;
    middle_y    FLOAT;
    i           INT := 1;
BEGIN
    array_point := ARRAY []::GEOMETRY[];
    array_line := ARRAY []::GEOMETRY[];

    FOR diff_x IN SELECT unnest(array_diff_x)
        LOOP
            IF diff_x >= 340 THEN
                --                 raise notice '%',i;
--                 raise notice '%',st_astext(array_geom[i]);
                array_point := array_point || array_geom[i];
                -- 计算纬度中间值
                middle_y := (st_y(array_geom[i]) + st_y(array_geom[i + 1])) / 2;
                array_point := array_point || ST_SetSRID(ST_MakePoint(180, middle_y), 4326);
                -- 下一条轨迹线的第一个点要补充一个点
                temp_point := ST_SetSRID(ST_MakePoint(-180, middle_y), 4326);

                array_line := array_line || ST_MakeLine(array_point);
                array_point := ARRAY []::GEOMETRY[];
            ELSEIF diff_x <= -340 THEN
                array_point := array_point || array_geom[i];
                middle_y := (st_y(array_geom[i]) + st_y(array_geom[i + 1])) / 2;
                array_point := array_point || ST_SetSRID(ST_MakePoint(-180, middle_y), 4326);
                temp_point := ST_SetSRID(ST_MakePoint(180, middle_y), 4326);

                array_line := array_line || ST_MakeLine(array_point);
                array_point := ARRAY []::GEOMETRY[];
            ELSE
                IF NOT ST_IsEmpty(temp_point) THEN
                    -- 补充的点
                    array_point := array_point || temp_point;
                    temp_point := NULL;
                END IF;
                array_point := array_point || array_geom[i];
            END IF;
            i := i + 1;
        END LOOP;
    array_line := array_line || ST_MakeLine(array_point);

    FOR line IN SELECT unnest(array_line)
        LOOP
            RETURN NEXT line;
        END LOOP;
END;
$$
    LANGUAGE plpgsql;

最终结果

最终轨迹线被分割了若干份,效果如图所示:

在这里插入图片描述

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

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

相关文章

使用Alpha Vantage API和Python进行金融数据分析

Alpha Vantage通过一套强大且开发者友好的数据API和电子表格&#xff0c;提供实时和历史的金融市场数据。从传统资产类别&#xff08;例如股票、ETF、共同基金&#xff09;到经济指标&#xff0c;从外汇汇率到大宗商品&#xff0c;从基本数据到技术指标&#xff0c;Alpha Vanta…

HashSet使用-力扣349做题总结

349. 两个数组的交集 分析代码HashSet出错的知识点1、HashSet新建2、HashSet添加add3、是否包含某元素4、集合->数组5、增强for循环 分析 没做出来的原因代码随想录的视频文字学习 为什么没做出来&#xff0c;因为没有理解好题意。根据示例1可知是去重的。且题目明确说“不考…

机器学习算法(12) — 集成技术(Boosting — Xgboost 分类)

一、说明 时间这是集成技术下的第 4 篇文章&#xff0c;如果您想了解有关集成技术的更多信息&#xff0c;您可以参考我的第 1 篇集成技术文章。 机器学习算法&#xff08;9&#xff09; - 集成技术&#xff08;装袋 - 随机森林分类器和...... 在这篇文章中&#xff0c;我将解释…

先进制造身份治理现状洞察:从手动运维迈向自动化身份治理时代

在新一轮科技革命和产业变革的推动下&#xff0c;制造业正面临绿色化、智能化、服务化和定制化发展趋势。为顺应新技术革命及工业发展模式变化趋势&#xff0c;传统工业化理论需要进行修正和创新。其中&#xff0c;对工业化水平的判断标准从以三次产业比重标准为主回归到工业技…

服务器数据恢复-昆腾存储StorNext文件系统下raid5数据恢复案例

服务器数据恢复环境&#xff1a; 昆腾某型号存储&#xff0c;StorNext文件存储系统。 共有9个分别配置了24块磁盘的磁盘柜&#xff0c;其中8个磁盘柜存放普通数据&#xff0c;1个磁盘柜存放元数据。 存放元数据的磁盘柜中的24块磁盘组建了8组RAID1阵列和1组4盘RAID10阵列&#…

Ubuntu 常用命令之 history 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 history命令在Ubuntu系统中用于显示用户执行过的命令列表。这个命令在bash shell中非常有用&#xff0c;特别是当你需要记住你之前执行过的命令时。 history命令的参数如下 -c&#xff1a;清除历史记录。-d offset&#xff1a;删…

全功能知识付费小程序系统源码是什么?有什么好处?

全功能知识付费小程序系统源码&#xff0c;是一个集课程管理、用户管理、支付管理、数据分析等于一体的综合性解决方案。它支持多种形式的课程内容&#xff0c;如视频、音频、图文等&#xff0c;满足不同用户的学习需求。同时&#xff0c;系统具备完善的支付功能&#xff0c;保…

怎么开通独立站支付?独立站客户退款谁支付运费?——站斧浏览器

怎么开通独立站支付&#xff1f; 选择支付服务提供商&#xff1a;开通独立站支付首先需要选择一个可靠的支付服务提供商。目前市场上有许多知名的支付服务提供商&#xff0c;如支付宝、微信支付、PayPal等。根据自己的业务需求和目标市场选择合适的支付服务提供商。 注册账号…

目前电视盒子哪个最好?工程师盘点超值电视盒子推荐

因工作原因每天都会跟各种各样类型的电视盒子打交道&#xff0c;拆机、维修&#xff0c;身边朋友在挑选电视盒子的时候会问我目前电视盒子哪个最好&#xff0c;哪些电视盒子最值得入手&#xff0c;我整理了五款超值电视盒子推荐给大家&#xff0c;在挑选电视盒子时可以把这几款…

两套高质量可视化模板套件,需要进!

小编整理了两套高质量可视化模板套件&#xff0c;均来自于山海鲸可视化&#xff0c;需要源文件可私。 一、「星曜蓝」主题可视化模板 可以自由调用模板库中的所有内容&#xff0c;轻松搭建风格统一的地图、工厂、城市多种数字孪生项目。真免费、0代码数字孪生设计搭建&#xf…

可狱可囚的爬虫系列课程 07:BeautifulSoup4(bs4)库的使用

前面一直在讲 Requests 模块如何使用&#xff0c;那都是在请求阶段要做的事情&#xff0c;相信很多网友都在等一个能够开始爬网站信息的教程&#xff0c;今天它来了&#xff0c;今天我要给大家讲一个很简单易懂的库&#xff1a;BeautifulSoup4。 一、概述&安装 Beautiful…

BWS2000倾角传感器c++测试代码【2】

问题一&#xff1a;串口频率的初始化 由于本次项目之中使用的线长为40米的倾角传感器&#xff0c;需要对于其频率输出存在要求&#xff0c;如何测试其频率如下所示&#xff1a; 如上所示相应的软件&#xff0c;软件中存在一句如果设置后不保存&#xff0c;则存在传感器断电后设…

众和策略:大盘涨手中的股票却大跌,到底怎么回事?

大盘涨手中的股票却大跌&#xff0c;究竟怎么回事&#xff1a; 1、大盘上涨是权重股所造成的 大盘上涨可能是受一些权重比较大的工作所影响&#xff0c;比如证券工作、钢铁工作、银行工作等等&#xff0c;这些工作的大涨&#xff0c;可以拉升大盘的上涨&#xff0c;可是其它工…

C++20形式的utf-8字符串转宽字符串,不依赖编译器编码形式

默认的char[]编码都是要看编译器编译选项的&#xff0c;你选了ANSI那它就是ANSI&#xff0c;你选了UTF8那它就是UTF8. 【注意&#xff1a;经典DevC只支持ANSI编码&#xff08;痛苦&#xff09;&#xff1b;上图是小熊猫DevC&#xff0c;则有这个选项】 这一点对我的代码造成了…

20231220将NanoPC-T4(RK3399)开发板的Android10的SDK按照Rockchip官方挖掘机开发板编译打包刷机之后启动跑飞

20231220将NanoPC-T4(RK3399)开发板的Android10的SDK按照Rockchip官方挖掘机开发板编译打包刷机之后启动跑飞 2023/12/20 17:19 简略步骤&#xff1a;rootrootrootroot-X99-Turbo:~/3TB$ tar --use-compress-programpigz -xvpf rk3399-android-10.git-20210201.tgz rootrootro…

Poi实现复杂Excel导出,理解POI操作Excel思路!!!

前言 对于简单excel报表导出&#xff0c;有很多简单的工具如easypoi&#xff0c;而且现在网上已经有很多工具类整合easypoi使用起来非常方便。但是简单的弊端往往无法适配一些负责场景&#xff0c;而我们实际生产中面临的都是客户自定以的一个负责报表导出&#xff0c;这是利用…

【RTOS学习】源码分析(信号量和互斥量 事件组 任务通知)

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《RTOS学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f353;信号量和互斥量&#x1f345;创建&#x1f345;Take&#x1f345;Give &#x…

百川大模型AI对话实战——Python开发一个对话机器人

百川大模型开放提供API体验中心&#xff0c;体验不错&#xff0c;有小伙伴也对搭建自己的对话机器人比较兴趣&#xff0c;今天通过Python来简单介绍下&#xff0c;如何调用百川大模型的API来构建自己的小产品。 在开发环境中安装Python&#xff0c;如何安装&#xff1f;参照网…

(附源码)基于Springboot框架的网络投票系统 计算机毕设42855

基于springboot网络投票系统 摘 要 随着全球Internet的迅猛发展和计算机应用的普及&#xff0c;特别是近几年无线网络的广阔覆盖以及无线终端设备的爆炸式增长&#xff0c;使得人们能够随时随地的访问网络&#xff0c;以获取最新信息、参与网络活动、和他人在线互动。为了能及时…

Python Pandas 多重索引DataFrame数据(第19讲)

Python Pandas 多重索引DataFrame数据(第19讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…