如何在 SQL 服务器中进行空间重投影
How do spatial reprojection in SQL Server
我正在尝试在 SQL 服务器中进行一些重新投影,但遗憾的是我找不到任何解决方案。我知道这在 SQL 服务器中没有实现,所以想知道是否有任何快捷方式。
有些人建议使用外部库,例如 ogr2ogr,但这对我来说不是一个好的选择,因为我需要即时进行。我在这里找到了 post
Transform/ Project a geometry from one SRID to another,但是这个转换为纬度和经度并且功能有限。
我正在寻找一个允许我在任何 srid 中进行重投影的解决方案,非常类似于 postgresql (postgis)
中的 ST_Transform
好吧,如果您正在寻找一个简单的选择,恐怕这是不可能的。但是这个问题有一个解决方法。前段时间我遇到了类似的问题,我不得不在 .NET 中实现 CLR,然后将其作为程序集导入 MSSQL,速度有点慢,但工作正常。
输入是:源投影、目标投影和作为文本的几何体,输出是将几何体重新投影为文本。请查看彼此相邻的 .Net 和 PostgreSQL 的此程序集的结果(几乎相同)。
输入是:
POLYGON ((1755828.2027002387 5944302.7072003055, 1755826.3549002428 5944302.8313003061, 1755825.1724002406 5944285.1574003045, 1755809.5710002393 5944286.4007003047, ......))";// geomobj.line; //"POINT (1736946.0983 5923253.9175)";
所以,解决方法如下:
1- 打开 .NET 并创建一个 c# 库并将以下代码粘贴到那里(此代码获取每一行数据,遍历每个元素,分解它,提取每个经度和纬度,重新投影并替换它)
using Microsoft.SqlServer.Server;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class classval
{
public string line;
public string src;
public string dst;
public classval(string line, string src, string dst)
{
this.line = line;
this.src = src;
this.dst = dst;
}
}
public class RPG_Transform
{
private static IEnumerable<classval> ConvertedEnumerable(string line, string src, string dst)
{
return new List<classval> { new classval(line, src, dst) };
}
[SqlFunction(FillRowMethodName = "FillRow")]
public static IEnumerable STRPG_Transform(string Geometry, string src, string dst)
{
return ConvertedEnumerable(Geometry, src, dst);
}
private static void FillRow(Object classvalobj, out string Geometry, out string srcprj, out string dstprj)
{
classval geomobj = (classval)classvalobj;
string _geometry = geomobj.line;
string proj4_src = geomobj.src;
string proj4_dst = geomobj.dst;
string _originalgeom = _geometry;
_geometry = _geometry.Remove(0, _geometry.IndexOf('('));
// _geometry = _geometry.Replace("(", "[ ");
// _geometry = _geometry.Replace(")", " ]");
string[] splitbycomma = _geometry.Split(',');
foreach (var itembycomma in splitbycomma)
{
string tmpitem = itembycomma;
tmpitem = tmpitem.Replace('(', ' ');
tmpitem = tmpitem.Replace(')', ' ');
tmpitem = tmpitem.Trim();
string[] splitbyspace = tmpitem.Split(' ');
for (int ibs = 0; ibs < splitbyspace.Length - 1; ibs++)
{
string originallonglat = splitbyspace[ibs] + " " + splitbyspace[ibs + 1];
double[] yxval = new double[] { double.Parse(splitbyspace[ibs]), double.Parse(splitbyspace[ibs + 1]) };
double[] z = new double[1] { 0 };
DotSpatial.Projections.ProjectionInfo src =
DotSpatial.Projections.ProjectionInfo.FromProj4String(proj4_src);
DotSpatial.Projections.ProjectionInfo trg =
DotSpatial.Projections.ProjectionInfo.FromProj4String(proj4_dst);
DotSpatial.Projections.Reproject.ReprojectPoints(yxval, z, src, trg, 0, 1);
string longlat = yxval[0] + " " + yxval[1];
_originalgeom = _originalgeom.Replace(originallonglat, longlat);
}
}
srcprj = proj4_src;
dstprj = proj4_dst;
Geometry = _originalgeom;
}
}
2- 在 运行 之前,您需要安装一些库,最简单的是使用 Nugget Manager,如下所示。
3-接下来你需要编译代码并得到一个dll,然后将这些dll复制到一个新的文件夹中,如果你喜欢你把它们留在调试文件夹中(其中一些dll被添加到由于依赖关系的项目)如下所示。
4- 在 MSSQL 中启用 CLR,然后将程序集添加到 MSSQL 服务器(通过创建如下所示的函数)
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'clr enabled', 1;
GO
RECONFIGURE;
GO
ALTER DATABASE Prod SET trustworthy ON
CREATE ASSEMBLY CLRFunctionAssem
FROM N'E:\CLR\RPG_Transform\RPG_Transform\bin\Debug\RPG_Transform.dll'
WITH PERMISSION_SET = UNSAFE
GO
--DROP ASSEMBLY CLRFunctionAssem ---if you need to drop it in the future
CREATE FUNCTION dbo.RPG_STTransform(@Geometry nvarchar(max), @src
nvarchar(max),@dst nvarchar(max))
RETURNS TABLE
( _geom nvarchar(max) ,srcprj nvarchar(max) ,dstprj nvarchar(max)
) with execute as caller
AS
EXTERNAL NAME CLRFunctionAssem.[RPG_Transform].STRPG_Transform
5- 获取源投影和目标投影的公式。
你需要从某个地方得到这两个公式。对我来说,为了获取源投影和目标投影,我使用以下网站。(此解决方案使用 Proj4j,因此您需要从某处获取与 srid 等效的内容)。
https://epsg.io/
如下图:
目的地也一样
6-运行sql函数如下图:
SELECT top 1000 pk , geom.STAsText() as input,conv._geom as ouput,
geometry::STGeomFromText(conv._geom,4326)
FROM [Prod].[dbo].DPO_Geographic_Units as a CROSS APPLY dbo.RPG_STTransform (
a.Geom.STAsText()--geometry as text
,'+proj=tmerc +lat_0=0 +lon_0=173 +k=0.9996 +x_0=1600000 +y_0=10000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs ',--source =2193
'+proj=longlat +datum=WGS84 +no_defs'--destination =4326
) as conv
where [Geom] is not null
并且输入和输出彼此相邻:
如果喜欢,可以在代码中保存这些投影公式,使用switch case获取每个投影对应的公式,然后传入一个数字(如2193),然后编码找到等效公式。
我正在尝试在 SQL 服务器中进行一些重新投影,但遗憾的是我找不到任何解决方案。我知道这在 SQL 服务器中没有实现,所以想知道是否有任何快捷方式。
有些人建议使用外部库,例如 ogr2ogr,但这对我来说不是一个好的选择,因为我需要即时进行。我在这里找到了 post Transform/ Project a geometry from one SRID to another,但是这个转换为纬度和经度并且功能有限。
我正在寻找一个允许我在任何 srid 中进行重投影的解决方案,非常类似于 postgresql (postgis)
中的ST_Transform
好吧,如果您正在寻找一个简单的选择,恐怕这是不可能的。但是这个问题有一个解决方法。前段时间我遇到了类似的问题,我不得不在 .NET 中实现 CLR,然后将其作为程序集导入 MSSQL,速度有点慢,但工作正常。
输入是:源投影、目标投影和作为文本的几何体,输出是将几何体重新投影为文本。请查看彼此相邻的 .Net 和 PostgreSQL 的此程序集的结果(几乎相同)。
输入是:
POLYGON ((1755828.2027002387 5944302.7072003055, 1755826.3549002428 5944302.8313003061, 1755825.1724002406 5944285.1574003045, 1755809.5710002393 5944286.4007003047, ......))";// geomobj.line; //"POINT (1736946.0983 5923253.9175)";
所以,解决方法如下:
1- 打开 .NET 并创建一个 c# 库并将以下代码粘贴到那里(此代码获取每一行数据,遍历每个元素,分解它,提取每个经度和纬度,重新投影并替换它)
using Microsoft.SqlServer.Server;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class classval
{
public string line;
public string src;
public string dst;
public classval(string line, string src, string dst)
{
this.line = line;
this.src = src;
this.dst = dst;
}
}
public class RPG_Transform
{
private static IEnumerable<classval> ConvertedEnumerable(string line, string src, string dst)
{
return new List<classval> { new classval(line, src, dst) };
}
[SqlFunction(FillRowMethodName = "FillRow")]
public static IEnumerable STRPG_Transform(string Geometry, string src, string dst)
{
return ConvertedEnumerable(Geometry, src, dst);
}
private static void FillRow(Object classvalobj, out string Geometry, out string srcprj, out string dstprj)
{
classval geomobj = (classval)classvalobj;
string _geometry = geomobj.line;
string proj4_src = geomobj.src;
string proj4_dst = geomobj.dst;
string _originalgeom = _geometry;
_geometry = _geometry.Remove(0, _geometry.IndexOf('('));
// _geometry = _geometry.Replace("(", "[ ");
// _geometry = _geometry.Replace(")", " ]");
string[] splitbycomma = _geometry.Split(',');
foreach (var itembycomma in splitbycomma)
{
string tmpitem = itembycomma;
tmpitem = tmpitem.Replace('(', ' ');
tmpitem = tmpitem.Replace(')', ' ');
tmpitem = tmpitem.Trim();
string[] splitbyspace = tmpitem.Split(' ');
for (int ibs = 0; ibs < splitbyspace.Length - 1; ibs++)
{
string originallonglat = splitbyspace[ibs] + " " + splitbyspace[ibs + 1];
double[] yxval = new double[] { double.Parse(splitbyspace[ibs]), double.Parse(splitbyspace[ibs + 1]) };
double[] z = new double[1] { 0 };
DotSpatial.Projections.ProjectionInfo src =
DotSpatial.Projections.ProjectionInfo.FromProj4String(proj4_src);
DotSpatial.Projections.ProjectionInfo trg =
DotSpatial.Projections.ProjectionInfo.FromProj4String(proj4_dst);
DotSpatial.Projections.Reproject.ReprojectPoints(yxval, z, src, trg, 0, 1);
string longlat = yxval[0] + " " + yxval[1];
_originalgeom = _originalgeom.Replace(originallonglat, longlat);
}
}
srcprj = proj4_src;
dstprj = proj4_dst;
Geometry = _originalgeom;
}
}
2- 在 运行 之前,您需要安装一些库,最简单的是使用 Nugget Manager,如下所示。
3-接下来你需要编译代码并得到一个dll,然后将这些dll复制到一个新的文件夹中,如果你喜欢你把它们留在调试文件夹中(其中一些dll被添加到由于依赖关系的项目)如下所示
4- 在 MSSQL 中启用 CLR,然后将程序集添加到 MSSQL 服务器(通过创建如下所示的函数)
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'clr enabled', 1;
GO
RECONFIGURE;
GO
ALTER DATABASE Prod SET trustworthy ON
CREATE ASSEMBLY CLRFunctionAssem
FROM N'E:\CLR\RPG_Transform\RPG_Transform\bin\Debug\RPG_Transform.dll'
WITH PERMISSION_SET = UNSAFE
GO
--DROP ASSEMBLY CLRFunctionAssem ---if you need to drop it in the future
CREATE FUNCTION dbo.RPG_STTransform(@Geometry nvarchar(max), @src
nvarchar(max),@dst nvarchar(max))
RETURNS TABLE
( _geom nvarchar(max) ,srcprj nvarchar(max) ,dstprj nvarchar(max)
) with execute as caller
AS
EXTERNAL NAME CLRFunctionAssem.[RPG_Transform].STRPG_Transform
5- 获取源投影和目标投影的公式。
你需要从某个地方得到这两个公式。对我来说,为了获取源投影和目标投影,我使用以下网站。(此解决方案使用 Proj4j,因此您需要从某处获取与 srid 等效的内容)。
https://epsg.io/
如下图:
目的地也一样
6-运行sql函数如下图:
SELECT top 1000 pk , geom.STAsText() as input,conv._geom as ouput,
geometry::STGeomFromText(conv._geom,4326)
FROM [Prod].[dbo].DPO_Geographic_Units as a CROSS APPLY dbo.RPG_STTransform (
a.Geom.STAsText()--geometry as text
,'+proj=tmerc +lat_0=0 +lon_0=173 +k=0.9996 +x_0=1600000 +y_0=10000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs ',--source =2193
'+proj=longlat +datum=WGS84 +no_defs'--destination =4326
) as conv
where [Geom] is not null
并且输入和输出彼此相邻:
如果喜欢,可以在代码中保存这些投影公式,使用switch case获取每个投影对应的公式,然后传入一个数字(如2193),然后编码找到等效公式。