diff --git a/Infrastructure/Infrastructure.csproj b/Infrastructure/Infrastructure.csproj index 0a9fd22..37b40b6 100644 --- a/Infrastructure/Infrastructure.csproj +++ b/Infrastructure/Infrastructure.csproj @@ -14,6 +14,8 @@ + + diff --git a/Infrastructure/Utils/BoundingBox.cs b/Infrastructure/Utils/BoundingBox.cs new file mode 100644 index 0000000..92aaf6e --- /dev/null +++ b/Infrastructure/Utils/BoundingBox.cs @@ -0,0 +1,27 @@ +namespace Infrastructure.Utils; + +public class BoundingBox +{ + public double MinX { get; set; } + public double MinY { get; set; } + public double MaxX { get; set; } + public double MaxY { get; set; } + + public BoundingBox(double minX, double minY, double maxX, double maxY) + { + MinX = minX; + MinY = minY; + MaxX = maxX; + MaxY = maxY; + } + + public static BoundingBox Merge(BoundingBox bbox1, BoundingBox bbox2) + { + return new BoundingBox( + Math.Min(bbox1.MinX, bbox2.MinX), + Math.Min(bbox1.MinY, bbox2.MinY), + Math.Max(bbox1.MaxX, bbox2.MaxX), + Math.Max(bbox1.MaxY, bbox2.MaxY) + ); + } +} \ No newline at end of file diff --git a/Infrastructure/Utils/DateTimeExtensions.cs b/Infrastructure/Utils/DateTimeExtensions.cs new file mode 100644 index 0000000..a41b543 --- /dev/null +++ b/Infrastructure/Utils/DateTimeExtensions.cs @@ -0,0 +1,12 @@ +namespace Infrastructure.Utils; + +public static class DateTimeExtensions +{ + public static int GetWeekOfYear(this DateTime time) + { + // 获取当前文化的日历 + System.Globalization.CultureInfo cultureInfo = System.Globalization.CultureInfo.CurrentCulture; + // 使用 CalendarWeekRule.FirstFourDayWeek 规则计算周数 + return cultureInfo.Calendar.GetWeekOfYear(time, System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); + } +} \ No newline at end of file diff --git a/Infrastructure/Utils/GeoContext.cs b/Infrastructure/Utils/GeoContext.cs new file mode 100644 index 0000000..704316b --- /dev/null +++ b/Infrastructure/Utils/GeoContext.cs @@ -0,0 +1,57 @@ +using Microsoft.Extensions.Configuration; + +namespace Infrastructure.Utils; + +public sealed class GeoContext +{ + public readonly string GeoserverUrl; + public readonly string JobwebUrl; + public readonly string Workspace; + public readonly string Username; + public readonly string Password; + public readonly string DbSchema; + public readonly string DbStoreName; + + public readonly string TiffStoreNamePrefix; + public readonly string TiffDir; + public readonly string ConnectionString; + + private static readonly Lazy Lazy = new(() => new GeoContext()); + + public static GeoContext Instance => Lazy.Value; + + public GeoContext() + { + // 创建配置构建器 + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile( + $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development"}.json", + optional: true) + .AddEnvironmentVariables(); + // 构建配置 + var configuration = builder.Build(); + // 读取连接字符串 + var connectionString = configuration.GetConnectionString("OpenAuthDBContext"); + var geoserver = configuration["GeoServer:GeoserverUrl"]; + var jobwebUrl = configuration["GeoServer:JobwebUrl"]; + var workspace = configuration["Geoserver:Workspace"]; + var username = configuration["Geoserver:Username"]; + var password = configuration["Geoserver:Password"]; + var dbSchema = configuration["Geoserver:DbSchema"]; + var dbStoreName = configuration["Geoserver:DbStoreName"]; + var tiffStoreNamePrefix = configuration["Geoserver:TiffStoreNamePrefix"]; + var tiffDir = configuration["Geoserver:TiffDir"]; + ConnectionString = connectionString; + GeoserverUrl = geoserver; + JobwebUrl = jobwebUrl; + Workspace = workspace; + Username = username; + Password = password; + DbSchema = dbSchema; + DbStoreName = dbStoreName; + TiffStoreNamePrefix = tiffStoreNamePrefix; + TiffDir = tiffDir; + } +} \ No newline at end of file diff --git a/Infrastructure/Utils/GeoUtil.cs b/Infrastructure/Utils/GeoUtil.cs new file mode 100644 index 0000000..fd09bff --- /dev/null +++ b/Infrastructure/Utils/GeoUtil.cs @@ -0,0 +1,1152 @@ +using System.Dynamic; +using System.Net; +using System.Net.Http.Headers; +using System.Text; +using System.Xml.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using OSGeo.GDAL; +using OSGeo.OSR; + +namespace Infrastructure.Utils; + +public class GeoUtil +{ + private static GeoContext _geoContext = GeoContext.Instance; + + // geosever 地址 + public static readonly string GeoserverUrl = _geoContext.GeoserverUrl; + + // 默认工作空间 + public static readonly string Workspace = _geoContext.Workspace; + + // geoserver 用户名 + public static readonly string Username = _geoContext.Username; + + // geoserver 密码 + public static readonly string Password = _geoContext.Password; + + // geosever 地址 + public static readonly string JobwebUrl = _geoContext.JobwebUrl; + + // 默认数据库 schema + public static readonly string DbSchema = _geoContext.DbSchema; + public static readonly string DbStoreName = _geoContext.DbStoreName; + public static readonly string ConnectionString = _geoContext.ConnectionString; + + /// + /// 创建数据存储和图层,样式存在的话,绑定样式 + /// + /// + /// + /// + public static async Task CreateStoreAndLayer(string workspace, string srs, string layerName, string styleName) + { + //{"PORT":"5432","Database":"drone_enforcement_linyi_20240712","HOST":"192.168.10.102","PASSWORD":"123456","USER ID":"postgres"} + var dbElement = ParseConnectionString(ConnectionString); + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + // 判断是否存在工作空间 + var existWorkspaceResponse = await ExistWorkspace(workspace); + if (!existWorkspaceResponse.Result) + { + var createWorkspaceResponse = await CreateWorkspace(workspace); + if (!createWorkspaceResponse.Result) + { + throw new Exception("创建工作空间失败"); + } + } + + // 判断Db仓库是否存在 + var dbStoreExUrl = GeoserverUrl + "/rest/workspaces/" + workspace + "/datastores/" + DbStoreName + ".xml"; + var response = await client.GetAsync(dbStoreExUrl); + if (response.StatusCode == HttpStatusCode.NotFound) + { + // 创建数据存储 + var contentStr = "" + + "" + DbStoreName + "" + + "PostGIS" + + "true" + + "" + + "" + dbElement["HOST"] + "" + + "" + dbElement["PORT"] + "" + + "" + DbSchema + "" + + "" + dbElement["Database"] + "" + + "" + dbElement["USER ID"] + "" + + "" + dbElement["PASSWORD"] + "" + + "postgis" + + //"" + layerName + "
" + + "
" + + "
"; + + var createDataStoreUrl = GeoserverUrl + "/rest/workspaces/" + workspace + "/datastores"; + var content = new StringContent(contentStr, Encoding.UTF8, + "text/xml"); + // 执行创建数据存储 + var result = await client.PostAsync(createDataStoreUrl, content); + var resultStr = result.Content.ReadAsStringAsync(); + // 未校验,一般失败就是已存在 + Console.WriteLine("创建数据存储StatusCode:" + result.StatusCode); + Console.WriteLine("创建数据存储返回结果:" + resultStr.Result); + } + + // 判断图层存不存在 + var layerExUrl = GeoserverUrl + "/rest/layers/" + workspace + ":" + layerName + ".xml"; + var layerExResult = await client.GetAsync(layerExUrl); + if (layerExResult.StatusCode == HttpStatusCode.OK) + { + // 图层存在时间直接更新数据表即可 + return true; + /*var deleteLayerUrl = GeoserverUrl + "/rest/layers/" + Workspace + ":" + layerName; + var deleteLayerResult = await client.DeleteAsync(deleteLayerUrl); + var deleteLayerResultStr = deleteLayerResult.Content.ReadAsStringAsync(); + Console.WriteLine("删除图层返回结果:" + deleteLayerResultStr.Result); + if (deleteLayerResult.StatusCode != HttpStatusCode.OK) + { + throw new Exception("图层已存在但删除失败"); + }*/ + // 结束 + } + + // 创建图层1 + var createLayerUrl = GeoserverUrl + "/rest/workspaces/" + workspace + "/datastores/" + DbStoreName + + "/featuretypes"; + // var createLayerUrl = GEOSERVER_URL + "/rest/workspaces/"+WORKSPACE+"layers"; + + var createLayerContentStr = "" + + "true" + + "" + + "" + + "" + + "" + + "" + layerName + "" + + "" + layerName + "" + + "" + srs + "" + + "" + layerName + "" + + ""; + Console.WriteLine("创建图层参数:" + createLayerContentStr); + var createLayerContent = new StringContent(createLayerContentStr, Encoding.UTF8, "text/xml"); + var createLayerResult = await client.PostAsync(createLayerUrl, createLayerContent); + var createLayerResultStr = createLayerResult.Content.ReadAsStringAsync(); + Console.WriteLine("创建图层返回code:" + createLayerResult.StatusCode); + Console.WriteLine("创建图层返回结果:" + createLayerResultStr.Result); + // 发布图层 + var publishLayerUrl = GeoserverUrl + "/rest/layers/" + workspace + ":" + layerName + ""; + var styleXmlPart = ""; + if (!string.IsNullOrEmpty(styleName)) + { + styleXmlPart = "" + + "" + styleName + "" + + "" + workspace + "" + + ""; + } + + var publishContentStr = + "" + + "true" + + "" + + "" + + "" + + styleXmlPart + + ""; + Console.WriteLine("发布图层参数:" + publishContentStr); + var publishContent = new StringContent(publishContentStr, Encoding.UTF8, "text/xml"); + var publishLayerResult = await client.PutAsync(publishLayerUrl, publishContent); + Console.WriteLine("发布图层返回结果:" + publishLayerResult.Content); + if (publishLayerResult.StatusCode != HttpStatusCode.OK) + { + throw new Exception("发布图层失败"); + } + + return true; + } + + /// + /// 判断样式是否存在 + /// + /// + /// + public static async Task ExistStyle(string workspace, string styleName) + { + using var client = new HttpClient(); + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + var existStyleUrl = GeoserverUrl + "/rest/workspaces/" + workspace + "/styles/" + + styleName + ".xml"; + + var response = await client.GetAsync(existStyleUrl); + return response.StatusCode == HttpStatusCode.OK; + } + + /// + /// 创建SLD样式并发布 + /// + /// + /// + public static async Task PublishSldStyle(string sldPath, string styleName) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + // 样式名称 + var sldFileName = Path.GetFileName(sldPath); + //var styleName = Path.GetFileNameWithoutExtension(sldPath); + + // todo 判断样式是否存在 + // 如何存在更新样式 + //string apiUrl = GEOSERVER_URL + "/rest/styles?format=json&namespace=default&type=style&name={styleName}"; + + // 发布SLD样式两步骤 curl -v -u admin:geoserver -XPOST -H "Content-type: text/xml" -d "" http://localhost:8080/geoserver/rest/styles + var prePublicSld = GeoserverUrl + "/rest/workspaces/" + Workspace + "/styles"; + // "" + var prePublishSldXml = ""; + + var response = await client.PostAsync(prePublicSld, + new StringContent(prePublishSldXml, Encoding.UTF8, "text/xml")); + + if (response.StatusCode == HttpStatusCode.Created) + { + Console.WriteLine("SLD样式预发布返回结果:" + response.Content.ReadAsStringAsync().Result); + // 发布SLD样式第二步 + // curl -v -u admin:geoserver -XPUT -H "Content-type: application/vnd.ogc.sld+xml" -d @roads.sld http://localhost:8080/geoserver/rest/styles/roads_style + //var publishSldToWorkspaceUrl = GeoserverUrl + "/rest/workspaces/" + Workspace + "/styles?name=" + Uri.EscapeDataString(styleName); + var publishSldToWorkspaceUrl = GeoserverUrl + "/rest/workspaces/" + Workspace + "/styles/" + styleName; + + // SLD文件内容 + var sldContent = await File.ReadAllTextAsync(sldPath, Encoding.UTF8); + // 创建请求 + var content = new StringContent(sldContent, Encoding.UTF8, + "application/vnd.ogc.sld+xml"); + // 执行创建数据存储 + response = await client.PutAsync(publishSldToWorkspaceUrl, content); + // 检查响应 + Console.WriteLine(response.StatusCode); + Console.WriteLine("SLD样式发布返回结果:" + response.Content.ReadAsStringAsync().Result); + if (response.StatusCode == HttpStatusCode.OK) + { + Console.WriteLine("SLD发布成功"); + return true; + } + } + else + { + Console.WriteLine("SLD图层已存在"); + return false; + } + + Console.WriteLine($"SLD发布失败,状态码:{response.StatusCode}"); + return false; + } + + // # 创建工作区(如果不存在) + //curl -u $USERNAME:$PASSWORD -X POST -H "Content-type: text/xml" -d "$WORKSPACE" $GEOSERVER_URL/rest/workspaces + /// + /// 解析数据库连接字符串 + /// + /// + /// + static Dictionary ParseConnectionString(string connectionString) + { + var details = new Dictionary(); + var parts = connectionString.Split(';'); + + foreach (var part in parts) + { + var index = part.IndexOf('='); + if (index != -1) + { + var key = part.Substring(0, index); + var value = part.Substring(index + 1); + details[key] = value; + } + } + + return details; + } + + /// + /// 获取图层bbox + /// + /// + /// + /// + /// + public static async Task> GetDBLayerBBox(string workspace, string storeName, string layerName) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + var dbStoreExUrl = + $"{GeoserverUrl}/rest/workspaces/{workspace}/datastores/{storeName}/featuretypes/{layerName}.json"; + var dbStoreExResult = await client.GetAsync(dbStoreExUrl); + var resultObj = JsonConvert.DeserializeObject(dbStoreExResult.Content.ReadAsStringAsync().Result); + if (dbStoreExResult.StatusCode == HttpStatusCode.OK) + { + var bbox = resultObj["featureType"]["latLonBoundingBox"]; + var minx = bbox["minx"]; + var miny = bbox["miny"]; + var maxx = bbox["maxx"]; + var maxy = bbox["maxy"]; + Console.WriteLine("minx:" + minx); + Console.WriteLine("miny:" + miny); + Console.WriteLine("maxx:" + maxx); + Console.WriteLine("maxy:" + maxy); + Console.WriteLine("信息:" + dbStoreExResult.Content.ReadAsStringAsync().Result); + return new Response + { + Result = minx + "," + miny + "," + maxx + "," + maxy, + Code = 200 + }; + } + + return new Response + { + Code = 500 + }; + } + + /// + /// 取得bbox 及 srs 信息 + /// + /// 工作空间名称 + /// 存储仓库名称 + /// 图层名称 + /// + public static async Task> GetTiffLayerInfo(string workspace, string storeName, string layerName) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + var dbStoreExUrl = + $"{GeoserverUrl}/rest/workspaces/{workspace}/coveragestores/{storeName}/coverages/{layerName}.json"; + var dbStoreExResult = await client.GetAsync(dbStoreExUrl); + if (dbStoreExResult.StatusCode == HttpStatusCode.OK) + { + var resultObj = JsonConvert.DeserializeObject(dbStoreExResult.Content.ReadAsStringAsync().Result); + Console.WriteLine("GetTiffLayerInfo信息:" + dbStoreExResult.Content.ReadAsStringAsync().Result); + var bbox = resultObj["coverage"]?["nativeBoundingBox"]; + var srs = resultObj["coverage"]?["srs"]; + var minx = bbox["minx"]; + var miny = bbox["miny"]; + var maxx = bbox["maxx"]; + var maxy = bbox["maxy"]; + Console.WriteLine("minx:" + minx); + Console.WriteLine("miny:" + miny); + Console.WriteLine("maxx:" + maxx); + Console.WriteLine("maxy:" + maxy); + return new Response + { + Result = new { bbox = minx + "," + miny + "," + maxx + "," + maxy, srs = srs }, + Code = 200 + }; + } + + return new Response + { + Code = 500, + Message = dbStoreExResult.Content.ReadAsStringAsync().Result + }; + } + + /// + /// 删除栅格仓库 + /// + /// + /// + /// 递归删除(true 仓库下所有资源都将被删除) + /// + public static async Task> DeleteCoveragesStore(string workspace, string storeName, + bool recurse = false) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + // 删除 coverageStore + // The recurse controls recursive deletion. When set to true all resources contained in the store are also removed. The default value is "false". + var deleteTiffStoreUrl = $"{GeoserverUrl}/rest/workspaces/{workspace}/coveragestores/{storeName}"; + if (recurse) + { + deleteTiffStoreUrl += "?recurse=true"; + } + else + { + deleteTiffStoreUrl += "?recurse=false"; + } + + var deleteTiffStoreResult = await client.DeleteAsync(deleteTiffStoreUrl); + Console.WriteLine("删除栅格仓库"); + Console.WriteLine(deleteTiffStoreResult.Content.ReadAsStringAsync().Result); + Console.WriteLine(deleteTiffStoreResult.StatusCode); + switch (deleteTiffStoreResult.StatusCode) + { + case HttpStatusCode.OK: + return new Response + { + Result = true, + Code = 200 + }; + case HttpStatusCode.NotFound: + return new Response + { + Result = false, + Code = 404 + }; + default: + return new Response + { + Result = false, + Code = 500, + Message = deleteTiffStoreResult.Content.ReadAsStringAsync().Result + }; + } + } + + /// + /// 删除图层 + /// + /// 工作空间,可不填 + /// + /// + public static async Task> DeleteLayer(string workspace, string layerName) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + var deleteLayerUrl = $"{GeoserverUrl}/rest/layers/{workspace}:{layerName}"; + var deleteLayerResponse = await client.DeleteAsync(deleteLayerUrl); + Console.WriteLine("删除图层返回结果:"); + Console.WriteLine(deleteLayerResponse.Content.ReadAsStringAsync().Result); + Console.WriteLine(deleteLayerResponse.StatusCode); + if (deleteLayerResponse.StatusCode == HttpStatusCode.OK) + { + return new Response + { + Result = true, + Code = 200 + }; + } + + return new Response + { + Result = false, + Code = 500 + }; + } + + /// + /// 发布GeoTiff + /// + /// + /// tif文件全路径 + /// 栅格仓库名称 + /// 图层名称 + /// + public static async Task> PublishExternalGeoTIFF(string workspace, string tifFullName, + string storeName, string layerName) + { + var srs = "4548"; + var tifCrs = GetTifCrs(tifFullName); + if (tifCrs.srs != null) + { + srs = tifCrs.srs; + } + + if (string.IsNullOrEmpty(tifFullName) || string.IsNullOrEmpty(storeName) || string.IsNullOrEmpty(layerName)) + { + return new Response + { + Result = false, + Message = "参数不能为空" + }; + } + + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + var existWorkspaceResponse = await ExistWorkspace(workspace); + if (!existWorkspaceResponse.Result) + { + var createWorkspaceResponse = await CreateWorkspace(workspace); + if (!createWorkspaceResponse.Result) + { + throw new Exception("创建工作空间失败,请检查geoserver状态"); + } + } + + // 创建 coverageStore + var createTiffStoreUrl = $"{GeoserverUrl}/rest/workspaces/{workspace}/coveragestores"; + var tiffXml = $"" + + $"{storeName}{workspace}" + + "true" + + "GeoTIFF" + + $"file://{tifFullName}" + + ""; + //Console.WriteLine("栅格仓库xml: " + tiffXml); + //GeoUtil.DeleteCoveragesStore(workspace, storeName, false); + var content = new StringContent(tiffXml, Encoding.UTF8, "application/xml"); + var response = await client.PostAsync(createTiffStoreUrl, content); + if (response.StatusCode != HttpStatusCode.Created) + { + var msg = await response.Content.ReadAsStringAsync(); + Console.Write("创建栅格仓库code: " + response.StatusCode + "错误信息:" + msg); + return new Response + { + Result = false, + Message = "创建仓库失败:" + msg + }; + } + + // 创建coverages + var createTiffCoveragesUrl = $"{GeoserverUrl}/rest/workspaces/{workspace}/coveragestores/{storeName}/coverages"; + + var tiffFileName = Path.GetFileNameWithoutExtension(tifFullName); + // nativeName 参数必须是tiff文件名称 + var createTiffCoveragesXml = @$" + {storeName} + {tiffFileName} + true + + + + {layerName} + {layerName} + EPSG:{srs}REPROJECT_TO_DECLARED + + + InputTransparentColor + #000000 + + + "; + var createTiffCoveragesContent = + new StringContent(createTiffCoveragesXml, Encoding.UTF8, "text/xml"); + var createTiffCoveragesResponse = await client.PostAsync(createTiffCoveragesUrl, createTiffCoveragesContent); + if (createTiffCoveragesResponse.StatusCode != HttpStatusCode.Created) + { + var msg = await createTiffCoveragesResponse.Content.ReadAsStringAsync(); + //Console.Write(createTiffCoveragesResponse.StatusCode + " "); + //Console.WriteLine(msg); + return new Response + { + Result = false, + Message = "创建仓库失败:" + msg + }; + } + + var publishTiffLayerUrl = + $"{GeoserverUrl}/rest/layers/{workspace}:{layerName}"; + var publishTiffLayerXml = "true"; + var publishTiffLayerContent = new StringContent(publishTiffLayerXml, Encoding.UTF8, "text/xml"); + var publishTiffLayerResponse = await client.PutAsync(publishTiffLayerUrl, publishTiffLayerContent); + if (publishTiffLayerResponse.StatusCode != HttpStatusCode.OK) + { + var msg = await publishTiffLayerResponse.Content.ReadAsStringAsync(); + //Console.Write(publishTiffLayerResponse.StatusCode + " "); + //Console.WriteLine(msg); + return new Response + { + Result = false, + Message = "发布图层失败:" + msg + }; + } + + return new Response + { + Result = true + }; + } + + /// + /// 判断工作空间是否存在 + /// + /// + /// + public static async Task> ExistWorkspace(string workspace) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + var url = $"{GeoserverUrl}/rest/workspaces/{workspace}.xml"; + var response = await client.GetAsync(url); + Console.WriteLine($"状态码:{response.StatusCode} 返回结果:{response.Content.ReadAsStringAsync().Result}"); + if (response.StatusCode == HttpStatusCode.NotFound) + { + return new Response + { + Result = false + }; + } + + return new Response + { + Result = true + }; + } + + /// + /// 创建工作空间 + /// + /// + /// + public static async Task> CreateWorkspace(string workspace) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + var createWorkspaceUrl = $"{GeoserverUrl}/rest/workspaces"; + var createWorkspaceXml = $"{workspace}"; + var createWorkspaceContent = new StringContent(createWorkspaceXml, Encoding.UTF8, "text/xml"); + var createWorkspaceResponse = await client.PostAsync(createWorkspaceUrl, createWorkspaceContent); + if (createWorkspaceResponse.StatusCode == HttpStatusCode.Created) + { + return new Response + { + Result = true + }; + } + + return new Response + { + Result = false + }; + } + + + /// + /// 删除工作空间 + /// + /// + /// + public static async Task> DeleteWorkspace(string workspace) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + var deleteWorkspaceUrl = $"{GeoserverUrl}/rest/workspaces/{workspace}" + "?recurse=true"; + var deleteWorkspaceResponse = await client.DeleteAsync(deleteWorkspaceUrl); + Console.WriteLine("删除工作空间返回结果:"); + Console.WriteLine(deleteWorkspaceResponse.Content.ReadAsStringAsync().Result); + Console.WriteLine(deleteWorkspaceResponse.StatusCode); + if (deleteWorkspaceResponse.StatusCode == HttpStatusCode.OK) + { + return new Response + { + Result = true, + Code = 200 + }; + } + + return new Response + { + Result = false, + Code = 500 + }; + } + + + public static async Task> SettingLayerReturnFormat(string workspace, string layerName) + { + using var client = new HttpClient(); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + + var queryLayerUrl = GeoserverUrl + "/gwc/rest/layers/" + workspace + ":" + layerName + ".xml"; + var queryResult = await client.GetAsync(queryLayerUrl); + Console.WriteLine( + $" 结果:{queryResult.Content.ReadAsStringAsync().Result} code: {queryResult.StatusCode}"); + if (queryResult.StatusCode == HttpStatusCode.OK) + { + var split = queryResult.Content.ReadAsStringAsync().Result.Split(""); + var xml = split[0] + "" + + "application/json;type=geojson" + + "application/json;type=topojson" + + "application/vnd.mapbox-vector-tile" + + "application/json;type=utfgrid" + + split[1]; + var setGwc = GeoserverUrl + "/gwc/rest/layers/" + workspace + ":" + layerName; + var content = new StringContent(xml, Encoding.UTF8, + "application/xml"); + var result = await client.PostAsync(setGwc, content); + if (result.StatusCode == HttpStatusCode.OK) + { + return new Response() + { + Result = true, + Code = 200 + }; + } + } + + return new Response + { + Result = false, + Code = 500 + }; + } + + /// + /// 更新样式名,更新样式 + /// + /// + /// + /// + public static async Task UpdateSldStyle(string sldPath, string styleName) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + var publishSldToWorkspaceUrl = GeoserverUrl + "/rest/workspaces/" + Workspace + "/styles/" + styleName; + // SLD文件内容 + var sldContent = await File.ReadAllTextAsync(sldPath, Encoding.UTF8); + // 创建请求 + var content = new StringContent(sldContent, Encoding.UTF8, + "application/vnd.ogc.sld+xml"); + // 执行创建数据存储 + var response = await client.PutAsync(publishSldToWorkspaceUrl, content); + // 检查响应 + Console.WriteLine(response.StatusCode); + Console.WriteLine("SLD样式发布返回结果:" + response.Content.ReadAsStringAsync().Result); + if (response.StatusCode == HttpStatusCode.OK) + { + Console.WriteLine("SLD发布成功"); + return true; + } + + return false; + } + + public static async Task> UpdateStyle(string workspace, string layerName, string styleName) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + var url = $"{GeoserverUrl}/rest/layers/{workspace}:{layerName}.xml"; + //var xml = $"{styleName}"; + var xml = $@" + + + {styleName} + + "; + var content = new StringContent(xml, Encoding.UTF8, + "application/xml"); + var result = await client.PutAsync(url, content); + if (result.StatusCode == HttpStatusCode.OK) + { + return new Response + { + Result = true, + }; + } + + return new Response + { + Result = false, + Code = 500 + }; + } + + public static async Task> publishLayerGroup(string workspace, string layers, string layerGroupName) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + var url = $"{GeoserverUrl}/rest/workspaces/{workspace}/layergroups"; + var xml = "" + + "" + + " " + layerGroupName + "" + + " " + layerGroupName + "" + + " " + + " " + workspace + "" + + " " + + " " + + layers + + " " + + ""; + var content = new StringContent(xml, Encoding.UTF8, + "text/xml"); + var result = await client.PostAsync(url, content); + if (result.StatusCode == HttpStatusCode.OK) + { + return new Response + { + Result = true, + }; + } + + return new Response + { + Result = false, + Code = 500 + }; + } + + /// + /// 发布GeoTiff + /// + /// + /// tif文件全路径 + /// 栅格仓库名称 + /// 图层名称 + /// + public static async Task> PublishSingleGeoTIFF(string workspace, string tifFullName, + string storeName, string layerName) + { + if (string.IsNullOrEmpty(tifFullName) || string.IsNullOrEmpty(storeName) || string.IsNullOrEmpty(layerName)) + { + return new Response + { + Result = false, + Message = "参数不能为空" + }; + } + + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + var existWorkspaceResponse = await ExistWorkspace(workspace); + if (!existWorkspaceResponse.Result) + { + var createWorkspaceResponse = await CreateWorkspace(workspace); + if (!createWorkspaceResponse.Result) + { + throw new Exception("创建工作空间失败,请检查geoserver状态"); + } + } + + // 创建 coverageStore + var createTiffStoreUrl = $"{GeoserverUrl}/rest/workspaces/{workspace}/coveragestores"; + var tiffXml = $"" + + $"{storeName}{workspace}" + + "true" + + "GeoTIFF" + + $"file://{tifFullName}" + + ""; + //Console.WriteLine("栅格仓库xml: " + tiffXml); + //GeoUtil.DeleteCoveragesStore(workspace, storeName, false); + var content = new StringContent(tiffXml, Encoding.UTF8, "application/xml"); + var response = await client.PostAsync(createTiffStoreUrl, content); + if (response.StatusCode != HttpStatusCode.Created) + { + var msg = await response.Content.ReadAsStringAsync(); + Console.Write("创建栅格仓库code: " + response.StatusCode + "错误信息:" + msg); + return new Response + { + Result = false, + Message = "创建仓库失败:" + msg + }; + } + + // 创建coverages + var createTiffCoveragesUrl = $"{GeoserverUrl}/rest/workspaces/{workspace}/coveragestores/{storeName}/coverages"; + + var tiffFileName = Path.GetFileName(tifFullName).Replace(Path.GetExtension(tifFullName), ""); + // nativeName 参数必须是tiff文件名称 + var createTiffCoveragesXml = @$" + + true + + + + {layerName} + {layerName} + EPSG:4548REPROJECT_TO_DECLARED + + "; + var createTiffCoveragesContent = + new StringContent(createTiffCoveragesXml, Encoding.UTF8, "text/xml"); + var createTiffCoveragesResponse = await client.PostAsync(createTiffCoveragesUrl, createTiffCoveragesContent); + if (createTiffCoveragesResponse.StatusCode != HttpStatusCode.Created) + { + var msg = await createTiffCoveragesResponse.Content.ReadAsStringAsync(); + Console.Write(createTiffCoveragesResponse.StatusCode + " "); + Console.WriteLine(msg); + return new Response + { + Result = false, + Message = "创建仓库失败:" + msg + }; + } + + var publishTiffLayerUrl = + $"{GeoserverUrl}/rest/layers/{workspace}:{layerName}"; + var publishTiffLayerXml = "true"; + var publishTiffLayerContent = new StringContent(publishTiffLayerXml, Encoding.UTF8, "text/xml"); + var publishTiffLayerResponse = await client.PutAsync(publishTiffLayerUrl, publishTiffLayerContent); + if (publishTiffLayerResponse.StatusCode != HttpStatusCode.OK) + { + var msg = await publishTiffLayerResponse.Content.ReadAsStringAsync(); + Console.Write(publishTiffLayerResponse.StatusCode + " "); + Console.WriteLine(msg); + return new Response + { + Result = false, + Message = "发布图层失败:" + msg + }; + } + + return new Response + { + Result = true + }; + } + + public static async Task GetTiffLayerInfo1(string workspace, string storeName, string layerName) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; + } + + var dbStoreExUrl = + $"{GeoserverUrl}/rest/workspaces/{workspace}/coveragestores/{storeName}/coverages/{layerName}.json"; + var dbStoreExResult = await client.GetAsync(dbStoreExUrl); + if (dbStoreExResult.StatusCode == HttpStatusCode.OK) + { + var resultObj = JsonConvert.DeserializeObject(dbStoreExResult.Content.ReadAsStringAsync().Result); + return JsonConvert.SerializeObject(resultObj); + } + + return "执行失败"; + } + + /// + /// 查询图层组是否存在 + /// + /// 工作空间名称 + /// 图层组名称 + /// 图层组是否存在 + public static async Task ExistLayerGroup(string workspace, string layerGroupName) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; // 如果没有指定工作空间,默认使用 "default" + } + + var existLayerGroupUrl = $"{GeoserverUrl}/rest/workspaces/{workspace}/layergroups/{layerGroupName}.json"; + + var response = await client.GetAsync(existLayerGroupUrl); + return response.StatusCode == HttpStatusCode.OK; + } + + /// + /// 向图层组中添加图层 + /// + /// 工作空间名称 + /// 图层组名称 + /// 要添加的图层名称列表 + /// 操作是否成功 + public static async Task AddLayersToLayerGroup(string workspace, string layerGroupName, + List layerNames) + { + using var client = new HttpClient(); + var usernamePassword = Username + ":" + Password; + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword))); + + if (string.IsNullOrEmpty(workspace)) + { + workspace = Workspace; // 如果没有指定工作空间 + } + + var addLayersUrl = $"{GeoserverUrl}/rest/workspaces/{workspace}/layergroups/{layerGroupName}.xml"; + var groupResponse = client.GetAsync(addLayersUrl).Result.Content.ReadAsStringAsync(); + var existingLayers = XDocument.Parse(groupResponse.Result).Descendants("publishables").Descendants("published") + .Descendants("name").Select(l => l.Value).ToList(); + var layersXml = string.Join("", + layerNames.Select(layerName => $"{workspace}:{layerName}") + .Union(existingLayers.Select(x => $"{x}")).Distinct()); + layerNames.AddRange(existingLayers); + var stylesXml = string.Join("", layerNames.Select(layerName => "")); + const string fixedBbox = + "117.34046403513817,34.331716698906675,119.27763051149853,36.263686454243626"; + var requestBody = $@" + + {layerGroupName} + + {workspace} + + {layerGroupName} + + {layersXml} + + + {fixedBbox.Split(',')[0]} + {fixedBbox.Split(',')[1]} + {fixedBbox.Split(',')[2]} + {fixedBbox.Split(',')[3]} + EPSG:4326 + + + {stylesXml} + + "; + var content = new StringContent(requestBody, Encoding.UTF8, "text/xml"); + // 发送 PUT 请求 + var response = await client.PutAsync(addLayersUrl, content); + // 检查响应状态码 + return response.IsSuccessStatusCode; + } + + public static dynamic GetTifCrs(string filePath) + { + GdalConfiguration.ConfigureGdal(); + GdalConfiguration.ConfigureOgr(); //矢量 + GdalConfiguration.ConfigureOgr(); + // 初始化 GDAL + Gdal.AllRegister(); + + // 打开 TIFF 文件 + //string filePath = "C:/ChengGuoFinal/tiff/无人机巡飞影像数据/20240713/兰山区/20240713兰山区马厂湖_0.2.tif.aux_更新.tif"; + Dataset dataset = Gdal.Open(filePath, Access.GA_ReadOnly); + + if (dataset == null) + { + Console.WriteLine("无法打开 TIFF 文件"); + throw new Exception("无法打开 TIFF 文件"); + } + + // 获取投影信息 + string projection = dataset.GetProjection(); + var x = dataset.GetProjectionRef(); + if (!string.IsNullOrEmpty(projection)) + { + Console.WriteLine("投影信息:"); + Console.WriteLine(projection); + } + else + { + Console.WriteLine("无法获取投影信息"); + } + + // 获取空间参照信息 + SpatialReference srs = dataset.GetSpatialRef(); + Console.WriteLine("空间参照信息:"); + // 解析EPSG代码 + // 如果没有EPSG代码,可以尝试其他方法获取 + var epsgCode = srs.GetAuthorityCode(null); + Console.WriteLine($"EPSG代码: {epsgCode}"); + // 关闭数据集 + dataset.Dispose(); + dynamic obj = new ExpandoObject(); + obj.projection = projection; + obj.srs = epsgCode; + return obj; + } +} \ No newline at end of file diff --git a/Infrastructure/Utils/ShapeUtil.cs b/Infrastructure/Utils/ShapeUtil.cs new file mode 100644 index 0000000..906e0fc --- /dev/null +++ b/Infrastructure/Utils/ShapeUtil.cs @@ -0,0 +1,160 @@ +using System.IO.Compression; +using System.Text; +using NetTopologySuite.Geometries; +using NetTopologySuite.IO; + +namespace Infrastructure.Utils; + +public class ShapeUtil +{ + /// + /// + /// + /// + /// + /// + public static List> getRows(string shpPath) + { + throw new NotImplementedException(); + } + + /// + /// 支持 + /// + /// + /// + public static List> ReadAllGeometry(string shpPath) + { + // 取得后缀,如果是zip,则解压 + var fileExtension = Path.GetExtension(shpPath); + if (!fileExtension.Equals(".shp") && !fileExtension.Equals(".zip")) + { + throw new Exception("不支持的文件格式"); + } + + // 设置字符编码 + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + var gbk = Encoding.GetEncoding("gbk"); + if (fileExtension == ".zip") + { + var extractPath = Path.GetDirectoryName(shpPath) + "\\temp"; + Directory.CreateDirectory(extractPath); + + using (var archive = ZipFile.OpenRead(shpPath)) + { + // 列举ZIP文件中的条目 + foreach (var entry in archive.Entries) + { + Console.WriteLine("zip 包含文件:"); + var fileName = gbk.GetString(Encoding.Default.GetBytes(entry.FullName)); + Console.WriteLine(fileName); + } + + // 提取ZIP文件中的所有文件到指定目录 + + foreach (var entry in archive.Entries) + { + if (entry.Name != string.Empty) + { + // 确保完整路径存在 entry.FullName 是否可以编码 + var fixedEntryName = entry.FullName.Replace("/", ""); + var destinationPath = Path.GetFullPath(Path.Combine(extractPath, fixedEntryName)); + Console.WriteLine("解压文件路径:" + destinationPath); + if (!destinationPath.StartsWith(Path.GetFullPath(extractPath) + Path.DirectorySeparatorChar)) + { + throw new UnauthorizedAccessException("试图提取的文件超出了目标文件夹的路径边界。"); + } + + // 提取条目到目标路径 + entry.ExtractToFile(destinationPath, overwrite: true); + } + } + } + + // 遍历解压目录,是否有shp后缀的文件 + if (!Directory.Exists(extractPath)) + { + throw new Exception("文件解压失败"); + } + + // 查找 + var dirInfo = new DirectoryInfo(extractPath); + // zip 压缩包中只有一个shp文件 + shpPath = dirInfo.GetFiles("*.shp", SearchOption.AllDirectories)[0].FullName; + } + + var data = ParseShape(shpPath, null); + var tifPath = data.First()["Image"].ToString(); + if (CheckForGarbledText(tifPath)) + { + // 乱码,需要解码 + data = ParseShape(shpPath, gbk); + } + + return data; + } + + private static List> ParseShape(string shpPath, Encoding code) + { + using var dataReader = code == null + ? new ShapefileDataReader(shpPath, GeometryFactory.Default) + : new ShapefileDataReader(shpPath, GeometryFactory.Default, code); + Console.WriteLine("记录数" + dataReader.RecordCount); + List> rows = new List>(); + while (dataReader.Read()) + { + Dictionary row = new Dictionary(); + // 读取列 + for (int i = 0, j = 0; i < dataReader.FieldCount; i++) + { + var colName = dataReader.GetName(i); + var value = dataReader.GetValue(i); + row.Add(colName, value); + } + + rows.Add(row); + } + + return rows; + // return NetTopologySuite.IO.Esri.Shapefile.ReadAllGeometries(shpPath); + } + + /// + /// 判断是否乱码 + /// + /// + /// + private static bool CheckForGarbledText(string content) + { + try + { + // 检查是否有不可打印的字符 + foreach (char c in content) + { + if (char.IsControl(c) && !char.IsWhiteSpace(c)) + { + return true; // 发现不可打印的控制字符 + } + } + // 检查是否有不常见的字符 + foreach (char c in content) + { + if (c > 0x7F && !char.IsLetterOrDigit(c) && !char.IsPunctuation(c) && !char.IsSymbol(c) && + !char.IsWhiteSpace(c)) + { + return true; // 发现不常见的字符 + } + } + + return false; // 没有发现乱码 + } + catch (DecoderFallbackException) + { + return true; // 解码失败,存在乱码 + } + catch (Exception ex) + { + return true; // 其他异常,可能表示存在乱码 + } + } +} \ No newline at end of file