【LBS学习笔记2】RTree判断点在多边形内-Java版本

简介: RTree判断点在多边形内-Java版本

1.什么是RTree

待补充

2.RTree java依赖

rtree的java开源版本在GitHub上:R树-Java版本

上面有详细的使用说明

最新版本的maven依赖可在中央仓库查到:
maven仓库

这里我们使用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树.png

构建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);
    }
相关文章
|
3月前
|
安全 架构师 Java
Java LTS版本进化秀:从8到21的欢乐升级之旅
困惑于Java版本选择?轻松幽默地穿越Java LTS版本时光隧道,掌握从Java 8到21的关键特性。通过一家初创公司的系统升级故事,直观了解每个版本如何解决代码冗余、性能瓶颈等开发痛点,助你在技术选型中做出明智决策。
|
3月前
|
Java
Java 数组学习笔记
本文整理Java数组常用操作:遍历、求和、查找、最值及二维数组行求和等典型练习,涵盖静态初始化、元素翻倍、去极值求平均等实例,帮助掌握数组基础与应用。
|
3月前
|
小程序 Java 知识图谱
Java 学习笔记 —— BMI & BMR 计算器
这是一个使用 Java 编写的 BMI 与 BMR 计算器小程序,可输入年龄、性别、身高和体重,计算身体质量指数(BMI)和基础代谢率(BMR),并输出健康评估结果。通过该项目,掌握了 Java 的输入处理、数据验证、条件判断、数学运算及格式化输出等基础知识,是 Java 初学者的理想练习项目。
|
4月前
|
Cloud Native Java API
Java Spring框架技术栈选和最新版本及发展史详解(截至2025年8月)-优雅草卓伊凡
Java Spring框架技术栈选和最新版本及发展史详解(截至2025年8月)-优雅草卓伊凡
760 0
|
5月前
|
安全 Java API
Java 17 及以上版本核心特性在现代开发实践中的深度应用与高效实践方法 Java 开发实践
本项目以“学生成绩管理系统”为例,深入实践Java 17+核心特性与现代开发技术。采用Spring Boot 3.1、WebFlux、R2DBC等构建响应式应用,结合Record类、模式匹配、Stream优化等新特性提升代码质量。涵盖容器化部署(Docker)、自动化测试、性能优化及安全加固,全面展示Java最新技术在实际项目中的应用,助力开发者掌握现代化Java开发方法。
226 1
|
6月前
|
Java API 微服务
2025 年 Java 从入门到精通学习笔记全新版
《Java学习笔记:从入门到精通(2025更新版)》是一本全面覆盖Java开发核心技能的指南,适合零基础到高级开发者。内容包括Java基础(如开发环境配置、核心语法增强)、面向对象编程(密封类、接口增强)、进阶技术(虚拟线程、结构化并发、向量API)、实用类库与框架(HTTP客户端、Spring Boot)、微服务与云原生(容器化、Kubernetes)、响应式编程(Reactor、WebFlux)、函数式编程(Stream API)、测试技术(JUnit 5、Mockito)、数据持久化(JPA、R2DBC)以及实战项目(Todo应用)。
359 5
|
7月前
|
JavaScript Java 关系型数据库
家政系统源码,java版本
这是一款基于SpringBoot后端框架、MySQL数据库及Uniapp移动端开发的家政预约上门服务系统。
220 6
家政系统源码,java版本
|
9月前
|
存储 Java
# 【Java全栈学习笔记-U1-day02】变量+数据类型+运算符
本篇笔记主要围绕Java全栈学习的第二天内容展开,涵盖了变量、数据类型、运算符以及Scanner类的应用。首先介绍了变量的概念与命名规范,以及如何定义和使用变量;接着详细讲解了Java中的基本数据类型,包括整型、浮点型、字符型、布尔型等,并通过实例演示了数据类型的运用。随后,深入探讨了各类运算符(赋值、算术、关系、逻辑)及其优先级,帮助理解表达式的构成。最后,介绍了如何利用Scanner类实现用户输入功能,并通过多个综合示例(如计算圆面积、购物打折、变量交换及银行利息计算)巩固所学知识。完成相关作业将进一步加深对这些基础概念的理解与实践能力。
162 13
|
9月前
|
开发框架 Java 开发工具
【Java全栈学习笔记-U1-day01】Java介绍
本笔记整理了Java学习的基础内容,涵盖程序理解、Java语言特性、JDK安装与配置、Java程序开发工具及编写步骤。重点介绍了Java程序的基本结构、编译和运行过程,以及输出语句的使用。通过实例演示了IDEA创建Java程序的方法,并强调了编码规范和注意事项。适合初学者复习和交流学习。 主要内容: 1. 理解程序:计算机组成、程序定义。 2. 简介:Java语言特点、技术平台、JDK作用。 3. 编写Java程序:编写、编译、运行步骤,基本结构。 4. 输出语句 5. DEA使用:新建工程、保存位置、文件介绍、新建类。 6. 扩展:注释、代码规范、大小写敏感、缩进等。
|
10月前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
474 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡