前要
最近在研究海量空间数据应用技术方案,技术栈依然依托于geoserver,在此基础衍生和扩展。前期写过一篇Geoserver+GeoMesa技术博客,感兴趣的可以关注查阅。考虑到es的查询性能优秀,特此研究geoserver与es结合结束路线。说到此,博主对国内的一些知识权和博客质量略感失望。知识产权意识非常低下,博主搜索geoserver+es方案,某度前六条搜索结果完全一致,但并不是同一作者,不清楚是作者不同的id号还是知识作品被别人剽窃(剽窃可能性非常大,博主之前深受其害,曾经投诉过),最重要的这六条搜索结果完全是照搬geoserver官网,甚至可以说单纯中英文翻译了一编就发博客,但是官网漏了一条非常重要的技术,没有这项技术,根本无法支撑空间数据geoserver与es结合应用,可见搜索结果作者完全没有进行验证,就开始误人子弟,更可气的是盲目剽窃和跟风的偷盗者(更加不可能自己验证),完全是对知识的亵渎和不尊重。博主在此写博客做一篇补充,希望能帮到更多的地理空间开发爱好者。
环境搭建
- jdk 1.8
-
nodejs(可选)
使用npm安装es-head依赖包与启动。
-
geoserver用与连接es,空间数据服务发布,空间数据展示。
-
geoserver es插件 官网插件缺失一geojson依赖包,如果不添加将无法使用wms地图服务,很多博文照搬官网不做验证,很让人诟病。
-
geojson依赖jar包
-
es安装步骤可自行查阅部署
-
kibana用于es可视化性能监控和管理,可提供空间数据导入展示功能
Kibana 空间数据上传展示效果 -
Logstash (可选)
可用于关系型数据库(Oracle、Postgresql、Mysql等)和文本文件与es数据的同步和导入功能。详情可参照官网介绍。
-
web端es可视化系统,可用于数据查询,类似于Kibana
es-head效果图
集成部署
- 将geoserver es插件包复制与geoserver/lib文件夹下,tomcat启动
geoserver es效果图 es参数配置可见官网说明:
https://docs.geoserver.org/latest/en/user/community/elasticsearch/index.html
-
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服务发布




后续
geoserver+es海量数据应用暂时讲解到这里,如果有感兴趣的博友可关注和评论博主,一起探讨。后续博主打算追踪验证geoserver+es数据查询展示效率,与geoserver+关系型数据库数据查询展示效率做综合对比。知识点稍显凌乱,如有错误,欢迎指正。谢谢大家地支持。
java程序代码地址(下载develop分支):
https://gitee.com/yangdengxian/geodatastore/tree/develop/
poi数据大家可以自行在osm网站下载