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

Posted 潇潇雨歇_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hbase geohash实现地理轨迹的空间搜索实现思路设计相关的知识,希望对你有一定的参考价值。

需求背景:现有用户的出行轨迹都存储在分布式的hbase库中,管理员需要从地图上框选一个矩形或多边形区域,找出该区域和指定时间内活动的用户及其出行轨迹,进行分析。经过调研,了解到关于地理索引有一套比较通用的GeohHash算法,于是使用geohash实现该空间搜索的逻辑。GeoHash是将二维的经纬度转换成字符串,每一个字符串代表了某一矩形区域。也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,比如说我在七天酒店,我朋友在附近的世纪百货,我们的经纬度点会得到相同的GeoHash串。这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存。

一.RowKey的设计

hbase的查询效率,跟hbase的rowkey设计密不可分,要避免全表扫描。因此根据业务查询场景,设计了2种RowKey设计:

1.单个用户某个时间段的轨迹范围查询

rowkey = user_id + date

2.指定区域内的轨迹查询

rowkey = geohash + date + user_id

geohash生成

GeoHash.withCharacterPrecision(lat, lon, 6).toBase32();

二.生成矩形区域的geohash

import ch.hsr.geohash.BoundingBox;
import ch.hsr.geohash.GeoHash;
import ch.hsr.geohash.WGS84Point;
import ch.hsr.geohash.util.BoundingBoxGeoHashIterator;
import ch.hsr.geohash.util.TwoGeoHashBoundingBox;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import javafx.scene.shape.Rectangle;
import java.util.HashSet;
import java.util.List;

/**
 * 矩形区域搜索geohash生成工具类
 * @author linhaiy
 * @since 2022.04.20
 */
public class RectQueryUtil 
    
    /**
     * 获取指定经纬度范围内的geoHash编码
     * @param maxLat      矩形内纬度的最大值
     * @param minLng      矩形内经度的最小值
     *                    左上角的经纬度
     * @param minLat      矩形内纬度的最小值
     * @param maxLng      矩形内经度的最大值
     *                    右下角的经纬度
     * @param precision   geoHash的精度   默认6级
     * @return
     */
    public static HashSet<String> getGeoHashByFence(double maxLat, double minLng, double minLat, double maxLng, int precision) 
        //通过矩形的左下角 (西南角) 构建一个精度为precision的geoHash值
        GeoHash southWestCorner = GeoHash.withCharacterPrecision(minLat, minLng, precision);
        //通过矩形的右上角 (东北角) 构建一个精度为precision的geoHash值
        GeoHash northEastCorner = GeoHash.withCharacterPrecision(maxLat, maxLng, precision);
        //使用两个geoHash构建一个外接盒型
        TwoGeoHashBoundingBox twoGeoHashBoundingBox = new TwoGeoHashBoundingBox(southWestCorner, northEastCorner);
        //盒型geoHash迭代器,获取矩形内的全部geoHash
        BoundingBoxGeoHashIterator iterator = new BoundingBoxGeoHashIterator(twoGeoHashBoundingBox);
        List<Rectangle> list = Lists.newArrayList();
        HashSet<String> set = Sets.newHashSet();
        GeoHash geoHash;
        //循环遍历
        while (iterator.hasNext()) 
            geoHash = iterator.next();
            list.add(getFence(geoHash));
             //获取geoHash编码
            set.add(geoHash.toBase32());
        
        return set;
    

    /**
     * 获取指定geoHash的矩形区域
     * @param geoHash
     * @return
     */
    public static Rectangle getFence(GeoHash geoHash) 
        //获取geoHash的矩形
        BoundingBox boundingBox = geoHash.getBoundingBox();
        //获取矩形的左下角(西南角)经纬度
        WGS84Point northWestCorner = boundingBox.getSouthWestCorner();
        //获取矩形的右上角(东北角)经纬度
        WGS84Point southEastCorner = boundingBox.getNorthEastCorner();
        //包装成矩形
        return new Rectangle(northWestCorner.getLongitude(), northWestCorner.getLatitude(),
                southEastCorner.getLongitude(), southEastCorner.getLatitude());
    

三.空间区域查询的逻辑实现

HashSet<String> geoHashSet = RectQueryUtil.getGeoHashByFence(trackQueryInputDTO.getYMax(), trackQueryInputDTO.getXMin(),
                trackQueryInputDTO.getYMin(), trackQueryInputDTO.getXMax(), 6);
     for (String geohash : geoHashSet) 
            log.error("geoHash=> " + geohash);
            resultScanner = hBaseUtil.getScanner(hbaseRowKeyTable, geohash + "_" + trackQueryInputDTO.getStartTime(), geohash + "_" + trackQueryInputDTO.getEndTime());
			if (resultScanner != null) 
                for (Result result : resultScanner) 
                    //字段解析,结果接收
                
            
	 

四.优化点

值得注意的是,为了提高查询效率,应该将地图放大到某一层级,再进行框选查询,避免层级过小,造成要查的geohash个数过多,查询循环层数过多,造成查询效率较慢。还有就是如果选择的时间差较大,前端需要做分段调用,截取成较小的时间差进行范围查询,再取多个结果的并集,例如查询2022年4月20至2022年4月22日的某个区域的数据,可以按2小时一次的时间差进行分段查询,或者使用webScokt的分段推送。优化的途径主要是,先减少单个接口的查询hbase库的次数,再减少扫描hbase的行数,扫描的行数越少,查询越快,尽量使用rowkey的范围查询。

五.开发涉及的hbase服务器查询语句

scan 'vb_test:device', STARTROW=>'asdvfr_2022-04-20',ENDROW=>'asdvfr_2022-04-21'
		
scan 'vb_test:device', FILTER=>"RowFilter(=,'substring:gejsss_2555225_')"

以上是关于Hbase geohash实现地理轨迹的空间搜索实现思路设计的主要内容,如果未能解决你的问题,请参考以下文章

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

基于GeoHash算法的地理位置检索

地理空间索引:GeoHash原理

地理空间索引:GeoHash原理

Geohash 边界框搜索

地理空间索引的划分查询