通过 SQL 实现海量 GIS 数据的轨迹关联计算,确定不了解下?

news2024/11/26 0:30:57

作者:于成铭 | YMatrix 解决方案与架构总监

前言

统计与分析轨迹和地理区域的关联问题,是 GIS 主要的应用类别之一。最早的 GIS 应用以桌面应用程序为主,但随着需处理应用越来越复杂,GIS 的开发框架也在逐年演进。就像复杂的数据操作和应用催生了以数据独立性为主旨的数据库一样,Spatial RDBMS (空间数据库) ,也就是 GIS in Database 逐渐形成。我们知道,在数据库内,数据的操作被逐渐标准化,让更多的应用开发有了更低的门槛。

随着万物互联时代的到来,车联网、物联网以及人们的出行产生了大量时空数据。疫情的爆发也催生了海量时空数据交联的分析需求。如何在每天新增数十亿条的时空数据中发现时空关联关系成为重要挑战。将数据放到一个节点的数据库中分析已无法满足需求,我们需要在分布式的数据基座中来完成数据的关联。

超融合数据库 YMatrix 基于 PostgreSQL 与 Greenplum 深度优化而来。具有前者优秀的 GIS 能力与后者的分布式、库内计算能力的同时,在存储引擎层的深度优化让其在性能上更上一个新的台阶。不仅支持海量时空数据的接入,存储与库内计算,而且可以完善支持标准 SQL(从 SQL92 到 SQL2016),支持其他多模态数据类型,包括关系数据、JSON 数据、文本数据、图片等;支持多模式操作,包括高并发低延迟增删改查、点查、明细查询、聚合查询、窗口查询、关联查询、多维查询、库内机器学习等,是一款轻量级、高性能数据基座。

YMatrix 为 GIS 数据应用插上了分布式的翅膀,解放了资源的可扩展性,同时提高了单位资源的计算性能,本文将通过一个例子来介绍如何基于 YMatrix 来进行库内的时空数据分析。

案例描述

GIS 应用中常见的需求是轨迹与区域之间关联关系的查询实现。比如查询某段时间通过某一区域的轨迹有哪些。举一个防疫中时空关联的具体的例子来说明,注意:本文为方便理解,将采用相对坐标说明。实际数据是以 GPS 转换后的 GEOMETRY 类型计算,精度会更高,但是实现原理一致。

通过上图,我们构建一个简单的地图坐标系,最左下角是 (0 0) 点。我们定义两个风险区域 Area1 与 Area2 ,分别位于门头沟、昌平和海淀的交界与昌平顺义与怀柔的交界。每个风险区域有自己的开始结束时间。再定义 3 条轨迹,Line1、Line2 与 Line3 来模拟人的行动。实际上,轨迹和区域都会有很多,而这正是分布式计算的优势所在。

实现优势

考虑到实际情况中我们是对千万人和上百个区域的关联分析,并且人员的轨迹数据会持续的加载进数据库,业务方的查询并发也非常高。因此要采用一定的数据分层思路,通过定期对现有数据进行筛查并且将结果存入结果表,再以相对简单的逻辑提供给业务方查询,可以避免大量并发带来的重复计算。

以下为具体的操作步骤,在有测试环境的前提下,可以一步一步操作直到得出最终结果。(YMatrix 的安装参考见文末)

3.1 地理信息算法扩展

针对地理信息应用,YMatrix 支持地理信息数据插件 PostGIS,提供丰富的空间信息服务功能,如空间对象、空间索引、空间操作函数和空间操作符等。同时,地理信息数据插件遵循 OpenGIS 的规范,支持十几种标准空间数据类型,内置 200 多种相关的数据处理分析函数。

基于此,我们可以建立空间索引来更完成高效的查询,也可以通过其内置的函数分析空间交叠、轨迹交叉等问题。其安装方法如下:在每个节点上安装 rpm 包( postgis 依赖 geos39,所以先安装依赖):

sudo yum install https://ftp.postgresql.org/pub/repos/yum/common/redhat/rhel-7-x86_64/geos39-3.9.1-1.rhel7.x86_64.rpm
sudo yum install mxdb-postgis-2.5-1.el7.x86_64.rpm

连接数据库并创建扩展:CREATE EXTENSION postgis;

3.2 建表与数据录入

1. 结合坐标系,Area 按 PostGIS 的规则表示如下:

Area1: 'POLYGON((18 18,18.5 19,19 19,19 17.5,19.5 17,19.5 16.5,19 16.5,
                 18.5 16,19 15.5,19 14.5,18.5 14,18 14.5,17.5 14.5,
                 17.5 16,17 16,17 17,18 17,18 18))'
       '2022-11-23 10:00:00','2022-11-30 10:00:00'
Area2: 'POLYGON((26.5 21,27.5 21.5,28.5 21,28 20,26.5 19.5,26.5 21))'
       '2022-11-15 12:00:00','2022-11-24 12:00:00'

创建 area_of_interest 表并将该信息录入:

CREATETABLE area_of_risk (
    area_id         bigint-- 区域ID
  , area_name       varchar(30)                   -- 区域名称
  , area_detail     geometry                      -- 描述区域的数据类型
  , start_time      timestampwithouttime zone   -- 风险开始时间
  , end_time        timestampwithouttime zone   -- 风险结束时间
) DISTRIBUTED REPLICATED;
INSERTINTO area_of_risk VALUES
(1,
 'area1',
 ST_GeometryFromText('POLYGON((18 18,18.5 19,19 19,19 17.5,19.5 17,19.5 16.5,19 16.5,18.5 16,19 15.5,19 14.5,18.5 14,18 14.5,17.5 14.5,17.5 16,17 16,17 17,18 17,18 18))',4269),
 '2022-11-23 10:00:00',
 '2022-11-30 10:00:00'),
(2,
'area2',
ST_GeometryFromText('POLYGON((26.5 21,27.5 21.5,28.5 21,28 20,26.5 19.5,26.5 21))',4269),
 '2022-11-30 10:00:00',
 '2022-12-07 10:00:00');

数据插入后查看结果:

SELECT area_id,area_name,ST_AsText(area_detail) FROM area_of_risk;

2. 轨迹时序表

如图所示,另外定义三个轨迹,Line1、Line2、Line3,分别来自于三个目标(也就是形成轨迹的人),实际工作中,我们的 Line 信息,随着时间产生,格式应该是时序数据,格式类似于如下:

时间:'2022-11-24 10:27:30',
目标ID:'Tar-0',
轨迹ID:'Tar-0-221124',
定位:ST_GeometryFromText('POINT(20 22)',4269))

因此首先,我们按上述格式创建 travel_path_ts 表

CREATE EXTENSION IFNOTEXISTS matrixts;
ALTER EXTENSION matrixts UPDATE;

CREATETABLE travel_path_ts (
    ts            timestampwithouttime zone     -- 时间
  , target_id     varchar(50)                     -- 目标ID
  , trajectory_id varchar(30)                     -- 轨迹编号
  , position      geography(POINT,4269)           -- 定位
)
USING mars2
DISTRIBUTEDBY (target_id)
PARTITIONBYRANGE(ts)
(
  START ('2022-08-01') INCLUSIVE
  END ('2022-09-30') EXCLUSIVE
  EVERY (INTERVAL'24 hour'),
  DEFAULTPARTITION default_p
);
CREATEINDEX travel_path_ts_index ON travel_path_ts USING mars2_btree(target_id,ts);

再将示例数据按时序格式录入至 travel_path_ts 表:

INSERTINTO travel_path_ts VALUES
('2022-11-24 10:27:30','Tar-0','Tar-0-20221124',ST_GeometryFromText('POINT(09 15)',4269)),
('2022-11-24 10:57:30','Tar-0','Tar-0-20221124',ST_GeometryFromText('POINT(14 16)',4269)),
('2022-11-24 11:17:30','Tar-0','Tar-0-20221124',ST_GeometryFromText('POINT(19 19)',4269)),
('2022-11-24 12:27:30','Tar-0','Tar-0-20221124',ST_GeometryFromText('POINT(27 21)',4269)),
('2022-11-24 13:07:30','Tar-0','Tar-0-20221124',ST_GeometryFromText('POINT(35 26)',4269)),
('2022-11-30 08:39:30','Tar-1','Tar-1-20221031',ST_GeometryFromText('POINT(27 21)',4269)),
('2022-11-30 09:27:30','Tar-1','Tar-1-20221031',ST_GeometryFromText('POINT(30 13)',4269)),
('2022-11-30 10:37:30','Tar-1','Tar-1-20221031',ST_GeometryFromText('POINT(34 10)',4269)),
('2022-11-30 11:42:30','Tar-1','Tar-1-20221031',ST_GeometryFromText('POINT(35 03)',4269)),
('2022-11-15 15:21:30','Tar-2','Tar-2-20221015',ST_GeometryFromText('POINT(14 08)',4269)),
('2022-11-15 16:53:30','Tar-2','Tar-2-20221015',ST_GeometryFromText('POINT(25 06)',4269)),
('2022-11-15 18:32:30','Tar-2','Tar-2-20221015',ST_GeometryFromText('POINT(34 10)',4269));

数据插入后查看结果:

SELECT ts,target_id,trajectory_id,ST_AsText(position) FROM travel_path_ts;

3.3 按照目标 ID 精准查询轨迹

至此,我们可以通过系统进行指定目标、指定轨迹的精准查询:

select target_id,
       time_bucket_gapfill('3 second', ts) tb,
       last_not_null_value(position, ts) as signals
from travel_path_ts
where target_id = 'Tar-0'
    and ts >= '2022-11-24 00:00:00'
    and ts < '2022-11-24 23:59:59'
groupby target_id, tb
orderby tb;

3.4 聚合定时任务查询,维度表和时序表关联结果表

除按目标 ID 的精准查询外,还有一类查询非常常用,就是前文提到的根据指定区域查经过这个区域的轨迹。当然可以直接进行关联查询,但对于海量数据的查询,为提高后续查询效率,考虑到区域的信息变动会远小于人们的行程数据接入。可以定时的将人员信息聚合,并进一步和风险区域进行聚合,发现高危人员。因此,设定人员整合表如下:

CREATETABLE travel_path_agg (
    target_id           varchar(50)                   -- 目标ID
  , trajectory_id       varchar(30)                   -- 轨迹编号
  , trajectory_path     text-- 轨迹,用于存储聚合后的linestring
  , start_time          timestampwithouttime zone   -- 轨迹开始时间
  , end_time            timestampwithouttime zone   -- 轨迹结束时间
  , area_of_risk   bigint []
) DISTRIBUTEDBY (target_id);
-- 将一系列point数据通过makeline聚合成航迹信息,存入trajectory_path中。
-- 起止实际聚合用于后续可能的过滤条件。
-- 其他信息同航迹内相同,可直接存储

再通过未来每日执行的聚集任务来插入 trajectory_agg 表,设计类似如下语句:

接下来,我们要更新 travel_path_agg 表中的 area_of_risk 字段,这个字段用于存储该轨迹与多少我们关注的风险区域之间存在时空关联,是我们未来查询指定区域经过了多少轨迹的依据。我们通过如下语句实现轨迹的经过判断,并将经过的区域存储于 travel_path_agg 表中的 area_of_risk 字段:

with
aoi_ta_join as (
select * from travel_path_agg ta
leftjoin area_of_risk aor
on ta.trajectory_path && aor.area_detail
and  (ta.start_time, ta.end_time) OVERLAPS (aor.start_time, aor.end_time)),
aoi_ta_join_group as (
select trajectory_id,array_agg(area_id)as areas_of_risk from aoi_ta_join
where area_id isnotnullgroupby trajectory_id)
update travel_path_agg
set area_of_risk = atjg.areas_of_risk
from aoi_ta_join_group atjg
where travel_path_agg.trajectory_id = atjg.trajectory_id;

至此,我们通过每日定时任务,拿到了轨迹聚合信息与经过的关键区域信息,基于示例数据的查询结果如下,其中 area_of_risk 是一个数组,保存了经过关键区域的 area_id

这里需要注意,定时任务的聚合计算会有更高的效率,但同时其只判断行程起止时间和风险区域的时间是否相关,可以说是进行了一次初筛。初筛不会漏掉风险信息,但是可能会造成信息的误判,要再执行精准查询来确定,初筛最大的作用是过滤掉明细无风险的条目。但由于精准查询要查询最底层的时序表,数据量非常大,如果不进行初筛会造成非常大的资源消耗,远远大于初筛和精准查询的组合。精准查询基于初筛表,执行逻辑如下:

with path_of_interest_detail as
(select ts,ti.target_id,trajectory_id,ST_AsText(position),
        area_of_risk.area_detail,area_of_risk.area_id,
        area_of_risk.start_time,area_of_risk.end_time
 from
    (select target_id,area_of_risk
     from travel_path_agg
     where array_length(area_of_risk,1) >= 1)ti
 leftjoin travel_path_ts
 on ti.target_id = travel_path_ts.target_id
 leftjoin area_of_risk
 on array_position(ti.area_of_risk,area_of_risk.area_id)>0
and area_of_risk.start_time <= travel_path_ts.ts
nd travel_path_ts.ts <= area_of_risk.end_time
andposition && area_of_risk.area_detail),
aoi_ta_join_group as
(select trajectory_id,array_agg(area_id) as areas_of_risk
from path_of_interest_detail
where area_id isnotnull
groupby trajectory_id),
acture_result as
(select ti.trajectory_id,areas_of_risk
 from aoi_ta_join_group
 rightjoin
(select trajectory_id
 from travel_path_agg
 where array_length(area_of_risk,1) >= 1)ti
 on ti.trajectory_id = aoi_ta_join_group.trajectory_id)
update travel_path_agg
set area_of_risk = ar.areas_of_risk
from acture_result ar
where travel_path_agg.trajectory_id = ar.trajectory_id;

结果如下,可以看到,初筛中因时间范围不够精细而误判的 Tar-1 风险关联被正确的排除,但真正关联的 Tar-0 不会因此被忽略,从结果上看,与样例图中的轨迹经过关系一致:

值得一提的是,上述任务可以的执行时机不仅限于行程数据的输入,在风险区域新增时亦可执行来根据新的风险区域数据更新 travel_path_agg 表,让查询的结果具备更高的实效性。

3.5 查询经过指定区域的轨迹信息

select trajectory_id from travel_path_agg
where array_position(area_of_risk,2::bigint) > 0;
-- 2::bigint 指 area_id,转换成bigint类型以适应数组元素类型。
-- 整个查询的核心是判断经过关系,也可以加入其他列的查询与其他 where 条件限制。

3.6 小结

经过上述步骤,我们初步实现了轨迹空间经过关系批量处理的数据框架。实际上还会更复杂,比如经过的判定与风险区域的时间窗口更新机制等,均可基于相同的思路来实现。

总结

YMatrix 作为一款轻量级的分布式超融合数据基座,与时空交联类应用复杂、性能挑战大的场景匹配。这些系统基于 YMatrix 来实现不但可以达到甚至超出性能预期,在实施和后续的运维中也将事半功倍。让使用者从繁琐的数据底层抽身,更关注业务本身,更好的完成任务。

附录:

YMatrix 快速安装:快速安装

本文为 YMatrix 原创内容,未经允许不得转载。

欲了解更多超融合时序数据库相关信息,请访问 “YMatrix 超融合数据库” 官方网站

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

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

相关文章

adb-linux 调试桥

这里写自定义目录标题 摘要&#xff1a;一、简介二、adb使用参考连接 摘要&#xff1a; adb 可替代网络、串口等调试手段&#xff0c;可以方便的进行文件传输、终端登录等 一、简介 ADB的全称为Android Debug Bridge&#xff0c;即调试桥&#xff0c;方便调试设备或调试开发…

(值得收藏)境外投资备案申请指南

随着全球化的不断深入&#xff0c;越来越多的企业开始寻求境外投资的机会。然而&#xff0c;在进行境外投资前&#xff0c;需要进行备案手续&#xff0c;以确保投资的合法性和可行性。 境外投资备案条件&#xff1a; 具有完全民事行为能力的法人或自然人。拥有足够的资金和实力…

CK_Label-V23(Battery)System Developer‘s Manual

Query PTL Tags Information Introduction to the API&#xff1a; Query all PTL tags information Basic Information&#xff1a; Attributes 接口信息 Status Finished URL http://localhost/wms/associate/getTagsMsg Request Method GET/POST Content-Type …

Shader变体自定义组合压缩方案

前言 本篇文章不讲什么是变体&#xff0c;不讲shader_feature和multi_compile的区别&#xff0c;也不讲如何收集变体。 关于什么是变体&#xff0c;如何优化变体&#xff0c;看这篇文章 Shader&#xff1a;优化破解变体的“影分身”之术 - 知乎 (zhihu.com) 关于变体的收集…

自然语言处理历史史诗:NLP的范式演变与Python全实现

目录 一、引言什么是自然语言处理&#xff1f;语言与人类思维自然语言的复杂性NLP的历史轨迹 二、20世纪50年代末到60年代的初创期符号学派重要的研究和突破 随机学派重要的研究和突破 三、20世纪70年代到80年代的理性主义时代基于逻辑的范式重要的研究和突破 基于规则的范式重…

亚马逊云科技数据分析为这伴科技赋能,实现“零”中断目标

当前&#xff0c;利用数据分析能力赋能精准营销逐渐成为全球企业的主流趋势之一&#xff0c;然而复杂的基础设施和运维压力也不容忽视&#xff0c;因此如何才能构建行之有效的数据分析平台&#xff0c;支撑业务营销服务&#xff0c;实现与客户的共创互赢&#xff1f; 数字营销时…

Spring MVC入门必读:注解、参数传递、返回值和页面跳转的关键步骤

目录 引言 一、常用注解 1.1.RequestMapping 1.2.RequestParam 1.3.RequestBody 1.4.RequestHeader 1.5.PathVariable 二、参数传递 2.1.基础类型String 2.2.复杂类型 2.3.RequestParam 2.4.PathVariable 2.5.RequestBody 2.6.RequestHeader 三、返回值 3.1.vo…

jdk软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 JDK&#xff08;Java Development Kit&#xff09;是Java开发工具包的缩写&#xff0c;是Java开发人员必备的软件。它提供了一系列的工具和库&#xff0c;用于开发、编译、调试和运行Java应用程序。 JDK的主要组成部分包括&#…

手把手教你写一个简单的ioc容器

Ioc IOC&#xff08;控制反转&#xff09; 就是 依赖倒置原则的一种代码设计思路。就是把原先在代码里面需要实现的对象创建、对象之间的依赖&#xff0c;反转给容器来帮忙实现。 Spring IOC容器通过xml,注解等其它方式配置类及类之间的依赖关系&#xff0c;完成了对象的创建和…

wu-ui 多平台快速开发的UI框架

WU-UI 多平台快速开发的UI框架(无论平台&#xff0c;一致体验) 官方群 wu-ui官方1群: 767943089 说明 wu-ui(如虎添翼) 是 全面兼容多端的uniapp生态框架&#xff0c;基于vue2、vue3和nvue开发。丰富组件库&#xff0c;便捷工具库&#xff0c;简单高效。无论平台&#x…

COMSOL Multiphysics软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 COMSOL Multiphysics是一款基于有限元分析&#xff08;FEA&#xff09;的多物理场仿真软件。它提供了一个强大的平台&#xff0c;用于模拟和优化各种物理现象和工程问题。 COMSOL Multiphysics具有广泛的应用领域&#xff0c;包括…

redis持久化、主从和哨兵架构

一、redis持久化 1、RDB快照&#xff08;snapshot&#xff09; redis配置RDB存储模式&#xff0c;修改redis.conf文件如下配置&#xff1a; # 在300s内有100个或者以上的key被修改就会把redis中的数据持久化到dump.rdb文件中 # save 300 100# 配置数据存放目录&#xff08;现…

【前端】场景题:如何在ul标签中插入多个节点 使用文档片段

直接插入的问题&#xff1a;会回流多次。每插入一次li就会回流一次&#xff0c;消耗性能。 这里可以使用文档片段来解决这个问题。 // 创建文档片段 let node document.createDocumentFragment()DocumentFragment节点存在于内存中&#xff0c;并不在DOM中&#xff0c;所以将子…

VR农学虚拟仿真情景实训教学演示

首先&#xff0c;VR农学虚拟仿真情景实训教学提供了更为真实的实践环境。传统的农学实训往往受制于时间、空间和资源的限制&#xff0c;学生只能通过观察或简单的模拟来学习农业知识和技能。而借助虚拟现实技术&#xff0c;学生可以进入虚拟农场&#xff0c;与各种农作物、工具…

直播软件app开发流程全解析

直播软件app开发是当今互联网行业中备受瞩目的领域。随着移动用户的爆发式增长和即时互动的需求日益增加&#xff0c;开发一款高质量的直播应用已经成为各个企业和个人创作者追逐的目标。本文将深入探讨直播软件app开发的全过程&#xff0c;为您揭示开发直播应用的关键步骤&…

uni-app语音转文字功能demo(同声传译)

目录 首先去微信开发者官网申请一下同声传译的插件 微信公众平台 在文件中开始引用&#xff1a; 首先去微信开发者官网申请一下同声传译的插件 微信公众平台 后续使用的时候可以看详情里面的信息进行使用 在文件中开始引用&#xff1a; 注意&#xff01;&#xff01;在这个…

【RuoYi移动端】uni-app中通过vuex的store来实现全局变量的修改和读取

一、在store文件中新建csjVar.js文件 const csjVar {csjMess: [{aaa:"ok"},{bbb:"no"}] } export default csjVar 二、修改store文件中新建index.js文件 import Vue from vue import Vuex from vuex import user from /store/modules/user import gette…

对于array的.toLocaleString()与.flat()区别

最近都使用到这两个方法&#xff0c;对于array。记录下具体区别 先是他们的简介,我这里用的就是string的.toLocaleString() &#xff0c;因为array的就是分别去调用里面的.toLocaleString() 在拼接成 string.toLocaleString() string[].toLocaleString()方法是一个JavaScrip…

解决Maven依赖下载问题:从阿里云公共仓库入手

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Vue 2 条件渲染

条件渲染相关的指令有哪些&#xff1f; v-if、v-else、v-else-ifv-show v-if 的作用 <div v-if"expression"></div>v-if 根据表达式 expression 返回的值是否为 truthy 来决定其内容是否被渲染。 Vue还实现了 v-else 和 v-else-if&#xff1a; <d…