C#将shapefile转换为geojson—通过GDAL
最近项目上,后台需要将数据处理的结果返回geojson数据的格式,自己写的拼接geojson方法始终存在问题。面太复杂,各种内环外环,面的相交等,无法完美解析,就采用这个办法,直接通过gdal,将结果转换为geojson,再返回,用别人造好的轮子,结实耐用。
一、下载
下载链接:http://www.gisinternals.com/release.php
这是我下载的版本
解压后找到如下几个文件
二、环境搭建
项目中引用上面8个dll中带csharp后缀的四个类库,如下图所示:
将上面8个文件复制到程序输出目录debug下。
网上有文章说将/bin/gdal204.dll文件拷贝到debug目录下,我试了试不管用,程序启动后,抛异常,找不到类库。于是将目录下所有dll都拷贝到debug下,就没有报错了。
三、代码
转换的代码如下所示:
public static bool TranslateShapeFile(string shapefilepath,string geojsonfilepath) { bool isok = false; try { LogManager.Write("shapefile文件路径:" + shapefilepath); LogManager.Write("geojson文件输出路径:" + geojsonfilepath); GdalConfiguration.ConfigureGdal(); GdalConfiguration.ConfigureOgr(); if (string.IsNullOrWhiteSpace(shapefilepath) || string.IsNullOrWhiteSpace(geojsonfilepath)) { LogManager.Write("输入参数路径不合法"); return false; } OSGeo.GDAL.Gdal.AllRegister(); OSGeo.OGR.Ogr.RegisterAll(); // 为了支持中文路径,请添加下面这句代码 OSGeo.GDAL.Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES"); // 为了使属性表字段支持中文,请添加下面这句 OSGeo.GDAL.Gdal.SetConfigOption("SHAPE_ENCODING", ""); //打开数据 DataSource ds = Ogr.Open(shapefilepath, 0); if (ds == null) { LogManager.Write("打开文件失败!"); return false; } Driver dv = Ogr.GetDriverByName("GeoJSON"); if (dv == null) { LogManager.Write("打开驱动失败!"); return false; } dv.CopyDataSource(ds, geojsonfilepath, null); isok = true; } catch(Exception ex) { LogManager.Write("shapefile文件读取转换失败!"); LogManager.Write(ex); } return isok; }
代码中有如下两句,是初始化代码,网上的教程上大多都有了。我这是VS里面NuGet添加的gdal引用,自动添加了这个类,后来发现缺失其他类库,就直接下载了类库。自动生成两个文件,一个C#,一个VB版。可新建类,复制过去即可。
GdalConfiguration.ConfigureGdal();
GdalConfiguration.ConfigureOgr();
C#
/****************************************************************************** * * Name: GdalConfiguration.cs.pp * Project: GDAL CSharp Interface * Purpose: A static configuration utility class to enable GDAL/OGR. * Author: Felix Obermaier * ****************************************************************************** * Copyright (c) 2012-2018, Felix Obermaier * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *****************************************************************************/ using System; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using Gdal = OSGeo.GDAL.Gdal; using Ogr = OSGeo.OGR.Ogr; namespace Utility.ShapeFileToGeoJson { public static partial class GdalConfiguration { private static volatile bool _configuredOgr; private static volatile bool _configuredGdal; private static volatile bool _usable; [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)] static extern bool SetDefaultDllDirectories(uint directoryFlags); // LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_SYSTEM32 private const uint DllSearchFlags = 0x00000400 | 0x00000800; [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool AddDllDirectory(string lpPathName); /// <summary> /// Construction of Gdal/Ogr /// </summary> static GdalConfiguration() { string executingDirectory = null, gdalPath = null, nativePath = null; try { if (!IsWindows) { const string notSet = "_Not_set_"; string tmp = Gdal.GetConfigOption("GDAL_DATA", notSet); _usable = tmp != notSet; return; } string executingAssemblyFile = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath; executingDirectory = Path.GetDirectoryName(executingAssemblyFile); if (string.IsNullOrEmpty(executingDirectory)) throw new InvalidOperationException("cannot get executing directory"); // modify search place and order SetDefaultDllDirectories(DllSearchFlags); gdalPath = Path.Combine(executingDirectory, "gdal"); nativePath = Path.Combine(gdalPath, GetPlatform()); if (!Directory.Exists(nativePath)) throw new DirectoryNotFoundException("GDAL native directory not found at '{nativePath}'"); if (!File.Exists(Path.Combine(nativePath, "gdal_wrap.dll"))) throw new FileNotFoundException( "GDAL native wrapper file not found at"); // Add directories AddDllDirectory(nativePath); AddDllDirectory(Path.Combine(nativePath, "plugins")); // Set the additional GDAL environment variables. string gdalData = Path.Combine(gdalPath, "data"); Environment.SetEnvironmentVariable("GDAL_DATA", gdalData); Gdal.SetConfigOption("GDAL_DATA", gdalData); string driverPath = Path.Combine(nativePath, "plugins"); Environment.SetEnvironmentVariable("GDAL_DRIVER_PATH", driverPath); Gdal.SetConfigOption("GDAL_DRIVER_PATH", driverPath); Environment.SetEnvironmentVariable("GEOTIFF_CSV", gdalData); Gdal.SetConfigOption("GEOTIFF_CSV", gdalData); string projSharePath = Path.Combine(gdalPath, "share"); Environment.SetEnvironmentVariable("PROJ_LIB", projSharePath); Gdal.SetConfigOption("PROJ_LIB", projSharePath); _usable = true; } catch (Exception e) { _usable = false; //Trace.WriteLine(e, "error"); //Trace.WriteLine($"Executing directory: {executingDirectory}", "error"); //Trace.WriteLine($"gdal directory: {gdalPath}", "error"); //Trace.WriteLine($"native directory: {nativePath}", "error"); //throw; } } /// <summary> /// Gets a value indicating if the GDAL package is set up properly. /// </summary> public static bool Usable { get { return _usable; } } /// <summary> /// Method to ensure the static constructor is being called. /// </summary> /// <remarks>Be sure to call this function before using Gdal/Ogr/Osr</remarks> public static void ConfigureOgr() { if (!_usable) return; if (_configuredOgr) return; // Register drivers Ogr.RegisterAll(); _configuredOgr = true; PrintDriversOgr(); } /// <summary> /// Method to ensure the static constructor is being called. /// </summary> /// <remarks>Be sure to call this function before using Gdal/Ogr/Osr</remarks> public static void ConfigureGdal() { if (!_usable) return; if (_configuredGdal) return; // Register drivers Gdal.AllRegister(); _configuredGdal = true; PrintDriversGdal(); } /// <summary> /// Function to determine which platform we're on /// </summary> private static string GetPlatform() { return Environment.Is64BitProcess ? "x64" : "x86"; } /// <summary> /// Gets a value indicating if we are on a windows platform /// </summary> private static bool IsWindows { get { var res = !(Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX); return res; } } private static void PrintDriversOgr() { #if DEBUG if (_usable) { var num = Ogr.GetDriverCount(); for (var i = 0; i < num; i++) { var driver = Ogr.GetDriver(i); Trace.WriteLine("OGR {i}: {driver.GetName()}", "Debug"); } } #endif } private static void PrintDriversGdal() { #if DEBUG if (_usable) { var num = Gdal.GetDriverCount(); for (var i = 0; i < num; i++) { var driver = Gdal.GetDriver(i); Trace.WriteLine("GDAL {i}: {driver.ShortName}-{driver.LongName}"); } } #endif } } }
VB
'****************************************************************************** '* '* Name: GdalConfiguration.vb.pp '* Project: GDAL VB.NET Interface '* Purpose: A static configuration utility class to enable GDAL/OGR. '* Author: Felix Obermaier '* '****************************************************************************** '* Copyright (c) 2012-2018, Felix Obermaier '* '* Permission is hereby granted, free of charge, to any person obtaining a '* copy of this software and associated documentation files (the "Software"), '* to deal in the Software without restriction, including without limitation '* the rights to use, copy, modify, merge, publish, distribute, sublicense, '* and/or sell copies of the Software, and to permit persons to whom the '* Software is furnished to do so, subject to the following conditions: '* '* The above copyright notice and this permission notice shall be included '* in all copies or substantial portions of the Software. '* '* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS '* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, '* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL '* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER '* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING '* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER '* DEALINGS IN THE SOFTWARE. '*****************************************************************************/ Option Infer On Imports System Imports System.Diagnostics Imports System.IO Imports System.Reflection Imports System.Runtime.InteropServices Imports Gdal = OSGeo.GDAL.Gdal Imports Ogr = OSGeo.OGR.Ogr Namespace Utility.ShapeFileToGeoJson ''' <summary> ''' Configuration class for GDAL/OGR ''' </summary> Partial Public Class GdalConfiguration Private Shared _configuredOgr As Boolean Private Shared _configuredGdal As Boolean Private Shared _usable As Boolean <DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> Public Shared Function SetDefaultDllDirectories(ByVal directoryFlags As UInteger) As Boolean End Function ' LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_SYSTEM32 Private Const DllSearchFlags As UInteger = &H400 Or &H800 <DllImport("kernel32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> Public Shared Function AddDllDirectory(ByVal lpPathName As String) As <MarshalAs(UnmanagedType.Bool)> Boolean End Function ''' <summary> ''' Construction of Gdal/Ogr ''' </summary> Shared Sub New() Dim nativePath As String = Nothing Dim executingDirectory As String = Nothing Dim gdalPath As String = Nothing Try If Not IsWindows Then Const notSet As String = "_Not_set_" Dim tmp As String = Gdal.GetConfigOption("GDAL_DATA", notSet) _usable = (tmp <> notSet) Return End If Dim executingAssemblyFile As String = New Uri(Assembly.GetExecutingAssembly.GetName.CodeBase).LocalPath executingDirectory = Path.GetDirectoryName(executingAssemblyFile) If String.IsNullOrEmpty(executingDirectory) Then Throw New InvalidOperationException("cannot get executing directory") End If SetDefaultDllDirectories(DllSearchFlags) gdalPath = Path.Combine(executingDirectory, "gdal") nativePath = Path.Combine(gdalPath, GetPlatform()) If Not Directory.Exists(nativePath) Then Throw New DirectoryNotFoundException($"GDAL native directory not found at '{nativePath}'") End If If Not File.Exists(Path.Combine(nativePath, "gdal_wrap.dll")) Then Throw New FileNotFoundException($"GDAL native wrapper file not found at '{Path.Combine(nativePath, ", gdal_wrap.dll, ")}'") End If AddDllDirectory(nativePath) AddDllDirectory(Path.Combine(nativePath, "plugins")) ' Set the additional GDAL environment variables. Dim gdalData As String = Path.Combine(gdalPath, "data") Environment.SetEnvironmentVariable("GDAL_DATA", gdalData) Gdal.SetConfigOption("GDAL_DATA", gdalData) Dim driverPath As String = Path.Combine(nativePath, "plugins") Environment.SetEnvironmentVariable("GDAL_DRIVER_PATH", driverPath) Gdal.SetConfigOption("GDAL_DRIVER_PATH", driverPath) Environment.SetEnvironmentVariable("GEOTIFF_CSV", gdalData) Gdal.SetConfigOption("GEOTIFF_CSV", gdalData) Dim projSharePath As String = Path.Combine(gdalPath, "share") Environment.SetEnvironmentVariable("PROJ_LIB", projSharePath) Gdal.SetConfigOption("PROJ_LIB", projSharePath) _usable = True Catch e As Exception _usable = False Trace.WriteLine(e, "error") Trace.WriteLine($"Executing directory: {executingDirectory}", "error") Trace.WriteLine($"gdal directory: {gdalPath}", "error") Trace.WriteLine($"native directory: {nativePath}", "error") 'throw; End Try End Sub ''' <summary> ''' Gets a value indicating if the GDAL package is set up properly. ''' </summary> Public Shared ReadOnly Property Usable As Boolean Get Return _usable End Get End Property ''' <summary> ''' Method to ensure the static constructor is being called. ''' </summary> ''' <remarks>Be sure to call this function before using Gdal/Ogr/Osr</remarks> Public Shared Sub ConfigureOgr() If Not _usable Then Return End If If _configuredOgr Then Return End If ' Register drivers Ogr.RegisterAll() _configuredOgr = True PrintDriversOgr() End Sub ''' <summary> ''' Method to ensure the static constructor is being called. ''' </summary> ''' <remarks>Be sure to call this function before using Gdal/Ogr/Osr</remarks> Public Shared Sub ConfigureGdal() If Not _usable Then Return End If If _configuredGdal Then Return End If ' Register drivers Gdal.AllRegister() _configuredGdal = True PrintDriversGdal() End Sub ''' <summary> ''' Function to determine which platform we're on ''' </summary> Private Shared Function GetPlatform() As String If (Environment.Is64BitProcess) Then Return "x64" Return "x86" End Function ''' <summary> ''' Gets a value indicating if we are on a windows platform ''' </summary> Private Shared ReadOnly Property IsWindows As Boolean Get Dim res = Not ((Environment.OSVersion.Platform = PlatformID.Unix) _ OrElse (Environment.OSVersion.Platform = PlatformID.MacOSX)) Return res End Get End Property Private Shared Sub PrintDriversOgr() #If (DEBUG) Then If _usable Then Dim num = Ogr.GetDriverCount Dim i = 0 Do While (i < num) Dim driver = Ogr.GetDriver(i) Trace.WriteLine($"OGR {i}: {driver.GetName()}", "Debug") i = (i + 1) Loop End If #End If End Sub Private Shared Sub PrintDriversGdal() #If (DEBUG) Then If _usable Then Dim num = Gdal.GetDriverCount Dim i = 0 Do While (i < num) Dim driver = Gdal.GetDriver(i) Trace.WriteLine($"GDAL {i}: {driver.ShortName}-{driver.LongName}") i = (i + 1) Loop End If #End If End Sub End Class End Namespace
记录一下!