前言:最近我们项目有个需求,就是将shp文件转为geojson。网上有很多的网站可以进行shp与geojson互转,但是这种做法并不能集成到我们系统中来,只适合单次调用。于是折腾了好多种办法,终于出来了,这里记录一下。
由于我们的项目采用的架构是Postgresql+Geoserver+OpenLayers+Asp.Net Core MVC,所以我们最初的使用方法是将shp导入postgresql,这样我们就可以调用了。并且postgresql也提供了一个工具叫做,shp2pgsql,专门用来导入shp文件的,于是我们就试了一下。
shp2pgsql的这个工具的使用cmd命令如下:
shp2pgsql -s 3857 -a -W "utf-8" -k -g "Shape" D:\shp\test.shp "YZL"."YZL_ZLXB_PY" | psql -a test123 postgres
由于网上有很多的文章介绍这个工具怎么使用,我们在这里就不详细介绍了。上面给出的cmd命令并没有输入密码,那是因为我们在postgresql的配置文件中已经写好了,这里同样不再介绍,只给出shp2pgsql工具的常用命令:
OPTIONS:
●-s
设置srid,缺省为-1
●(-d|a|c|p)互斥选项:
-d 重新建立表,并插入数据。
-a 在同一个表中增加数据
-c 建立新表,并插入数据。(缺省)
-p 只创建表
●-g 指定要创建的表的空间字段名称(在追加数据时有用)
●-D 使用dump方式,缺省是生成sql
●-G Use geography type (requires lon/lat data).
●-k 保持PostgreSQL标识符方式
●-i 使用int4类型dbf文件里的integer类型
●-I 在空间字段上建立索引
●-S Generate simple geometries instead of MULTI geometries.
●-W shape文件属性列的字符格式。缺省是ascII
●-N 指定geometries为空时的操作(insert,skip,abort)
●-n 只导入dbf文件
●-? 显示帮助
上面我们的命令指定的空间字段是“Shape”,shp2pgsql识别出来的sql语句是:
insert into test123."YZL"."YZL_ZLXB_PY"("字段1","字段2",Shape) values("value1","value2","ShapeValue")
执行的时候总是报错,这就奇怪了,仔细看上面的sql语句,Shape是没有加引号的,所以一直报错,加上引号就好了。另外在网上查询资料的得到的结果是shp2pgsql是不识别大写的空间字段的,所以我加了一个测试的小写字段,居然真的成功了,这工具就是这么奇葩。。。。。
由于我们的表已经建好了,是不允许随便乱加字段的,所以回过头来继续说我们的解决方案。所幸shp2pgsql这个工具是可以生成sql文件的,里面的内容就是一条条insert语句。它的命令是这样的:
shp2pgsql -s 3857 -a -W "utf-8" -k -g "Shape" D:\shp\test.shp "YZL"."YZL_ZLXB_PY" >d:\test.sql
这样,生成了test.sql文件,我们就可以用程序读取这个文件,把每一条sql语句都更改了,即给加上Shape加上引号。于是,将shp文件导入数据库的问题就成功解决了。
至此,关于postgresql的相关操作就结束了
-----------------------------------------------------------------------------
上面我们虽然成功的解决了将shp导入postgresql,但是我们只能导入特定的表,也就是说我们的shp文件表格式必须和我们数据库里的表结构一毛一样,这时非常操蛋的,于是我们就转向了鼎鼎大名的数据转换工具GDAL。
于是,我们本着内事不决问百度,外事不决问谷歌的原则,打开了谷歌浏览器,一搜,果然在Stack Overflow上找到了解决方案,代码也不复杂,于是我们在Nuget服务器上找到了GDAL .Net Core的包,引用dll,开始上代码:
C# Code
///
/// 将shp转为geojson
///
/// shp文件全路径
///
public static List<</span>object> ConvertShp2Geojson(string shapeFilePath)
{
try
{
// 为了支持中文路径
Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
// 为了使属性表字段支持中文
Gdal.SetConfigOption("SHAPE_ENCODING", "");
Gdal.AllRegister();
Ogr.RegisterAll();
Driver drv = Ogr.GetDriverByName("ESRI Shapefile");
var ds = drv.Open(shapeFilePath, 0);
Layer layer = ds.GetLayerByIndex(0);
Feature f;
layer.ResetReading();
List<</span>object> sb = new List<</span>object>();
while ((f = layer.GetNextFeature()) != null)
{
var geom = f.GetGeometryRef();
if (geom != null)
{
var geometryJson = geom.ExportToJson(new string[] { });
var obj = JsonConvert.DeserializeObject(geometryJson);
sb.Add(obj);
}
}
return sb;
}
catch (Exception e)
{
throw e;
}
}
高高兴兴的发布网站,本地测试,能跑!!!来吧,部署远程服务器,测试,报错 The type initializer for 'OSGeo.OGR.OgrPINVOKE' threw an exception.
有没有搞错!!!本地可以跑,为什么服务器不能跑,本地是win10系统,服务器是windows Server2008,难道环境不一样?于是我们就开始给服务器安装环境,GDAL是C++开发出来,我们装C++2005,不行!C++2008,还不行!!,C++2010,还不行!!!这就尴尬了,算了,环境问题太难搞,此路不通,我们就换思路。
还是本着内事不决问百度,外事不决问谷歌的原则,我们打开了谷歌,一搜,原来GDAL也提供了一个工具,叫做ogr2ogr,就是做shp与geojson互转的。那么我们先来下这个工具,在GDAL的官网找了半天,没找到!!!这不应该啊。好吧,外面找不到,我们就回来找,一搜,果然有,居然要50个积分,你咋不去抢呢。还是忍痛弄了下来,打开压缩包一看,tmd,居然没有这个工具,你个死骗子。差评加曝光。
不到黄河不死心,再次上GDAL官网,还是没找上,算了,下载一个它的包吧,也许在那里面,下载下来一搜,还真有!!!哈哈哈,皇天不负有心人呀。贴上下载方式:
1.在GISinternal下载zip压缩包, 地址
2.解压,找到bin\gdal\apps
现在就可以开始转换了:
以管理员身份打开cmd窗口,进入这个路径:
cd D:\迅雷下载\release\bin\gdal\apps
1、shp转geojson命令:
ogr2ogr -f "GeoJSON" C:\goal.json C:\source.shp
其中goal.json文件会由这个工具自己创建
2、geojson转shp命令:
ogr2ogr -f "ESRI Shapefile" C:\goal.shp C:\source.json
其中goal.shp文件也会由这个工具自己创建
知道了这些个命令,我们在程序里调用它就很容易了,不废话,直接上代码:
C# Code
///
/// 调用ogr2ogr.exe将shp转为geojson
///
/// shp文件路径
public static string RunJsonConvertProcess( string shpfile)
{
try
{
string dataDir = Directory.GetCurrentDirectory();
string accessPath = dataDir + @ "\bin\gdal\apps" ;
string fileName = Path.GetFileNameWithoutExtension(shpfile);
var jsonPath = shpfile.Substring( 0 , shpfile.LastIndexOf( "\" ) + 1 ) + fileName + ".json" ;
if (!File.Exists(accessPath + "\\ogr2ogr.exe" ))
throw new Exception( "不存在转换工具ogr2ogr" );
string cmd = $ "ogr2ogr -f " GeoJSON\ " {jsonPath} {shpfile}" ;
string str = string .Format(@ "C:&cd {0}&{1}&{2}" , accessPath, cmd, "exit" );
RunCmd(str);
if (File.Exists(jsonPath))
return jsonPath;
else
return "" ;
}
catch (Exception e)
{
return "error:" +e.Message;
}
}
///
/// 执行cmd命令
/// 多命令请使用批处理命令连接符:
///
/// &:同时执行多个命令
/// |:将上一个命令的输出,作为下一个命令的输入
/// &&:当&&前的命令成功时,才执行&&后的命令
/// ||:当||前的命令失败时,才执行||后的命令
///
///
public static void RunCmd( string cmd)
{
cmd = cmd.Trim().TrimEnd( '&' ) + "&exit" ; //说明:不管命令是否成功均执行exit命令,否则当调用ReadToEnd()方法时,会处于假死状态
using (Process p = new Process())
{
Console.InputEncoding = Encoding.UTF8;
p.StartInfo.FileName = @ "C:\Windows\System32\cmd.exe" ;
p.StartInfo.UseShellExecute = false ; //是否使用操作系统shell启动
p.StartInfo.RedirectStandardInput = true ; //接受来自调用程序的输入信息
p.StartInfo.RedirectStandardOutput = true ; //由调用程序获取输出信息
p.StartInfo.RedirectStandardError = true ; //重定向标准错误输出
p.StartInfo.CreateNoWindow = true ; //不显示程序窗口
p.Start(); //启动程序
//向cmd窗口写入命令
p.StandardInput.WriteLine(cmd);
p.StandardInput.AutoFlush = true ;
//获取cmd窗口的输出信息
//output = p.StandardOutput.ReadToEnd();
p.WaitForExit(); //等待程序执行完退出进程
p.Close();
}
}
总结:shp2pgsql工具只能向某张特定的表导入数据,灵活性太差。ogr2ogr可以转换任意shp为geojson,非常灵活,我们当然选择后者。周五了,解决了一个大问题,可以愉快地过周末了,本次教程就分享到这里,我们下次再会。
------------------------------------------------------------------------------
2019.4.24更新:我在postgresql的安装目录中的bin文件夹中也找到ogr2ogr工具,太令人吃惊了!!!
-----------------------------------------------------------------------------------------------------------------
2019.4.26更新:之前代码好好的运行,最近不知道怎么突然不能用了,报错'ogr2ogr ' is not recognized as an internal or external command, operable program or batch file.折腾了好久才发现是环境变量的问题,但是我们已经设置了环境变量了啊,但是程序不认,于是我们只好在程序里设置下环境变量。
废话不多说,我们直接上代码,这次的代码包括三个方法,因为之前的函数名命名并不好,所以我们这次重新命名一下:
1、Shp2GeoJsonByGDAL 使用GDAL将shp转为geojson
2、Shp2GeoJsonByOgr 调用ogr2ogr.exe将shp转为geojson
3、Shp2PostgreSql 调用shp2pgsql工具生成sql文件
4、RunCmd 执行cmd命令
C# Code
///
/// 使用GDAL将shp转为geojson
///
/// shp文件全路径
///
public static List<</span>object> Shp2GeoJsonByGDAL(string shapeFilePath)
{
try
{
// 为了支持中文路径
Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
// 为了使属性表字段支持中文
Gdal.SetConfigOption("SHAPE_ENCODING", "");
Gdal.AllRegister();
Ogr.RegisterAll();
Driver drv = Ogr.GetDriverByName("ESRI Shapefile");
var ds = drv.Open(shapeFilePath, 0);
Layer layer = ds.GetLayerByIndex(0);
Feature f;
layer.ResetReading();
List<</span>object> sb = new List<</span>object>();
while ((f = layer.GetNextFeature()) != null)
{
var geom = f.GetGeometryRef();
if (geom != null)
{
var geometryJson = geom.ExportToJson(new string[] { });
var obj = JsonConvert.DeserializeObject(geometryJson);
sb.Add(obj);
}
}
return sb;
}
catch (Exception e)
{
throw e;
}
}
///
/// 调用ogr2ogr.exe将shp转为geojson
///
/// shp文件路径
public static string Shp2GeoJsonByOgr(string shpfile)
{
try
{
string dataDir = Directory.GetCurrentDirectory();
string accessPath = dataDir + @"\bin\postgresql";
string fileName = Path.GetFileNameWithoutExtension(shpfile);
var jsonPath = shpfile.Substring(0, shpfile.LastIndexOf("\") + 1) + fileName + ".json";
if (!File.Exists(accessPath + "\\ogr2ogr.exe"))
throw new Exception("不存在转换工具ogr2ogr");
string cmd = $"ogr2ogr -f "GeoJSON\" {jsonPath} {shpfile}";
string str = string.Format(@"C:&cd {0}&{1}&{2}", accessPath, cmd,"exit");
RunCmd(accessPath,str);
if(File.Exists(jsonPath))
return jsonPath;
else
return "";
}
catch (Exception e)
{
return "error:"+e.Message;
}
}
///
/// 执行cmd命令
/// 多命令请使用批处理命令连接符:
///
/// &:同时执行多个命令
/// |:将上一个命令的输出,作为下一个命令的输入
/// &&:当&&前的命令成功时,才执行&&后的命令
/// ||:当||前的命令失败时,才执行||后的命令
///
/// 要设置的环境变量
/// 要执行的cmd命令
public static string RunCmd(string environmentVariable, string cmd)
{
cmd = cmd.Trim().TrimEnd('&') + "&exit";//说明:不管命令是否成功均执行exit命令,否则当调用ReadToEnd()方法时,会处于假死状态
string res = "";
using (Process p = new Process())
{
//先设置环境变量,再运行
string path = Environment.GetEnvironmentVariable("path");
Environment.SetEnvironmentVariable("path", path + environmentVariable + ";");
//.NET Core不支持gbk和gb2312,这里需要处理一下
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Console.InputEncoding = Encoding.UTF8;
p.StartInfo.FileName = @"C:\Windows\System32\cmd.exe";
p.StartInfo.UseShellExecute = false; //是否使用操作系统shell启动
p.StartInfo.RedirectStandardInput = true; //接受来自调用程序的输入信息
p.StartInfo.RedirectStandardOutput = true; //由调用程序获取输出信息
p.StartInfo.RedirectStandardError = true; //重定向标准错误输出
p.StartInfo.CreateNoWindow = true; //不显示程序窗口
p.Start();//启动程序
//向cmd窗口写入命令
p.StandardInput.WriteLine(cmd);
p.StandardInput.AutoFlush = true;
p.WaitForExit();//等待程序执行完退出进程
//获取cmd窗口的输出信息
string s = p.StandardOutput.ReadToEnd();
//获取错误信息
string error = p.StandardError.ReadToEnd();
byte[] bytes = Encoding.GetEncoding("GB2312").GetBytes(s);
res = Encoding.UTF8.GetString(bytes);
p.Close();
}
return res;
}
///
/// 调用shp2pgsql工具生成sql文件
///
///
public static string Shp2PostgreSql(string shpfile)
{
try
{
string dataDir = Directory.GetCurrentDirectory();
string accessPath = dataDir + @"\bin\postgresql";
string fileName = Path.GetFileNameWithoutExtension(shpfile);
var sqlPath = shpfile.Substring(0, shpfile.LastIndexOf("\") + 1) + fileName + ".sql";
if (!File.Exists(accessPath + "\\shp2pgsql.exe"))
throw new Exception("不存在转换工具shp2pgsql");
string cmd = $"shp2pgsql -s 3857 -a -W "gbk\" -k -g "Shape\" {shpfile} "YZL\"."LDBG_QHTB_PY\" >{sqlPath}";
string str = string.Format(@"C:&cd {0}&{1}&{2}", accessPath, cmd, "exit");
RunCmd(accessPath, str);
if (File.Exists(sqlPath))
return sqlPath;
else
return "";
}
catch (Exception e)
{
return "error:" + e.Message;
}
}