Java中使用JTS实现WKT字符串读取转换线、查找LineString的list中距离最近的线、LineString做缓冲区扩展并计算点在缓冲区内的方位角
场景
Java中使用JTS对空间几何计算(读取WKT、距离、点在面内、长度、面积、相交等):
Java中使用JTS对空间几何计算(读取WKT、距离、点在面内、长度、面积、相交等)_jts-core_霸道流氓气质的博客-CSDN博客
Java+GeoTools实现WKT数据根据EPSG编码进行坐标系转换:
Java+GeoTools实现WKT数据根据EPSG编码进行坐标系转换_霸道流氓气质的博客-CSDN博客
基于gis的业务场景中,需要在地图中录入区域数据的wkt数据,然后根据某个坐标点判断是属于哪个区域,
以及距离所属区域中最近的端点的方位角,比如坐标点位于某区域东南方向100米。
注:
博客:
霸道流氓气质_C#,架构之路,SpringBoot-CSDN博客
实现
1、参考上面引入jts的依赖。
首先数据库中存储的所有线的WKT数据为

其中region_name为线的名称,region_wkt为线的wkt字符串。
首先从数据库中读取所有的wkt字符串数据,并转换为map类型数据方便处理以及赋值线的名称到linestring的userData字段。
List<LineString> regionList = new ArrayList<>();Map<String, List<LineString>> regionMap = new HashMap<>();//读取录入的区域位置信息RegionManagement param = RegionManagement.builder().deleteFlag(false).build();List<RegionManagement> regionManagements = regionManagementMapper.selectList(param);for (RegionManagement regionManagement : regionManagements) {LineString lineString = readWKT(regionManagement.getRegionWKT());RegionDTO regionDTO = JSON.parseObject(JSON.toJSONString(regionManagement), RegionDTO.class);regionDTO.setUpdateTime(regionManagement.getUpdateTime().toString());lineString.setUserData(regionDTO);regionList.add(lineString);}//将区域list流处理为map,方便快速查找Map<String, List<RegionManagement>> collect = regionManagements.stream().collect(Collectors.groupingBy(RegionManagement::getRegionName));for (String name : collect.keySet()) {List<LineString> tmp = new ArrayList<>();collect.get(name).forEach(item -> tmp.add(readWKT(item.getRegionWKT())));regionMap.put(name, tmp);}
这里的RegionManagement用来读取数据库中存储的wkt字符串等数据,实现为
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class RegionManagement {private Long id;private String regionName;private String regionWKT;// 0 false ; 1 trueprivate boolean deleteFlag;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date updateTime;}
调用读取wkt字符串并转换为jts的LineString对象的方法readWKT实现为
//读取wkt数据为LineStringpublic LineString readWKT(String regionWKT){GeometryFactory fact = new GeometryFactory();WKTReader reader = new WKTReader(fact);LineString geometry1 = null;try {geometry1 = (LineString) reader.read(regionWKT);} catch (ParseException e) {e.printStackTrace();}return geometry1;}
中间获取所需要的数据的RegionDTO的实现为
import lombok.Data;@Data
public class RegionDTO {private Long id;private String regionName;private String updateTime;
}
2、将要判断方位的坐标值声明为Point2D对象
//目标点位Point2D.Double carPoint = new Point2D.Double(36582834.745, 4259820.7951);
3、获取距离目标点位最近的线
//获取离目标点位最近的线LineString lineString = findNearestLine(carPoint, 10D, regionList);
这里调用的findNearestLine方法的实现
//查找最近的线,jts工具做线的缓冲区,扩展宽度为10public LineString findNearestLine(java.awt.geom.Point2D.Double point, Double FuzzyLookupRange, List<LineString> lineStringList) {Point a = createPoint(point.getX(), point.getY());return lineStringList.parallelStream().filter((lineString) -> lineString.buffer(FuzzyLookupRange).contains(a)).min((o1, o2) -> {Double ax = o1.distance(a);Double axx = o2.distance(a);return ax.compareTo(axx);}).orElse(null);}
这里调用了createPoint用来创建point对象
//根据坐标x y创建点对象public static Point createPoint(Double x, Double y) {GeometryFactory a = JTSFactoryFinder.getGeometryFactory();return a.createPoint(new Coordinate(x, y));}
然后使用lineString.buffer方法对线做缓冲区,扩展宽度为10,即将线向外扩充成类似区域的概念,判断点是否在扩充后
的区域内,如果有多个区域,则取距离最小的一个。
LineString.buffer方法的使用可参考:
Geometry (JTS Topology Suite 1.13 API) - Javadoc Extreme)
Computes a buffer area around this geometry having the given width. The buffer of a Geometry is the Minkowski sum or difference of the geometry
with a disc of radius abs(distance).
Mathematically-exact buffer area boundaries can contain circular arcs.
To represent these arcs using linear geometry they must be approximated with line segments.
The buffer geometry is constructed using 8 segments per quadrant to approximate the circular arcs. The end cap style is CAP_ROUND.
The buffer operation always returns a polygonal result. The negative or zero-distance buffer of lines and points is always an empty Polygon.
This is also the result for the buffers of degenerate (zero-area) polygons.
直译:
计算具有给定宽度的几何体周围的缓冲区。几何体的缓冲区是具有半径为abs(距离)的圆盘的几何体的Minkowski和或差。
数学上精确的缓冲区边界可以包含圆弧。要使用线性几何图形表示这些圆弧,必须使用线段对其进行近似。
缓冲区几何结构使用每个象限8个线段来近似圆弧。端盖样式为cap_ROUND。
缓冲区操作总是返回多边形结果。直线和点的负或零距离缓冲区始终为空多边形。
这也是退化(零面积)多边形缓冲区的结果。

然后获取距离最近的线的名称并输出
//获取离目标点位最近的线LineString lineString = findNearestLine(carPoint, 10D, regionList);String regionName = "区域位置为空";if (lineString != null) {RegionDTO userData = (RegionDTO) lineString.getUserData();regionName = userData.getRegionName();}System.out.println(regionName);
4、获取坐标点相对于该线的方位角
String azimuth;if (!regionName.equals("区域位置为空")) {List<LineString> lineStringList = regionMap.get(regionName);LineString closeLine;if (lineStringList.size() > 1) {closeLine = findNearestLine(carPoint, 10D, lineStringList);} else {closeLine = lineStringList.get(0);}//获取线的两个端点Point startPoint = closeLine.getStartPoint();Point endPoint = closeLine.getEndPoint();//获取点位到两个端点的距离double startDistance = startPoint.distance(createPoint(carPoint.getX(), carPoint.getY()));double endDistance = endPoint.distance(createPoint(carPoint.getX(), carPoint.getY()));//获取较近的点作为参考点判断方位距离if (startDistance <= endDistance) {//获取方位角azimuth = regionName + DirectionUtil.getAzimuth(startPoint.getX(), startPoint.getY(), carPoint.getX(), carPoint.getY()) + "方向路口" + BigDecimal.valueOf(startDistance).intValue() + "米";} else {azimuth = regionName + DirectionUtil.getAzimuth(endPoint.getX(), endPoint.getY(), carPoint.getX(), carPoint.getY()) + "方向路口" + BigDecimal.valueOf(endDistance).intValue() + "米";}} else {azimuth = "[" + carPoint.getX() + "," + carPoint.getY() + "]";}System.out.println(azimuth);
其中获取方位角的工具类DirectionUtil.getAzimuth实现
import org.locationtech.jts.geom.LineSegment;public class DirectionUtil {/*** 笛卡尔坐标系*/enum DirectionEnum {DUE_EAST("正东", "==0 || ==360"),DUE_NORTHEAST("东北", "==45"),DUE_NORTH("正北", "==90"),NORTH_NORTHWEST("西北", "90<theta<135"),DUE_WEST("正西", "==180"),WEST_SOUTHWEST("西南", "180<theta<225"),DUE_SOUTH("正南", "==270"),DUE_SOUTHEAST("东南", "==315");private String direction;private String describe;DirectionEnum(String direction, String describe) {this.direction = direction;this.describe = describe;}public String getDirection() {return direction;}public void setDirection(String direction) {this.direction = direction;}public String getDescribe() {return describe;}public void setDescribe(String describe) {this.describe = describe;}}/*** 获取方位角** @param x1 观测点x* @param y1 观测点y* @param x2 目标点x* @param y2 目标点y* @return 返回距离观测点的方位角*/public static String getAzimuth(double x1, double y1, double x2, double y2) {LineSegment lineSegment = new LineSegment(x1, y1, x2, y2);double angle1 = lineSegment.angle();double angle = Math.toDegrees(lineSegment.angle());if (angle < 0) {angle = angle + 360;}if ((0 < angle && angle < 12.5) || (347.5 < angle && angle < 360)) {return DirectionEnum.DUE_EAST.getDirection();} else if (12.5 < angle && angle < 77.5) {return DirectionEnum.DUE_NORTHEAST.getDirection();} else if (77.5 < angle && angle < 102.5) {return DirectionEnum.DUE_NORTH.getDirection();} else if (102.5 < angle && angle < 167.5) {return DirectionEnum.NORTH_NORTHWEST.getDirection();} else if (167.5 < angle && angle < 192.5) {return DirectionEnum.DUE_WEST.getDirection();} else if (192.5 < angle && angle < 257.5) {return DirectionEnum.WEST_SOUTHWEST.getDirection();} else if (257.5 < angle && angle < 282.5) {return DirectionEnum.DUE_SOUTH.getDirection();} else if (282.5 < angle && angle < 347.5) {return DirectionEnum.WEST_SOUTHWEST.getDirection();} else {return "ERROR";}}
}
逻辑就是对比目标点到线的两个端点的距离,取较近的进行判断,然后做方位角判断。
运行效果测试

相关文章:
Java中使用JTS实现WKT字符串读取转换线、查找LineString的list中距离最近的线、LineString做缓冲区扩展并计算点在缓冲区内的方位角
场景 Java中使用JTS对空间几何计算(读取WKT、距离、点在面内、长度、面积、相交等): Java中使用JTS对空间几何计算(读取WKT、距离、点在面内、长度、面积、相交等)_jts-core_霸道流氓气质的博客-CSDN博客 JavaGeoTools实现WKT数据根据EPSG编码进行坐标系转换&…...
【异步VS多线程】异步VS多线程区别
异步VS多线程区别 1、异步 异步概念:异步是并发编程的一种形式,在同一时刻可以独立于主程序外,可以并发执行另外一些任务。异步的实现方式有两种: 第一种:通过 async TASK来实现异步,第二种:通…...
【nosql】redis之高可用(主从复制、哨兵、集群)搭建
redis群集有三种模式 redis群集有三种模式,分别是主从同步/复制、哨兵模式、Cluster集群,下面会讲解一下三种模式的工作方式,以及如何搭建cluster群集 ●主从复制:主从复制是高可用Redis的基础,哨兵和集群都是在主从…...
js如何实现数组去重的常用方法
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用 Set(ES6)⭐ 使用 filter 和 indexOf⭐ 使用 reduce⭐ 使用对象属性⭐ 使用 includes 方法(ES6)⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方…...
XREAL Air 2 Pro发布,加入电致变色技术,拓展AR眼镜使用场景
【2023年9月6日 中国北京】继刚刚宣布XREAL Air在全球销量突破20万台后,全球领先的消费级AR眼镜品牌XREAL今日于中国市场正式推出XREAL Air 2系列新品。全新Air 2系列包含两款AR眼镜产品:在显示、佩戴舒适性、音频等核心维度全面升级,体验全面…...
Go基础11-理解Go语言的包导入
Go语言是使用包(package)作为基本单元来组织源码的,可以说一个Go程序就是由一些包链接在一起构建而成的。虽然与Java、Python等语言相比这算不上什么创新,但与祖辈C语言的头文件包含机制相比则是“先进”了许多。 编译速度快是这种…...
【MySQL数据库原理】在MySQL Workbench界面运行SQL代码——学生管理系统
在 MySQL Workbench 8.0 中,你可以使用以下步骤新建内容并运行 MySQL 语言代码: 1、打开 MySQL Workbench 并连接到你的 MySQL 数据库服务器。 2、在左侧的导航栏中,展开你的连接以查看数据库。选择你要在其中运行 SQL 代码的数据库。 3…...
高分三号1米分辨率飞机检测识别数据集
二、背景介绍 合成孔径雷达(Synthetic Aperture Radar, SAR) 是一种主动式的微波成像系统,它不受光照、云雾 和气候等自然条件影响,具备全天时、全天候对地 观测的能力,已成为遥感领域重要的信息获取平 台。近年来,随着遥感成像技…...
Unity 之Material 类型和 MeshRenderer 组件中的 Materials 之间有一些重要的区别
文章目录 区别代码例子 区别 在Unity中,Material 类型和 MeshRenderer 组件中的 Materials 之间有一些重要的区别。 Material 类型: Material 是 Unity 中用来定义渲染属性的资源。它包含了一系列定义了如何绘制一个对象的属性,比如颜色、纹…...
【LeetCode-简单题】977. 有序数组的平方
文章目录 题目方法一:双指针方法二: 题目 方法一:双指针 class Solution { // 方法一 :双指针public int[] sortedSquares(int[] nums) {int left 0;int right nums.length -1 ;int[] res new int[nums.length];//结果集新数组…...
【笔试强训选择题】Day39.习题(错题)解析
作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!ÿ…...
Prometheus-Alertmanager 警报管理器-部署和设置
文章目录 一、介绍二、核心概念1 Grouping 分组2 Inhibition 抑制3 Silences 静默(静音)5 High Availability 高可用性 三、部署1 二进制方式下载配置 systemd 2 docker-compose 方式 四、配置1 配置文件介绍1.1 全局配置1.2 receiver 接收器标准接收器相…...
恒运资本:小盘股的优点?投资小盘股要注意哪些方面?
股市是一个充溢时机和危险的当地,不同出资者有不同的偏好,有的人喜爱追逐大盘蓝筹股,有的人则钟情于小盘股。那么小盘股的长处?出资小盘股要注意哪些方面?恒运资本也为我们准备了相关内容,以供参考。 小盘股…...
LeetCode:2. 两数之和
这个解题思路来自代码随想录:代码随想录 (programmercarl.com) class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {std::unordered_map <int,int> map;for(int i 0; i < nums.size(); i) {// 遍历当前元素&am…...
OpenCV(二十四):可分离滤波
目录 1.可分离滤波的原理 2.可分离滤波函数sepFilter2D() 3.示例代码 1.可分离滤波的原理 可分离滤波的原理基于滤波器的可分离性。对于一个二维滤波器,如果它可以表示为水平方向和垂直方向两个一维滤波器的卷积,那么它就是可分离的。也就是说&#x…...
【JS进阶】防抖与节流
防抖与节流 1.防抖 1.1 为什么要防抖? 在项目中,有的操作是高频触发的,但是其实触发一次就好了,比如我们短时间内多次缩放页面,那么我们不应该每次缩放都去执行操作,应该只做一次就好。再比如说监听输入…...
【css】linear-gradient()的用法
linear-gradient() CSS函数创建一个由两种或多种颜色沿一条直线进行线性过渡的图像,其结果是<gradient>数据类型的对象,此对象是一种特殊的<image> 数据类型。 语法 /* 渐变轴为 45 度,从蓝色渐变到红色 */ linear-gradient(45deg, blue, red);/* 从右…...
java: 读取snakeyaml-1.26.jar各种jar包时出错; error in opening zip file
可能的问题 jar有问题idea没有权限等等其他问题。但执行后报错就是读取不了,还报error in opening zip file这个错。 解决问题 我的错就是jar包有问题。我先后进行了很多次把jar包位置里的东西全部删除,然后重新maven下载但是不管用。最后从网站上下载…...
医疗知识图谱 neo4j
开源项目: https://github.com/liuhuanyong/QASystemOnMedicalKG 一.效果 二.需要安装: pip install pyahocorasick pip install py2neo 三.需要修改: 需要改的点: 1.改连接的方式 2.改读文件的方式 MedicalGraph 运行&am…...
【LeetCode-简单题】367. 有效的完全平方数
文章目录 题目方法一:二分查找 题目 方法一:二分查找 找 1 - num 之间的 mid, 开方是整数 就找得到 mid, 不是整数自然找不到mid class Solution { // 二分查找 ;找 1 - num 之间的mid 开方是整数 就找得到 不是…...
告别Arduino IDE:用Python玩转ESP8266,保姆级Micropython固件烧录与点灯实战
从Arduino到Micropython:用Python解锁ESP8266的物联网潜能 当硬件爱好者第一次接触Arduino时,往往会被其简单的开发方式所吸引。但随着项目复杂度提升,C/C的编译等待、内存管理和语法冗长开始成为创新路上的绊脚石。这就是为什么越来越多的开…...
如何一次性解决Windows系统“应用程序无法启动“的终极指南
如何一次性解决Windows系统"应用程序无法启动"的终极指南 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载…...
FreeRTOS在RISC-V上的第一个main.c:从创建任务到理解Hook函数的完整流程
FreeRTOS在RISC-V上的第一个main.c:从创建任务到理解Hook函数的完整流程 当你在RISC-V平台上第一次打开main.c文件准备编写FreeRTOS应用时,可能会被那些看似神秘的函数和配置选项所困扰。这篇文章将带你从零开始,逐步构建一个完整的FreeRTOS应…...
保姆级教程:用EEGLAB搞定脑电数据预处理,从导入到ICA去伪迹全流程避坑
零基础EEGLAB脑电预处理全流程:从数据导入到ICA去伪迹实战指南 当你第一次打开EEGLAB界面,面对密密麻麻的菜单选项和看似复杂的参数设置,是否感到无从下手?作为脑电研究的第一步,数据预处理的质量直接决定后续分析的可…...
Midjourney Anthotype印相工作流全拆解(含v6.1专属--style raw+自定义光照映射公式)
更多请点击: https://intelliparadigm.com 第一章:Anthotype印相工艺的历史溯源与数字转译本质 Anthotype(植物感光印相)是一种诞生于1839年的前摄影术实践,由英国科学家Sir John Herschel首次系统记录。它利用植物汁…...
LogExpert终极指南:Windows平台最强大的免费开源日志分析工具
LogExpert终极指南:Windows平台最强大的免费开源日志分析工具 【免费下载链接】LogExpert Windows tail program and log file analyzer. 项目地址: https://gitcode.com/gh_mirrors/lo/LogExpert LogExpert是Windows平台上最强大的免费开源日志分析工具&…...
如何用AI智能分层工具告别繁琐的PSD手动制作
如何用AI智能分层工具告别繁琐的PSD手动制作 【免费下载链接】layerdivider A tool to divide a single illustration into a layered structure. 项目地址: https://gitcode.com/gh_mirrors/la/layerdivider 还在为复杂的插画作品手动创建PSD分层文件而烦恼吗ÿ…...
爱快路由器下搞定水星AC跨三层管理AP:一个Option字段引发的抓包实战
爱快路由器下搞定水星AC跨三层管理AP:一个Option字段引发的抓包实战 当企业网络规模扩大,跨三层管理无线AP成为刚需。最近在帮客户部署水星(Mercury)无线AC控制器时,遇到了一个看似简单却耗费数小时才解决的"坑&q…...
C++性能优化
C性能优化是个系统工程,不是靠一两个“奇技淫巧”就能搞定的。我把它拆成四个层次来讲,从最立竿见影的到最底层的,你面试或实战时按这个框架去思考,思路会非常清晰。 第一层:算法与数据结构(性价比最高&…...
气象数据分析实战:用Python+cinrad从雷达基数据中提取组合反射率并可视化
气象数据分析实战:用Pythoncinrad从雷达基数据中提取组合反射率并可视化 雷达基数据是气象业务和科研中的宝贵资源,尤其在强对流天气监测和短临预报中发挥着关键作用。对于气象从业者来说,如何高效地从原始雷达数据中提取组合反射率…...
