美文网首页Spring-Boot学习
elasticsearch之十八springboot测试地理信息

elasticsearch之十八springboot测试地理信息

作者: Java及SpringBoot | 来源:发表于2020-04-01 10:39 被阅读0次

    个人专题目录](https://www.jianshu.com/p/140e2a59db2c)


    1. elasticsearch地理信息搜索

    随着生活服务类应用最近一年的崛起和普及,基于地理位置的内容正在日益重要。LBS已是老生常谈,不过在PC、在移动互联网时代,LBS在导航之外都未出现第二个杀手级应用。在没有O2O之前,LBS所依重的POI(Point of Interest)并未真正成为用户的“Interest”,人们的兴趣还是只存在于网络的虚拟内容,游戏、新闻、文学、网页、多媒体,等等。

    O2O大热之后,越来越多的杀手级应用开始出现:打车拼车租车等用车服务进去主界面是地图,陌陌这个声名鹊起的后来者基于位置做出了社交的差异化,团购上门分享所有热门O2O应用均离不开地图。线下实体、生活服务、身边内容的呈现形式都基于地图的“点”而不是基于时间的“线”,人们不需刷而是搜索、缩放、点选,乃至不做任何操作根据位置移动来与之交互。我将这类内容称之为LocationPoint,这将成为Timeline之后的又一种至关重要的内容形式。

    1.1 地理坐标点

    地理坐标点 是指地球表面可以用经纬度描述的一个点。 地理坐标点可以用来计算两个坐标间的距离,还可以判断一个坐标是否在一个区域中,或在聚合中。

    地理坐标点不能被动态映射 (dynamic mapping)自动检测,而是需要显式声明对应字段类型为 geo-point

    PUT /map
    {
      "settings": {
        "number_of_shards": 3,
        "number_of_replicas": 1
      },
      "mappings": {
        "cp": {
          "properties": {
            "name": {
              "type": "keyword"
            },
            "location": {
              "type": "geo_point" #经度lon,纬度lat
            }
          }
        }
      }
    }
    

    1.2 经纬度坐标格式

    如上例,location 字段被声明为 geo_point 后,我们就可以索引包含了经纬度信息的文档了。 经纬度信息的形式可以是字符串、数组或者对象:

    ####  北科
    POST /map/cp/1
    {
      "name": "北科",
      "location": {
        "lon": 116.2577,
        "lat": 40.1225
      }
    }
    
    #### 巩华城地铁站
    POST /map/cp/2
    {
      "name": "巩华城地铁站",
      "location": {
        "lon": 116.3004,
        "lat": 40.137399
      }
    }
    
    #### 生命科学园地铁站
    POST /map/cp/3
    {
      "name": "生命科学园地铁站",
      "location": {
        "lon": 116.300582,
        "lat": 40.101053
      }
    }
    
    字符串形式以半角逗号分割,如 "lat,lon"
    对象形式显式命名为 latlon
    数组形式表示为 [lon,lat]

    大家可以上百度或者高德的地图开放平台了解相关的操作,以百度地图开放为例(http://lbsyun.baidu.com/jsdemo.htm),比如想获取地图上某个点的经纬度,如下图所示:

    geo3.png

    1.3 通过地理坐标点过滤

    我们有时候,希望可以根据当前所在的位置,找到自己身边的符合条件的一些商店,酒店之类的。它主要支持两种类型的地理查询:

    一种是地理点(geo_point),即经纬度查询,另一种是地理形状查询(geo_shape),即支持点、线、圈、多边形查询.

    ES中有3中位置相关的过滤器,用于过滤位置信息:

    • geo_distance: 查找距离某个中心点距离在一定范围内的位置
    • geo_bounding_box: 查找某个长方形区域内的位置
    • geo_polygon: 查找位于多边形内的地点。

    1.4 geo_distance

    地理距离过滤器( geo_distance )以给定位置为圆心画一个圆,来找出那些地理坐标落在其中的文档.

    广泛地应用在O2O,LBS领域,比如查找附近的酒店、包店、共享单车等。如下图所示,按距用户的距离查询周边酒店。

    例如:查询以某个经纬度为中心周围500KM以内的城市

    #### 定位一个点,使用长度定义一个圆
    POST /map/cp/_search
    {
      "query": {
        "geo_distance": {
          "location": {             ### 确定坐标位置
            "lon": 116.2577,
            "lat": 40.1225
          },
          "distance": 5000,         ### 半径
          "distance_type": "arc"    ### 指定圆
        }
      }
    }
    
    /**
     * 以某个经纬度为中心查询周围限定距离的文档
     *
     * @param indexName 索引
     * @param lot       经度
     * @param lon       纬度
     * @param distance  距离
     * @throws Exception
     */
    @Override
    public void geoDistanceQuery(String indexName, double lot, double lon, int distance) throws Exception {
        GeoDistanceQueryBuilder geoDistanceQueryBuilder = QueryBuilders.geoDistanceQuery("location")
                .point(lot, lon)
                .distance(distance, DistanceUnit.KILOMETERS)
                .geoDistance(GeoDistance.ARC);
        baseQuery.builder(indexName, geoDistanceQueryBuilder);
    }
    
    代码依次执行:
    1、建立索引
    2、初始化数据
    3、执行testGeoDistanceQuery,搜索“距厦门500公里以内的城市”
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest(classes = SearchServiceApplication.class)
    @WebAppConfiguration
    @Log4j2
    public class GeoQueryTest {
        private String indexName = "cn_large_cities";
    
        @Autowired
        private IndexService indexService;
        @Autowired
        private DocService docService;
        @Autowired
        private GeoQuery geoQuery;
    
        @Test
        public void testCreateIndex() throws Exception {
            CreateIndexRequest request = new CreateIndexRequest(indexName);
            buildSetting(request);
            buildIndexMapping(request);
            indexService.createIndex(indexName, request, false);
        }
    
        @Test
        public void testDelIndex() throws Exception {
            indexService.deleteIndex(indexName, false);
        }
    
        //设置分片
        private void buildSetting(CreateIndexRequest request) {
            request.settings(Settings.builder().put("index.number_of_shards", 3)
                    .put("index.number_of_replicas", 2));
        }
    
        /**
         * 生成地理信息表索引结构
         * <p>
         * city 城市
         * state 省
         * location 位置
         *
         * @param request
         * @throws IOException
         */
        private void buildIndexMapping(CreateIndexRequest request) throws IOException {
            XContentBuilder mappingBuilder = JsonXContent.contentBuilder()
                    .startObject()
                    .startObject("properties")
                    .startObject("city")
                    .field("type", "keyword")
                    .field("index", "true")
                    .endObject()
    
                    .startObject("state")
                    .field("type", "keyword")
                    .field("index", "true")
                    .endObject()
    
                    .startObject("location")
                    .field("type", "geo_point")
    //                        .field("index", "true")
                    .endObject()
                    .endObject()
                    .endObject();
            request.mapping(mappingBuilder);
        }
    
        @Test
        public void testInitData() throws IOException {
            String json1 = "{" +
                    "\"city\": \"北京\", " +
                    "\"state\": \"北京\"," +
                    "\"location\": {\"lat\": \"39.91667\", \"lon\": \"116.41667\"}"
                    + "}";
            String json2 = "{" +
                    "\"city\": \"上海\", " +
                    "\"state\": \"上海\"," +
                    "\"location\": {\"lat\": \"34.50000\", \"lon\": \"121.43333\"}"
                    + "}";
            String json3 = "{" +
                    "\"city\": \"厦门\", " +
                    "\"state\": \"福建\"," +
                    "\"location\": {\"lat\": \"24.46667\", \"lon\": \"118.10000\"}"
                    + "}";
            String json4 = "{" +
                    "\"city\": \"福州\", " +
                    "\"state\": \"福建\"," +
                    "\"location\": {\"lat\": \"26.08333\", \"lon\": \"119.30000\"}"
                    + "}";
            String json5 = "{" +
                    "\"city\": \"广州\", " +
                    "\"state\": \"广东\"," +
                    "\"location\": {\"lat\": \"23.16667\", \"lon\": \"113.23333\"}"
                    + "}";
    
            docService.addDoc(indexName, json1, null);
            docService.addDoc(indexName, json2, null);
            docService.addDoc(indexName, json3, null);
            docService.addDoc(indexName, json4, null);
            docService.addDoc(indexName, json5, null);
        }
    
        @Test
        public void testGeoDistanceQuery() throws Exception {
            //距厦门500公里以内的城市
            geoQuery.geoDistanceQuery(indexName, 24.46667, 118.0000, 500);
        }
    
        @Test
        public void testGeoBoundingBox() throws Exception {
            geoQuery.geoBoundingBoxQuery(indexName, 40.8, -74.0, 40.715, -73.0);
        }
    
        @Test
        public void testPolygonQuery() throws Exception {
            geoQuery.geoPolygonQuery(indexName);
        }
    }
    

    1.5 geo_bounding_box

    查找某个长方形区域内的位置,以高德地图开放平台为例(https://lbs.amap.com/api/javascript-api/example/overlayers/rectangle-draw-and-edit),通过在地图上用矩形框选取一定范围来搜索。如下图所示:

    geo5.png

    这是目前为止最有效的地理坐标过滤器了,因为它计算起来非常简单。 你指定一个矩形的 顶部 , 底部 , 左边界 ,和 右边界 ,然后过滤器只需判断坐标的经度是否在左右边界之间,纬度是否在上下边界之间:

    他可以指定一下几个属性:

    top_left: 指定最左边的经度和最上边的纬度

    bottom_right: 指定右边的经度和最下边的纬度

    例如:查询某个矩形范围内的文档

    POST /map/cp/_search
    {
      "query": {
        "geo_bounding_box": {
          "location": {
            "top_left": {
              "lon": 116.242461,
              "lat": 40.139123
            },
            "bottom_right": {
              "lon": 116.305702,
              "lat": 40.11987
            }
          }
        }
      }
    }
    
    /**
     * 搜索矩形范围内的文档
     *
     * @param indexName 索引
     * @param top       最上边的纬度
     * @param left      最左边的经度
     * @param bottom    最下边的纬度
     * @param right     右边的经度
     * @throws Exception
     */
    @Override
    public void geoBoundingBoxQuery(String indexName, double top, double left, double bottom, double right) throws Exception {
        GeoBoundingBoxQueryBuilder address = QueryBuilders.geoBoundingBoxQuery("location")
                .setCorners(top, left, bottom, right);
        baseQuery.builder(indexName, address);
    }
    
    @Test
    public void testGeoBoundingBoxh() throws Exception {
        geoQuery.geoBoundingBoxQuery(indexName,40.8,-74.0,40.715,-73.0);
    }
    

    1.6 geo_polygon

    根据给定的多个点组成的多边形,查询范围内的点。以高德地图开放平台为例(https://lbs.amap.com/api/javascript-api/example/overlayers/polygon-draw-and-edit),通过多个点来确定一个面积,在些面积区域内搜索。

    geo6.png

    例如:搜索某个多边型区域的文档。

    POST /map/cp/_search
    {
      "query": {
        "geo_polygon": {
          "location": {
            "points": [
              {
                "lon": 116.292695,
                "lat": 40.110104
              },
              {
                "lon": 116.296288,
                "lat": 40.096114
              },
              {
                "lon": 116.303798,
                "lat": 40.099977
              },
              {
                "lon": 116.304876,
                "lat": 40.102902
              }
            ]
          }
        }
      }
    }
    
    @Override
    public void geoPolygonQuery(String indexName) throws Exception {
        List<GeoPoint> points = new ArrayList<>();
        points.add(new GeoPoint(40.110104, 116.292695));
        points.add(new GeoPoint(40.096114, 116.296288));
        points.add(new GeoPoint(40.099977, 116.303798));
        points.add(new GeoPoint(40.102902, 116.304876));
        GeoPolygonQueryBuilder geoPolygonQueryBuilder = QueryBuilders.geoPolygonQuery("location", points);
        baseQuery.builder(indexName, geoPolygonQueryBuilder);
    }
    
    @Test
    public void testPolygonQuery() throws Exception {
        geoQuery.geoPolygonQuery(indexName);
    }
    

    相关文章

      网友评论

        本文标题:elasticsearch之十八springboot测试地理信息

        本文链接:https://www.haomeiwen.com/subject/zedouhtx.html