首页 > 编程语言 >在Java中利用GeoHash实现高效的‘附近xxx‘功能

在Java中利用GeoHash实现高效的‘附近xxx‘功能

时间:2024-07-30 15:26:50浏览次数:18  
标签:Java LIKE xxx longitude geohash latitude GeoHash 个字符

GeoHash的介绍

GeoHash是一种高效的地理编码系统,它通过将地球表面划分为网格并用字母数字组合的字符串来表示每个区域。
这种编码方法将二维的经纬度坐标转换为一维的字符串,使得地理位置的存储和检索变得更加简单。GeoHash的核心原理是将经纬度坐标转换为二进制,然后交替取位组合,最后转换为base32编码。这种方法的一个重要特性是,相邻区域通常会有相同的GeoHash前缀,这使得它非常适合用于快速查找附近位置。GeoHash字符串的长度决定了编码的精度,越长越精确,例如6位GeoHash可以精确到约1.2公里,而10位可以精确到2.4米。
简单来说,GeoHash就是将某个地点的地理位置转换成可以比较可以排序的唯一字符串

功能分析

通常我们在实现发现附近xxx功能的时候,需要统计我们当前所在地点附近终端的数量或者具体的地理位置。以共享单车举例子,当我们在A地点的时候通过APP可以看到附近200米的所存在的共享单车的数量以及每辆单车的具体位置,然后就可以根据地图的定位过去寻找车辆了。这个时候我们只需要通过A地点的GeoHash字符串进行模糊匹配,找到相关区域的所有车辆然后再根据200米距离的限制就可以实现发现附近共享单车的功能了。

Java代码实现

写个小DEMO来实现这个功能

首先引入GeoHash的相关依赖

<dependency>
    <groupId>ch.hsr</groupId>
    <artifactId>geohash</artifactId>
    <version>1.4.0</version>
</dependency>

写一个方法获取给定位置的GeoHash及其相邻区域的方位

    public List<String> neighbour(float longitude, float latitude, int length) {

        List<String> geoHashes = Lists.newArrayList();
        if (length == 0 || (longitude == 0 && latitude == 0)) {
            return geoHashes;
        }
        GeoHash geoHash = GeoHash.withCharacterPrecision(latitude, longitude, length);
        geoHashes.add(geoHash.toBase32());
        GeoHash[] neighbourArray = geoHash.getAdjacent();
        for (GeoHash child : neighbourArray) {
            geoHashes.add(child.toBase32());
        }
        return geoHashes;
    }

传入参数:经度、纬度和GeoHash的精度

这里解释一下GeoHash的精度:
GeoHash使用base32编码(32个字符),每增加一个字符,精度就会提高。

  • 1个字符:约 5,000km × 5,000km
  • 2个字符:约 1,250km × 625km
  • 3个字符:约 156km × 156km
  • 4个字符:约 39.1km × 19.5km
  • 5个字符:约 4.89km × 4.89km
  • 6个字符:约 1.22km × 0.61km
  • 7个字符:约 153m × 153m
  • 8个字符:约 38.2m × 19.1m
  • 9个字符:约 4.77m × 4.77m

再解释一下方位:
在GeoHash系统中,方位指的是相对于中心GeoHash的周围8个相邻区域的位置。这8个方位形成了一个3x3的网格,中心是我们关注的GeoHash

NW | N | NE
---+---+---
W | C | E
---+---+---
SW | S | SE

通过知道相邻区域的方位,我们可以快速扩展搜索范围,而不需要重新计算整个区域的GeoHash。同时GeoHash有一个特性,就是相邻的地理位置可能会有完全不同的GeoHash编码。

举个例子:
假设有两家咖啡店

  • 一家在城市的东边(编码为 E999)
  • 另一家在城市的西边(编码为 W001)

这两家店实际上可能只隔着一条街,非常近,,但是这条街就是中轴线将城市的东西边划分开来。如果有人站在东边的咖啡店门口,想找附近的咖啡店。
他们使用一个只基于编码前缀的搜索系统(类似于简单的GeoHash搜索)。
系统可能会搜索所有以 “E99” 开头的地点。这个搜索会找到东边(E区)的许多咖啡店。但它可能会完全忽略那家就在街对面、编码完全不同(W001)的咖啡店。所以使用方位寻找各个方向的门店,以防止出现边界问题。

通过数据库中根据每条数据的geohash进行过滤

List<String> geoHashList = GeoHashUtil.neighbour(Point.getLongitude(), 
                                Point.getLatitude(), DistanceEunm.TWOHUNDRED.getLength());

String geoHashSql = geoHashList.stream()
                               .map(hash -> "?")
                               .collect(Collectors.joining(","));

String filtersql = "ST_Distance_Sphere(POINT(bike_table.longitude, bike_table.latitude), " +
                   "POINT(?, ?)) < 200 AND geohash IN (" + geoHashSql + ")";

// 使用参数化查询来执行 SQL
bikeservice.getBikeList(filtersql, Point.getLongitude(), Point.getLatitude(), geoHashList.toArray());

执行的SQL

SELECT 
    ID,
	ST_Distance_Sphere ( POINT ( bike_table.longitude, bike_table.latitude ), POINT ( longitude, latitude ) ) AS distance 
FROM
	bike_table 
WHERE
	ST_Distance_Sphere ( POINT ( bike_table.longitude, bike_table.latitude ), POINT ( longitude, latitude ) ) < 200 
	AND (
		geohash LIKE'ws0e%' 
		OR geohash LIKE'ws0s%' 
		OR geohash LIKE'ws0u%' 
		OR geohash LIKE'ws0g%' 
		OR geohash LIKE'ws0f%' 
		OR geohash LIKE'ws0d%' 
		OR geohash LIKE'ws06%' 
		OR geohash LIKE'ws07%' 
		OR geohash LIKE'ws0k%' 
	)

最后就能得到相关车辆的ID以及与当前位置的距离了

标签:Java,LIKE,xxx,longitude,geohash,latitude,GeoHash,个字符
From: https://blog.csdn.net/Superkom666/article/details/140797973

相关文章

  • Java修炼 Java SE 面试题目 (简答) 2024.7.26 22:16
    目录1.基础知识2.控制流和循环3.集合框架4.异常处理5.多线程编程6.输入输出操作7.类和接口8.Lambda表达式和函数式编程9.内存管理和垃圾回收:10.Java虚拟机(JVM):1.基础知识解释Java的面向对象特性,如封装、继承和多态。Java的面向对象特性包括封装(将数据和代码封......
  • JavaScript の 闭包
    闭包概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域(什么鸟语)简单理解就是:闭包=内层函数+外层函数的变量如functionouter(){leta=0functioninner(){a++console.log(a)}returninner}//这......
  • day11 Java基础——基本运算符
    day11Java基础——基本运算符小技巧:CTRL+D复制当前行到下一行例1:packageoperator;publicclassDemo01{publicstaticvoidmain(String[]args){//二元运算符inta=10;intb=20;intc=25;intd=25;......
  • 全网最全JAVA面试题,终于整理完了
    一、Java基础1、基础概念与常识Java语言有哪些特点?简单易学(语法简单,上手容易);面向对象(封装,继承,多态);平台无关性(Java虚拟机实现平台无关性);支持多线程(C++语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而Java语言却提供了多线程支持);可靠......
  • 当我尝试在 flink 集群上运行 Beam Pipeline 时,为什么会出现 ERROR:root:java.lang.Nu
    我正在尝试在本地托管的Flink集群上运行一个简单的Beam管道,但在执行此操作时遇到错误。我已经尝试了在互联网上可以找到的所有内容。importapache_beamasbeamfromapache_beam.ioimportReadFromTextfromapache_beam.ioimportWriteToTextfromapache_beam.option......
  • 微信公众号发送模板消息java
    packagecom.cloud.module.management.message.handler.mp;importcn.hutool.core.util.ObjectUtil;importcn.hutool.core.util.StrUtil;importcom.alibaba.fastjson2.JSON;importcom.alibaba.fastjson2.JSONObject;importcom.cloud.module.management.common.const......
  • Java代理模式详解
    Java代理模式详解概念代理模式是一种设计模式,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。在Java中,代理模式主要分为静态代理和动态代理。静态代理静态......
  • 基于java+ssm+jsp旅游论坛设计与实现+vue录像(源码+lw+部署文档+讲解等)
    前言......
  • 基于java+ssm+jsp旅行社管理系统的设计与实现录像(源码+lw+部署文档+讲解等)
    前言......
  • 初识Java多线程
    Java中如何创建新线程?第一种方式:继承Thread类写一个子类继承Thread重写run方法创建该类的对象,代表一个线程调用start方法启动线程,该线程会执行run方法这种方式的优点在于编码方式简单,但是该类已经继承了Thread类,不能继承其他类。注意:启动线程时一定调用start方法,而非ru......