大哥们,别再用最大最小经纬度进行Between来做围栏查询啦

oracle多边形经纬范围筛选_电子围栏:根据经纬度判断一点是否在不规则多边形区域内... 一、不规则多边形:奇内偶外/***$lon 经度 string*$lat 维度 string*$coords 不规则多边形的点 json字符串*/function isPointInPolygon( $lon, $lat, $coords ){$wn = 0;$shift = false;if( $coords[0]['lat'] > $lat ) {$shift = true;}for( ... 阅读详情

需求背景

通过一组闭环的经纬度查询数据库中在此围栏内的数据。

原始做法

原来的做法是先计算出经纬度的最大值和最小值,然后用 BETWEEN 语句进行范围查询。具体实现代码如下:

/**
 * 构建最大经度、最大纬度、最小经度、最小纬度
 *
 * @param points
 * @return
 */
private SignalGridQueryVo buildMaxAndMinPoint(List<VerticesPoint> points) {
    BigDecimal maxLatitude = points.stream()
            .map(VerticesPoint::getLatitude)
            .max(BigDecimal::compareTo)
            .orElse(null);

    BigDecimal minLatitude = points.stream()
            .map(VerticesPoint::getLatitude)
            .min(BigDecimal::compareTo)
            .orElse(null);

    BigDecimal maxLongitude = points.stream()
            .map(VerticesPoint::getLongitude)
            .max(BigDecimal::compareTo)
            .orElse(null);

    BigDecimal minLongitude = points.stream()
            .map(VerticesPoint::getLongitude)
            .min(BigDecimal::compareTo)
            .orElse(null);

    return SignalGridQueryVo.builder()
            .maxLatitude(maxLatitude)
            .maxLongitude(maxLongitude)
            .minLatitude(minLatitude)
            .minLongitude(minLongitude)
            .build();
}

基于此,通过数据库查询以下数据:

SELECT * FROM table WHERE lon BETWEEN #{minLon} AND #{maxLon} AND lat BETWEEN #{minLat} AND #{maxLat};

虽然查询结果似乎正确,但在地图上展示时,某些经纬度点却漂移到了围栏外部。问题原因可能如下:

问题分析

  1. 经度范围问题(跨越国际日期变更线)
    经度的范围是 -180 到 +180 度。当围栏跨越国际日期变更线(经度180°)时,简单的最大经度和最小经度查询会导致错误。例如,经度最大值可能是180°,最小值可能是-179°,但这两个值实际表示的是跨越经线的范围。

  2. 纬度网格的物理距离差异
    由于纬度的物理距离在地球不同纬度有所不同,简单的矩形范围查询可能不完全符合实际地理范围,特别是在高纬度地区,纬度差异较小的情况下,物理距离较大。

解决方案

为了解决以上问题,我们可以使用数据库中的空间索引,并基于点与多边形关系来查询数据,从而避免因经纬度范围计算带来的误差。

数据库表设计与空间索引

首先,在数据库中添加一个 POINT 类型的字段来存储经纬度信息,并创建空间索引以提高查询效率:

CREATE TABLE new_table ( id INT AUTO_INCREMENT PRIMARY KEY, pos_location POINT NOT NULL COMMENT '经纬度', -- 其他字段... SPATIAL KEY `idx_pos_location` (`pos_location`) );

查询语句

使用空间查询,基于多边形包含关系查询是否在围栏内。查询的 SQL 语句如下:

select * from table WHERE st_contains( ST_GeomFromText(#{queryVo.polygon, typeHandler=com.xxx.config.PolygonTypeHandler}), point(pos_longitude, pos_latitude) )

自定义类型转换器

为了支持上述查询,首先需要创建一个自定义的类型转换器 PolygonTypeHandler,将经纬度点列表转换为 POLYGON 格式:

@Slf4j
@MappedTypes(List.class)
public class PolygonTypeHandler extends BaseTypeHandler<List<VerticesPoint>> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<VerticesPoint> parameter, JdbcType jdbcType) throws SQLException {
        StringBuilder stringBuffer = new StringBuilder();
        stringBuffer.append("POLYGON((");
        for (VerticesPoint point : parameter) {
            stringBuffer.append(point.getLongitude().toString())
                        .append(" ")
                        .append(point.getLatitude().toString())
                        .append(",");
        }
        stringBuffer.deleteCharAt(stringBuffer.length() - 1);
        stringBuffer.append("))");
        ps.setString(i, stringBuffer.toString());
    }

    @Override
    public List<VerticesPoint> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return new ArrayList<>();
    }

    @Override
    public List<VerticesPoint> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return new ArrayList<>();
    }

    @Override
    public List<VerticesPoint> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return new ArrayList<>();
    }
}

VerticesPoint 类

VerticesPoint 类用于表示经纬度信息,代码如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class VerticesPoint {

    @GeoField
    private BigDecimal longitude;
    @GeoField
    private BigDecimal latitude;

    @Override
    public String toString() {
        return "POINT(" + longitude.toString() + " " + latitude.toString() + ")";
    }

    public VerticesPoint deepCopy() {
        return new VerticesPoint(longitude, latitude);
    }
}

插入数据

为了将经纬度信息存储到数据库中,需要使用自定义类型解析器 GeoPointTypeHandler:

@Slf4j
@MappedTypes(VerticesPoint.class)
public class GeoPointTypeHandler extends BaseTypeHandler<VerticesPoint> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, VerticesPoint parameter, JdbcType jdbcType) throws SQLException {
        log.info("执行了setNonNullParameter");
        ps.setString(i, parameter.toString());
    }

    @Override
    public VerticesPoint getNullableResult(ResultSet rs, String columnName) throws SQLException {
        log.info("执行了getNullableResult");
        String wkt = rs.getString(columnName);
        if (wkt == null) {
            return null;
        }
        String[] parts = wkt.replace("POINT(", "").replace(")", "").split(" ");
        BigDecimal longitude = new BigDecimal(parts[0]);
        BigDecimal latitude = new BigDecimal(parts[1]);
        return new VerticesPoint(longitude, latitude);
    }

    @Override
    public VerticesPoint getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        log.info("执行了getNullableResult");
        String wkt = rs.getString(columnIndex);
        if (wkt == null) {
            return null;
        }
        String[] parts = wkt.replace("POINT(", "").replace(")", "").split(" ");
        BigDecimal longitude = new BigDecimal(parts[0]);
        BigDecimal latitude = new BigDecimal(parts[1]);
        return new VerticesPoint(longitude, latitude);
    }

    @Override
    public VerticesPoint getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        log.info("执行了getNullableResult");
        String wkt = cs.getString(columnIndex);
        if (wkt == null) {
            return null;
        }
        String[] parts = wkt.replace("POINT(", "").replace(")", "").split(" ");
        BigDecimal longitude = new BigDecimal(parts[0]);
        BigDecimal latitude = new BigDecimal(parts[1]);
        return new VerticesPoint(longitude, latitude);
    }
}

数据插入 SQL

插入数据时,使用自定义类型处理器来处理经纬度字段:

INSERT INTO table ..., pos_location VALUES (ST_GeomFromText(#{}, #{}, ... #{entity.posLocation, typeHandler=com.xxx.config.GeoPointTypeHandler}));

总结

通过引入空间索引、空间查询以及自定义类型处理器,我们实现了更加精确且高效的围栏查询,解决了经纬度漂移问题。此方法避免了简单矩形范围查询带来的误差,特别适用于复杂的地理查询,同时保持了业务代码的简洁与优雅。

使用Mybatis向Mysql中的插入Point类型的数据全方位解析 从基础开始,解决你使用mybatis 到 mysql数据库Point类的插入问题。 阅读详情

相关推荐

编程范式的本质:阴阳二分,相融相异

本文源于一次真实的设计讨论,上片博客沿着四种语言实现的路子走下去,本篇博客则沿着设计模式的路线归结到计算机哲学的根。需求本身小得不能再小:给一个存放数据库读取结果的 `Dict` 列表按字段名排序。但当我们把约束加码到"字段演进时排序器永不改动"时,这个需求就不再是工程问题,而成了三种编程范式的正面交锋。沿着这条线走下去,我们最终抵达了一个更根本的结论——**编程范式不是一张并列的表格,而是一棵树,而树的根只有两个。**

i saw you walking in the rain 351

自定义typeHandler将包含经纬度对象插入到mysql的point类型的字段中

自定义typeHandler将包含经纬度对象插入到mysql的point类型的字段中

e_watermelons的博客 623

OpenCV视频防抖算法实现与优化指南

本文详细解析了如何使用OpenCV实现视频防抖算法。通过特征点检测与光流跟踪估算帧间运动,利用滑动平均平滑运动轨迹,并应用仿射变换重塑视频帧。文章提供了完整的代码实现、参数调优指南及黑边处理等优化技巧,帮助开发者高效处理手持拍摄等场景下的视频抖动问题。

weixin_29062823的博客 339

mysql+mybatis-plus+springboot使用代码生成器,生成point数据类型的,需要修改Mapper和实体类

mysql表:字段:position_point, 类型:point 生成的实体类: 生成的Mapper文件 这里发现生成的是Integer类型,所以需要修改实体类,以及对应的Mapper文件 修改后的实体类 注意:我这里用到的PointTypeHandler.class的包是:cn.changeforyou.location.mybatis.PointTypeHandler; 修改后的Mapper文件 修改完毕。 ...

qq_42991839的博客 2558

C#上位机实战:借助NetToPLCSim与西门子S7-1200仿真通信全攻略

本文详细介绍了如何使用C#开发上位机程序,通过NetToPLCSim与西门子S7-1200进行仿真通信的全过程。从环境准备、PLC仿真环境搭建、网络配置到C#代码实现,提供了完整的实战指南和常见问题解决方案,帮助开发者快速掌握工业自动化通信技术。

weixin_30596343的博客 440

拥有AI助手便能解放双手?——抖音直播脚本的生成

科技改变生活,AI助手帮你完成抖音创作!

Scientist_的博客 2352

Oracle静默安装,含仅软件安装和数据库安装及常见报错处理方法DBT-10317 DBT-05509 [FATAL] ORA-03114

本文详解Oracle 19c单机版静默安装与数据库创建全流程,涵盖目录配置、响应文件编写、安装及建库命令,附常见报错(如DBT-10317、ORA-03114)解析与解决方案,包含删库指令与日志分析,适合内网环境自学实战,免费干货,实用性强。

qq_41474349的博客 177

跨境TRS投资操作指南与系统解决方案

3 点击【申请 TRS 账户】,查看风险披露,录入账户参数、确定录入的参数信息,进行身份验证,输入验证码,TRS 账户开户申请已提交,后台对已提交的 TRS 开户申请进行审核,审核成功后,短信邮箱提醒,进行登录;2.点击【申请TRS账户】,查看风险披露,录入账户参数、确定录入的参数信息,进行身份验证,输入验证码,TRS账户开户申请已提交,后台对已提交的TRS开户申请进行审核,审核成功后,短信邮箱提醒,进行登录;4)更广泛的投资选择:相比A股通,TRS可以交易更多的A股标的,提供了更广泛的投资选择。

2401_85487119的博客 2849

Spring 事务详解:@Transactional 如何生效、何时回滚、传播行为怎么选

从订单与库存写入出发,拆解 @Transactional 的代理调用链、默认回滚规则与自调用问题;通过 Spring Boot + H2 的五个验证用例比较 REQUIRED、REQUIRES_NEW、NESTED,并说明数据库事务与 Redis、消息和远程接口的边界。

qq_74421990的博客 304

Matlab使用GitHub的踩坑记录(持续更新)

Matlab使用git的踩坑记录前言Matlab设置Git的官方帮助文档系统配置新建Git两种代码管理方式典型源代码管理SVN分布式源代码管理 Git注意:SVN和Git都可以用GitHub创建新的库 前言 作为Git初学者,对Git的代码版本管理非常非常喜欢,给了我极大的安全感。特别是想到之前的几次Matlab、Latex和python的代码莫名其妙被折腾没了又没法找回的悲剧,决定慢慢的走入gi...

voluntry的博客 7296

下载MNIST数据集并使用python将数据转换成NumPy数组(源码解析)

下载MNIST数据集并使用python将数据转换成NumPy数组首先来分析init_mnist函数接下来继续分析load_mnist函数实现数据集转换的python脚本的代码显示MNIST图像并确认数据 下载MNIST数据集并将数据转换成NumPy数组的Python脚本里面最重要的就是load_mnist函数,其他项目想要调用数据集的话,就可以调用load_mnist函数,得到一个字典类型的数据,字典的值是一个Numpy数组。 这些过程是如何实现的,现在开始逐字逐句分析源码: 在load_mnist函数中第

及时总结 4268

Oracle多租户集群PDB异机恢复至单机部署数据库

本文详细演示了将Oracle RAC集群中的PDB(RMAN_PDB)通过RMAN备份恢复至单机CDB的全过程。首先在源库创建测试PDB并插入数据,随后配置RMAN备份路径,备份该PDB及其元数据文件。接着将备份文件传输至单机环境,启动实例并恢复控制文件与数据文件,最后利用DBMS_PDB.EXPOSE_PDB_FROM_RMAN生成PDB描述文件,将其插入新单机CDB中,实现PDB的独立使用。整个流程涵盖数据准备、备份、恢复及插入,完整验证了跨环境PDB迁移的可行性。

qq_57492774的博客 171

YOLO26优化:注意力魔改 | SKAttention注意力,自适应地调整其感受野大小,效果优于SENet

SKAttention输入自适应地调整其感受野大小的能力

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 250

Mysql数据库基础篇(三):全面掌握数据库与数据表操作:深度剖析底层文件差异与核心管理机制

如果你想彻底改掉某个字段的名字,应该使用 change。实例:将 employee表中的 name列修改为xingming注意:使用 change时,即便不改变数据类型,在指定了新列名后依然需要完整地写一遍它的数据类型与约束定义。

2501_93641778的博客 234

基于STM32+华为云IOT设计的云平台监控系统_102

本次设计结合现代生活的具体需求,设计出一套基于华为云IOT设计的云平台监控系统,可以远程实时查看家里光照强度、温湿度等信息,支持在云端控制家里的电器设备,实现远程控制。

7643

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

智叟云集系统 · 数据分层与管理体验

智叟云集系统 · 数据分层与管理体验 · 数据分层与管理体验。

2301_77178996的博客 416

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

oracle直方图测试

预期:没有直方图时,优化器假设 status 分布均匀(选择性 = 1/NDV ≈ 1/100),两个查询的预估行数(E-Rows)会非常接近(约 1000 行),很可能对两个查询都选择索引扫描,或者都选择全表扫描,无法区分数据量的巨大差异。

ZHANG123HUO的博客 51

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Oracle 19c RAC ASM 在线扩容实战

最近有一套 Oracle 19c RAC 数据库,DATA 磁盘组空间使用率已经接近 90%,需要扩容。环境是两节点 RAC,底层使用 Dell EMC SAN 存储,通过 Multipath 管理共享磁盘,存储侧已经分配好一块 2TB 的新 LUN,只需要在操作系统上识别,然后加入 ASM 磁盘组。整个过程不需要停库,本文记录一下实际操作步骤。这次 Oracle 19c RAC ASM 扩容整体比较顺利。

LuciferLiu_DBA 288

15_MySQL 数据同步与迁移方案 —— 复制 / DataX / Canal / 异构同步(对标 Oracle GoldenGate)

15_MySQL 数据同步与迁移方案 —— 复制 / DataX / Canal / 异构同步(对标 Oracle GoldenGate) 系列定位 :《MySQL 数据库全栈运维实战系列》第 15 篇,与《Oracle 数据库全栈运维实战系列(VMware 版)》同构对标(Oracle 15 OG…

dk843108982的博客 233

任务8-1 数据库用户权限管理

本实战训练MySQL权限管理的完整流程。通过CREATE USER建用户,GRANT按需授权,SHOW GRANTS核查权限。岗位变动时,使用REVOKE回收多余权限,并用FLUSH PRIVILEGES刷新缓存。全流程旨在落实最小权限原则,规范数据库安全运维。

howard2005的专栏 153

带着小白学习-微服务-5 实战:配置中心-数据库连接

如前所述,服务治理主要包含注册中心,前面所有的微服务子工程均已注册到nacos,然后由相应的应用到nacos进行拉取。接下来,我们将使用nacos的配置中心,用于保存和分发应用运行所需的动态配置。比如数据库的各类信息等。

wdyan297的博客 211

SQLServer常用指令手册

覆盖数据库创建、表与约束、增删改查、查询进阶、索引、视图/存储过程/函数/触发器、事务、权限、运维排查。适用版本:SQL Server 2012 及以上(标注的条目为对应版本起可用)。

lzcbaba的博客 229

MySQL数据库和表的操作

本文系统介绍了MySQL数据库与表的基本操作。包括创建、查看、修改和删除数据库,重点讲解字符集与校验规则对大小写敏感性的影响;通过SHOW、ALTER、DROP等命令实现数据库管理;介绍数据备份(mysqldump)与恢复(SOURCE)方法;并详细阐述表的创建、结构查看(DESC)、字段增删改(ALTER TABLE)及表删除操作,涵盖字段类型、注释、索引等关键要素,为数据库开发与维护提供完整指导。

2201_75570391的博客 364

Oracle 19c 在线重定义后旧表空间清理及 ASM 存储资源回收 SOP

Oracle 19c 在线重定义后旧表空间清理与 ASM 存储资源回收 SOP

「YYL DBA手记」 数据库 · 架构 · 运维 · 实战 yyldba.blog.youkuaiyun.com 483

搭建Oracle RAC 集群

配置SSH互信,分别RAC1和RAC2的root,grid,Oracle三个用户下执行以下命令。安装LINUX.X64_193000_grid_home.zip文件包。添加6块硬盘,每块50G,添加控制器,设为虚拟,添加网络适配器。设置主机名,配置网卡,编写/etc/hosts文件()配置磁盘规则,执行writerule.sh脚本。运行test.sh脚本,完成基础配置。

x_SpiderMan的博客 44

【服务器数据恢复】RAID5阵列双盘掉线Oracle+OA系统的数据恢复案例

某医药公司服务器组建RAID5磁盘阵列,阵列内两块硬盘先后离线,直接造成服务器宕机。服务器操作系统为RedhatLinux,承载基于Oracle数据库搭建的OA业务系统,业务数据价值高、恢复时效要求紧急。由于该OA配套Oracle版本早已停止技术支持,客户需求为:完整恢复业务数据,同时尽可能复原原有操作系统环境。

beiya123的博客 127
上一篇: 提升效率的神器——uTools,程序员必备,试过的人都说好!
下一篇: 参数与返回值类型一致时如何返回
null.equals()
博客等级 码龄12年 209粉丝 46原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值