R树判断点在多边形内-Java版本
1.什么是RTree
待补充
2.RTree java依赖
rtree的java开源版本在GitHub上:https://github.com/davidmoten/rtree
上面有详细的使用说明
最新版本的maven依赖可在中央仓库查到:https://mvnrepository.com/artifact/com.github.davidmoten/rtree
这里我们使用0.8.7版本
<!-- https://mvnrepository.com/artifact/com.github.davidmoten/rtree -->
<dependency>
<groupId>com.github.davidmoten</groupId>
<artifactId>rtree</artifactId>
<version>0.8.7</version>
</dependency>
3.使用
3.1创建R树
创建R树
//创建时,可以指定最小、最大孩子结点数,splitter,selector
RTree<String, Geometry> tree = RTree.minChildren(3).maxChildren(6).create();
创建R*树
R*树是R树的变种,是在节点分裂方法上做了改进的R树,这点后续再写篇博客详细介绍
RTree<String, Geometry> tree = RTree.star().maxChildren(6).create();
这样,第一步创建R树操作就完成了,是不是很简单!!!
3.2 往R树插入数据
可插入4种空间数据:点、线、圆、矩形
Geometries.Rectangle
Geometries.circle
Geometries.point
Geometries.line
为什么没有面数据呢?
- 面其实也是多个线的组合,只需要将多边形的边依次插入R树就行
//插入点数据
tree = tree.add("testPoint", Geometries.point(116.0D, 32.0D));
3.3 删除R树里的数据
删除的时候需要匹配名称和地理信息
//删除点数据
tree = tree.delete("testPoint", Geometries.point(116.0D,32.0D));
3.4 搜索
R树对空间信息的查找平均时间复杂度是O(logN),最坏情况下是O(N)
搜索方法返回的结果需要Observable
泛型
Observable<Entry<String, Point>> entries = tree.search(Geometries.rectangle(8, 15, 30, 35));
或者返回Iterable
类型
Iterable<Entry<String, Point>> result = tree.search(Geometries.point(116.11D, 31.11D))
.toBlocking().toIterable();
4. 应用:判断点是否在多边形内
说明:R树是用外接矩形判断点是否在矩形框内,只能用作粗筛,因此粗筛了一遍后,还需要用射线法做精确判断。有关射线法判断点在多边形内的方法可自行搜索。
假设业务场景是:我有很多个多边形数据,如商场AOI数据。现在需要通过用户的经纬度坐标判断用户在哪个商场里,从而按地理位置精准给用户推荐周边店铺营销信息。
这里的输入就是用户经纬度,输出是用户所在的商场。
设计
上文说过,R树要存多边形只能存它的边数据。这样操作之后,一个多边形就是一棵R树了,但是搜索是在多个多边形里找到能命中的,因此还需要再加一层R树,即用每个多边形的外接矩形构建父R树。详细设计如下:
构建R树
具体逻辑如下:
//北京市东城区和西城区的边界,这里只展示部分数据,非完全边界数据
private String dongcheng = "MULTIPOLYGON(((116.38059 39.871148,116.399097 39.872205,116.397612 39.898675,1116.38059 39.871148)))";
private String xicheng = "MULTIPOLYGON(((116.387658 39.96093,116.38678 39.957014,116.393346 39.957355,116.387658 39.96093)))";
private RTree<String, Rectangle> secondTree = RTree.minChildren(3).maxChildren(6).create();
public void build() {
List<CityDTO> sourceData = buildCityDTOs();
//1.对每个多边形,存入所有边构建一级R树
for (CityDTO sourceDatum : sourceData) {
RTree<String, Line> tree = RTree.minChildren(3).maxChildren(6).create();
List<List<Double>> polygon = GeoHelper.transfer2List(sourceDatum.getShape());
for (int i = 0; i < polygon.size(); i++) {
List<Double> nextPoints = polygon.get((i + 1) % polygon.size());
List<Double> points = polygon.get(i);
Double lng1 = points.get(0);
Double lat1 = points.get(1);
Double lng2 = nextPoints.get(0);
Double lat2 = nextPoints.get(1);
tree = tree.add(String.valueOf(i), Geometries.line(lng1, lat1, lng2, lat2));
}
//2. 将每个多边形的外接矩形构造为二级R树
secondTree = secondTree.add(sourceDatum.getName(), tree.mbr().get());
}
}
搜索
/**
* 输入点坐标,查询命中的多边形name
* @param lng
* @param lat
* @return
*/
public String search(Double lng, Double lat) {
Point point = Geometries.point(lng, lat);
//r树粗筛一遍
Iterator<Entry<String, Rectangle>> iterator = tree.search(point).toBlocking().toIterable().iterator();
//射线法对粗筛的多边形精确计算
while (iterator.hasNext()) {
Entry<String, Rectangle> entry = iterator.next();
String name = entry.value();
//获取多边形wkt
String wkt = localShapeCache.get(name);
//射线法判断
PointDTO p = new PointDTO();
p.setLng(lng);
p.setLat(lat);
if (isInPolygon(p, GeoHelper.transfer2List(wkt))) {
return name;
}
}
return null;
}
射线法判断点在多边形内
/**
* 射线法判断点是否在多边形内
* @param pointDTO
* @param polygon
* @return
*/
private boolean isInPolygon(PointDTO pointDTO, List<List<Double>> polygon) {
int nCross = 0;
for (int i = 0; i < polygon.size(); i++) {
List<Double> p1 = polygon.get(i);
List<Double> p2 = polygon.get((i + 1) % polygon.size());
Double lng1 = p1.get(0);
Double lat1 = p1.get(1);
Double lng2 = p2.get(0);
Double lat2 = p2.get(1);
//p1p2 与 y = p0.y平行
if (lng1.equals(lng2)) {
continue;
}
//交点在p1p2的延长线上
if (pointDTO.getLng() < Math.min(lng1, lng2)) {
continue;
}
//交点在p1p2的延长线上
if (pointDTO.getLng() >= Math.max(lng1, lng2)) {
continue;
}
// 求交点的X坐标
double x = (pointDTO.getLng() - lng1) * (lat2 - lat1) / (lng2 - lng1) + lat1;
if (x > pointDTO.getLat()) {
//只统计单边
nCross++;
}
}
//单边交点为奇数,点在多边形内
return (nCross % 2 == 1);
}
生成多边形的外接矩形
/**
* 获取多边形的外接矩形
* @param wkt
* @return
*/
public Rectangle buildRectFromWkt(String wkt) {
double minLng = 180.00;
double minLat = 90;
double maxLng = -180.00;
double maxLat = -90.00;
//wkt格式数据转为点 list
List<List<Double>> polygon = GeoHelper.transfer2List(wkt);
for (List<Double> points : polygon) {
Double lng = points.get(0);
Double lat = points.get(1);
if (lng < minLng) {
minLng = lng;
}
if (lng > maxLng) {
maxLng = lng;
}
if (lat < minLat) {
minLat = lat;
}
if (lat > maxLat) {
maxLat = lat;
}
}
return Geometries.rectangle(minLng, minLat, maxLng, maxLat);
}
R树判断点在多边形内-Java版本的更多相关文章
- 判断点在多边形内算法的C++实现
目录 1. 算法思路 2. 具体实现 3. 改进空间 1. 算法思路 判断平面内点是否在多边形内有多种算法,其中射线法是其中比较好理解的一种,而且能够支持凹多边形的情况.该算法的思路很简单,就是从目标 ...
- hdu 1756:Cupid's Arrow(计算几何,判断点在多边形内)
Cupid's Arrow Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...
- POJ 2318 TOYS | 二分+判断点在多边形内
题意: 给一个矩形的区域(左上角为(x1,y1) 右下角为(x2,y2)),给出n对(u,v)表示(u,y1) 和 (v,y2)构成线段将矩形切割 这样构成了n+1个多边形,再给出m个点,问每个多边形 ...
- zoj 1081 判断点在多边形内
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=81Points Within Time Limit: 2 Second ...
- php之判断点在多边形内的api
1.判断点在多边形内的数学思想:以那个点为顶点,作任意单向射线,如果它与多边形交点个数为奇数个,那么那个点在多边形内,相关公式: <?php class AreaApi{ //$area是一个多 ...
- ZOJ 1081 Points Within | 判断点在多边形内
题目: 给个n个点的多边形,n个点按顺序给出,给个点m,判断m在不在多边形内部 题解: 网上有两种方法,这里写一种:射线法 大体的思想是:以这个点为端点,做一条平行与x轴的射线(代码中射线指向x轴正方 ...
- hdu 1756 判断点在多边形内 *
模板题 #include<cstdio> #include<iostream> #include<algorithm> #include<cstring> ...
- A Round Peg in a Ground Hole - POJ 1584 (判断凸多边形&判断点在多边形内&判断圆在多边形内)
题目大意:首先给一个圆的半径和圆心,然后给一个多边形的所有点(多边形按照顺时针或者逆时针给的),求,这个多边形是否是凸多边形,如果是凸多边形在判断这个圆是否在这个凸多边形内. 分析:判断凸多边形可 ...
- matlab inpolygon 判断点在多边形内
如何判断一个点在多边形内部? xv= [0 3 3 0 0]; %x坐标 yv= [0 0 3 3 0];%y坐标 x=1.5; y=1.5; in=inpolygon(x,y,xv,yv) plot ...
随机推荐
- 生命周期内create和mounted的区别?
created: 在模板渲染成html前调用,即通常初始化某些数据,然后再渲染成视图. mounted: 在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作 ...
- 解释 JDBC 抽象和 DAO 模块?
通过使用 JDBC 抽象和 DAO 模块,保证数据库代码的简洁,并能避免数据库资源 错误关闭导致的问题,它在各种不同的数据库的错误信息之上,提供了一个统一 的异常访问层.它还利用 Spring 的 A ...
- 学习openstack(六)
VIII openstack(1) 传统的数据中心面临的问题:成本.效率(要快).管理方面(物理机.云主机): 云计算:对运维需求总体量是减少了,尤其是硬件工程师,对运维的技术要求提高了: 云计算是个 ...
- springboot使用redis实现发布与订阅
配置redis连接地址 # Redis服务器地址 spring.redis.host=youxiu326.xin # Redis服务器连接端口 spring.redis.port=6379 # Red ...
- 微信小程序wx.login()获取openid,附:前端+后端代码
微信小程序开放了微信登录的api,无论是个人还是企业申请的小程序均可使用. 首先创建一个项目,把这些代码都清空,我们自己写! 然后,开始写了!首先index.wxml,写一个button用于发起登录 ...
- 深入理解ES6之《用模块封装代码》
什么是模块 模块是自动运行在严格模式下并且没有办法退出运行的Javascript代码 在模块的顶部this的值是undefined 其模块不支持html风格的代码注释除非用default关键字,否则不 ...
- 前端基础之CSS(1)
1.css3的新特性有哪些 (1)CSS3选择器(基本.属性.伪类具体见下) (2)CSS3边框与圆角 圆角border-radius 属性:border-top-left-radius 左上角 bo ...
- .NET程序设计实验二
实验二 面向对象程序设计 一.实验目的 1. 理解类的定义.继承等面向对象的的基本概念: 2. 掌握C#语言定义类及其各种成员(字段,属性,方法)的方法: 3. 掌握方法覆盖的应用: 4. 掌握接口 ...
- break,return,continue的区别和作用
学习目标: 理解break.return.continue在循环中的区别和作用 学习内容: 1.break break表示结束当前所在的循环. 循环输出到3,当i等于4后,跳出当前循环,继续向下执行循 ...
- css让文字显示特定行数,多余的显示省略号
/*css*/ .p{ width: 200px; word-break: break-all; text-overflow: ellipsis; display: -webkit-box; /** ...