[Rust开发]在Rust中使用geos的空间索引编码实例

news2024/11/17 4:35:52

geos的空间索引用的是STRTree,这是一种基于STR算法的四叉树索引,有如下特点:

  • 使用Sort-Tile-Recursive (STR) 算法创建的仅查询的R-tree空间索引

STR(Sort-Tile-Recursive,递归网格排序) 基本思想是将所有的矩形以“tile”的方式分配到r/n(取上界)个分组中,此处的tile和网格类似。

此算法易于实现且适用范围较广,在大多数场景下表现良好,且易于推广到高维空间。

按照MBR中心点第一维坐标对数据点进行排序,利用S=sqrt(N/b)个垂直slice切割数据空间,使每个slice包含S个节点和S*b个MBR;

在每个垂直slice中,按照MBR中心点第二维坐标进行排序,每b个MBR一组压入节点;

递归进行上述步骤,直至生成整个RTree,每个slice的MBR数据不超过b。

图片

  • 该树索引每个几何图形的边界框。树在初始化时直接构建,且一旦创建后不能添加或移除节点

  • 所有操作返回输入几何图形的索引

  • 边界框限于二维并且是轴对齐的

    • 几何图形中存在的任何Z值在树内索引时都会被忽略。

注意:使用STRTree索引的话,只会构建几何的外接矩形边界为索引区域,所以计算两个几何的时候,仅进行外接矩形相交判定,官方原文如下:

图片

https://libgeos.org/usage/c_api/

在c/cpp中,该空间索引支持相交查询和距离查询,在Rust的geos绑定中,目前仅实现了相交查询。

具体使用方式如下:

let mut tree = STRtree::<&str>::with_capacity(10).unwrap();

let point = Geometry::new_from_wkt("POINT(5 5)").unwrap();
let line = Geometry::new_from_wkt("LINESTRING (0 0, 10 0)").unwrap();
let polygon = Geometry::new_from_wkt("POLYGON((2 2, 8 2, 8 8, 2 8, 2 2))").unwrap();

//insert可以把把几何要素放入空间索引中,附带一个唯一标识
tree.insert(&point, "Point");
tree.insert(&line, "Line");
tree.insert(&polygon, "Polygon");

//对tree进行迭代,相当于把里面item(也就是标识)给迭代出来了。
tree.iterate(|item|println!("{}", item));

//做查询的时候,实际上也是一个闭包迭代器,可以选择把命中的数据扔到一个hashset里面
//也可以直接在命中的流程中直接进行处理。
let mut items = HashSet::<&str>::new();
tree.query(&point, |item| {
    items.insert(*item);
});

注意,直接query,仅进行外接多边形判定,如下:这两个三角形本身是不想交的,但是它们的外接矩形是相交的

图片

let mut tree = STRtree::<&str>::with_capacity(10).unwrap();

let poly1 = Geometry::new_from_wkt("POLYGON((12 360, 360 360, 12 100, 12 360))").unwrap();
let poly2 = Geometry::new_from_wkt("POLYGON((12 90, 390 350, 390 100,12 90))").unwrap();

//insert可以把把几何要素放入空间索引中,附带一个唯一标识
tree.insert(&poly1, "poly1");
tree.insert(&poly2, "poly2");
tree.query(&poly1, |item| {
    println!("{:?}", item);
});

assert_eq!(poly1.intersects(&poly2).unwrap(), true);

查询和相交判定的结果分别如下:

即空间索引查询判定通过(poly1与自身,以及与poly2都查询到了),但是相交触发了断言,判定失败

所以,空间索引仅是通过外接矩形进行判定,如果要精确的进行空间关联判定,就需要在进行二次过滤,代码如下:

let mut tree = STRtree::<&str>::with_capacity(10).unwrap();
//定一个hashmap来承载所有数据
let mut poly_hash = HashMap::<&str,Geometry>::new();

let poly1 = Geometry::new_from_wkt("POLYGON((12 360, 360 360, 12 100, 12 360))").unwrap();
let poly2 = Geometry::new_from_wkt("POLYGON((12 90, 390 350, 390 100,12 90))").unwrap();

//insert可以把把几何要素放入空间索引中,附带一个唯一标识
tree.insert(&poly1, "poly1");
tree.insert(&poly2, "poly2");

poly_hash.insert("poly1",poly1.to_owned());
poly_hash.insert("poly2",poly2.to_owned());

tree.query(&poly1, |item| {
    //进行二次判定
    if poly1.intersects(poly_hash.get(*item).unwrap()).unwrap() {
        println!("{:?}", item);
    }
});

结果如下:

空间查询使用索引进行预先过滤,可以在查询结果量级不大的情况下,极大的提高效率。

下面通过一个例子来进行效率对比:

这是一个300 对 6万空间关联查询

图片

前景红色黑边的查询用的图层,后面灰度的是target图层。

核心代码如下:

读取数据

//功能说明略
fn get_geometry_by_shp(shp:&str)->HashMap<i64,Geometry>{
    let shp = shapefile::read_as::<_,
        shapefile::Polygon, shapefile::dbase::Record>(shp,
        ).expect("Could not open polygon-shapefile");

    let mut h:HashMap<i64,Geometry> = HashMap::new();
    for (polygon, polygon_record) in shp {
        let poly: geo::MultiPolygon<f64> = polygon.into();
        let geom = geos::Geometry::try_from(poly).unwrap();
        for record in polygon_record{
            if record.0 == "OBJECTID"{
                let oid = match record.1{
                    FieldValue::Numeric(Some(s)) => s as i64,
                    _=>0 as i64
                };
                
                h.insert(oid,geom.to_owned());
            }
        }
    }    
    h
}

使用空间索引的空间关联方法

fn test_spindex_demo_useidx()->HashMap::<i64,HashSet<i64>>{
    let target = get_geometry_by_shp("E:\\data\\dltb\\dltb6w.shp");
    let query_lyr = get_geometry_by_shp("E:\\data\\dltb\\dltb300.shp");
    let mut tree = STRtree::<i64>::with_capacity(target.len()).unwrap();

    let start = SystemTime::now();
    //构建空间索引
    for (oid, geom) in target.iter() {
        tree.insert(geom,*oid);
    }
    let mut res = HashMap::<i64,HashSet<i64>>::new();

    //用query_lyr图层,逐个进行迭代关联
    //内层先用tree进行索引过滤一次
    for q in query_lyr.iter(){
        let mut items = HashSet::<i64>::new();
        tree.query(q.1, |item| {
            let tr_geom:&Geometry = target.get(item).unwrap();
            if q.1.intersects(tr_geom).unwrap(){
                items.insert(*item);
            }
        });
        res.insert(*q.0, items);
    }
    let end = SystemTime::now().duration_since(start);
    println!("use index 计算完成 {:?}",end); 
    res
}

不用空间索引的方法

fn test_spindex_demo_nouse() ->HashMap::<i64,HashSet<i64>>{
    let target = get_geometry_by_shp("E:\\data\\dltb\\dltb6w.shp");
    let query_lyr = get_geometry_by_shp("E:\\data\\dltb\\dltb300.shp");

    let start = SystemTime::now();
    let mut res = HashMap::<i64,HashSet<i64>>::new();
    //用query_lyr图层,逐个进行迭代关联
    //直接暴力迭代
    for q in query_lyr.iter() {
        let mut items = HashSet::<i64>::new();
        for hs in target.iter(){
            if q.1.intersects(hs.1).unwrap(){
                items.insert(*hs.0);
            }
        }
        res.insert(*q.0, items);
    }
    let end = SystemTime::now().duration_since(start);
    println!("不用空间索引,计算完成 {:?}",end); 
    res
}

可以看见,两种方法,最大的不同的就是一个用了空间索引预先进行过滤,之后再用intersects进行二次判断;一个直接用intersects进行暴力迭代判断,测试方法如下:

#[test]
fn test_index_demo(){
    let useidx = test_spindex_demo_useidx();
    let nouse = test_spindex_demo_nouse();

    //对两个结果进行对比,如果不一致,会抛出assert
    for key in useidx.keys(){
        let u = useidx.get(key).unwrap();
        let n = nouse.get(key).unwrap();
        println!("key = {:?}  使用空间索引 = {:?}  不使用空间索引 = {:?}",key,u.len(),n.len());
        assert_eq!(u.len(),n.len());
    }
}

运行结果如下:

时间效率对比

使用空间索引(包括了构建空间索引的开销在内),比不用空间索引的效率高了10倍以上,如果数据量更大的话,差距更大。

结果对比:

没有触发断言,说明二者是一致的。

结论:空间索引真是个好东西……

图片

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

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

相关文章

使用Azure AI Search和LlamaIndex构建高级RAG应用

RAG 是一种将公司信息合并到基于大型语言模型 &#xff08;LLM&#xff09; 的应用程序中的常用方法。借助 RAG&#xff0c;AI 应用程序可以近乎实时地访问最新信息&#xff0c;团队可以保持对其数据的控制。 在 RAG 中&#xff0c;您可以评估和修改各个阶段以改进结果&#x…

大型企业高效内部协同,向日葵SDK私有化部署案例解析

大型集团企业的内部&#xff0c;沟通协作的重要性不言而喻&#xff0c;我们时常能听到关于所谓“大企业病”的吐槽&#xff0c;多数也是源于企业内部沟通协作效率低&#xff0c;进而导致内耗加重。甚至我们可以这么说&#xff0c;越是发展壮大的集团企业&#xff0c;其内部的沟…

力扣刷题学习(跟随视频学着刷)

使用入门 视频链接 【手把手带你刷Leetcode力扣&#xff5c;各个击破数据结构和算法&#xff5c;大厂面试必备技能【已完结】-哔哩哔哩】 https://b23.tv/vIcRT61 时空复杂度 时间&#xff1a; 空间&#xff1a;主要有O(1)和O(n)两种&#xff0c;只用计算开辟的内存&#xff…

3月份牛奶线上市场数据分析:销售额累计近18亿元

随着消费者健康意识的提升&#xff0c;牛奶作为日常营养补充品&#xff0c;其市场需求逐年增加。线上市场具有购买便捷性、价格透明性等优势&#xff0c;越来越多的消费者倾向于线上购买。 今年3月份&#xff0c;牛奶乳品市场呈现稳定发展状态。根据鲸参谋数据显示&#xff0c…

深度解析1688关键字搜索API接口:技术实现与应用探索

1688关键字搜索API接口的技术实现主要依赖于阿里巴巴开放平台提供的API服务。商家和开发者通过调用这些API接口&#xff0c;可以实现商品的搜索、排序、筛选、分页等功能。具体来说&#xff0c;技术实现主要包括以下几个步骤&#xff1a; 注册并获取API密钥&#xff1a;首先&am…

python爬虫插件XPath的安装

概要 XPath Helper是一款专用于chrome内核浏览器的实用型爬虫网页解析工具。XPath可以轻松快捷地找到目标信息对应的Xpath节点&#xff0c;获取xpath规则&#xff0c;并提取目标信息&#xff0c;并进行校对测试&#xff1b;可对查询出的xpath进行编辑&#xff0c;正确编辑的结…

一文解析golang中的协程与GMP模型

文章目录 前言1、线程实现模型1.1、用户级线程与内核级线程1.2、内核级线程模型1.3、用户级线程模型1.3、两级线程模型 2、GMP模型2.1、GMP模型概述2.1、GMP v1版本 - GM模型2.2、GMP v2版本 - GMP模型2.3、GMP相关源码2.4 调度流程2.5 设计思想 3.总结 前言 并发(并行&#x…

Golang-Gin 框架写的免杀平台,内置分离、捆绑等多种BypassAV方式

Golang-Gin 框架写的免杀平台&#xff0c;内置分离、捆绑等多种BypassAV方式 Golang-Gin 框架写的免杀平台&#xff0c;内置分离、捆绑等多种BypassAV方式。 cool 时间线&#xff1a; Golang Gin 框架写的免杀平台- (2021.11.12)Golang Gin 框架写的免杀平台&#xff0c;更…

CVPR往年论文查看

这个就是查看论文的网站&#xff1a;https://openaccess.thecvf.com/ 先别急&#xff0c; 看完再操作&#xff0c;听话&#xff01;&#xff01; 如果你要查看CVPR2022年的论文那么就在网站后加上CVPR2022 例如:https://openaccess.thecvf.com/CVPR2022 如果是2023年的那么…

赋能智慧校园!A3D数字孪生可视化,轻量又高效!

放假之后&#xff0c;学生们会逐步返学&#xff0c;大量人员出入校园&#xff0c;安全更是不容忽视&#xff0c;如何在短时间内对大批人员及设施进行智能监管&#xff1f;数字化转型是关键手段&#xff0c;我们可以融合线上线下数据&#xff0c;搭建3D立体的智慧校园&#xff0…

RoadBEV:鸟瞰图中的道路表面重建

1. 代码地址 GitHub - ztsrxh/RoadBEV: Codes for RoadBEV: road surface reconstruction in Birds Eye View 2. 摘要 本文介绍了RoadBEV&#xff1a;鸟瞰图中的道路表面重建。道路表面条件&#xff08;特别是几何形状&#xff09;极大地影响了自动驾驶汽车的驾驶性能。基于…

【C#】Stopwatch计时器

使用Stopwatch检查C#中代码块的执行时间&#xff0c;比如歌曲&#xff0c;图片的下载时间问题 首先&#xff0c;我们可看到Stopwatch 类内部的函数。 根据需求&#xff0c;我们具体可使用到 Start() 开始计时&#xff0c;Stop() 停止计时等 //创建 Stopwatch 实例 Stopwatch …

ELK 日志分析系统(二)

一、ELK Kibana 部署 1.1 安装Kibana软件包 #上传软件包 kibana-5.5.1-x86_64.rpm 到/opt目录 cd /opt rpm -ivh kibana-5.5.1-x86_64.rpm 1.2 设置 Kibana 的主配置文件 vim /etc/kibana/kibana.yml --2--取消注释&#xff0c;Kiabana 服务的默认监听端口为5601 server.po…

AWR报告采集

一、windows下采集 自动负载信息库&#xff08;Automatic Workload Repository&#xff0c;AWR&#xff09;是在Oracle 10g中被引入的&#xff0c;缺省地被安装到Oracle10g数据库中&#xff0c;用于收集关于该特定数据库的操作统计信息和其他统计信息。AWR的采样工作由后台进程…

2018.3SDK工具融合bit文件不成功_microblaze加载程序不成功

2018.3SDK工具融合bit文件不成功_microblaze加载程序不成功 描述: 正常开发流程是vivado端把PL端开发好了,导出HDF平台文件,然后SDK根据HDF文件创建工程,完成PS端的开发工作,这时候就要下载调试了,发现SDK的做法是把vivado导出的bit文件和SDK编译谁生成的elf文件进行了融…

Idea:通义千问插件

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、通义千问大模型 二、程序编写助手 三、Idea安装通义千问插件 总结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、通义千问大模型…

力扣HOT100 - 200. 岛屿数量

解题思路&#xff1a; 岛屿题目一般使用dfs。 1.判断是否越界 2.用0&#xff0c;1&#xff0c;2三个状态标识当前格子的状态&#xff08;三个状态比两个状态更清晰&#xff09; 3.向周围四个方向遍历 class Solution {public int numIslands(char[][] grid) {int cnt 0;fo…

SpringBoot - java.lang.NoClassDefFoundError: XXX

问题描述 以 json-path 为例&#xff1a;java.lang.NoClassDefFoundError: com/jayway/jsonpath/Configuration 原因分析 编译不报错&#xff0c;但是运行时报错。 遇到这样类似的问题&#xff0c;首先就要想到是不是 Jar 包冲突引起的&#xff0c;或者引入的不是理想的 Jar…

深度学习的炼金术:转化数据为黄金的秘密

深度学习的炼金术&#xff1a;转化数据为黄金的秘密 1 引言 在现代深度学习的壮阔疆域中&#xff0c;数据是王冠上耀眼的宝石&#xff0c;而性能优化则是锻造这顶王冠的炼金术。这份融合了数据和算法魔力的艺术&#xff0c;不仅仅依赖于强大的计算资源和复杂的网络结构&#x…

VScode使用cmake编译

一&#xff1a;输入 ctrlshiftp打开用于命令执行的输入框 二&#xff1a;输入cmake&#xff0c;选择quick start 模式 三&#xff1a;选择版本最高的gcc版本 四&#xff1a;输入项目名称 选择C 五&#xff1a;选择executable 这样便创建好了最简单的cmake例程&#xff0c;一个…