GeoServer自动发布地图服务

 

1 NetCDF气象文件自动发布案例

GeoServer是一个地理服务器,提供了管理页面进行服务发布,样式,切片,图层预览等一系列操作,但是手动进行页面配置有时并不满足业务需求,所以GeoServer同时提供了丰富的rest接口可供用户自己组织业务逻辑进行自动化管理。
  本文以气象文件的NetCDF自动化发布的需求,阐述如何以rest接口实现用户多样性需求。气象文件特殊性在于几乎每隔一段时间就会更新,甚至逐小时或半小时的更新频率,用户如果手动发布了气象文件的若干图层作为专题服务,一旦获取到最新的气象文件,用户希望立马可以看到新的数据源上的专题图,而人工即时更新现有的图层服务几乎是不现实的,类似这种定时或者即时响应的需求应该交由自动化完成,本文实现NetCDF气象文件自动发布便是为了解决此类需求。

 

1.1 NetCDF插件安装

选择对应版本的下载地址:http://geoserver.org/release/2.11.0/

下载插件,解压,将jar文件全部复制到geoserver中的webapps\geoserver\WEB-INF\lib目录中,重启geoserver即可。

1.2 rest示例

发布nc文件数据存储

将E:\xxx.nc该文件发布成栅格数据存储,发布到cite工作区,数据存储名称为netcdfstore。

curl -v -u admin:geoserver -XPOST -H "Content-type: text/xml" -d 
"<coverageStore><name>netcdfstore</name><type>NetCDF</type><enabled>true</enabled>
<workspace><name>cite</name></workspace><__default>false</__default>
<url>file://E://xxx.nc</url></coverageStore>"
http://localhost:8090/geoserver/rest/workspaces/cite/coveragestores/netcdfstore

 注意路径格式是:file://E://xxx.nc,而不是file://E:\xxx.nc或file://E:\\xxx.nc,这应该是该插件的一个bug

修改nc文件数据存储

将netcdfstore的数据存储位置由E:\xxx.nc指向D:\xxv.nc。

curl -v -u admin:geoserver -XPUT -H "Content-type: text/xml" -d 
"<coverageStore><name>netcdfstore</name><type>NetCDF</type><enabled>true</enabled>
<workspace><name>cite</name></workspace><__default>false</__default>
<url>file://D://xxc.nc</url></coverageStore>"
http://localhost:8090/geoserver/rest/workspaces/cite/coveragestores/netcdfstore

发布栅格图层

将netcdfstore数据存储中的RH2图层发布

curl -v -u  admin:geoserver -XPOST -H "Content-type: text/xml" -d 
"<coverage><nativeCoverageName>RH2</nativeCoverageName><name>RH2</name></coverage>" 
http://localhost:8090/geoserver/rest/workspaces/cite/coveragestores/netcdfstore/coverages

 绑定图层样式

将发布的RH2样式绑定已经发布的一个名称叫RH2Style的样式。

curl -v -u admin:geoserver -XPUT -H "Content-type: text/xml" -d "<layer>
<defaultStyle><name>RH2Style</name></defaultStyle></layer>" 
http://localhost:8090/geoserver/rest/layers/RH2

1.3 自动化发布

Node.js

var child_process = require('child_process');
var async = require('async');
//构造一个netcdf管理类
function NetCDFManager(options){this.ip=options.ip;this.port=options.port;this._geoserverurl=`http://${this.ip}:${this.port}/geoserver/rest`;this.user=options.user;//geoserver的用户名密码this.password=options.password;this.layerlist=options.layerlist;this.ws=(options.ws!==undefined)?options.ws:'netcdf';//工作区间,默认是netcdf工作区间this.storename=(options.storename!==undefined)?options.storename:'netcdfstore';//netcdf数据存储名称,默认是netcdfstore
}
//根据名称获取栅格数据存储
NetCDFManager.prototype.getCoverageStorebyName=function(cb){let storename=this.storename;let url=this._geoserverurl+`/workspaces/${this.ws}/coveragestores/${storename}.json`;var cmd=`curl -v -u ${this.user}:${this.password} -XGET ${url}`;child_process.exec(cmd, function(err,stdout,stderr) {if(stdout.indexOf('No such')>-1){cb(false);return;}if(JSON.parse(stdout).coverageStore.name===storename)cb(true);elsecb(false);});
}
//发布一个栅格数据存储
NetCDFManager.prototype.publishCoverageStore = function(netcdffile,cb){netcdffile=netcdffile.replace(/\\/g,'//');var xml=`<coverageStore><name>${this.storename}</name><type>NetCDF</type><enabled>true</enabled><workspace><name>${this.ws}</name></workspace><__default>false</__default><url>file://${netcdffile}</url></coverageStore>`;var cmd=`curl -v -u ${this.user}:${this.password} -XPOST -H "Content-type: text/xml" -d "${xml}" ${this._geoserverurl}/workspaces/${this.ws}/coveragestores`;child_process.exec(cmd, function(err,stdout,stderr) {if(stdout=='')cb(true);elsecb(false);});
}
//修改已发布的数据存储
NetCDFManager.prototype.updateCoverageStore = function(netcdffile,cb){netcdffile=netcdffile.replace(/\\/g,'//');var xml=`<coverageStore><name>${this.storename}</name><type>NetCDF</type><enabled>true</enabled><workspace><name>${this.ws}</name></workspace><__default>false</__default><url>file://${netcdffile}</url></coverageStore>`;var cmd=`curl -v -u ${this.user}:${this.password} -XPUT -H "Content-type: text/xml" -d "${xml}" ${this._geoserverurl}/workspaces/${this.ws}/coveragestores/${this.storename}`;child_process.exec(cmd, function(err,stdout,stderr) {if(stdout=='')cb(true);elsecb(false);});}
//发布一个图层
NetCDFManager.prototype.publishCoverage = function(coverage_name,cb){let xml=`<coverage><nativeCoverageName>${coverage_name}</nativeCoverageName><name>${coverage_name}</name></coverage>`;let url=`${this._geoserverurl}/workspaces/${this.ws}/coveragestores/${this.storename}/coverages`;var cmd=`curl -v -u ${this.user}:${this.password} -XPOST -H "Content-type: text/xml" -d "${xml}" ${url}`;child_process.exec(cmd, function(err,stdout, stderr) {if(stdout=='')cb(true);elsecb(false);});
}
//给发布的图层赋予样式
NetCDFManager.prototype.setLayerStyle = function(layername,stylename,cb){let xml=`<layer><defaultStyle><name>${stylename}</name></defaultStyle></layer>`;let url=`${this._geoserverurl}/layers/${layername}`;var cmd=`curl -v -u ${this.user}:${this.password} -XPUT -H "Content-type: text/xml" -d "${xml}" ${url}`;child_process.exec(cmd, function(err,stdout, stderr) {if(stdout=='')cb(true);elsecb(false);});
}/*
伪逻辑代码1 根据数据存储名称,判定是否有该数据存储。没有,publishCoverageStore一个,接步骤2.有,updateCoverageStore即可,end!
2 publishCoverageStore发布数据存储后,将规定要发布的图层逐一发布publishCoverage,逐一赋予样式setLayerStyle
注意都是异步的,需要后台代码转同步,js中的async库负责处理异步陷阱,其他语言自行百度。*/var netCDFManager=new NetCDFManager({ip:'localhost',port:'8090',user:'admin',password:'geoserver',ws:'netcdf',storename:'netcdfstore',layerlist:['RH2','SKT','TP','V10','VIS']
});
function publish(ncfile) {async.waterfall([//查询是否已经存在命名为netcdfstore的数据存储function (done) {netCDFManager.getCoverageStorebyName(function (info) {done(null, info);});},function (info, done) {//已存在数据存储,直接替换其数据源为新的nc文件if (info) {console.log('指定的数据存储已存在,直接进行更新操作');netCDFManager.updateCoverageStore(ncfile, function (info) {if (info) {console.log('数据存储已经更新成功!');done(null, info);} else {console.log('数据存储已经更新失败!');done(info, null);}});}//不存在数据存储,新发布else {console.log('指定的数据存储不存在,发布数据存储');publishNC(ncfile, done);}}], function (error, result) {if (error)console.log('自动发布存在错误!');elseconsole.log('自动发布完成!');})
}function publishNC(ncfile,cb){async.waterfall([function (done) {netCDFManager.publishCoverageStore(ncfile,function(info){if(info){console.log('数据存储已经发布成功!');done(null, info);}else{console.log('数据存储已经发布失败!');done(info, null);}});}, function (resule,done) {//发布图层
            publishLayers(netCDFManager.layerlist,done);},function (result,done) {//发布样式
            publishStyles(netCDFManager.layerlist,done);}],function (error, result) {if(error){console.log('自动发布存在错误!');cb(error,null);}else{console.log('自动发布完成!');cb(null,result);}})
}
//自动发布一些列图层
function publishLayers(layerlist,cb){let asyncs={};for(let i=0;i<layerlist.length;i++){asyncs[i]=function(done){let layername=layerlist[i];netCDFManager.publishCoverage(layername,function(info){if(info){console.log(`${layername}发布成功!`);done(null, info);}else{console.log(`${layername}发布失败!`);done(info, null);}});}}async.parallel(asyncs, function (error, result) {if(error)cb(error,null);elsecb(null,result);})
}//修改指定图层为指定样式
function publishStyles(stylelist,cb){let asyncs={};for(let i=0;i<stylelist.length;i++){asyncs[i]=function(done){let layername=stylelist[i];netCDFManager.setLayerStyle(layername,layername,function(info){if(info){console.log(`${layername}样式发布成功!`);done(null, info);}else{console.log(`${layername}样式发布失败!`);done(info, null);}});}}async.parallel(asyncs, function (error, result) {if(error)cb(error,null);elsecb(null,result);})
}publish('D:\\G_2017070419.nc');

执行node app.js后

 

 

perfect!

 

2 实现批量发布地图服务

上文《GeoServer发布地图服务 》介绍了如何利用GeoServer发布WCS服务,那么如果我有很多数据需要进行发布,这样利用GeoServer提供的UI界面进行操作显然很不显示。那能不能利用GeoServer提供的API进行操作呢?GeoServer提供了REST API方便我们利用代码进行操作。用户手册中提供了如下语言或方法进行操作:cURL,PHP,Python,Java和Ruby。

可惜的是除了cURL有详细的文档之外,其它语言参考文档很少。不得不说开源软件就是没有很好的技术支持,毕竟是开源免费的,也不可能有很好的技术支持,免费开源给你用就是最大的奉献了。哈哈,支持开源!

Java篇

我先使用了Java语言的geoserver manager。在Eclipse新建一个Maven工程,添加相应的依赖包,下面是一个读出数据的例子:

public static boolean read() {String restUrl = "http://localhost/geoserver";String username = "admin";String password = "geoserver";GeoServerRESTReader reader;try {reader = new GeoServerRESTReader(restUrl, username, password);} catch (MalformedURLException e) {e.printStackTrace();return false;}String workspace = "whu.images";String store = "00N006E";String name = "00N006E";RESTCoverage coverage = reader.getCoverage(workspace, store, name);System.out.println(coverage.getAbstract());return true;}

但是我在写入栅格数据的时候出现了一些问题,如下是数据存储的类继承关系:

 

 我们可以看到Coverage Store没有实现类,GSAbstractCoveragestoreEncoder是一个抽象类,而且是被标注@Deprecated的,所以我不知道怎么新建Coverage Store,本来想自己写一个实现类,最终还是放弃了。

Python篇

后来才用的Python解决了问题,但是也不是一帆风顺的。
首先安装gsconfig包,如果不知道如何安装,参考Python模块常用的几种安装方式。
安装完以后,代码如下:
如下,采用默认的用户名,密码,默认的工作空间,所以函数的参数很少,如果你要自定义这些,详细查看函数的说明。

from geoserver.catalog import Cataloggeourl = "http://localhost/geoserver/rest"  # the url of geoserver
geocat = Catalog(geourl)  # create a Catalog objectstore_name = "00N010E"
data = "E:/RSImageService/data/images/00N010E.tif"
geocat.create_coveragestore(store_name, data)

但是上面使用create_coveragestore有一个问题,即会将你的文件默认拷贝到你的Data Directory中,如果你数据很多,这样你就会有两份数据了,极大的浪费了磁盘空间。

后来发现Catalog类有提供一个create_coveragestore2的方法,可以创建一个UnSavedCoveragestore,数据不会上传。

from geoserver.catalog import Cataloggeourl = "http://localhost/geoserver/rest"  # the url of geoserver
geocat = Catalog(geourl)  # create a Catalog object

store_name = "00N010E"
data_url = "fiel:E:/RSImageService/data/images/00N010E.tif"
geostore = geocat.create_coveragestore2(store_name)
geostore.url = data_url
geocat.save(geostore)

但是程序一运行就回返回一个服务器内部错误505,Error code (505) from geoserver:: data store must be part of a workspace.

最后自己写了一个方法用于发布GeoTIFF影像(从GitHub上看到的一段代码,运行有点问题,然后自己修改了下)。给Catalog类添加一个create_coveragestore3方法,用户发布栅格数据,同时不复制数据。这需要修改gsconfig源代码,然后重新编译下。

create_coveragestore3方法如下:

def create_coveragestore3(self, name, data_url, workspace=None, overwrite=False):if not overwrite:try:store = self.get_store(name, workspace)msg = "There is already a store named " + nameif workspace:msg += " in " + str(workspace)raise ConflictingDataError(msg)except FailedRequestError:# we don't really expect that every layer name will be takenpassif workspace is None:workspace = self.get_default_workspace()headers = {"Content-type": "text/plain","Accept": "application/xml"}ext = "geotiff"cs_url = url(self.service_url,["workspaces", workspace.name, "coveragestores", name, "external." + ext],{ "configure" : "first", "coverageName" : name})headers, response = self.http.request(cs_url, "PUT", data_url, headers)self._cache.clear()if headers.status != 201:raise UploadError(response)

最后的客户端调用代码:

from geoserver.catalog import Cataloggeourl = "http://localhost/geoserver/rest"  # the url of geoserver
geocat = Catalog(geourl)  # create a Catalog object

store_name = "00N010E"
data_url = "file:E:/RSImageService/data/images/00N010E.tif"
geocat.create_coveragestore3(store_name, data_url)

如果你要发布很多数据,遍历文件夹调用create_coveragestore3即可。

3. 利用java后台进行geoserver查询

使用后台的原因

 

由于项目要求,之前的函数必须要拆开封装,但对于jsonp来说,回调函数一旦分开,就会有异步的问题(jsonp永远都是异步的,除非你将处理都放到回调中去)。所以考虑从前台传参到后台方法去处理,后台再通过url来进行写入。

后台的主要实现方式

    /** * geoserver查询 * @param url 基地址 * @param layer 图层名 * @param key 键 * @param value 值 * @return  */  public String Geo2server(String url,String layer,String key,String value){  StringBuilder json = new StringBuilder();    MsgBox box = null;  try {    url += "?service=WFS&version=1.1.0&request=GetFeature&typeName=" + layer  +  "&outputFormat=application%2Fjson&filter=<Filter><PropertyIsEqualTo>" +  "<PropertyName>"+ key +"</PropertyName>" + "<Literal>"+  value +"</Literal>" +  "</PropertyIsEqualTo></Filter>";  URL newUrl = new URL(url);  HttpURLConnection conn = (HttpURLConnection) newUrl.openConnection();  BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(),"utf-8"));      String inputLine = null;    while ( (inputLine = in.readLine()) != null) {    json.append(inputLine);    }    in.close();    } catch (MalformedURLException e) {    e.printStackTrace();    } catch (IOException e) {    e.printStackTrace();    }   return json.toString();  }  

注意事项

由于没有做数据的分析,所以有可能返回错误的数据,但概率很小,只要你地址没写出,没数据的时候也能返回null,也能成功。

 

 

 

 

 

 

 

 

参考文章

遥想公瑾当年,GeoServer实现NetCDF气象文件自动发布

TheOneGIS, GeoServer:代码实现批量发布地图服务

WilsonOnIsland, 利用java后台进行geoserver查询

转载于:https://www.cnblogs.com/arxive/p/8416427.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

selenium+ python自动化--断言assertpy

前言&#xff1a; 在对登录验证时&#xff0c;不知道为何原因用unittest的断言不成功&#xff0c;就在网上发现这个assertpy&#xff0c;因此做个笔记 准备&#xff1a; pip install assertypy 例子&#xff1a; 1 from assertpy import assert_that2 3 4 def check_login():5 …

11. 盛最多水的容器

11. 盛最多水的容器 给你 n 个非负整数 a1&#xff0c;a2&#xff0c;…&#xff0c;an&#xff0c;每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线&#xff0c;垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线&#xff0c;使得它们与 x 轴共同构…

深入理解ES6 pdf

下载地址&#xff1a;网盘下载目录 第1章 块级作用域绑定 1var声明及变量提升&#xff08;Hoisting&#xff09;机制 1块级声明 3-- let声明 3-- 禁止重声明 4-- const声明 4-- 临时死区&#xff08;Temporal Dead Zone&#xff09; 6循环中的块作用域绑定 7-- 循环中的函…

MyBatis之输入与输出(resultType、resultMap)映射

2019独角兽企业重金招聘Python工程师标准>>> 在MyBatis中&#xff0c;我们通过parameterType完成输入映射(指将值映射到sql语句的占位符中&#xff0c;值的类型与dao层响应方法的参数类型一致)&#xff0c;通过resultType完成输出映射(从数据库中输出&#xff0c;通…

2021-08-25556. 下一个更大元素 III

556. 下一个更大元素 III 给你一个正整数 n &#xff0c;请你找出符合条件的最小整数&#xff0c;其由重新排列 n 中存在的每位数字组成&#xff0c;并且其值大于 n 。如果不存在这样的正整数&#xff0c;则返回 -1 。 注意 &#xff0c;返回的整数应当是一个 32 位整数 &…

gradle tool升级到3.0注意事项

Gradle版本升级 其实当AS升级到3.0之后&#xff0c;Gradle Plugin和Gradle不升级也是可以继续使用的&#xff0c;但很多新的特性如&#xff1a;Java8支持、新的依赖匹配机制、AAPT2等新功能都无法正常使用。 Gradle Plugin升级到3.0.0及以上&#xff0c;修改project/build.grad…

如何使用React,TypeScript和React测试库创建出色的用户体验

Im always willing to learn, no matter how much I know. As a software engineer, my thirst for knowledge has increased a lot. I know that I have a lot of things to learn daily.无论我知道多少&#xff0c;我总是愿意学习。 作为软件工程师&#xff0c;我对知识的渴望…

PowerDesigner常用设置

2019独角兽企业重金招聘Python工程师标准>>> 使用powerdesigner进行数据库设计确实方便&#xff0c;以下是一些常用的设置 附加&#xff1a;工具栏不见了 调色板(Palette)快捷工具栏不见了 PowerDesigner 快捷工具栏 palette 不见了&#xff0c;怎么重新打开&#x…

bzoj5090[lydsy11月赛]组题

裸的01分数规划,二分答案,没了. #include<cstdio> #include<algorithm> using namespace std; const int maxn100005; int a[maxn]; double b[maxn]; double c[maxn]; typedef long long ll; ll gcd(ll a,ll b){return (b0)?a:gcd(b,a%b); } int main(){int n,k;s…

797. 所有可能的路径

797. 所有可能的路径 给你一个有 n 个节点的 有向无环图&#xff08;DAG&#xff09;&#xff0c;请你找出所有从节点 0 到节点 n-1 的路径并输出&#xff08;不要求按特定顺序&#xff09; 二维数组的第 i 个数组中的单元都表示有向图中 i 号节点所能到达的下一些节点&#…

深入框架本源系列 —— Virtual Dom

该系列会逐步更新&#xff0c;完整的讲解目前主流框架中底层相通的技术&#xff0c;接下来的代码内容都会更新在 这里 为什么需要 Virtual Dom 众所周知&#xff0c;操作 DOM 是很耗费性能的一件事情&#xff0c;既然如此&#xff0c;我们可以考虑通过 JS 对象来模拟 DOM 对象&…

网络工程师常备工具_网络安全工程师应该知道的10种工具

网络工程师常备工具If youre a penetration tester, there are numerous tools you can use to help you accomplish your goals. 如果您是渗透测试人员&#xff0c;则可以使用许多工具来帮助您实现目标。 From scanning to post-exploitation, here are ten tools you must k…

configure: error: You need a C++ compiler for C++ support.

安装pcre包的时候提示缺少c编译器 报错信息如下&#xff1a; configure: error: You need a C compiler for C support. 解决办法&#xff0c;使用yum安装&#xff1a;yum -y install gcc-c 转载于:https://www.cnblogs.com/mkl34367803/p/8428264.html

程序编写经验教训_编写您永远都不会忘记的有效绩效评估的经验教训。

程序编写经验教训This article is intended for two audiences: people who need to write self-evaluations, and people who need to provide feedback to their colleagues. 本文面向两个受众&#xff1a;需要编写自我评估的人员和需要向同事提供反馈的人员。 For the purp…

删除文件及文件夹命令

方法一&#xff1a; echo off ::演示&#xff1a;删除指定路径下指定天数之前&#xff08;以文件的最后修改日期为准&#xff09;的文件。 ::如果演示结果无误&#xff0c;把del前面的echo去掉&#xff0c;即可实现真正删除。 ::本例需要Win2003/Vista/Win7系统自带的forfiles命…

BZOJ 3527: [ZJOI2014]力(FFT)

题意 给出\(n\)个数\(q_i\),给出\(Fj\)的定义如下&#xff1a; \[F_j\sum \limits _ {i < j} \frac{q_iq_j}{(i-j)^2}-\sum \limits _{i >j} \frac{q_iq_j}{(i-j)^2}.\] 令\(E_iF_i/q_i\)&#xff0c;求\(E_i\). 题解 一开始没发现求\(E_i\)... 其实题目还更容易想了... …

c# 实现刷卡_如何在RecyclerView中实现“刷卡选项”

c# 实现刷卡Lets say a user of your site wants to edit a list item without opening the item and looking for editing options. If you can enable this functionality, it gives that user a good User Experience. 假设您网站的用户想要在不打开列表项并寻找编辑选项的情…

批处理命令无法连续执行

如题&#xff0c;博主一开始的批处理命令是这样的&#xff1a; cd node_modules cd heapdump node-gyp rebuild cd .. cd v8-profiler-node8 node-pre-gyp rebuild cd .. cd utf-8-validate node-gyp rebuild cd .. cd bufferutil node-gyp rebuild pause执行结果&#xff1…

sql语句中的in用法示例_示例中JavaScript in操作符

sql语句中的in用法示例One of the first topics you’ll come across when learning JavaScript (or any other programming language) are operators. 学习JavaScript(或任何其他编程语言)时遇到的第一个主题之一是运算符。 The most common operators are the arithmetic, l…

vue项目实战总结

马上过年了&#xff0c;最近工作不太忙&#xff0c;再加上本人最近比较懒&#xff0c;毫无斗志&#xff0c;不愿学习新东西&#xff0c;或许是要过年的缘故(感觉像是在找接口)。 就把前一段时间做过的vue项目&#xff0c;进行一次完整的总结。 这次算是详细总结&#xff0c;会从…