Geoserver + Elasticsearch海量空间数据应用

8 篇文章 1 订阅
1 篇文章 0 订阅

前要

       最近在研究海量空间数据应用技术方案,技术栈依然依托于geoserver,在此基础衍生和扩展。前期写过一篇Geoserver+GeoMesa技术博客,感兴趣的可以关注查阅。考虑到es的查询性能优秀,特此研究geoserver与es结合结束路线。说到此,博主对国内的一些知识权和博客质量略感失望。知识产权意识非常低下,博主搜索geoserver+es方案,某度前六条搜索结果完全一致,但并不是同一作者,不清楚是作者不同的id号还是知识作品被别人剽窃(剽窃可能性非常大,博主之前深受其害,曾经投诉过),最重要的这六条搜索结果完全是照搬geoserver官网,甚至可以说单纯中英文翻译了一编就发博客,但是官网漏了一条非常重要的技术,没有这项技术,根本无法支撑空间数据geoserver与es结合应用,可见搜索结果作者完全没有进行验证,就开始误人子弟,更可气的是盲目剽窃和跟风的偷盗者(更加不可能自己验证),完全是对知识的亵渎和不尊重。博主在此写博客做一篇补充,希望能帮到更多的地理空间开发爱好者。

环境搭建

  1. jdk 1.8
  2. nodejs(可选)

    使用npm安装es-head依赖包与启动。

  3. geoserver 2.19

    geoserver用与连接es,空间数据服务发布,空间数据展示。

  4. geoserver es插件

    geoserver es插件
    geoserver es插件

    官网插件缺失一geojson依赖包,如果不添加将无法使用wms地图服务,很多博文照搬官网不做验证,很让人诟病。

  5. gt-geojsondatastore-25.0

    geojson依赖jar包

  6.  

    elasticsearch 7.12.1

    es安装步骤可自行查阅部署

  7. Kibana 7.12.1

    kibana用于es可视化性能监控和管理,可提供空间数据导入展示功能

    Kibana 空间数据上传展示效果
    Kibana 空间数据上传展示效果

     

  8.  

    Logstash (可选)

    可用于关系型数据库(Oracle、Postgresql、Mysql等)和文本文件与es数据的同步和导入功能。详情可参照官网介绍。

  9.  elasticsearch-head

    web端es可视化系统,可用于数据查询,类似于Kibana

    es-head效果图

     


 集成部署

  1. 将geoserver es插件包复制与geoserver/lib文件夹下,tomcat启动
    geoserver es效果图

    es参数配置可见官网说明:

     https://docs.geoserver.org/latest/en/user/community/elasticsearch/index.html

  2.  es空间数据导入

    es服务端开放两种空间数据类型geo-point(用于单点)和geo-shape(可用于复杂空间矢量要素),es支持geojson、geohash、wkt标准空间数据格式,很遗憾不支持wkb格式,因此关系型数据库数据同步要记得格式转换,目前只支持wgs84经纬度数据,数据导入记得坐标系转换。

      我们可使用上述kibana工具导入geojson文件数据,展示图如下,默认点聚合算法效果:

    效果图展示

     es-head数据列表查看如下:

    es-head数据查询展示

     

      使用es java api数据同步

      java程序同步更新es,局部代码如下:

/**
     * Bean name default  函数名字
     *
     * @return
     */
    @Bean(name = "transportClient")
    public TransportClient transportClient() {
        LOGGER.info("Elasticsearch初始化开始。。。。。");
        TransportClient transportClient = null;
        try {
            // 配置信息
            Settings esSetting = Settings.builder()
                    .put("cluster.name", clusterName) //集群名字
                    .put("client.transport.sniff", true)//增加嗅探机制,找到ES集群
                    .put("thread_pool.search.size", Integer.parseInt(poolSize))//增加线程池个数,暂时设为5
                    .build();
            //配置信息Settings自定义
            transportClient = new PreBuiltTransportClient(esSetting);
            TransportAddress transportAddress = new TransportAddress(InetAddress.getByName(hostName), Integer.valueOf(port));
            transportClient.addTransportAddresses(transportAddress);
        } catch (Exception e) {
            LOGGER.error("elasticsearch TransportClient create error!!", e);
        }
        return transportClient;
    }

初始化es客户端 

/**
         * 创建索引
     *
     * @param index
     * @return
     */
    @Override
    public boolean createIndex(String index) {
        if (!isIndexExist(index)) {
            LOGGER.info("Index is not exits!");
        }
        CreateIndexResponse indexresponse = client.admin().indices().prepareCreate(index).execute().actionGet();
        LOGGER.info("执行建立成功?" + indexresponse.isAcknowledged());
        return indexresponse.isAcknowledged();
    }

    /**
     * 删除索引
     *
     * @param index
     * @return
     */
    @Override
    public boolean deleteIndex(String index) {
        if (!isIndexExist(index)) {
            LOGGER.info("Index is not exits!");
        }
        AcknowledgedResponse dResponse = client.admin().indices().prepareDelete(index).execute().actionGet();
        if (dResponse.isAcknowledged()) {
            LOGGER.info("delete index " + index + "  successfully!");
        } else {
            LOGGER.info("Fail to delete index " + index);
        }
        return dResponse.isAcknowledged();
    }

    /**
     * 判断索引是否存在
     *
     * @param index
     * @return
     */
    @Override
    public boolean isIndexExist(String index) {
        IndicesExistsResponse inExistsResponse = client.admin().indices().exists(new IndicesExistsRequest(index)).actionGet();
        if (inExistsResponse.isExists()) {
            LOGGER.info("Index [" + index + "] is exist!");
        } else {
            LOGGER.info("Index [" + index + "] is not exist!");
        }
        return inExistsResponse.isExists();
    }

    /**
     * @Description: 判断inde下指定type是否存在
     */
    @Override
    public boolean isTypeExist(String index, String type) {
        return isIndexExist(index)
                ? client.admin().indices().prepareTypesExists(index).setTypes(type).execute().actionGet().isExists()
                : false;
    }

    @Override
    public boolean createMapping(String index, String type) {
        // 创建index
        Map<String, Object> settings = new HashMap<>();
        settings.put("number_of_shards", 4);    // 分片数量
        settings.put("number_of_replicas", 0);    // 复制数量, 导入时最好为0, 之后2-3即可
        settings.put("refresh_interval", "10s");// 刷新时间

        CreateIndexRequestBuilder prepareCreate = client.admin().indices().prepareCreate(index);
        prepareCreate.setSettings(settings);
        try {
            // 创建mapping
            XContentBuilder mapping = XContentFactory.jsonBuilder()
                    .startObject()
                    .startObject(type)
                    .startObject("properties")
                    .startObject("osm_id").field("type", "text").endObject()
                    .startObject("code").field("type", "long").endObject()
                    .startObject("fclass").field("type", "text").endObject()
                    .startObject("name").field("type", "text").endObject()
                    .startObject("geom").field("type", "geo_point").endObject()
                    .endObject()
                    .endObject()
                    .endObject();
            prepareCreate.addMapping(type, mapping);
            CreateIndexResponse response = prepareCreate.execute().actionGet();
            return response.isAcknowledged();
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }
        return false;
    }

 索引功能封装

@Override
    public String addData(JSONObject jsonObject, String index, String type, String id) {
        IndexResponse response;
        try {
            XContentBuilder xContentBuilder = XContentFactory.jsonBuilder()
                    .startObject()
                    .field("osm_id",jsonObject.getString("osmId"))
                    .field("code",jsonObject.getIntValue("code"))
                    .field("fclass",jsonObject.getString("fclass"))
                    .field("name",jsonObject.getString("name"))
                    .startObject("geom").field("lat", jsonObject.getDoubleValue("lat")).field("lon", jsonObject.getDoubleValue("lon")).endObject()
                    .endObject();
            response = client.prepareIndex(index, type, id).setSource(xContentBuilder).get();
            LOGGER.info("addData response status:{},id:{}", response.status().getStatus(), response.getId());
            return response.getId();
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }

        return null;
    }

    @Override
    public String addData(Map<String, ?> source, String index, String type, String id) {
        IndexResponse response = client.prepareIndex(index, type, id).setSource(source).get();
        LOGGER.info("addData response status:{},id:{}", response.status().getStatus(), response.getId());
        return response.getId();
    }

    @Override
    public String addData(JSONObject jsonObject, String index, String type) {
        return addData(jsonObject, index, type, UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
    }

    /**
     * 通过ID删除数据
     *
     * @param index 索引,类似数据库
     * @param type  类型,类似表
     * @param id    数据ID
     */
    @Override
    public String deleteDataById(String index, String type, String id) {
        DeleteResponse response = client.prepareDelete(index, type, id).execute().actionGet();
        LOGGER.info("deleteDataById response status:{},id:{}", response.status().getStatus(), response.getId());
        return response.getId();
    }

    @Override
    public String updateDataById(JSONObject jsonObject, String index, String type, String id) {
        UpdateRequest updateRequest = new UpdateRequest();

        updateRequest.index(index).type(type).id(id).doc(jsonObject);

        ActionFuture<UpdateResponse> updateResponseActionFuture =  client.update(updateRequest);
        return updateResponseActionFuture.actionGet().getId();
    }

    @Override
    public String updateDataById(Map<String, ?> source, String index, String type, String id) {
        return null;
    }

    /**
     * 通过ID获取数据
     *
     * @param index  索引,类似数据库
     * @param type   类型,类似表
     * @param id     数据ID
     * @param fields 需要显示的字段,逗号分隔(缺省为全部字段)
     * @return
     */
    @Override
    public Map<String, Object> searchDataById(String index, String type, String id, String fields) {
        GetRequestBuilder getRequestBuilder = client.prepareGet(index, type, id);

        if (StringUtils.isNotEmpty(fields)) {
            getRequestBuilder.setFetchSource(fields.split(","), null);
        }

        GetResponse getResponse = getRequestBuilder.execute().actionGet();

        return getResponse.getSource();
    }

es数据的增删改查操作

 

geoserver服务发布

界面
可选字段

 

WMS效果图
WFS请求

 

后续

       geoserver+es海量数据应用暂时讲解到这里,如果有感兴趣的博友可关注和评论博主,一起探讨。后续博主打算追踪验证geoserver+es数据查询展示效率,与geoserver+关系型数据库数据查询展示效率做综合对比。知识点稍显凌乱,如有错误,欢迎指正。谢谢大家地支持。

java程序代码地址(下载develop分支):

https://gitee.com/yangdengxian/geodatastore/tree/develop/

poi数据大家可以自行在osm网站下载

http://download.geofabrik.de/asia/

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值