Geoserver + Elasticsearch海量空间数据应用

本文详细介绍了如何在geoserver中整合elasticsearch,包括环境搭建、geoserveres插件的geojson依赖、Kibana数据展示及Java程序同步更新es。博主提醒了关于知识产权的问题,并指出部分博客内容的不足。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前要

       最近在研究海量空间数据应用技术方案,技术栈依然依托于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,局部代码如下:


  
  1. /**
  2. * Bean name default 函数名字
  3. *
  4. * @return
  5. */
  6. @Bean(name = "transportClient")
  7. public TransportClient transportClient() {
  8. LOGGER.info( "Elasticsearch初始化开始。。。。。");
  9. TransportClient transportClient = null;
  10. try {
  11. // 配置信息
  12. Settings esSetting = Settings.builder()
  13. .put( "cluster.name", clusterName) //集群名字
  14. .put( "client.transport.sniff", true) //增加嗅探机制,找到ES集群
  15. .put( "thread_pool.search.size", Integer.parseInt(poolSize)) //增加线程池个数,暂时设为5
  16. .build();
  17. //配置信息Settings自定义
  18. transportClient = new PreBuiltTransportClient(esSetting);
  19. TransportAddress transportAddress = new TransportAddress(InetAddress.getByName(hostName), Integer.valueOf(port));
  20. transportClient.addTransportAddresses(transportAddress);
  21. } catch (Exception e) {
  22. LOGGER.error( "elasticsearch TransportClient create error!!", e);
  23. }
  24. return transportClient;
  25. }

初始化es客户端 


  
  1. /**
  2. * 创建索引
  3. *
  4. * @param index
  5. * @return
  6. */
  7. @Override
  8. public boolean createIndex(String index) {
  9. if (!isIndexExist(index)) {
  10. LOGGER.info( "Index is not exits!");
  11. }
  12. CreateIndexResponse indexresponse = client.admin().indices().prepareCreate(index).execute().actionGet();
  13. LOGGER.info( "执行建立成功?" + indexresponse.isAcknowledged());
  14. return indexresponse.isAcknowledged();
  15. }
  16. /**
  17. * 删除索引
  18. *
  19. * @param index
  20. * @return
  21. */
  22. @Override
  23. public boolean deleteIndex(String index) {
  24. if (!isIndexExist(index)) {
  25. LOGGER.info( "Index is not exits!");
  26. }
  27. AcknowledgedResponse dResponse = client.admin().indices().prepareDelete(index).execute().actionGet();
  28. if (dResponse.isAcknowledged()) {
  29. LOGGER.info( "delete index " + index + " successfully!");
  30. } else {
  31. LOGGER.info( "Fail to delete index " + index);
  32. }
  33. return dResponse.isAcknowledged();
  34. }
  35. /**
  36. * 判断索引是否存在
  37. *
  38. * @param index
  39. * @return
  40. */
  41. @Override
  42. public boolean isIndexExist(String index) {
  43. IndicesExistsResponse inExistsResponse = client.admin().indices().exists( new IndicesExistsRequest(index)).actionGet();
  44. if (inExistsResponse.isExists()) {
  45. LOGGER.info( "Index [" + index + "] is exist!");
  46. } else {
  47. LOGGER.info( "Index [" + index + "] is not exist!");
  48. }
  49. return inExistsResponse.isExists();
  50. }
  51. /**
  52. * @Description: 判断inde下指定type是否存在
  53. */
  54. @Override
  55. public boolean isTypeExist(String index, String type) {
  56. return isIndexExist(index)
  57. ? client.admin().indices().prepareTypesExists(index).setTypes(type).execute().actionGet().isExists()
  58. : false;
  59. }
  60. @Override
  61. public boolean createMapping(String index, String type) {
  62. // 创建index
  63. Map<String, Object> settings = new HashMap<>();
  64. settings.put( "number_of_shards", 4); // 分片数量
  65. settings.put( "number_of_replicas", 0); // 复制数量, 导入时最好为0, 之后2-3即可
  66. settings.put( "refresh_interval", "10s"); // 刷新时间
  67. CreateIndexRequestBuilder prepareCreate = client.admin().indices().prepareCreate(index);
  68. prepareCreate.setSettings(settings);
  69. try {
  70. // 创建mapping
  71. XContentBuilder mapping = XContentFactory.jsonBuilder()
  72. .startObject()
  73. .startObject(type)
  74. .startObject( "properties")
  75. .startObject( "osm_id").field( "type", "text").endObject()
  76. .startObject( "code").field( "type", "long").endObject()
  77. .startObject( "fclass").field( "type", "text").endObject()
  78. .startObject( "name").field( "type", "text").endObject()
  79. .startObject( "geom").field( "type", "geo_point").endObject()
  80. .endObject()
  81. .endObject()
  82. .endObject();
  83. prepareCreate.addMapping(type, mapping);
  84. CreateIndexResponse response = prepareCreate.execute().actionGet();
  85. return response.isAcknowledged();
  86. } catch (IOException e) {
  87. LOGGER.error(e.getMessage());
  88. }
  89. return false;
  90. }

 索引功能封装


  
  1. @Override
  2. public String addData(JSONObject jsonObject, String index, String type, String id) {
  3. IndexResponse response;
  4. try {
  5. XContentBuilder xContentBuilder = XContentFactory.jsonBuilder()
  6. .startObject()
  7. .field( "osm_id",jsonObject.getString( "osmId"))
  8. .field( "code",jsonObject.getIntValue( "code"))
  9. .field( "fclass",jsonObject.getString( "fclass"))
  10. .field( "name",jsonObject.getString( "name"))
  11. .startObject( "geom").field( "lat", jsonObject.getDoubleValue( "lat")).field( "lon", jsonObject.getDoubleValue( "lon")).endObject()
  12. .endObject();
  13. response = client.prepareIndex(index, type, id).setSource(xContentBuilder).get();
  14. LOGGER.info( "addData response status:{},id:{}", response.status().getStatus(), response.getId());
  15. return response.getId();
  16. } catch (IOException e) {
  17. LOGGER.error(e.getMessage());
  18. }
  19. return null;
  20. }
  21. @Override
  22. public String addData(Map<String, ?> source, String index, String type, String id) {
  23. IndexResponse response = client.prepareIndex(index, type, id).setSource(source).get();
  24. LOGGER.info( "addData response status:{},id:{}", response.status().getStatus(), response.getId());
  25. return response.getId();
  26. }
  27. @Override
  28. public String addData(JSONObject jsonObject, String index, String type) {
  29. return addData(jsonObject, index, type, UUID.randomUUID().toString().replaceAll( "-", "").toUpperCase());
  30. }
  31. /**
  32. * 通过ID删除数据
  33. *
  34. * @param index 索引,类似数据库
  35. * @param type 类型,类似表
  36. * @param id 数据ID
  37. */
  38. @Override
  39. public String deleteDataById(String index, String type, String id) {
  40. DeleteResponse response = client.prepareDelete(index, type, id).execute().actionGet();
  41. LOGGER.info( "deleteDataById response status:{},id:{}", response.status().getStatus(), response.getId());
  42. return response.getId();
  43. }
  44. @Override
  45. public String updateDataById(JSONObject jsonObject, String index, String type, String id) {
  46. UpdateRequest updateRequest = new UpdateRequest();
  47. updateRequest.index(index).type(type).id(id).doc(jsonObject);
  48. ActionFuture<UpdateResponse> updateResponseActionFuture = client.update(updateRequest);
  49. return updateResponseActionFuture.actionGet().getId();
  50. }
  51. @Override
  52. public String updateDataById(Map<String, ?> source, String index, String type, String id) {
  53. return null;
  54. }
  55. /**
  56. * 通过ID获取数据
  57. *
  58. * @param index 索引,类似数据库
  59. * @param type 类型,类似表
  60. * @param id 数据ID
  61. * @param fields 需要显示的字段,逗号分隔(缺省为全部字段)
  62. * @return
  63. */
  64. @Override
  65. public Map<String, Object> searchDataById(String index, String type, String id, String fields) {
  66. GetRequestBuilder getRequestBuilder = client.prepareGet(index, type, id);
  67. if (StringUtils.isNotEmpty(fields)) {
  68. getRequestBuilder.setFetchSource(fields.split( ","), null);
  69. }
  70. GetResponse getResponse = getRequestBuilder.execute().actionGet();
  71. return getResponse.getSource();
  72. }

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/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值