项目需求需要空间计算能力,开始选型Sedona(GeoSpark)来完成,

需求需要每一条数据在满足某条件的情况下,去查找某张表进行空间匹配,找到离这个点(point)最近的一条道路(lineString)

第一个方案: 使用sedona来使用临近道路的判断

由于sedona本质还是使用spark的能力,所以遵循spark的开发规则,不能在rdd.map 里面干活,sedona也不支持批量查,只能一条一条匹配。 伪代码如下

	val spatial_sql =
"""
| select
| ST_GeomFromWKT(geom) geom, name, adcode
| from ods.ods_third_party_road_data
|""".stripMargin
val third_party_road_df = spark.sql(spatial_sql).toDF() aoi_day_s_df.rdd.collect().par.map(row => {
val tmp_location = row.getAs[String]("poi_location")
val near_street = spatialQueryStreet(third_party_road_df, city_code, tmp_location)
println(near_street)
...
) def spatialQueryStreet(third_party_road_df:DataFrame, city_code:String, location: String): String = {
val frame = third_party_road_df.where("adcode = '%s'".format(city_code)).toDF()
val tp_road_spatial_rdd = Adapter.toSpatialRdd(frame, "geom")
tp_road_spatial_rdd.buildIndex(IndexType.RTREE, false)
val geometryFactory = new GeometryFactory()
val x = location.substring(location.indexOf("(") + 1, location.indexOf(" "))
val y = location.substring(location.indexOf(" ") + 1, location.indexOf(")"))
val pointObject = geometryFactory.createPoint(new Coordinate(x.toDouble, y.toDouble))
val usingIndex = true
val result = KNNQuery.SpatialKnnQuery(tp_road_spatial_rdd, pointObject, 1, usingIndex)
if (result.isEmpty) {
return ""
} else {
val dst = result.get(0)
//System.out.println("==== dst.getUserData: " + dst.getUserData.toString)
val strings = dst.getUserData.toString.split("\t")
val near_street = strings(0)
//System.out.println("==== near_street: " + near_street)
near_street
}
结果效率不高,因为每条数据都要匹配,sedona又不能在rdd.map中使用,所以必须先collect().map,这就不能利用到spark多节点并行的特性; 2. 每条数据都基于third_party_road_df创建了空间索引来查,效率更低了(如果只有一条数据还勉强可以接受)

方案2: 改sedona为JTS来处理,jts直接创建rtree,可以在rdd.map中处理,而且创建速度也更快一些,效率更高了

伪代码如下

  poi_build_aoi_aoi_day_s_df.rdd.map(row => {
val tmp_location = row.getAs[String]("poi_location")
val rtree = createRtree(model_list)
near_street = spatialQueryStreet(rtree, tmp_location)
println(near_street)
...
) def createRtree(third_party_road_list: Array[ThirdPartyModel]): STRtree = {
val rtree = new STRtree()
for (model <- third_party_road_list) {
val geom = model.geometry
geom.setUserData(model.name)
rtree.insert(geom.getEnvelopeInternal, model.geometry)
}
rtree.build()
rtree
} def spatialQueryStreet(rtree: STRtree, location: String): String = {
if (rtree == null) {
""
}
val geometryFactory = new GeometryFactory()
val x = location.substring(location.indexOf("(") + 1, location.indexOf(" "))
val y = location.substring(location.indexOf(" ") + 1, location.indexOf(")"))
val pointObject = geometryFactory.createPoint(new Coordinate(x.toDouble, y.toDouble))
val result = rtree.nearestNeighbour(pointObject.getEnvelopeInternal, pointObject, new GeometryItemDistance())
val name = result.asInstanceOf[Geometry].getUserData.asInstanceOf[String]
println(s"nearestNeighbour name: $name")
name
}

通过这次修改,由原来跑3个小时(甚至更多)的任务在15分钟内就跑完了

PS: 经尝试rtree 不能通过广播变量发送出去,会报序列化异常

其实还可以再优化一下,上面每条数据还是创建了一次rtree, 可以改为mapPartition,然后只建一次rtree, 数据量大时效果更佳

aoi_day_s_df.rdd.mapPartitions(iterator => {
// rtree 放到iterator.map 外面创建,搞一次就ok了,更快(不过我没有试验,应该是百分百可行的)
val rtree = createRtree(model_list) val seq = iterator.map(row => {
val tmp_location = row.getAs[String]("poi_location")
near_street = spatialQueryStreet(rtree, tmp_location)
println(near_street)
...
)
seq
)

记一次 Sedona(GeoSpark) 空间计算优化的更多相关文章

  1. 从程序到系统:建立一个更智能的世界——记Joseph Sifakis“21世纪的计算”大会主题演讲

    Sifakis"21世纪的计算"大会主题演讲" title="从程序到系统:建立一个更智能的世界--记Joseph Sifakis"21世纪的计算&q ...

  2. (数据科学学习手札88)基于geopandas的空间数据分析——空间计算篇(下)

    本文示例代码及数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 在基于geopandas的空间数据分析系列 ...

  3. Linux启动时间优化-内核和用户空间启动优化实践

    关键词:initcall.bootgraph.py.bootchartd.pybootchart等. 启动时间的优化,分为两大部分,分别是内核部分和用户空间两大部分. 从内核timestamp 0.0 ...

  4. (转) Delete/Truncate删除,释放表空间、降低高水位线、resize释放磁盘空间相关优化

    硬盘空间不足,打算删除数据库中的多余数据,但删除数据后,硬盘硬盘空间不能释放.[delete后用:alter table table_name move    truncate后用:alter tab ...

  5. 2019牛客多校第二场F Partition problem 暴力+复杂度计算+优化

    Partition problem 暴力+复杂度计算+优化 题意 2n个人分成两组.给出一个矩阵,如果ab两个在同一个阵营,那么就可以得到值\(v_{ab}\)求如何分可以取得最大值 (n<14 ...

  6. 递归、尾递归和使用Stream延迟计算优化尾递归

    我们在学数据结构的时候必然会接触栈(Stack),而栈有一个重要的应用是在程序设计语言中实现递归.递归用途十分广泛,比如我们常见的阶乘,如下代码: 1234 public static int (in ...

  7. (数据科学学习手札84)基于geopandas的空间数据分析——空间计算篇(上)

    本文示例代码.数据及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 在本系列之前的文章中我们主要讨论了g ...

  8. 记一次 spinor flash 读速度优化

    背景 某个项目使用的介质是 spinor, 其 bootloader 需要从 flash 中加载 os. 启动速度是一个关键指标,需要深入优化.其他部分的优化暂且略过,此篇主要记录对 nor 读速度的 ...

  9. 【算法随记】Canny边缘检测算法实现和优化分析。

    以前的博文大部分都写的非常详细,有很多分析过程,不过写起来确实很累人,一般一篇好的文章要整理个三四天,但是,时间越来越紧张,后续的一些算法可能就以随记的方式,把实现过程的一些比较容易出错和有价值的细节 ...

随机推荐

  1. python基础教程:定义类创建实例

    类的定义 在Python中,类通过class关键字定义,类名以大写字母开头 >>>class Person(object): #所有的类都是从object类继承 pass #pass ...

  2. Java中StringBuffer 简单学习,LeetCode中1323题运用

    StringBuffer 学习 StringBuffer() 构造一个没有字符的字符串缓冲区,初始容量为16个字符. deleteCharAt(int index) 删除char在这个指定序列inde ...

  3. NC24622 Brownie Slicing

    NC24622 Brownie Slicing 题目 题目描述 Bessie has baked a rectangular brownie that can be thought of as an ...

  4. 013(oulipo)

    题目:http://ybt.ssoier.cn:8088/problem_show.php?pid=1455 题目描述:在母串里找子串出现的次数 题目思路:与字符串的搜索有关那就立刻找到哈希 从s[1 ...

  5. 2022-07-13 第六组 润土 Java01学习笔记

    1.数据类型: 基本数据类型: 整型: byte 字节型 -128-127 1个字节 short 短整型 2个字节 int 整型 4个字节 long 长整型 8个字节 浮点型: float 单精度 4 ...

  6. 基于ABP实现DDD--仓储实践

      由于软件系统中可能有着不同的数据库,不同的ORM,仓储思想的本质是解耦它们.在ABP中具体的实现仓储接口定义在领域层,实现在基础设施层.仓储接口被领域层(比如领域服务)和应用层用来访问数据库,操作 ...

  7. 丽泽普及2022交流赛day15 社论

    前言 link 太牛逼了,补完我一定放代码 . orz 越看越牛逼 orz . 时间复杂度都是口胡,不要信 . 以下是目录 目录 目录 前言 A 题面 题解 代码 B 题面 题解 代码 C 题面 题解 ...

  8. 干货分享:小技巧大用处之Bean管理类工厂多种实现方式

    前言:最近几个月很忙,都没有时间写文章了,今天周末刚好忙完下班相对早点(20:00下班)就在家把之前想总结的知识点写出来,于是就有了这篇文章.虽无很高深的技术,但小技巧有大用处. 有时我们经常需要将实 ...

  9. python面向对象的特征及反射

    目录 派生类实操 面向对象特征之封装 property伪装属性(python内置装饰器) 面向对象特征之多态 面向对象之反射 派生类实操 1.将时间字典序列化成json格式,由于序列化数据类型的要求, ...

  10. 从RabbitMQ平滑迁移到RocketMQ技术实战

    作者:vivo 互联网中间件团队- Liu Runyun 大量业务使用消息中间件进行系统间的解耦.异步化.削峰填谷设计实现.公司内部前期基于RabbitMQ实现了一套高可用的消息中间件平台.随着业务的 ...