程序编辑SHP文件并应用更改到数据源
在上一篇Blog中峻祁连介绍了在Map 3D中通过程序删除图层及数据源的方法,并且卖了个关子,这个方法还有另外一个妙用,今天就简单介绍一下。对数据源的编辑估计是Map 3D开发中最常见的功能了,包括对添加、删除和修改要素。这里以删除要素为例介绍。下面的代码实现了使用Map 3D API来删除地图上选中的要素。
[CommandMethod("DeleteSelectedFeatures", CommandFlags.UsePickSet | CommandFlags.Modal | CommandFlags.Redraw)] public void DeleteSelectedFeatures() { Editor ed = Autodesk.AutoCAD.ApplicationServices .Application.DocumentManager.MdiActiveDocument.Editor; MgFeatureService _featureService = AcMapServiceFactory .GetService(MgServiceType.FeatureService) as MgFeatureService; PromptSelectionResult res = ed.SelectImplied(); if (PromptStatus.OK == res.Status) { // Convert the SelectionSet to MgSelectionBase // using AcMapFeatureEntityService. Debug.Assert(res.Value != null); ObjectId entityID = res.Value[0].ObjectId; int entitytype = AcMapFeatureEntityService.GetEntityType(entityID); if (entitytype == EntityType.BulkEntity) { MgSelectionBase curSelection = AcMapFeatureEntityService .GetSelection(res.Value); foreach (MgLayerBase layer in curSelection.GetLayers()) { string filter = curSelection .GenerateFilter(layer, layer.FeatureClassName); if (filter != "") { MgFeatureCommandCollection featCommands = new MgFeatureCommandCollection(); string strclassName = layer.FeatureClassName; MgDeleteFeatures delFeat = new MgDeleteFeatures(strclassName, filter); featCommands.Add(delFeat); try { AcMapLayer aclayer = layer as AcMapLayer; MgPropertyCollection pProps = aclayer .UpdateFeatures(featCommands); // save and update the layer, commit the changes,
// 注意这里,必须要通过下面的代码来checkin来应用对数据源的更改 MgFeatureQueryOptions opt = new MgFeatureQueryOptions(); // build the filter string, this is required for // data source such as Sql Server/Oracle... String commitFilter = ""; if (aclayer.IsCached() && EditMode.EditSet == aclayer.EditMode) { commitFilter = ClassSystemProperties.FeatureStatus + " = " + ClassSystemProperties.FeatureStatusDeleted; } opt.SetFilter(commitFilter); //This is a must aclayer.SaveFeatureChanges(opt); aclayer.ForceRefresh(); } catch (Autodesk.AutoCAD.Runtime.Exception ex) { ed.WriteMessage(ex.Message); } } } //remove the highlight AcMapFeatureEntityService.UnhighlightFeatures(curSelection); } } }
下面是执行时的截图,首先用鼠标选中一些要素,然后执行自定义命令DeleteSelectedFeatures
执行结束后,选用的要素被成功删除。
这个例子中我使用的FDO Provider for SHP连接到SHP文件。下面是上面自定义命令执行之前,注意一下shp相关文件的日期。
下面是DeleteSelectedFeatures执行完毕后,注意到Shp文件的相关变化,其中*.dbf文件是日期发在了变化,但*.shp文件并没有变化。即上面的代码并没有对*.shp进行更改。
也许有人会说,这是个Map 3D的bug,他们的证据就是这时在ArcGIS中打开shp文件,发现刚才在Map 3D中删除的要素还是ArcGIS中显示,好像并没有被删除。其实不然。根据DBF规范,DBF保存属性信息和删除标志,SHP文件也采用了这个DBF规范。shp文件中被删除的geometry只在DBF中做删除标记。Map 3D的确对DBF文件做了更改,即按照规范添加了删除标志。但不知道为什么ArcGIS并没有遵守DBF规范,而忽略了该删除标志,所以在ArcGIS查看时中你会认为该要素没有删除。下面是DBF规范相关点的摘录:
http://www.dbf2002.com/dbf-file-format.html
Note The data in dbf file starts at the position indicated in bytes 8 to 9 of the header record. Data records begin with a delete flag byte. If this byte is an ASCII space (0x20), the record is not deleted. If the first byte is an asterisk (0x2A), the record is deleted. The data from the fields named in the field subrecords follows the delete flag.
尽管不是Map 3D的错,但ArcGIS作为业内占统治地位的GIS软件,我们也得迁就一下,有什么办法在Map 3D中更改的结果也能让ArcGIS显示出来呢? 答案就是不但要按照DBF规范添加删除标记,同时还要压缩shp文件。目前在Map 3D中还没有这样的API来压缩shp文件来反应要素变化,不过可以用下面的workaround,就是关闭到shp文件的连接(必要时再按照BuildMap中的例子再创建连接)。当FDO连接关闭时,Map 3D会压缩shp文件,从而让ArcGIS也能检验到变化。压缩shp文件这个过程比较耗时,这也是Map 3D只是在关闭连接时才做这一步的原因。如果你要求你在Map 3D中的更改能更快的体现在ArcGIS中,就需要手动来关闭连接让Map 3D强制压缩shp。
关闭连接的代码我们已经上一篇博客Map 3D中通过程序删除图层及数据源中介绍过了。这里还是把代码贴一下:
[CommandMethod("CloseConnection")] public void CloseConnectionForLayer() { RemoveLayer("Layer1"); } public void RemoveLayer(string layerName) { Document doc = Application.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; Database db = doc.Database; AcMapMap map = AcMapMap.GetCurrentMap(); // remove the layer var layers = map.GetLayers(); if (!layers.Contains(layerName)) { ed.WriteMessage("\nLayer does not exist: " + layerName); return; } MgLayerBase layer = layers.GetItem(layerName); layers.Remove(layer); // remove the layer resource MgResourceIdentifier identifier = layer.LayerDefinition; MgResourceService resourceService = AcMapServiceFactory.GetService(MgServiceType.ResourceService) as MgResourceService; if (resourceService.ResourceExists(identifier)) resourceService.DeleteResource(identifier); // remove the feature source identifier = new MgResourceIdentifier(layer.FeatureSourceId); if (resourceService.ResourceExists(identifier)) resourceService.DeleteResource(identifier); }
上面的代码会从地图中删除图层,并且关闭下图中的DataConnection。
执行完毕,你会发现shp文件的日期已经反正了变化,这是在arcGIS中查看就可以看到修改后的效果了。
好了,希望对你有帮助。