最近公司事情太多,都一周没有写博客了,最近在做的事情是做一个将CAD矢量数据按照属性字段导出成shapefile的功能,CAD我不太了解,如果文章中对于CAD矢量数据有错误的地方也请大家指正,互相学习。
要知道导出的方法,那么就应该先了解ArcGIS打开CAD数据的原理,下图是在ArcGIS中打开的一个dwg格式的矢量文件,在CAD中矢量数据是按照点线面注记来分层存储的,当然,这个数据还额外添加了一些矢量图层,每一个矢量图层都可以理解成ArcGIS中的“FeatureClass”。
然后,我们打开某一个图层的属性表观察属性表的结构,我们需要根据制定的一个或多个"Layer"属性将矢量数据导出shp文件,shp文件以POINT/POLYLINE/POLYGON+"Layer"来命名。
明确了需求,设计了下图的界面。选择要素的类型(点线面注记),选择后checkedListBox会列出"Layer"属性的唯一值,根据需要进行勾选,选择输出路径导出。
这个功能主要通过三个方法来实现:
定义类的变量和重载构造函数,在不同的情况下进行初始化:
#region 定义变量
/// <summary>
/// 定义CAD工作空间
/// </summary>
private IWorkspaceFactory pWorkspaceFactory=new CadWorkspaceFactoryClass();
private IFeatureWorkspace pFeaWorkspace;
private string CADPath;
private string SavePath;
private string TypeTag;
IFeatureClassContainer pFeaClassContainer;
#endregion
#region 构造函数
/// <summary>
/// 初始化CADtoShape类(进行转换操作时)
/// </summary>
/// <param name="_CADPath">"*.dwg"文件的路径</param>
public CADtoShape(string _CADPath,string savePath,string _TypeTag)
{
CADPath = _CADPath;
//建立存储点线面的文件夹
int index = CADPath.LastIndexOf("\\");
string name = CADPath.Substring(index + 1);
string path = CADPath.Substring(0, index);
pFeaWorkspace = pWorkspaceFactory.OpenFromFile(path, 0) as IFeatureWorkspace;
SavePath = savePath;//导出要素的路径
TypeTag = _TypeTag;
IFeatureDataset pFeaDataset = pFeaWorkspace.OpenFeatureDataset(name);
pFeaClassContainer = pFeaDataset as IFeatureClassContainer;
}
/// <summary>
/// 初始化CADtoShape类(遍历属性表操作时)
/// </summary>
/// <param name="_CADPath"></param>
public CADtoShape(string _CADPath)
{
CADPath = _CADPath;
//建立存储点线面的文件夹
int index = CADPath.LastIndexOf("\\");
string name = CADPath.Substring(index + 1);
string path = CADPath.Substring(0, index);
pFeaWorkspace = pWorkspaceFactory.OpenFromFile(path, 0) as IFeatureWorkspace;
IFeatureDataset pFeaDataset = pFeaWorkspace.OpenFeatureDataset(name);
pFeaClassContainer = pFeaDataset as IFeatureClassContainer;
}
#endregion
首先,查找"Layer"属性字段的唯一值,返回唯一值的列表:
/// <summary>
/// 查找某一属性的唯一值,返回属性唯一值的列表
/// </summary>
/// <param name="pFeatureClass">待查找的FeatureClass</param>
/// <param name="field">待查找唯一值的字段</param>
/// <returns>返回字段唯一值字符串的唯一值列表</returns>
public List<string> getUniqueValue(IFeatureClass pFeatureClass, string field)
{
string s = pFeatureClass.AliasName;
List<string> uniqueValue = new List<string>();
IFeatureCursor pCursor = pFeatureClass.Search(null, false);
IDataStatistics pDataStat = new DataStatisticsClass();
pDataStat.Field = field;
pDataStat.Cursor = pCursor as ICursor;
IEnumerator pEnumerator = pDataStat.UniqueValues;
pEnumerator.Reset();
while (pEnumerator.MoveNext())
{
uniqueValue.Add(pEnumerator.Current.ToString());
}
return uniqueValue;
}
接着,根据求唯一值返回的字符串列表作为参数创建空的shp文件,返回空shp文件的"FeatureClass":
/// <summary>
/// 根据属性不同建立空的shp文件
/// </summary>
/// <param name="CADfeatureClass">待转换的CAD要素类</param>
/// <param name="ShpPath">存储新建空shp文件的路径</param>
/// <returns>返回创建好的FeatureClass列表</returns>
private List<IFeatureClass> ExportFeatureClassByAtt(IFeatureClass CADfeatureClass, string ShpPath, List<string> FieldName)
{
List<string> AttList = new List<string>();
List<IFeatureClass> feaClassList = new List<IFeatureClass>();
IFeatureClass target_FeatureClass;
ISpatialReferenceFactory spatialReferenceFactory = new SpatialReferenceEnvironmentClass();
string s = CADfeatureClass.ShapeType.ToString();
//定义属性字段
DataManager DM = new DataManager();
IFields pFields = new FieldsClass();
IFieldsEdit pFieldsEdit = (IFieldsEdit)pFields;
IField pField = new FieldClass();
IFieldEdit pFieldEdit = (IFieldEdit)pField;
//设置地理属性字段,点线面,空间参考等
pFieldEdit.Name_2 = "shape";
pFieldEdit.Type_2 = esriFieldType.esriFieldTypeGeometry;
IGeometryDef pGeoDef = new GeometryDefClass();
IGeometryDefEdit pGeoDefEdit = pGeoDef as IGeometryDefEdit;
pGeoDefEdit.GeometryType_2 = CADfeatureClass.ShapeType;
pGeoDefEdit.HasM_2 = true;
pGeoDefEdit.HasZ_2 = true;
ISpatialReference spatialReference = DataManager.getSpatialReference(CADfeatureClass);
pFieldEdit.GeometryDef_2 = pGeoDef;
pFieldsEdit.AddField(pField);
//遍历各字段设置其他属性字段
for (int i = 2; i < CADfeatureClass.Fields.FieldCount; i++)
{
pField = new FieldClass();
pFieldEdit = (IFieldEdit)pField;
pFieldEdit.Name_2 = CADfeatureClass.Fields.Field[i].Name;
pFieldEdit.AliasName_2 = CADfeatureClass.Fields.Field[i].AliasName;
pFieldEdit.Type_2 = CADfeatureClass.Fields.Field[i].Type;
pFieldsEdit.AddField(pField);
}
AttList = getUniqueValue(CADfeatureClass, "Layer");
int n = FieldName.Count;
for (int i = 0; i < n; i++)
{
//建立空的shapefile
target_FeatureClass = DM.CreateVoidShp(ShpPath, FieldName[i], pFields, spatialReference);
feaClassList.Add(target_FeatureClass);
}
return feaClassList;
}
最后,往这些空的FeatureClass中写入图形和属性:
/// <summary>
/// 复制featureclass图形和属性的方法
/// </summary>
/// <param name="name">待创建要素类的名称</param>
/// <param name="target_FeatureClass">需要写入图形和属性的空要素</param>
/// <param name="CADFeatureClass">需要复制的CAD要素类</param>
public void CreateFeatureClassByAtt(string name,IFeatureClass target_FeatureClass,IFeatureClass CADFeatureClass)
{
List<string> AttList = new List<string>();
IQueryFilter pQueryFilter = new QueryFilterClass();
IFeatureCursor pFeaCursor;
IFeature pFeature;
IFeatureBuffer pFeaBuffer = null;
AttList = getUniqueValue(CADFeatureClass, "Layer");
pQueryFilter.WhereClause = "Layer = '" + name.Substring(name.IndexOf('-')+1) + "'";
pFeaCursor = CADFeatureClass.Search(pQueryFilter, false);
//用IFeatureBuffer提高运算速度
IFeatureCursor targetCursor = target_FeatureClass.Insert(true);
while ((pFeature = pFeaCursor.NextFeature()) != null)
{
pFeaBuffer = target_FeatureClass.CreateFeatureBuffer();
IGeoDataset pGeoDataset = pFeature.Class as IGeoDataset;
pFeaBuffer.Shape = pFeature.Shape;
for (int j = 2; j < CADFeatureClass.Fields.FieldCount; j++)
{
try
{
pFeaBuffer.set_Value(j, pFeature.Value[CADFeatureClass.FindField(target_FeatureClass.Fields.Field[j].Name)]);
}catch
{
continue;
}
}
targetCursor.InsertFeature(pFeaBuffer);
targetCursor.Flush();
}
}
将上述三个方法集成,在前端直接调用下面的方法即可:
/// <summary>
/// 将CAD的"*.dwg"文件根据要素类型(点线面)和选择的唯一值属性,转换为相应名称的FeatureClass
/// </summary>
public void CADLayerToFeatureClass(List<string> FeatureClassNameList)
{
List<IFeatureClass> featureClassList = new List<IFeatureClass>();
//存在不同要素类型但是"Layer"属性唯一值相同的情况,需要进行区分
List<string> fileNameList = new List<string>();
for(int i = 0; i < FeatureClassNameList.Count; i++)
{
fileNameList.Add(String.Concat(TypeTag.ToUpper(),"-", FeatureClassNameList[i]));
}
IFeatureClass CADFeatureClass;
int n = FeatureClassNameList.Count;
int x = pFeaClassContainer.ClassCount;
CADFeatureClass = pFeaClassContainer.ClassByName[TypeTag];
try
{
featureClassList = ExportFeatureClassByAtt(CADFeatureClass, SavePath, fileNameList);
for (int i = 0; i < n; i++)
{
CreateFeatureClassByAtt(fileNameList[i], featureClassList[i], CADFeatureClass);
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
完整的代码已上传至github,欢迎大家交流学习:https://github.com/ranhongwu/190717DataSwitch 。