ABAP实现Geohash

Posted hhelibeb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ABAP实现Geohash相关的知识,希望对你有一定的参考价值。

前几天群里有人问ABAP有没有Geohash函数,用来帮助SAP存储门店位置、实现查找附近门店的功能。因为没有查到,所以我动手写了一个。

 

Geohash是什么

Geohash是一种公共域地理编码系统,它将一个地理位置编码成一串字母和数字。字符串越长,表示的范围越精确。两个字符串的相同前缀越多,表示它们所代表的地点的距离越近,这样就可以利用字符串的前缀匹配来快速查询附近的地点信息。

关于Geohash的更多介绍,可以参考:

 

本文链接:https://www.cnblogs.com/hhelibeb/p/11426826.html 

原创内容,转载请注明

实现

我在Github创建了一个repo,包含了自己写的编码、解码方法。地址是:https://github.com/hhelibeb/geohash-abap

代码如下,目前还有更多功能在实现中,有兴趣的朋友可以来一起写。

class zcl_geohash definition
  public
  final
  create public.

  public section.

    types:
      ty_tude type p length 16 decimals 12.

    constants c_max_hash_length type i value 12 ##NO_TEXT.

    class-methods class_constructor.
    class-methods encode
      importing
        !i_longitude      type ty_tude
        !i_latitude       type ty_tude
        !i_length         type i default 8
      returning
        value(r_geo_hash) type string .
    class-methods decode
      importing
        !i_geo_hash  type string
      exporting
        !e_longitude type ty_tude
        !e_latitude  type ty_tude .
  protected section.
  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_code,
             code type string,
           end of ty_code.
    types: ty_code_t type standard table of ty_code with empty key.

    class-data mt_base32_code1 type ty_base32_t1 .
    class-data mt_base32_code2 type ty_base32_t2 .
    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 0 ##NO_TEXT.
    constants c_one type c value 1 ##NO_TEXT.

    class-methods bin_to_dec
      importing
        !i_bin       type string default 0
      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_code_neighbor importing i_table        type standard table
                                               i_member       type string
                                     returning value(r_table) type ty_code_t.

ENDCLASS.
CLASS ZCL_GEOHASH IMPLEMENTATION.


  method bin_to_dec.

    if contains( val = i_bin regex = `[^01]` ).
      return.
    endif.

    data(length) = strlen( i_bin ).

    data(l_index) = 0.

    do length times.

      data(temp) = i_bin+l_index(1).

      if temp = 1.
        r_dec = r_dec + 2 ** ( length - l_index - 1 ).
      endif.

      l_index = l_index + 1.

    enddo.

  endmethod.


  method class_constructor.

    mt_base32_code1 = value #(
      ( decimals = 0  base32 = 0 )
      ( decimals = 1  base32 = 1 )
      ( decimals = 2  base32 = 2 )
      ( decimals = 3  base32 = 3 )
      ( decimals = 4  base32 = 4 )
      ( decimals = 5  base32 = 5 )
      ( decimals = 6  base32 = 6 )
      ( decimals = 7  base32 = 7 )
      ( decimals = 8  base32 = 8 )
      ( decimals = 9  base32 = 9 )
      ( decimals = 10 base32 = b )
      ( decimals = 11 base32 = c )
      ( decimals = 12 base32 = d )
      ( decimals = 13 base32 = e )
      ( decimals = 14 base32 = f )
      ( decimals = 15 base32 = g )
      ( decimals = 16 base32 = h )
      ( decimals = 17 base32 = j )
      ( decimals = 18 base32 = k )
      ( decimals = 19 base32 = m )
      ( decimals = 20 base32 = n )
      ( decimals = 21 base32 = p )
      ( decimals = 22 base32 = q )
      ( decimals = 23 base32 = r )
      ( decimals = 24 base32 = s )
      ( decimals = 25 base32 = t )
      ( decimals = 26 base32 = u )
      ( decimals = 27 base32 = v )
      ( decimals = 28 base32 = w )
      ( decimals = 29 base32 = x )
      ( decimals = 30 base32 = y )
      ( decimals = 31 base32 = z )
    ).

    mt_base32_code2 = mt_base32_code1.

  endmethod.


  method decode.

    types: numc5 type n length 5.

    data(length) = strlen( i_geo_hash ).

    if length <= 0.
      return.
    endif.

    if length > c_max_hash_length.
      length = c_max_hash_length.
    endif.

    data(geo_hash) = to_lower( i_geo_hash ).

    data(hash_index) = 0.

    do length times.

      data(base32) = geo_hash+hash_index(1).

      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 + 1.

    enddo.

    data(bin_index) = 0.

    do strlen( mix_bin ) times.

      data(bin) = mix_bin+bin_index(1).

      if bin_index mod 2 = 0.
        longitude_bin = longitude_bin && bin.
      else.
        latitude_bin = latitude_bin && bin.
      endif.

      bin_index = bin_index + 1.

    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) = 0.

    do strlen( longitude_bin ) times.

      data(bin_longitude) = longitude_bin+longitude_index(1).

      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  = e_longitude
      ).

      longitude_index = longitude_index + 1.

    enddo.

    data(latitude_index) = 0.

    do strlen( latitude_bin ) times.

      data(bin_latitude) = latitude_bin+latitude_index(1).

      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  = e_latitude
      ).

      latitude_index = latitude_index + 1.

    enddo.

  endmethod.


  method dec_to_bin.

    "ignore negative number
    data(temp) = 0.
    data(dec) = i_dec.

    while dec > 0.
      temp = dec mod 2.
      dec  = dec / 2 - temp.
      r_bin = r_bin && conv char1( temp ).
    endwhile.

    r_bin = reverse( r_bin ).

  endmethod.


  method encode.

    if i_length < 1.
      return.
    endif.

    if i_length > c_max_hash_length.
      data(hash_length) = c_max_hash_length.
    else.
      hash_length = i_length.
    endif.

    data(loop_times) = hash_length * 5 / 2 + 1.

    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  = i_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  = i_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) = 0.

    do hash_length times.

      data(offset) = code_index * 5 .
      data(bin)    = mix_bin+offset(5).

      r_geo_hash = r_geo_hash && value #(
        mt_base32_code1[ decimals = bin_to_dec( i_bin = bin  ) ]-base32 optional ).

      code_index = code_index + 1.

    enddo.

  endmethod.


  method get_bin.

    data(mid) = conv ty_tude( ( i_left + i_right ) / 2 ).

    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.


  endmethod.


  method get_tude.

    data(mid) = conv ty_tude( ( i_left + i_right ) / 2 ).

    if i_bin = c_zero.
      e_left  = i_left.
      e_right = mid.
      e_tude  = ( i_left + mid ) / 2.
    else.
      e_left  = mid.
      e_right = i_right.
      e_tude  = ( mid + i_right ) / 2.
    endif.

  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    = 12
).

 

可以得到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%‘等等...

以上是关于ABAP实现Geohash的主要内容,如果未能解决你的问题,请参考以下文章

Hbase geohash实现地理轨迹的空间搜索实现思路设计

Hbase geohash实现地理轨迹的空间搜索实现思路设计

Hbase geohash实现地理轨迹的空间搜索实现思路设计

基于geohash6编码实现相邻4916网格合并

php通过geohash算法实现查找附近的商铺

空间索引 - GeoHash算法及其实现优化