最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

环球微动态丨不通过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中获取对应的要素属性

///         /// 更新要素表数据(属性表以DataTable交换)        ///         /// dbf文件路径        /// mdb图层表名        /// 属性表DataTable        /// 属性表唯一字段名称(供比较使用)        /// dbf唯一字段名称(供比较用)        ///         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 featureExtent = new Tuple(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,

///         /// 写要素空间关联表        ///         ///         ///         ///         public void InsertShapeIndex(string tableName, int indexedObjectId, Tuple 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 layerExtent = new Tuple(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

关键词: 空间数据 空间关系