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

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倍以上,如果数据量更大的话,差距更大。

结果对比:

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

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

图片

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

netsh int ipv4 show dynamicport tcp动态端口port设置

netsh int ipv4 show dynamicport tcp netsh int ipv4 set dynamicport tcp start4000 num10000

STM32_舵机的实战

一、配置相应的管脚 二、写代码

linux+ndk把jni制作成so库供apk使用(带线程的回调)

我们就不墨迹了,直接开始,往往我们需要jni给我们回调一些数据,并且是实时的回调,这里我们就需要多写一些东西了 1.先在安卓里面设置好接口以及回调,我自己给你们看源代码 package com.example.myndkapplicationimport android.os.Bundle import android.util.Log import androi…

基于Python实现心脏病数据可视化DEA+预测【500010103.1】

一、数据说明 该心脏病数据集是通过组合 5 个已经独立可用但以前未合并的流行心脏病数据集来策划的。在这个数据集中&#xff0c;5 个心脏数据集结合了 11 个共同特征&#xff0c;使其成为迄今为止可用于研究目的的最大心脏病数据集。 该数据集由 1190 个实例和 11 个特征组成…

wstunnel (websocket模式ssh)

接上一篇 修改客户端运行参数 ssh -o ProxyCommand"./wstunnel client -L stdio://%h:%p ws://192.168.254.131:8080" 127.0.0.1 其中127.0.0.1为服务端的本地ssh访问&#xff0c;可以修改为通过服务端访问其他设备的ssh服务。例如&#xff1a; ssh -o ProxyComma…

线性代数-行列式-p1 矩阵的秩

目录 1.定义 2. 计算矩阵的秩 3. 矩阵的秩性质 1.定义 2. 计算矩阵的秩 3. 矩阵的秩性质

JavaEE——spring MVC请求处理

目录 主要目的&#xff1a; 1. Spring web 项目搭建 2. 添加依赖 3. 配置插件 4. 配置设置类 5. 编写controller层类 6. 编写测试的http请求 主要目的&#xff1a; 创建一个spring web项目&#xff1b; 创建控制类&#xff1b; 掌握如何配置MVC&#xff1b; 编写htt…

HTTP 网络协议的请求头信息,响应头信息,具体详解(2024-04-26)

1、通用头部 2、常见的 HTTP请求头信息 HTTP 响应头信息是服务器在响应客户端的HTTP请求时发送的一系列头字段&#xff0c;它们提供了关于响应的附加信息和服务器的指令。 3、常见的 HTTP 响应头信息 响应头向客户端提供一些额外信息&#xff0c;比如谁在发送响应、响应者的功…

AI预测福彩3D第9套算法实战化测试第3弹2024年4月25日第3次测试

今天继续进行新算法的测试&#xff0c;今天是第3次测试。好了&#xff0c;废话不多说了&#xff0c;直接上图上结果。 2024年4月25日福彩3D预测结果 6码定位方案如下&#xff1a; 百位&#xff1a;6、4、3、7、2、8 十位&#xff1a;8、4、9、3、1、0 个位&#xff1a;7、6、9、…

审稿快、出版效率高的8本检验医学中文期刊推荐!

常笑医学整理了8本比较好投的检验医学中文期刊&#xff0c;以及期刊详细参数&#xff0c;供大家在论文投稿时参考。这些检验医学期刊&#xff0c;审稿快、出版效率高&#xff0c;有需要的赶紧收藏&#xff01; 1.《中华检验医学杂志》 &#xff08;详细投稿信息请点击刊物名称…

【网站项目】考研助手

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

uniapp-css:拼图(不规则图片拼插)、碎片

拼图案例样式 高斯模糊的地方可以对应的使用fliter属性和opacity来调节样式。 其余碎片和图片对应: 这段代码实现了一个拼图效果的Vue组件。以下是对代码的详细解析: 模板部分: 在模板中使用v-for指令遍历imgs数组中的每个图片对象,为每个图片创建一个元素。 使用:cla…

Day 31 贪心算法理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和

贪心算法理论基础 ​ 贪心算法的本质&#xff1a;选择每一个阶段的局部最优&#xff0c;从而达到系统的整体最优&#xff1b; ​ 贪心的套路就是没有套路&#xff0c;最好的策略就是举反例&#xff0c;因为大多数时候并不要求严格证明&#xff0c;只需要得到普遍性结论即可&a…

优化大模型的解释性提示以提升文本推理性能:一种无监督数据驱动的方法

介绍一篇大模型前沿论文&#xff0c;《Explanation Selection Using Unlabeled Data for Chain-of-Thought Prompting》。在这篇论文中&#xff0c;作者Xi Ye和Greg Durrett探讨了如何通过优化大语言模型&#xff08;LLMs&#xff09;的解释性提示来提升文本推理任务的性能。他…

CSS 标准流 浮动 Flex布局

目录 1. 标准流2. 浮动2.1 清除浮动 3. Flex 布局3.1 Flex 组成3.2 Flex 布局 - 主轴与侧轴对齐方式3.2.1 主轴对齐方式3.2.2 侧轴对齐方式 3.3 Flex 布局 - 修改主轴方向3.4 Flex 布局 - 弹性伸缩比3.5 Flex 布局 - 弹性盒子换行3.6 Flex 布局 - 行对齐方式 1. 标准流 标准流…

OU和域用户的创建

OU和域用户的创建 导航 文章目录 OU和域用户的创建导航一、创建ou二、创建用户三、验证 一、创建ou 在服务器管理器里面点击右上角的工具,选择Active Directory 用户和计算机右击我们的域,选择新建,选择组织单位,并填入我们的单位名字 二、创建用户 右击我们刚刚新建的组织…

Linux(Centos)服务器探索ffmpeg笔记 (命令行、Nvidia硬件加速、GPU、CPU、CUDA、h264_nvenc、过滤器、加水印)

目录 前言内容简介为什么会有这篇文章 1、服务器上怎么使用ffmpeg1.1 使用编译好的&#xff08;需要root权限&#xff09;1.2 自己怎么编译&#xff08;需要root权限&#xff09; 2 、非Root用户要怎么安装和使用3、ffmpeg命令的一些使用引导和参数介绍3.1 编译参数3.2 查询支持…

解读六西格玛培训:企业为何不能忽视其重要性?

六西格玛培训&#xff0c;听起来可能是一个陌生的名词&#xff0c;但当深入探索其内涵后&#xff0c;会发现它实际上是企业追求卓越的必由之路。 想象一下&#xff0c;你正在驾驶一辆赛车&#xff0c;在赛道上追求极致的速度与精准。然而&#xff0c;每一个微小的失误都可能导致…

window平台C#实现软件升级功能(控制台)

window平台C#实现软件升级功能 之前用window窗体实现过一个升级功能&#xff0c;后来发现多个项目都需要升级功能&#xff0c;现改成可接收参数实现一种通用的exe.改用控制台方式实现这个升级功能&#xff0c;这样不仅实现了接收参数&#xff0c;升级程序体积也比原来的窗体形式…

如何让Ubuntu上的MySQL开发更便捷

前言 作为一款开源的数据库开发与数据库管理协同工具&#xff0c;&#xff08;OceanBase Developer Center&#xff0c;简称ODC&#xff09;&#xff0c;针对MySQL数据源&#xff0c;已提供了涵盖SQL开发、变更风险管控、数据安全合规等多个方面的功能&#xff0c;从而为MySQL…