基于百度地图SDK和Elasticsearch GEO查询的地理围栏分析系统(1)

本文描述了一个系统,功能是评价和抽象地理围栏(Geo-fencing),以及监控和分析核心地理围栏中业务的表现。

技术栈:Spring-JQuery-百度地图WEB SDK

存储:Hive-Elasticsearch-MySQL-Redis

什么是地理围栏?

LBS系统中,地理围栏指的是虚拟边界围成的部分。

tips:这只是一个demo,支撑实习生的本科毕设,不代表生产环境,而且数据已经做了脱密处理,为了安全还是隐去了所有数据。

功能描述

1、地理围栏的圈选

(1)热力图

热力图展示的是,北京市最近一天的业务密度(这里是T+1数据,在实际工作场景中往往是通过实时流采集分析实时的数据)

(2)圈选地理围栏

系统提供了圆形(距中心点距离)、矩形、多边形三种类型的图形圈选,并通过百度地图SDK采集图形的信息。

2、地理围栏的持久化

(1)提供地理围栏的持久化功能

(2)地理围栏列表

下面是持久化的地理围栏列表,可以看到类型和围栏信息。

当圈选完成,可以选择持久化地理围栏,这个围栏将会沉淀下来,供后续业务分析和监控。

3、聚合分析

(1)提供日订单量,日盈利和日取消率的聚合分析

例如下图是在某个地理围栏区域内,11月这30天内,订单量的变化。

 (2)详细列表

提供每一天数据的详细信息,对异常点可以标红和预警

上面基本就是系统的全部核心功能。下面进入实现部分。

实现 - 数据准备

1、数据源

数据源应该是业务的数据库(例如订单库)以及客户端埋点日志(端动作),公司的离线采集和ETL团队经过了漫长的工作,将数据处理好存入了Hive中。

对于本文系统来说,数据源就是Hive中的order表。要做的是将Hive中的数据导入到Elasticsearch中,使用Elasticsearch强大的GEO Query支持进行分析。

2、数据导入

数据的导入使用的是一段Java的Spark脚本。

(1)先解决依赖

spark-core是必备依赖。引入spark-hive来处理Hive中的数据。引入elasticsearch-hadoop来搞定Hive到ES的写入。

<dependencies>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.10</artifactId>
            <version>1.6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-hive_2.10</artifactId>
            <version>1.6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch-hadoop</artifactId>
            <version>2.3.4</version>
        </dependency>

(2)编写spark脚本

先上代码

public class ToES implements Serializable {

    transient private JavaSparkContext javaSparkContext;
    transient private HiveContext hiveContext;
    private String num;

    /*
    *   初始化Load
    *   创建sparkContext, hiveContext
    * */
    public ToES(String num) {
        this.num = num;
        initSparckContext();
        initHiveContext();
    }

    /*
    *   创建sparkContext
    * */
    private void initSparckContext() {
        SparkConf sparkConf;
        String warehouseLocation = System.getProperty("user.dir");
        sparkConf = new SparkConf()
                .setAppName("to-es")
                .set("spark.sql.warehouse.dir", warehouseLocation)
                .setMaster("yarn-client")
                .set("es.nodes", "10.93.21.21,10.93.18.34,10.93.18.35,100.90.62.33,100.90.61.14")
                .set("es.port", "8049").set("pushdown", "true").set("es.index.auto.create", "true");
        javaSparkContext = new JavaSparkContext(sparkConf);
    }

    /*
    *   创建hiveContext
    *   用于读取Hive中的数据
    * */
    private void initHiveContext() {
        hiveContext = new HiveContext(javaSparkContext);
    }

    /*
    *   使用spark-sql从hive中读取数据, 然后写入es.
    * */
    public void hive2es() {
        String query = String.format("select * from kangaroo.order where concat_ws(‘-‘, year, month, day) = ‘%s‘ and product_id in (3,4) and area = 1",
                transTimeToFormat(System.currentTimeMillis() - Integer.parseInt(num)*24*60*60*1000L, "yyyy-MM-dd"));
        DataFrame rows = hiveContext.sql(query)
                .select("order_id", "starting_lng", "starting_lat", "order_status", "tip", "bouns",
                        "pre_total_fee", "dynamic_price", "product_id", "starting_name", "dest_name", "type");
        JavaRDD<Map<String, Object>> rdd = rows.toJavaRDD().map(new Function<Row, Map<String, Object>>() {
            /*
            *   转换成Map, 解决字段类型不匹配问题
            * */
            @Override
            public Map<String, Object> call(Row row) throws Exception {
                Map<String, Object> map =  new HashMap<String, Object>();
                Map<String, Object> location = new HashMap<String, Object>();
                for (int i=0; i<row.size(); i++) {
                    String key = row.schema().fields()[i].name();
                    Object value = row.get(i);
                    map.put(key, value);
                }
                location.put("lat", Double.parseDouble(map.get("starting_lat").toString()));
                location.put("lon", Double.parseDouble(map.get("starting_lng").toString()));
                map.remove("starting_lat");
                map.remove("starting_lng");
                map.put("location", location);
                map.put("date", transTimeToFormat(System.currentTimeMillis() - Integer.parseInt(num)*24*60*60*1000L, "yyyy-MM-dd"));
                return map;
            }
        });
        Map<String, String> map = new HashMap<String, String>();
        map.put("es.mapping.id", "order_id");
        JavaEsSpark.saveToEs(rdd, "moon/bj", map);
    }

    public String transTimeToFormat(long currentTime, String formatStr) {
        String formatTime = null;
        try {
            SimpleDateFormat format =  new SimpleDateFormat(formatStr);
            formatTime = format.format(currentTime);
        } catch (Exception e) {
        }
        return formatTime;
    }

    public static void main(String[] args) {
        String num = args[0];
        ToES toES = new ToES(num);
        toES.hive2es();
    }
}

SparkContext和HiveContext的初始化,请自行参考代码。

ES的集群配置是在sparkConf中加载进去的,加载方式请自己参照代码。

1)数据过滤

hive-sql

select * from kangaroo.order where concat_ws(‘-‘, year, month, day) = ‘%s‘ and product_id in (3,4) and area = 1

说明:

a)Hive的order表实现为一个外部表,year/month/day是分区字段,也就是说数据是按照天为粒度挂载的。

b)product_id是业务编号,这里过滤出了目标业务的订单。

c)area为城市编号,这里只过滤出北京。

2)列的裁剪

Elasticsearch有个弊端是由于索引的建立,当数据导入Elasticsearch数据量会膨胀,所以一定要进行维度的裁剪。

我们的订单Hive表姑且就叫它order吧,这个表有40+个字段,我们导入到ES中,只选用了其中的12个字段。

在代码中是,通过DataFrame的select实现的裁剪

DataFrame rows = hiveContext.sql(query)
                .select("order_id", "starting_lng", "starting_lat", "order_status", "tip", "bouns",
                        "pre_total_fee", "dynamic_price", "product_id", "starting_name", "dest_name", "type");

可能会有这样的好奇,这样做在hive-sql中把所有字段全拿到然后在裁剪?为什么不直接在sql语句中进行裁剪?简单解释一下,由于spark的惰性求值,应该是没有区别的。

3)map转换操作

下面将dataFrame转换成rdd,执行map操作,将每一条记录进行处理,处理的核心逻辑,是将starting_lng、starting_lat压成一个HashMap的location字段。

为什么要这样做呢?

因为在Elasticsearch中要这样存储点的经纬度,并且将location字段声明为geo_point类型,才能使用空间索引查询。

然后我们顺便生成了一个date字段,表示订单是哪一天的,方便后面的以天为粒度进行聚合查询。

4)批量存入ES

        Map<String, String> map = new HashMap<String, String>();
        map.put("es.mapping.id", "order_id");
        JavaEsSpark.saveToEs(rdd, "moon/bj", map);

这样就将rdd中的数据批量存入到ES中了,存入的索引是index=moon,type=bj,这里映射了order_id为ES文档的document_id。我们下面马上就会说如何建立moon/bj的mapping

5)ES索引建立 

再将数据导入到ES之前,要建立index和mapping。

创建index=moon

curl -XPOST "http://10.93.21.21:8049/moon?pretty"

创建type=bj的mapping

curl -XPOST "http://10.93.21.21:8049/moon/bj/_mapping?pretty" -d ‘
{
    "bj": {
        "properties": {
            "order_id": {"type": "long"},
            "order_status": {"type": "long"},
            "tip": {"type": "long"},
            "bouns": {"type": "long"},
            "pre_total_fee": {"type": "long"},
            "dynamic_price": {"type": "long"},
            "product_id": {"type": "long"},
            "type": {"type": "long"},
            "dest_name": {"index": "not_analyzed","type": "string"},
            "starting_name": {"index": "not_analyzed","type": "string"},
            "departure_time": {"index": "not_analyzed","type": "string"},
            "location": {"type" : "geo_point"},
            "date": {"index": "not_analyzed", "type" : "string"}
        }
    }
}‘

这里要注意的是,location字段的类型-geo_point。

6)打包编译spark程序

以yarn队列形式运行

spark-submit --queue=root.*** to-es-1.0-SNAPSHOT-jar-with-dependencies.jar

然后在ES的head中可以看到数据已经加载进去了

至此,数据已经准备好了。

今天先到这,后面的博客会描述如何搞定百度地图前端和Elasticsearch GEO查询。

时间: 2024-10-13 00:23:02

基于百度地图SDK和Elasticsearch GEO查询的地理围栏分析系统(1)的相关文章

基于百度地图SDK和Elasticsearch GEO查询的地理围栏分析系统(3)-前端实现

转载自:http://www.cnblogs.com/Auyuer/p/8086975.html MoonLight可视化订单需求区域分析系统实现功能: 在现实生活中,计算机和互联网迅速发展,人们越来越趋向于网络,于是我们就有了各种各样的系统,来帮助我们更好地生活.比如对于打车来说,我们也可以通过网上叫车,那么我们就会产生大量的用户订单,特别是对于一些固定时间.固定地点,叫车用户的订单量会非常大,那么我们同样也要很好的管理这些订单.那么我们便要采取某些策略来统计分析,比如我们可以使用区域化的管理

开源基于百度地图SDK的Android交通助手App

BaiduMap-TrafficAssistant ?? 该项目是基于百度地图SDK开发的一款交通助手App,目前已经上线豌豆荚.魅族应用市场.搜狗手机助手等多个安卓应用市场.目前我决定开源该项目,为更多的安卓应用开发者或者基于百度地图SDK开发人员提供服务和便利.当然App中还有不少bug和可扩展的功能模块,也希望各位开发者为该项目贡献自己的code力量.项目地址:https://github.com/chenyufeng1991/BaiduMap-TrafficAssistant 1.项目简

基于百度地图程序eclipse导出APK密匙key出错

       最近基于百度地图sdk写了个demo,在eclipse上真机测试的时候是正常运行的,没有任何问题,但是当我导出apk安装到手机上的时候,却发现地图都是白格子,经调试发现程序并没有访问百度地图后台失败,发现原来是密匙key出错了       通过再三调试,原来在打包成apk的时候,eclipse的用来申请密匙的sha1变了       如图示,这是eclipse原来的sha1                                                   图一  

Android 百度地图 SDK v3_3_0 (五) ---POI搜索和在线建议查询功能

转载请标明出处:http://blog.csdn.net/tanzuai/article/details/43835431 目前百度地图SDK所集成的检索服务包括:POI检索.公交信息查询.线路规划.地理编码.在线建议查询.短串分享. 本篇博客将先介绍POI检索和在线建议查询(在地图地位功能基础上实现的,还不知道定位的童靴,请参考Android 百度地图 SDK v3.3.0 (二)--- 地图定位和图层展示) 百度地图SDK提供三种类型的POI检索:周边检索.区域检索和城市内检索.下面将以城市

Android中级篇之百度地图SDK v3.5.0-一步一步带你仿各大主流APP地图定位移动选址功能

定位+移动选址 百学须先立志-学前须知: 我们经常在各大主流APP上要求被写上地址,如百度外卖.爱鲜蜂收货地址等等:其中他们大多数是可以让我们在地图上移动选址.就如下面这段GIF演示的一样: 尽信书,不如无书-能学到什么? 1.地图状态MapStatus类及监听setOnMapStatusChangeListener 2.定位LocationClient类 3.反地理编码GeoCoder类 工欲善其事必先利其器-申请Key 百度地图访问应用(AK)申请地址:http://lbsyun.baidu

使用百度地图SDK

百度地图 SDK升级了,所以就试试这个新版的,发现和以前的又很多不一样的地方,但是熟悉以前的版本改动挺大的,想设置个MapView的各种参数都不知道怎么搞,摸索了很久才有点眉目-_-! 尤其是百度的API文档,简直让人抓狂... 实现功能:使用自定义的Fragment作为百度地图,可以自动定位,手动定位,定位标志等等.. 还可以实现更多的功能... 首先下载好百度地图的最新SDK 定位SDKv4.2 ,   地图SDKv3.0.0 按照官方说明,下载好sdk,导入工程 需要先到百度控制台申请ak

Android定位&amp;地图&amp;导航——基于百度地图实现的定位功能

一.问题描述 LBS位置服务是android应用中重要的功能,应用越来越广泛,下面我们逐步学习和实现lbs相关的应用如定位.地图.导航等,首先我们看如何基于百度地图实现定位功能 二.配置环境 1.注册密钥:地址http://developer.baidu.com/map/ 2.下载定位SDK,并导入SDK如图所示: 三.编写MyApplication类 编写MyApplication类,为了使用方便我们可以将实现定位的方法封装的Application组件中 封装下列方法 1.  获取定位信息——

Android 百度地图 SDK v3_3_0 (六) ---驾车、步行、公交路线搜索

目前百度地图SDK所集成的检索服务包括:POI检索.公交信息查询.线路规划.地理编码.在线建议查询.短串分享. 上篇博客讲解了POI检索和在线建议查询,这篇博客将讲解经常用到的线路规划. 在讲解代码之前先上张效果图: 好了!现在我们上代码,来实现上面的功能(代码中都做了相应的注解) 路线规划检索有三种检索:驾车,步行,公交车!三种实现的步骤基本类似,下面我们就拿一种来做解析(公交车). 1.首先我们要实例化路线规划检索的实例 // 初始化搜索模块,注册事件监听 mSearch = RoutePl

Android 百度地图 SDK v3.0.0 (四) 引入离线地图功能

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37758097 一直觉得地图应用支持离线地图很重要啊,我等移动2G屌丝,流量不易,且用且珍惜. 对于官方开发指南对于离线地图的教程,提供了两种方案: 第一,手动导入,先将从官网下载的离线包解压,把vmp文件夹拷入SD卡根目录下的BaiduMapSDK文件夹内.好吧,我表示不能接受,无视了. 第二,接口下载方法如下:mOffline.start(cityid);还比较靠谱,就是没详