获取宗地内所有房屋占地面积主要步骤:
获取当前AutoCAD应用中的活动文档、数据库和编辑器对象,以便进行交互操作。
创建一个外部多段线的选择过滤器outerFilter
,限制用户只能选择图层为"宗地"上的LWPOLYLINE对象作为外部边界。
提示用户根据设定的过滤器规则选择实体,并获取用户的选择结果。
如果用户成功选择了实体,则开启事务处理,确保数据一致性。
遍历所有被选中的外部多段线,对每个多段线执行以下操作: a. 确认多段线闭合且至少有一个顶点。 b. 将多段线的所有顶点坐标收集到outerPoints
集合中。 c. 创建一个窗口选择过滤器innerFilter
,用于让用户选择位于外部多段线内部的其他LWPOLYLINE实体。 d. 使用多边形窗口方式让用户进行内部实体的选择,并获取选择结果。
计算房屋面积(areaFW
)、房屋附属面积(areaFWFS
)及总面积(total_area
),并初始化这三个变量为0。
针对用户在内部区域成功选择的每个闭合多段线,检查实体的XData信息以判断其类型,并据此计算相应的面积:
最后,在循环结束后输出累计得到的总面积。
完整代码
[CommandMethod("GetArea")]
public void GetArea()
{
// 获取当前AutoCAD应用中的活动文档、数据库和编辑器对象
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
// 创建一个选择过滤器,限制用户只能选择"宗地"图层上的LWPOLYLINE对象作为外部边界
SelectionFilter outerFilter = new SelectionFilter(new TypedValue[] {
new TypedValue((int)DxfCode.Start, "LWPOLYLINE"),
new TypedValue((int)DxfCode.LayerName, "宗地")
});
// 提示用户根据上述规则进行实体选择,并获取选择结果
PromptSelectionResult outerSelRes = ed.GetSelection(outerFilter);
// 检查用户是否成功选择了实体
if (outerSelRes.Status == PromptStatus.OK)
{
using (Transaction tr = db.TransactionManager.StartTransaction())// 开始事务处理以确保数据一致性
{
foreach (ObjectId outerId in outerSelRes.Value.GetObjectIds())// 遍历所有被选中的外部多段线
{
using (Polyline outerPolyline = (Polyline)tr.GetObject(outerId, OpenMode.ForRead))// 读取所选多段线
{
// 确保所选多段线是闭合的且至少有一个顶点
if (outerPolyline.Closed && outerPolyline.NumberOfVertices > 0)
{
// 创建并填充一个表示外部多段线边界坐标的点集合
Point3dCollection outerPoints = new Point3dCollection();
for (int i = 0; i < outerPolyline.NumberOfVertices; i++)
{
Point3d point = outerPolyline.GetPoint3dAt(i);
outerPoints.Add(point);
}
// 创建一个窗口选择过滤器,用于选择位于外部多段线内的所有实体
SelectionFilter innerFilter = new SelectionFilter(new TypedValue[] {
new TypedValue((int)DxfCode.Start, "LWPOLYLINE")
});
// 使用多边形窗口选择方式让用户选择位于外部多段线内的实体
PromptSelectionResult innerSelRes = ed.SelectWindowPolygon(outerPoints, innerFilter);
double areaFW = 0;//定义房屋面积
double areaFWFS = 0;//房屋附属
double total_area = 0;//总面积
// 检查用户是否成功在内部区域进行了实体选择
if (innerSelRes.Status == PromptStatus.OK)
{
SelectionSet innerSelectionSet = innerSelRes.Value;
// 遍历用户在内部区域所选的所有闭合多段线
foreach (ObjectId id2 in innerSelectionSet.GetObjectIds())
{
using (Polyline polyline2 = (Polyline)tr.GetObject(id2, OpenMode.ForRead))
{
if (polyline2.Closed && polyline2.NumberOfVertices > 0)
{
//ed.WriteMessage($"\n内部多段线 {id2} 内部的总多边形面积为: {area.ToString("0.00")}");
Entity ent = tr.GetObject(id2, OpenMode.ForRead) as Entity;
if (ent != null && ent.XData != null)
{
// 如果实体有XData,使用ResultBuffer类将其获取到,并将结果赋给变量'rb'。
ResultBuffer rb = ent.XData;
//将ResultBuffer的内容转换为TypedValue数组,并将其赋给变量'values'
TypedValue[] values = rb.AsArray();
// 141121 砖
// 141161 混
// 141151 砖
if (values[1].Value.ToString() == "141161" || values[1].Value.ToString() == "141121" || values[1].Value.ToString() == "141151")
{
if (values[2].Value != null)
{
areaFW = polyline2.Area;
int area2 = Convert.ToInt32(values[2].Value);
areaFW = areaFW * area2;
ed.WriteMessage($"\n层数 {area2} 层 面积为: {areaFW.ToString("0.00")} 平方米");
total_area += areaFW;
}
}
// 141800 飘楼 140001 阳台 143130 //143112
if (values[1].Value.ToString() == "143130" || values[1].Value.ToString() == "143112" || values[1].Value.ToString() == "140001" || values[1].Value.ToString() == "141800")
{
TypedValue[] tvs = new TypedValue[]
{
new TypedValue((int)DxfCode.Operator, "")
};
SelectionFilter sf = new SelectionFilter(tvs);
PromptSelectionResult psr = ed.SelectAll(sf);
if (psr.Status == PromptStatus.OK)
{
SelectionSet ss = psr.Value;
foreach (SelectedObject so in ss)
{
DBText text = tr.GetObject(so.ObjectId, OpenMode.ForRead) as DBText;
if (IsPointInside(polyline2, text.Position))
{
areaFWFS = polyline2.Area;
string input = text.TextString;
string[] parts = input.Split(' ');
int count = CountCharacterOccurrences(input, "Q");
if (count > 0)
{
areaFWFS = areaFWFS * count;
}
count = CountCharacterOccurrences(input, "B");
if (count > 0)
{
areaFWFS = (areaFWFS / 2) * count;
}
ed.WriteMessage($"\n 附属面积为: {areaFWFS.ToString("0.00")} 平方米");
total_area += areaFWFS;
}
}
}
}
}
}
}
}
ed.WriteMessage($"\n 总面积为: {total_area.ToString("0.00")} 平方米");
}
}
}
}
}
}
}
// 定义一个方法,输入参数为一个多段线对象和一个三维点,返回值为布尔类型,表示该点是否在多段线内部
public bool IsPointInside(Polyline polyline, Point3d point)
{
// 初始化交叉次数变量为0,用于记录点与多段线各线段相交的次数
int crossings = 0;
// 遍历多段线的所有顶点,从第一个顶点开始到最后一个顶点
for (int i = 0; i < polyline.NumberOfVertices; i++)
{
// 获取当前线段的起点坐标
Point3d start = polyline.GetPoint3dAt(i);
// 计算下一个顶点的索引,并使用取模运算确保最后一个顶点后回到第一个顶点形成闭合循环
int nextIndex = (i + 1) % polyline.NumberOfVertices;
Point3d end = polyline.GetPoint3dAt(nextIndex);
// 如果线段两端点都在检测点Y轴上方或下方,则此线段与过检测点的水平线不相交,跳过此次循环
if (start.Y > point.Y && end.Y > point.Y)
continue;
if (start.Y <= point.Y && end.Y <= point.Y)
continue;
// 如果检测点X坐标小于线段起点和终点的X坐标最小值,则此线段位于检测点左侧,跳过此次循环
if (point.X < Math.Min(start.X, end.X))
continue;
// 计算线段的斜率,并根据直线方程计算线段与过检测点Y坐标水平线的交点横坐标
double slope = (end.Y - start.Y) / (end.X - start.X);
double intersectX = start.X + (point.Y - start.Y) / slope;
// 如果检测点X坐标大于等于交点横坐标,则表示检测点在线段的一侧,增加交叉次数
if (point.X >= intersectX)
crossings++;
}
// 根据奇偶性判断:若交叉次数为奇数,则认为点在多段线内;否则点在多段线外
return (crossings % 2) == 1;
}
//包含字符 出现次数
public static int CountCharacterOccurrences(string str, string substring)
{
if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(substring))
return 0;
int index = 0, count = 0;
while ((index = str.IndexOf(substring, index)) != -1)
{
count++;
index += substring.Length; // 移动到下一个可能的位置
}
return count;
}
缺陷 跟宗地交集部分 面积无法算出,房屋结构0层无法算出,房屋及房屋附属不是封闭线段无法算出面积(通过修改房屋属性改变房屋结构层数)~代码只能参考。