C# 自动化发布GeoServer之NetCDF
最近新接触了GeoServer,从零开始的研究也是折磨了好几天,发现GeoServer这块很多大佬分享各种解决方案,也是帮了我大忙,刚好告一个段落了,所以也贡献一下我这几天的研究成果,希望能帮到大家。
目标:使用GeoServer自动化发布NetCDF文件,实现部署在Linux上自动化更新图层
环境:.net6.0,GeoServer 2.21,Linux
先看一下目录结构:
AbstracPublishHandler抽象类中写整体的处理方法,主要是对栅格文件处理的固定流程
附上源码:
1 using System.Runtime.InteropServices; 2 3 namespace ReservoirModelAnalysis.Module.GeoServicePublish.Common 4 { 5 /// <summary> 6 /// 发布抽象类 7 /// </summary> 8 public abstract class AbstracPublishHandler 9 { 10 /// <summary> 11 /// 测试的时候使用cmd命令,部署在linux服务器上的时候使用shell命令 12 /// </summary> 13 private Func<string, string> processCommand = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ProcessHandler.CmdProcess : ProcessHandler.ShellProcess; 14 15 /// <summary> 16 /// 数据仓库配置 17 /// </summary> 18 IDataTypeConfig dataTypeConfig; 19 /// <summary> 20 /// 发布处理 21 /// 1.先判断数据仓库是否存在,不存在直接进行第三步 22 /// 2.更新数据仓库,并且流程结束 23 /// 3.发布新的数据仓库 24 /// 4.发布指定的图层 25 /// 5.图层绑定已发布好的样式 26 /// </summary> 27 /// <param name="config">数据仓库类型配置</param> 28 /// <param name="path">文件路由</param> 29 public void Handling(IDataTypeConfig config, string path) 30 { 31 dataTypeConfig = config; 32 Console.WriteLine($"{DateTime.Now}:GeoServer发布准备开始!"); 33 34 if (getCoverageStorebyName()) 35 { 36 //仓库存在 37 UpdateCoverageStore(path); 38 } 39 else 40 { 41 //仓库不存在 42 43 //发布栅格存储 44 PublishCoverageStore(path); 45 46 //发布指定图层 47 PublishCoverages(); 48 49 //绑定样式 50 SetLayerStyle(); 51 52 } 53 54 Console.WriteLine($"{DateTime.Now}:GeoServer发布完成!"); 55 } 56 57 /// <summary> 58 /// 获取指定类型的数据仓库名称 59 /// </summary> 60 /// <returns></returns> 61 private bool getCoverageStorebyName() 62 { 63 string url = $"{GeoServerConfig.GeoServerUrl}/workspaces/{dataTypeConfig.WorkSpaceName}/coveragestores/{dataTypeConfig.StoreName}.json"; 64 string cmd = $"curl -v -u {GeoServerConfig.User}:{GeoServerConfig.Password} -XGET {url}"; 65 66 var result = processCommand.Invoke(cmd); 67 Console.WriteLine($"判断是否有指定的数据仓库:\n【{result}】"); 68 69 //简单的判断一下,内容不为nosuch的情况还有可能报其他错 70 return !(result.IndexOf("No such") > -1); 71 } 72 73 /// <summary> 74 /// 发布一个栅格数据存储 75 /// </summary> 76 /// <param name="path">栅格文件路径 使用//分割</param> 77 /// <returns></returns> 78 protected bool PublishCoverageStore(string path) 79 { 80 string xml = $"<coverageStore><name>{dataTypeConfig.StoreName}</name><type>NetCDF</type><enabled>true</enabled><workspace><name>{dataTypeConfig.WorkSpaceName}</name></workspace><__default>false</__default><url>file://{path}</url></coverageStore>"; 81 string cmd = $"curl -v -u {GeoServerConfig.User}:{GeoServerConfig.Password} -XPOST -H \"Content-type: text/xml\" -d \"{xml}\" {GeoServerConfig.GeoServerUrl}/workspaces/{dataTypeConfig.WorkSpaceName}/coveragestores"; 82 83 var result = processCommand.Invoke(cmd); 84 85 Console.WriteLine($"发布栅格存储结果:\n【{result}】"); 86 87 return true; 88 } 89 90 /// <summary> 91 /// 发布图层 92 /// </summary> 93 /// <returns></returns> 94 protected bool PublishCoverages() 95 { 96 dataTypeConfig.LayerList.ForEach(layerName => 97 { 98 string xml = $"<coverage><nativeCoverageName>{layerName}</nativeCoverageName><name>{layerName}</name></coverage>"; 99 string url = $"{GeoServerConfig.GeoServerUrl}/workspaces/{dataTypeConfig.WorkSpaceName}/coveragestores/{dataTypeConfig.StoreName}/coverages"; 100 string cmd = $"curl -v -u {GeoServerConfig.User}:{GeoServerConfig.Password} -XPOST -H \"Content-type: text/xml\" -d \"{xml}\" {url}"; 101 102 var result = processCommand.Invoke(cmd); 103 104 Console.WriteLine($"发布{layerName}图层结果:\n【{result}】"); 105 }); 106 107 return true; 108 } 109 /// <summary> 110 /// 给图层设置样式 111 /// </summary> 112 /// <returns></returns> 113 protected bool SetLayerStyle() 114 { 115 for (int i = 0; i < dataTypeConfig.LayerList.Count; i++) 116 { 117 string layerName = dataTypeConfig.LayerList[i]; 118 string styleName = dataTypeConfig.StyleList[i]; 119 120 string xml = $"<layer><defaultStyle><name>{styleName}</name></defaultStyle></layer>"; 121 string url = $"{GeoServerConfig.GeoServerUrl}/layers/{layerName}"; 122 string cmd = $"curl -v -u {GeoServerConfig.User}:{GeoServerConfig.Password} -XPUT -H \"Content-type: text/xml\" -d \"{xml}\" {url}"; 123 124 var result = processCommand.Invoke(cmd); 125 126 Console.WriteLine($"绑定{layerName}图层{styleName}样式结果:\n【{result}】"); 127 } 128 return true; 129 } 130 131 /// <summary> 132 /// 更新数据仓储 133 /// </summary> 134 /// <param name="path">栅格文件路径 使用//分割</param> 135 /// <returns></returns> 136 protected bool UpdateCoverageStore(string path) 137 { 138 string xml = $"<coverageStore><name>{dataTypeConfig.StoreName}</name><type>NetCDF</type><enabled>true</enabled><workspace><name>{dataTypeConfig.WorkSpaceName}</name></workspace><__default>false</__default><url>file://{path}</url></coverageStore>"; 139 string cmd = $"curl -v -u {GeoServerConfig.User}:{GeoServerConfig.Password} -XPUT -H \"Content-type: text/xml\" -d \"{xml}\" {GeoServerConfig.GeoServerUrl}/workspaces/{dataTypeConfig.WorkSpaceName}/coveragestores/{dataTypeConfig.StoreName}"; 140 141 var result = processCommand.Invoke(cmd); 142 143 Console.WriteLine($"更新数据仓库{dataTypeConfig.StoreName}结果:\n【{result}】"); 144 145 return true; 146 } 147 } 148 }
DataSourceType中放具体的栅格类型处理方法,目前只用到了NetCDF,NetCDFPublishHandler继承抽象类AbstracPublishHandler直接调用Handling方法即可。
我这边是使用Job定时执行发布任务
Linux上执行结果
1 2023/2/23 下午2:14:00:GeoServer发布准备开始! 2 Note: Unnecessary use of -X or --request, GET is already inferred. 3 * Trying 192.168.1.51:8080... 4 % Total % Received % Xferd Average Speed Time Time Time Current 5 Dload Upload Total Spent Left Speed 6 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 192.168.1.51 (192.168.1.51) port 8080 (#0) 7 * Server auth using Basic with user 'admin' 8 > GET /geoserver/rest/workspaces/workspace_demo/coveragestores/demostore.json HTTP/1.1 9 > Host: 192.168.1.51:8080 10 > Authorization: Basic YWRtaW46Z2Vvc2VydmVy 11 > User-Agent: curl/7.79.1 12 > Accept: */* 13 > 14 * Mark bundle as not supporting multiuse 15 < HTTP/1.1 200 OK 16 < X-Frame-Options: SAMEORIGIN 17 < Content-Type: application/json 18 < Transfer-Encoding: chunked 19 < Server: Jetty(9.4.44.v20210927) 20 < 21 { [496 bytes data] 22 100 491 0 491 0 0 8196 0 --:--:-- --:--:-- --:--:-- 8322 23 * Connection #0 to host 192.168.1.51 left intact 24 判断是否有指定的数据仓库: 25 【{"coverageStore":{"name":"demostore","type":"NetCDF","enabled":true,"workspace":{"name":"workspace_demo","href":"http://192.168.1.51:8080/geoserver/rest/workspaces/workspace_demo.json"},"_default":false,"dateCreated":"2023-02-23 05:33:01.37 UTC","dateModified":"2023-02-23 05:34:01.101 UTC","url":"file://D:\\gsFile\\HadCRUT.5.0.1.0.analysis.anomalies.ensemble_mean.nc","coverages":"http://192.168.1.51:8080/geoserver/rest/workspaces/workspace_demo/coveragestores/demostore/coverages.json"}}】 26 * Trying 192.168.1.51:8080... 27 % Total % Received % Xferd Average Speed Time Time Time Current 28 Dload Upload Total Spent Left Speed 29 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 192.168.1.51 (192.168.1.51) port 8080 (#0) 30 * Server auth using Basic with user 'admin' 31 > PUT /geoserver/rest/workspaces/workspace_demo/coveragestores/demostore HTTP/1.1 32 > Host: 192.168.1.51:8080 33 > Authorization: Basic YWRtaW46Z2Vvc2VydmVy 34 > User-Agent: curl/7.79.1 35 > Accept: */* 36 > Content-type: text/xml 37 > Content-Length: 252 38 > 39 } [252 bytes data] 40 * Mark bundle as not supporting multiuse 41 < HTTP/1.1 200 OK 42 < X-Frame-Options: SAMEORIGIN 43 < Content-Length: 0 44 < Server: Jetty(9.4.44.v20210927) 45 < 46 100 252 0 0 100 252 0 16046 --:--:-- --:--:-- --:--:-- 16800 47 * Connection #0 to host 192.168.1.51 left intact 48 更新数据仓库demostore结果: 49 【】 50 2023/2/23 下午2:14:00:GeoServer发布完成! 51 2023/2/23 下午2:15:00>>>>>>执行GeoService发布 52 2023/2/23 下午2:15:00:GeoServer发布准备开始! 53 Note: Unnecessary use of -X or --request, GET is already inferred. 54 * Trying 192.168.1.51:8080... 55 % Total % Received % Xferd Average Speed Time Time Time Current 56 Dload Upload Total Spent Left Speed 57 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 192.168.1.51 (192.168.1.51) port 8080 (#0) 58 * Server auth using Basic with user 'admin' 59 > GET /geoserver/rest/workspaces/workspace_demo/coveragestores/demostore.json HTTP/1.1 60 > Host: 192.168.1.51:8080 61 > Authorization: Basic YWRtaW46Z2Vvc2VydmVy 62 > User-Agent: curl/7.79.1 63 > Accept: */* 64 > 65 * Mark bundle as not supporting multiuse 66 < HTTP/1.1 404 Not Found 67 < X-Frame-Options: SAMEORIGIN 68 < Content-Type: text/plain 69 < Transfer-Encoding: chunked 70 < Server: Jetty(9.4.44.v20210927) 71 < 72 { [52 bytes data] 73 100 48 0 48 0 0 6269 0 --:--:-- --:--:-- --:--:-- 6857 74 * Connection #0 to host 192.168.1.51 left intact 75 判断是否有指定的数据仓库: 76 【No such coverage store: workspace_demo,demostore】 77 Note: Unnecessary use of -X or --request, POST is already inferred. 78 * Trying 192.168.1.51:8080... 79 % Total % Received % Xferd Average Speed Time Time Time Current 80 Dload Upload Total Spent Left Speed 81 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 192.168.1.51 (192.168.1.51) port 8080 (#0) 82 * Server auth using Basic with user 'admin' 83 > POST /geoserver/rest/workspaces/workspace_demo/coveragestores HTTP/1.1 84 > Host: 192.168.1.51:8080 85 > Authorization: Basic YWRtaW46Z2Vvc2VydmVy 86 > User-Agent: curl/7.79.1 87 > Accept: */* 88 > Content-type: text/xml 89 > Content-Length: 252 90 > 91 } [252 bytes data] 92 * Mark bundle as not supporting multiuse 93 < HTTP/1.1 201 Created 94 < X-Frame-Options: SAMEORIGIN 95 < Location: http://192.168.1.51:8080/geoserver/rest/workspaces/workspace_demo/coveragestores/demostore 96 < Content-Type: text/plain 97 < Content-Length: 9 98 < Server: Jetty(9.4.44.v20210927) 99 < 100 { [9 bytes data] 101 100 261 100 9 100 252 510 14302 --:--:-- --:--:-- --:--:-- 15352 102 * Connection #0 to host 192.168.1.51 left intact 103 发布栅格存储结果: 104 【demostore】 105 Note: Unnecessary use of -X or --request, POST is already inferred. 106 * Trying 192.168.1.51:8080... 107 % Total % Received % Xferd Average Speed Time Time Time Current 108 Dload Upload Total Spent Left Speed 109 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 192.168.1.51 (192.168.1.51) port 8080 (#0) 110 * Server auth using Basic with user 'admin' 111 > POST /geoserver/rest/workspaces/workspace_demo/coveragestores/demostore/coverages HTTP/1.1 112 > Host: 192.168.1.51:8080 113 > Authorization: Basic YWRtaW46Z2Vvc2VydmVy 114 > User-Agent: curl/7.79.1 115 > Accept: */* 116 > Content-type: text/xml 117 > Content-Length: 91 118 > 119 } [91 bytes data] 120 * Mark bundle as not supporting multiuse 121 < HTTP/1.1 201 Created 122 < X-Frame-Options: SAMEORIGIN 123 < Location: http://192.168.1.51:8080/geoserver/rest/workspaces/workspace_demo/coveragestores/demostore/coverages/tas_mean 124 < Content-Type: text/plain 125 < Content-Length: 8 126 < Server: Jetty(9.4.44.v20210927) 127 < 128 { [8 bytes data] 129 100 99 100 8 100 91 38 436 --:--:-- --:--:-- --:--:-- 478 130 * Connection #0 to host 192.168.1.51 left intact 131 发布tas_mean图层结果: 132 【tas_mean】 133 * Trying 192.168.1.51:8080... 134 % Total % Received % Xferd Average Speed Time Time Time Current 135 Dload Upload Total Spent Left Speed 136 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 192.168.1.51 (192.168.1.51) port 8080 (#0) 137 * Server auth using Basic with user 'admin' 138 > PUT /geoserver/rest/layers/tas_mean HTTP/1.1 139 > Host: 192.168.1.51:8080 140 > Authorization: Basic YWRtaW46Z2Vvc2VydmVy 141 > User-Agent: curl/7.79.1 142 > Accept: */* 143 > Content-type: text/xml 144 > Content-Length: 63 145 > 146 } [63 bytes data] 147 100 63 0 0 100 63 0 7387 --:--:-- --:--:-- --:--:-- 7875* Mark bundle as not supporting multiuse 148 < HTTP/1.1 200 OK 149 < X-Frame-Options: SAMEORIGIN 150 < Content-Length: 0 151 < Server: Jetty(9.4.44.v20210927) 152 < 153 100 63 0 0 100 63 0 1837 --:--:-- --:--:-- --:--:-- 1852 154 * Connection #0 to host 192.168.1.51 left intact 155 绑定tas_mean图层raster样式结果: 156 【】 157 2023/2/23 下午2:15:00:GeoServer发布完成!
目前只是一个简单跑通的状态,里面其实还有许多需要完善的地方,包括对curl命令执行结果的判断,多个仓库的发布等等,后面再持续更新吧!