不通過ArcGIS寫Personal Geodatabase(esri mdb)
Personal Geodatabse格式為mdb,是esri公司對Access做了擴展,由于既支持空間數據,也支持關系數據,在數據交換中,應用起來比較方便。由于PGeo并不沒有對外公開的接口,想要直接操作起來并非易事,網上關于CAD格式數據寫到shapefile,或者shapefile寫到FileGDB的方式很多,也有很多著名的工具,如GDAL、NetTopologySuite等,GDAL只支持PGeo的讀取,早期版本對中文支持也不好,很多時候涉及到空間數據交換時,大家都采用shapefile文件共享。通過分析PGeo格式mdb,分析存儲空間要素的關鍵,采用硬寫的方式實現shapefile數據寫入mdb中,以ArcGIS 9.3格式mdb為例:
mdb中關于空間數據的關鍵表有:GDB_FeatureDataset(要素集名稱及空間參考)、GDB_FeatureClasses(要素類)、GDB_FieldInfo(要素的字段定義)、GDB_GeomColumns(記錄各個要素類存儲幾何信息的字段名稱、幾何類型、空間參考等)、GDB_ObjectClasses(存儲要素類名稱及所屬的要素數據集ID)、GDB_SpatialRefs(空間參考),對于已有模板的,最重要的是GDB_FeatureDataset、GDB_GeomColumns、GDB_SpatialRefs,要設置三個表的空間參考(需要對應),設置GDB_GeomColumns中的Extent、GridSize和偏移量。另外就是所寫圖層的表以及對應的_shape_index表(圖層空間要素的對應關系表)。
寫記錄到圖層,從datatable中獲取對應的要素屬性
/// <summary>
/// 更新要素表數據(屬性表以DataTable交換)
/// </summary>
/// <param name="dbfPath">dbf文件路徑</param>
/// <param name="tableName">mdb圖層表名</param>
/// <param name="propertyDataTable">屬性表DataTable</param>
/// <param name="propertyKey">屬性表唯一字段名稱(供比較使用)</param>
/// <param name="dbfKey">dbf唯一字段名稱(供比較用)</param>
/// <returns></returns>
public bool InsertFeatureClassWithProperty(string dbfPath, string tableName, DataTable propertyDataTable,string propertyKey,string dbfKey)
{
DbfFile odbf = new DbfFile(Encoding.UTF8);
odbf.Open(dbfPath, FileMode.Open,FileAccess.Read,FileShare.ReadWrite);
var orec = new DbfRecord(odbf.Header);
int count = 0;
for (int i = 0; i < odbf.Header.RecordCount; i++)
{
if (!odbf.Read(i, orec))
break;
string condition = string.Format(@"{0}=""{1}""",propertyKey, orec[dbfKey]);
OleDbDataAdapter adapter = mdbMgr.GetOleDbDataAdapter(tableName, condition);
DataTable dataTable = new DataTable();
adapter.Fill(dataTable);
if (dataTable.Rows.Count == 0)
{
foreach (DataRow dataRow in propertyDataTable.Rows)
{
if ((string)dataRow[propertyKey] == orec[dbfKey])
{
DataRow row = dataTable.NewRow();
row["Shape_Length"] = orec["SHAPE_LEN"].Trim();
row["Shape_Area"] = orec["SHAPE_AREA"].Trim();
row["SHAPE"] = shpFile.shpRecords[i].bytes;
for (int j = 0; j < propertyDataTable.Columns.Count; j++)
row[propertyDataTable.Columns[j].ColumnName] = dataRow[j];
dataTable.Rows.Add(row);
if (adapter.Update(dataTable) == 1)
count++;
string sqlString = string.Format("select ObjectID from {0} where {1} ", tableName,condition);
OleDbCommand cmd = new OleDbCommand(sqlString, mdbMgr.mConn);
int indexedObjectId = (int)cmd.ExecuteScalar();
//要素的最小外接矩形
double[] MBR = shpFile.shpRecords[i].MBR;
Tuple<double, double, double, double> featureExtent = new Tuple<double, double, double, double>(MBR[0], MBR[1], MBR[2], MBR[3]); //改成shprecord.mbr
InsertShapeIndex(tableName,indexedObjectId, featureExtent);
break;
}
}
}
}
odbf.Close();
return count == odbf.Header.RecordCount;
}
一定要處理_shape_index表,否則arcgis打開后,屬性表有記錄,但無法定位和選中,那是因為空間關系不對造成的,gridsize可以設置為420,
/// <summary>
/// 寫要素空間關聯表
/// </summary>
/// <param name="tableName"></param>
/// <param name="indexedObjectId"></param>
/// <param name="extent"></param>
public void InsertShapeIndex(string tableName, int indexedObjectId, Tuple<double, double, double, double> extent)
{
/*按分析,四個值計算公式應該是。但上述IdxOrginX和IdxOriginY都設置成0了。
MinGX = (要素X最小值(MinX) - X方向偏移量(IdxOriginX)) / 格網尺寸(IdxGridSize);
MinGY = (要素Y最小值(MinY) - Y方向偏移量(IdxOriginY)) / 格網尺寸(IdxGridSize);
MaxGX = (要素X最大值(MaxX) - X方向偏移量(IdxOriginX)) / 格網尺寸(IdxGridSize);
MaxGY = (要素Y最大值(MaxY) - Y方向偏移量(IdxOriginY)) / 格網尺寸(IdxGridSize);
*/
//extent取整問題,實際上應該都是整數,
//采用math.floor返回最大的小于或等于的整數,采用math.celling返回最小大于等于的整數,兩者返回類型都是double
double minGX = Math.Floor(extent.Item1 / gridSize);
double minGY = Math.Floor(extent.Item2 / gridSize);
double maxGX = Math.Ceiling(extent.Item3 / gridSize);
double maxGY = Math.Ceiling(extent.Item4 / gridSize); ;
tableName += "_SHAPE_Index";
string sqlString = string.Format("insert into {0} (IndexedObjectId,MinGX,MinGY,MaxGX,MaxGY) " +
"values({1},{2},{3},{4},{5})", tableName, indexedObjectId, minGX, minGY, maxGX, maxGY);
OleDbCommand cmd = new OleDbCommand(sqlString, mdbMgr.mConn);
cmd.ExecuteNonQuery();
}
空間參考的更新
public void UpdateGDB_GeomColumnsSRID(int srid,string tableName)
{
double[] MBR = shpFile.shpHeader.MBR;
// 圖形的最小外接矩形
Tuple<double, double, double, double> layerExtent = new Tuple<double, double, double, double>(MBR[0], MBR[1], MBR[2], MBR[3]);
string sqlString = string.Format("update GDB_GeomColumns set SRID ='{0}',ExtentLeft={1},ExtentBottom={2},ExtentRight={3},ExtentTop={4},IdxOriginX=0,IdxOriginY=0,IdxGridSize={5} where TableName='{6}' ",
srid,layerExtent.Item1, layerExtent.Item2, layerExtent.Item3, layerExtent.Item4,gridSize, tableName);
OleDbCommand cmd = new OleDbCommand(sqlString, mdbMgr.mConn);
cmd.ExecuteNonQuery();
}
寫mdb的關鍵:
- 1、正確通過.shp文件獲取圖層的extent和每個要素的extent,該值是寫GDB_GeomColumns和_Shape_Index的關鍵
- 2、每插入一條要素,就要寫一條對應的_shape_index記錄,_shape_index的值要根據格網大小和偏移量計算,且注意Min和Max取值
- 3、要素、要素集等空間參考要對應
關于shapefile文件的數據存儲原理,請轉https://baike.baidu.com/item/shapefile%E6%96%87%E4%BB%B6
關于shapefile各文件的解析請轉https://github.com/YanzheZhang/SHPReaderCSharp
本文來自博客園,作者:GIS民工,轉載請注明原文鏈接:http://www.rzrgm.cn/kook2007/p/17151640.html

浙公網安備 33010602011771號