ABAP实现Geohash
前几天群里有人问ABAP有没有Geohash函数,用来帮助SAP存储门店位置、实现查找附近门店的功能。因为没有查到,所以我动手写了一个。
Geohash是什么
Geohash是一种公共域地理编码系统,它将一个地理位置编码成一串字母和数字。字符串越长,表示的范围越精确。两个Geohash字符串的相同前缀越多,表示它们所代表的地点的距离越近,这样就可以利用字符串的前缀匹配来快速查询附近的地点信息。
关于Geohash的更多介绍,可以参考:
本文链接:https://www.cnblogs.com/hhelibeb/p/11426826.html
原创内容,转载请注明
实现
我在Github创建了一个repo,包含了自己写的编码、解码方法。地址是:https://github.com/hhelibeb/geohash-abap
代码如下,目前还有更多功能在实现中,有兴趣的朋友可以来一起写。
(注:github上面的代码会不定时更新,博客中贴的只是初始代码,请前往github查看最新代码)
class zcl_geohash definition
public
final
create public . public section. types: begin of ty_hash,
hash type string,
end of ty_hash.
types: ty_hash_t type standard table of ty_hash with empty key. types:
ty_tude type p length decimals . constants c_max_hash_length type i value ##NO_TEXT. class-methods class_constructor .
class-methods encode
importing
!longitude type ty_tude
!latitude type ty_tude
!length type i default
returning
value(r_geo_hash) type string .
class-methods decode
importing
!geohash type string
exporting
!longitude type ty_tude
!latitude type ty_tude . class-methods neighbors importing geohash type string
returning value(neighbors) type ty_hash_t. class-methods validate importing geohash type string
returning value(valid) type abap_bool. private section. types:
begin of ty_base32,
decimals type i,
base32 type string,
end of ty_base32 .
types:
ty_base32_t1 type hashed table of ty_base32 with unique key decimals .
types:
ty_base32_t2 type hashed table of ty_base32 with unique key base32 . types: begin of ty_neighbors_odd,
f1 type string,
f2 type string,
f3 type string,
f4 type string,
f5 type string,
f6 type string,
f7 type string,
f8 type string,
end of ty_neighbors_odd.
types: ty_neighbors_odd_t type standard table of ty_neighbors_odd with empty key. types: begin of ty_neighbors_even,
f1 type string,
f2 type string,
f3 type string,
f4 type string,
end of ty_neighbors_even.
types: ty_neighbors_even_t type standard table of ty_neighbors_even with empty key. class-data mt_base32_code1 type ty_base32_t1 .
class-data mt_base32_code2 type ty_base32_t2 . class-data mt_neighbors_odd type ty_neighbors_odd_t.
class-data mt_neighbors_even type ty_neighbors_even_t. constants c_longitude_min type ty_tude value '-180.00' ##NO_TEXT.
constants c_longitude_max type ty_tude value '180.00' ##NO_TEXT.
constants c_latitude_min type ty_tude value '-90.00' ##NO_TEXT.
constants c_latitude_max type ty_tude value '90.00' ##NO_TEXT.
constants c_zero type c value '' ##NO_TEXT.
constants c_one type c value '' ##NO_TEXT. class-methods bin_to_dec
importing
!i_bin type string default ''
returning
value(r_dec) type int4 .
class-methods dec_to_bin
importing
!i_dec type int4
returning
value(r_bin) type string . class-methods get_bin
importing
!i_left type ty_tude
!i_right type ty_tude
!i_tude type ty_tude
exporting
!e_left type ty_tude
!e_right type ty_tude
!e_bin type char1 .
class-methods get_tude
importing
!i_left type ty_tude
!i_right type ty_tude
!i_bin type string
exporting
!e_left type ty_tude
!e_right type ty_tude
!e_tude type ty_tude . class-methods: get_index importing index type i
offset type i
max_index type i
returning value(r_index) type i.
class-methods: get_code_neighbor importing i_table type standard table
i_member type string
returning value(r_table) type ty_hash_t. endclass.
class zcl_geohash implementation.
method bin_to_dec.
if contains( val = i_bin regex = `[^]` ).
return.
endif.
data(length) = strlen( i_bin ).
data(l_index) = .
do length times.
data(temp) = i_bin+l_index().
if temp = .
r_dec = r_dec + ** ( length - l_index - ).
endif.
l_index = l_index + .
enddo.
endmethod.
method class_constructor.
mt_base32_code1 = value #(
( decimals = base32 = '' )
( decimals = base32 = '' )
( decimals = base32 = '' )
( decimals = base32 = '' )
( decimals = base32 = '' )
( decimals = base32 = '' )
( decimals = base32 = '' )
( decimals = base32 = '' )
( decimals = base32 = '' )
( decimals = base32 = '' )
( decimals = base32 = 'b' )
( decimals = base32 = 'c' )
( decimals = base32 = 'd' )
( decimals = base32 = 'e' )
( decimals = base32 = 'f' )
( decimals = base32 = 'g' )
( decimals = base32 = 'h' )
( decimals = base32 = 'j' )
( decimals = base32 = 'k' )
( decimals = base32 = 'm' )
( decimals = base32 = 'n' )
( decimals = base32 = 'p' )
( decimals = base32 = 'q' )
( decimals = base32 = 'r' )
( decimals = base32 = 's' )
( decimals = base32 = 't' )
( decimals = base32 = 'u' )
( decimals = base32 = 'v' )
( decimals = base32 = 'w' )
( decimals = base32 = 'x' )
( decimals = base32 = 'y' )
( decimals = base32 = 'z' )
).
mt_base32_code2 = mt_base32_code1.
mt_neighbors_odd = value #(
( f1 = 'b' f2 = 'c' f3 = 'f' f4 = 'g' f5 = 'u' f6 = 'v' f7 = 'y' f8 = 'z' )
( f1 = '' f2 = '' f3 = 'd' f4 = 'e' f5 = 's' f6 = 't' f7 = 'w' f8 = 'x' )
( f1 = '' f2 = '' f3 = '' f4 = '' f5 = 'k' f6 = 'm' f7 = 'q' f8 = 'r' )
( f1 = '' f2 = '' f3 = '' f4 = '' f5 = 'h' f6 = 'j' f7 = 'n' f8 = 'p' )
).
mt_neighbors_even = value #(
( f1 = 'p' f2 = 'r' f3 = 'x' f4 = 'z' )
( f1 = 'n' f2 = 'q' f3 = 'w' f4 = 'y' )
( f1 = 'j' f2 = 'm' f3 = 't' f4 = 'v' )
( f1 = 'h' f2 = 'k' f3 = 's' f4 = 'u' )
( f1 = '' f2 = '' f3 = 'e' f4 = 'g' )
( f1 = '' f2 = '' f3 = 'd' f4 = 'f' )
( f1 = '' f2 = '' f3 = '' f4 = 'c' )
( f1 = '' f2 = '' f3 = '' f4 = 'b' )
).
endmethod.
method decode.
types: numc5 type n length .
data(length) = strlen( geohash ).
if length <= .
return.
endif.
if length > c_max_hash_length.
length = c_max_hash_length.
endif.
data(geo_hash_internal) = to_lower( geohash ).
data(hash_index) = .
do length times.
data(base32) = geo_hash_internal+hash_index().
data(decimals) = value #( mt_base32_code2[ base32 = base32 ]-decimals optional ).
data(bin5) = conv numc5( dec_to_bin( decimals ) ).
data: mix_bin type string,
longitude_bin type string,
latitude_bin type string.
mix_bin = mix_bin && bin5.
hash_index = hash_index + .
enddo.
data(bin_index) = .
do strlen( mix_bin ) times.
data(bin) = mix_bin+bin_index().
if bin_index mod = .
longitude_bin = longitude_bin && bin.
else.
latitude_bin = latitude_bin && bin.
endif.
bin_index = bin_index + .
enddo.
data(longitude_left) = c_longitude_min.
data(longitude_right) = c_longitude_max.
data(latitude_left) = c_latitude_min.
data(latitude_right) = c_latitude_max.
data(longitude_index) = .
do strlen( longitude_bin ) times.
data(bin_longitude) = longitude_bin+longitude_index().
get_tude(
exporting
i_left = longitude_left
i_right = longitude_right
i_bin = bin_longitude
importing
e_left = longitude_left
e_right = longitude_right
e_tude = longitude
).
longitude_index = longitude_index + .
enddo.
data(latitude_index) = .
do strlen( latitude_bin ) times.
data(bin_latitude) = latitude_bin+latitude_index().
get_tude(
exporting
i_left = latitude_left
i_right = latitude_right
i_bin = bin_latitude
importing
e_left = latitude_left
e_right = latitude_right
e_tude = latitude
).
latitude_index = latitude_index + .
enddo.
endmethod.
method dec_to_bin.
"ignore negative number
data(temp) = .
data(dec) = i_dec.
while dec > .
temp = dec mod .
dec = dec / - temp.
r_bin = r_bin && conv char1( temp ).
endwhile.
r_bin = reverse( r_bin ).
endmethod.
method encode.
if length < .
return.
endif.
if length > c_max_hash_length.
data(hash_length) = c_max_hash_length.
else.
hash_length = length.
endif.
data(loop_times) = hash_length * / + .
data: longitude_bin type string,
latitude_bin type string,
mix_bin type string.
data(longitude_left) = c_longitude_min.
data(longitude_right) = c_longitude_max.
data(latitude_left) = c_latitude_min.
data(latitude_right) = c_latitude_max.
do loop_times times.
get_bin(
exporting
i_left = longitude_left
i_right = longitude_right
i_tude = longitude
importing
e_left = longitude_left
e_right = longitude_right
e_bin = data(longitude_bin_temp)
).
get_bin(
exporting
i_left = latitude_left
i_right = latitude_right
i_tude = latitude
importing
e_left = latitude_left
e_right = latitude_right
e_bin = data(latitude_bin_temp)
).
mix_bin = mix_bin && longitude_bin_temp && latitude_bin_temp.
enddo.
data(code_index) = .
do hash_length times.
data(offset) = code_index * .
data(bin) = mix_bin+offset().
r_geo_hash = r_geo_hash && value #(
mt_base32_code1[ decimals = bin_to_dec( i_bin = bin ) ]-base32 optional ).
code_index = code_index + .
enddo.
endmethod.
method get_bin.
data(mid) = conv ty_tude( ( i_left + i_right ) / ).
if i_tude <= mid.
e_bin = c_zero.
e_left = i_left.
e_right = mid.
else.
e_bin = c_one.
e_left = mid.
e_right = i_right.
endif.
endmethod.
method get_code_neighbor.
data(table_descr) = cast cl_abap_tabledescr( cl_abap_tabledescr=>describe_by_data( i_table ) ).
data(column_count) = lines(
cast cl_abap_structdescr( table_descr->get_table_line_type( ) )->components ).
data(col_index) = .
loop at i_table assigning field-symbol(<line>).
data(row_index) = sy-tabix.
col_index = .
while col_index <= column_count.
assign component col_index of structure <line> to field-symbol(<field>).
if sy-subrc = .
if <field> = i_member.
data(found) = abap_true.
exit.
endif.
endif.
col_index = col_index + .
endwhile.
if found = abap_true.
exit.
endif.
endloop.
if found = abap_false.
return.
endif.
types: begin of ty_direction,
row type i,
col type i,
end of ty_direction.
data: direction_index_table type standard table of ty_direction.
direction_index_table = value #(
( row = - col = - )
( row = - col = )
( row = - col = + )
( row = col = - )
( row = col = + )
( row = col = - )
( row = col = )
( row = col = + )
).
data(row_count) = lines( i_table ).
loop at direction_index_table assigning field-symbol(<direction_index>).
data(row_result) = get_index( index = row_index offset = <direction_index>-row max_index = row_count ).
data(col_result) = get_index( index = col_index offset = <direction_index>-col max_index = column_count ).
read table i_table assigning <line> index row_result.
if sy-subrc = .
assign component col_result of structure <line> to <field>.
if sy-subrc = .
r_table = value #( base r_table ( hash = <field> ) ).
endif.
endif.
endloop.
endmethod.
method get_index.
if abs( offset ) >= max_index.
return.
endif.
r_index = index + offset.
if r_index > max_index .
r_index = offset.
endif.
if r_index <= .
r_index = max_index + r_index.
endif.
endmethod.
method get_tude.
data(mid) = conv ty_tude( ( i_left + i_right ) / ).
if i_bin = c_zero.
e_left = i_left.
e_right = mid.
e_tude = ( i_left + mid ) / .
else.
e_left = mid.
e_right = i_right.
e_tude = ( mid + i_right ) / .
endif.
endmethod.
method neighbors.
if geohash is initial.
return.
endif.
data(geohash_internal) = to_lower( geohash ).
data(length) = strlen( geohash_internal ).
data(offset) = length - .
data(suffix) = geohash_internal+offset().
if length mod = .
data(code_table) = get_code_neighbor( i_table = mt_neighbors_even i_member = suffix ).
else.
code_table = get_code_neighbor( i_table = mt_neighbors_odd i_member = suffix ).
endif.
data(prefix) = geohash_internal(offset).
loop at code_table assigning field-symbol(<hash>).
neighbors = value #( base neighbors ( hash = prefix && <hash>-hash ) ).
endloop.
endmethod.
method validate.
valid = abap_false.
if geohash is initial .
return.
endif.
if strlen( geohash ) > c_max_hash_length.
return.
endif.
data(geohash_internal) = to_lower( geohash ).
data(geohash_index) = .
do strlen( geohash ) times.
data(hash) = geohash_internal+geohash_index().
if not line_exists( mt_base32_code2[ base32 = hash ] ).
return.
endif.
geohash_index = geohash_index + .
enddo.
valid = abap_true.
endmethod.
endclass.
使用
本节包含一些使用示例。
编码
以浙江省丽水中学的经纬度坐标 (28.4751600000, 119.9314500000) 为例,

编码代码如下,
report ztest_qq1. data(hash) = zcl_geohash=>encode(
i_latitude = '28.4751600000'
i_longitude = '119.9314500000'
). cl_demo_output=>display( hash ).
可以得到结果wtj3cper。

默认的geohash长度是8位,也可以使用更长的编码提高精度,比如,
data(hash) = zcl_geohash=>encode(
i_latitude = '28.4751600000'
i_longitude = '119.9314500000'
i_length =
).
可以得到wtj3cperv6d9。12是geohash-abap支持的最大长度。
解码
对上面得到的geohash编码结果wtj3cperv6d9进行解码,
zcl_geohash=>decode(
exporting
i_geo_hash = hash
importing
e_latitude = data(latitude)
e_longitude = data(longitude)
). cl_demo_output=>display( latitude && ',' && longitude ).
得到的结果是 (28.475159956144, 119.931449834260) 可以看到是一个近似结果,和原值有微小的差距。

查询
将地点的geohash存储在数据库中之后,可以方便地用SQL中的like关键字,查找到附近的地点。
比如select * from table where geohash like 'wtj3cperv%'等等...
相邻区域编码
为了准群找到最近的地址,需要找到一个geohash所代表的区域的周围8个区域。
为什么?
比如,下图中边缘附近的红点。

黄色的点要比黑色的点更加靠近红点,但是由于黑点跟红点的GeoHash前缀匹配数目更多,因此直接用like查询,会得到黑点。
(参考文章:https://blog.csdn.net/youhongaa/article/details/78816700)
获取附近8个区域编码的方法:
data(neighbors) = zcl_geohash=>neighbors( 'wtj3cper' ).
参考:用打表的方式解决求Geohash当前区域周围8个区域编码
ABAP实现Geohash的更多相关文章
- 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现
本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...
- ABAP单元测试最佳实践
本文包含了我在开发项目中经历过的实用的ABAP单元测试指导方针.我把它们安排成为问答的风格,欢迎任何人添加更多的Q&A's,以完成这个列表. 在我的项目中,只使用传统的ABAP report. ...
- ABAP实现屏幕自己刷新和跳转功能
ABAP开发工程中,有时候需要让跳转出的屏幕自动实现跳转和刷新的功能,该功能的实现需要在屏幕PBO 里面调用相应的事件执行. 关键代码为: SET TITLEBAR ' 屏幕自动程序'. IF g_c ...
- 一步步实现ABAP后台导入EXCEL到数据库【3】
在一步步实现ABAP后台导入EXCEL到数据库[2]里,我们已经实现计划后台作业将数据导入数据库的功能.但是,这只是针对一个简单的自定义结构的导入程序.在实践应用中,面对不同的表.不同的导入文件,我们 ...
- 一步步实现ABAP后台导入EXCEL到数据库【2】
前文:http://www.cnblogs.com/hhelibeb/p/5912330.html 既然后台作业只能在应用服务器运行,那么,我们可以先将要上传的数据保存在应用服务器中,之后再以后台作业 ...
- 让ABAP开发者愈加轻松的若干快捷键
引言 ABAP是一种和当代编程语言在许多方面有着相当不同的编程语言.ABAP的某些方面可能会让我们奇怪,为什么它会如此复杂?而它的某些方面又是那么杰出,给予了ABAP开发者们比其它任何语言更多的便利. ...
- ABAP游标的使用
在Oracle,SQLServer中游标的使用是经常的,所以在ABAP不懂是不行的...... 1.声明游标 OPEN CURSOR [WITH HOLD] <c> FOR SEL ...
- 一步步实现ABAP后台导入EXCEL到数据库【1】
在SAP的应用当中,导入.导出EXCEL文件的情况是一个常见的需求,有时候用户需要将大量数据定期导入到SAP的数据库中.这种情况下,使用导入程序在前台导入可能要花费不少的时间,如果能安排导入程序为后台 ...
- ABAP关键字SUBMIT的简单例子和学习小记
网上有关SUBMIT实现程序调用的例子稍显复杂,而相关的参考和解释则不是很完善.本文给出一个SUBMIT的小示例程序(代码见文末),实现了最简单的程序间调用及返回值,以及SAP官方文档中相关内容的翻译 ...
随机推荐
- cordova把我搞晕了
天啦,搞了几十次,这次求你成功好吗?
- 《VR入门系列教程》之16---第一个OculusVR应用
第一个VR应用 之前我们已经将Oculus的开发包导入到空工程中了,现在我们来构建第一个桌面VR的示例.开发包中已经有一个示例场景,只需要几步就可以让这个场景运行起来.我们将要构建的这个Demo ...
- centOS7 安装mysql-5.7.20-1.el7.x86_64.rpm-bundle.tar
在虚拟机上安装mysql走了不少弯路,在此备份... 首先感谢下这几篇博客提供的帮助: https://www.cnblogs.com/pythonal/p/6141516.html http://b ...
- web设计_5_自由的框式组件
1. CSS3 border-radius 圆角矩形框 圆角矩形框组件是页面布局中常常用到的,利用CSS3的border-radius可非常方便的创建. 并且在横向纵向上面都有很好的扩展性和灵活性. ...
- springboot的邮件服务
作者:纯洁的微笑出处:http://www.ityouknow.com/ 版权归作者所有,转载请注明出处 springboot仍然在狂速发展,才五个多月没有关注,现在看官网已经到1.5.3.RELEA ...
- GC是什么?为什么我们要去使用它
GC(Garbage Collection)是各大语言的宠儿,也是计算机科学领域里很热门的一个话题.最早在JVM中有看过这个算法,后来发现即使是js这种脚本语言也是有GC的.单纯就JVM来说的话,GC ...
- [ PyQt入门教程 ] PyQt5基本控件使用:消息弹出、用户输入、文件对话框
本文主要介绍PyQt界面实现中常用的消息弹出对话框.提供用户输入的输入框.打开文件获取文件/目录路径的文件对话框.学习这三种控件前,先想一下它们使用的主要场景: 1.消息弹出对话框.程序遇到问题需要退 ...
- C#:正则表达式类
Regex r = new Regex("abc"); // 定义一个Regex对象实例(Regex r = new Regex("abc", RegexOp ...
- 3、大型项目的接口自动化实践记录----开放API练习
开始做实际项目前,先拿个网上的简单API练下手 一.API说明: 接口信息 接口名:京东获取单个商品价格 地址:http://p.3.cn/prices/mgets 入参:skuids=J_商品ID& ...
- 国内CDH的MAVEN代理
在编译CDH版本的各个开源软件时,需要从cdh-repo下载对应的jar包,但发现下载速度非常慢,甚至有时候出现下载异常的情况. 下面是国内可用的.速度非常快的一个maven代理仓库,亲测可用: ht ...