不通过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民工,转载请注明原文链接:https://www.cnblogs.com/kook2007/p/17151640.html