專案有一需求,需要由座標字串(格式:x,y,點名)生成shapefile,由於在前臺開發類似功能比較麻煩而且也不適用,最終決定使用WCF來實現,不借助現有GIS軟體,基於GDAL實現。

實現過程如下:

編寫座標對生成shapefile的座標,並使用zipHelper將shapefile壓縮成zip檔案,返回前端下載,整個程式碼如下:

(1)編寫座標轉換服務程式碼

    [Description("座標服務介面")]
[ServiceContract]
public interface ICoord2Shapefile
{
//[WebGet(UriTemplate = "{points}", ResponseFormat = WebMessageFormat.Json)]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json,UriTemplate="/")]
[Description("將座標生成shapefile,並壓縮打包")]
[OperationContract] string point2zip(string points); } [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Coord2ShapefileService : ICoord2Shapefile
{
public string point2zip(string points)
{
LogHelper.Info(points);
string zip = "";
//List<Location> locs = JsonHelper.DeserializeJsonToList<Location>(points); try
{
List<Location> locs = new List<Location>();
string[] pps = points.Split(';');
for (int i = ; i < pps.Length; i++)
{
string[] pp = pps[i].Split(',');
double x = Convert.ToDouble(pp[]);
double y = Convert.ToDouble(pp[]);
Location loc = new Location() { X = x, Y = y, Name = pp[] };
locs.Add(loc);
} if (locs != null && locs.Count > )
{
LogHelper.Info(string.Format("{0} 個點",locs.Count)); string shpfile = Path.GetTempFileName();
shpfile = shpfile.Substring(, shpfile.Length - ) + "shp"; LogHelper.Info(shpfile); //shpfile = "e:\\aa.shp"; WriteVectorFileShp(shpfile, locs); if (File.Exists(shpfile))
{
FileInfo fileinfo = new FileInfo(shpfile);
DirectoryInfo dirInfo = fileinfo.Directory; string shpName = Path.GetFileNameWithoutExtension(shpfile); List<string> files = new List<string>();
files.Add(shpfile);
files.Add(Path.Combine(dirInfo.FullName, shpName + ".prj"));
files.Add(Path.Combine(dirInfo.FullName, shpName + ".dbf"));
files.Add(Path.Combine(dirInfo.FullName, shpName + ".shx")); //string zipFile = "d:\\point.zip";
string id = Guid.NewGuid().ToString();
string zipFile = string.Format("d:\\update\\temp\\{0}{1}{2}{3}{4}{5}{6}.zip", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second, DateTime.Now.Millisecond);
//string zipFile = string.Format("d:\\{0}{1}{2}{3}{4}{5}{6}.zip", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second, DateTime.Now.Millisecond);
//string zipFile = "www.simplemap.com.cn/update/temp";
int ret = ZipHelper.ZipFiles(zipFile, files.ToArray(), "data");
if (ret > )
{
foreach (var file in files)
{
try
{
File.Delete(file);
}
catch (Exception)
{
}
}
if (File.Exists(zipFile))
{
FileInfo ff = new FileInfo(zipFile);zip = "http://localhost/temp" + ff.Name;
}
}
}
else
{
LogHelper.Info(string.Format("{0} 不存在!", shpfile));
}
}
}
catch (Exception ex)
{
LogHelper.Error(ex.Message, ex);
} return zip;
} static void WriteVectorFileShp(String shapefile_path, List<Location> locs) //建立演算法生產的邊界向量圖
{
// 為了支援中文路徑,請新增下面這句程式碼
OSGeo.GDAL.Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
// 為了使屬性表字段支援中文,請新增下面這句
OSGeo.GDAL.Gdal.SetConfigOption("SHAPE_ENCODING", "");
// 註冊所有的驅動
Ogr.RegisterAll(); LogHelper.Info("註冊GDAL成功");
//建立資料,建立ESRI的shp檔案
string strDriverName = "ESRI Shapefile";
Driver oDriver = Ogr.GetDriverByName(strDriverName);
if (oDriver == null)
{
Debug.WriteLine("%s 驅動不可用!\n", shapefile_path);
return;
} // 步驟1、建立資料來源
DataSource oDS = oDriver.CreateDataSource(shapefile_path, null);
if (oDS == null)
{
Debug.WriteLine("建立向量檔案【%s】失敗!", shapefile_path);
return;
}
LogHelper.Info("建立向量檔案成功");
//步驟2、建立空間座標系
OSGeo.OSR.SpatialReference oSRS = new OSGeo.OSR.SpatialReference("");
oSRS.SetWellKnownGeogCS("WGS84");
//步驟3、建立圖層,並新增座標系,建立一個多邊形圖層(wkbGeometryType.wkbUnknown,存放任意幾何特徵)
Layer oLayer = oDS.CreateLayer("地名", oSRS, wkbGeometryType.wkbPoint, null);
if (oLayer == null)
{
Debug.WriteLine("圖層建立失敗!");
return;
} // 步驟4、下面建立屬性表
FieldDefn oFieldPlotArea = new FieldDefn("Name", FieldType.OFTString); // 先建立一個叫PlotArea的屬性
oFieldPlotArea.SetWidth();
// 步驟5、將建立的屬性表新增到圖層中
oLayer.CreateField(oFieldPlotArea, );
//步驟6、定義一個特徵要素oFeature(特徵要素包含兩個方面1.屬性特徵2.幾何特徵)
foreach (var loc in locs)
{
FeatureDefn oDefn = oLayer.GetLayerDefn();
Feature oFeature = new Feature(oDefn); //建立了一個特徵要素並將指向圖層oLayer的屬性表
//步驟7、設定屬性特徵的值
oFeature.SetField(, loc.Name);
OSGeo.OGR.Geometry pt = new Geometry(wkbGeometryType.wkbPoint);
pt.AddPoint(loc.X, loc.Y, );
oFeature.SetGeometry(pt);
//OSGeo.OGR.Geometry geomTriangle = OSGeo.OGR.Geometry.CreateFromWkt(wkt);//建立一個幾何特徵
//步驟8、設定幾何特徵
//oFeature.SetGeometry(geomTriangle);
//步驟9、將特徵要素新增到圖層中
oLayer.CreateFeature(oFeature);
}
oDS.Dispose();
LogHelper.Info("資料集建立完成!");
//Debug.WriteLine("資料集建立完成!");
} } public class Location
{
public double X { get; set; }
public double Y { get; set; }
public string Name { get; set; }
}

(2)由於GDAL只能在x86下使用,將目標平臺編譯成x86。

(3)建立一個空網站,新增Service.svc服務檔案,內容如下:

<%@ ServiceHost Language="C#" Debug="true" Service="CoordService.Coord2ShapefileService" %>

(4)設定web.config的配置檔案

<configuration>
<configSections>
<section name="log4net" type="System.Configuration.IgnoreSectionHandler"/>
</configSections>
<log4net>
<appender name="LogFileAppender" type="log4net.Appender.FileAppender">
<file type="log4net.Util.PatternString" value="%logger\%date{yyyy-MM-dd}\log.txt"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level %message%newline"/>
</layout>
</appender>
<root>
<appender-ref ref="LogFileAppender"/>
</root>
<logger name="Logger" additivity="false">
<level value="All"/>
<appender-ref ref="LogFileAppender"/>
</logger>
</log4net> <system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web> <system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true">
</serviceHostingEnvironment>
<services>
<!--behaviorConfiguration屬性開通服務的元資料釋出服務,無此屬性,服務的元資料釋出無法使用,但服務中的功能可以呼叫-->
<service name="CoordService.Coord2ShapefileService" behaviorConfiguration="metadataExchange">
<!--終結點不加behaviorConfiguration屬性,無法呼叫服務中的功能,會出現“由於 AddressFilter 在 EndpointDispatcher 不匹配”的錯誤-->
<endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpBinding_Default" contract="CoordService.ICoord2Shapefile" behaviorConfiguration="webHttp"/>
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="webHttpBinding_Default" maxReceivedMessageSize="" />
</webHttpBinding>
</bindings>
<behaviors>
<!--開通服務的元資料釋出-->
<serviceBehaviors>
<behavior name="metadataExchange">
<!-- 為避免洩漏元資料資訊,請在部署前將以下值設定為 false -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- 要接收故障異常詳細資訊以進行除錯,請將以下值設定為 true。在部署前設定為 false 以避免洩漏異常資訊-->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webHttp">
<webHttp helpEnabled="True"/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>

(5)建立IIS宿主服務

a.建立應用程式池,目標平臺.net4.0,應用32位應用程式=True,這個很重要,因為服務是32位的。

b.在預設網站下,新增應用程式目標,指向網站目錄。

(6)測試

a.由於座標字串生成shapefile檔案時,前臺給生成傳入的字串比較多,只能使用POST請求,而且還需要在配置檔案中新增如下配置資訊:

      <webHttpBinding>
<binding name="webHttpBinding_Default" maxReceivedMessageSize="" />
</webHttpBinding>

b.使用fiddler測試,選擇Composer頁,見下圖: