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.pswp.cn/news/390240.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/390240.shtml
英文地址,請注明出處:http://en.pswp.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;會從…