Вычисление общей и полезной площади пверхности стен
Вопрос: мне нужно определить площадь, занимаемая окнами и дверями в стене. Я думал это можно легко сделать просто, взяв значение параметра HOST_AREA_COMPUTED, так как при первоначальном тестировании значение параметра показывало верную цифру. Но, при дальнейшем тестирование это оказалось не так.
В тестовом файле есть стена с двумя дверями. Внешне они выглядят одинаково, но для одной площадь равна 4м2, для другой – 2м2.
Почему так? Что же на самом деле возвращает параметр HOST_AREA_COMPUTED? Какой самый правильный способ определения площади окон и дверей в стене?
Ответ: не могу сказать ничего конкретного по поводу того, какой же значение находится в этом параметре и как он рассчитывается. На сколько мне известно, его значение видно в пользовательском интерфейсе, и возможно будет лучше спросить у проектировщиков, что они знают о значении этого параметра.
Что касается API, я полагаю вы знаете о примере из Revit SDK для работы с материалами.
В этих примерах нет того, что вам нужно?
Ответ: Спасибо за ответ и за ссылку.
Я попробую пояснить что же все-таки хочу получить. Я хочу найти площадь поверхностей стен, которые являются границами помещений. Для этого я решил воспользоваться классом SpatialElementGeometryCalculator, так как он корректно считает площадь стен, перекрытий и потолков, даже если они наклонены. Но проблема в том, что этот класс не вычитает из итоговой площади площадь окон и стен.
Я знаю, что двери и окна связаны с помещением, точно так же, как и стена. Поэтому я мог бы получить площадь стен за вычетом проемов, затем временно удалить окна и двери, снова посчитать площадь. Разница между двумя значениями и будет являться площадью, занимаемой дверями и окнами.
Ответ: Идея выглядит интересной. На сайте The Building Coder пока нет подобных примеров, за исключением сложной статьи о расчете тепловой нагрузки (на англ.)
Ответ: Спасибо за ссылку. Хорошая новость в том, что временное удаление объектов вроде работает как надо. Но я так и не понял, почему значение параметра Площадь все же различно для похожих элементов, хотя это больше и не имеет значение.
Я посмотрел ваш пример. Это не совсем то, что мне нужно, так как мне нужно считать именно площадь поверхности стены, граничащей с помещением, а стена может быть границей нескольких помещений.
Поэтому мы и решили использовать класс SpatialElementGeometryCalculator, с которым после нескольких неудачных попыток все же удалось получить требуемый результат. И я буду рад поделиться результатом.
Ответ: Я думаю, что в некоторых случаях при расчете площади двери что-то считается дважды.
Безусловно, SpatialElementGeometryCalculator более мощный и более подходит в вашем случае.
Ответ: Вот пример код по вычислению поверхностей стен минус двери и окна.
Код на VB.NET, но при необходимсоти можно его конвертировать в C#.
- Imports Autodesk.Revit.ApplicationServices
- Imports System.IO
- Imports Autodesk.Revit.DB.Architecture
- Imports System.Collections.Generic
- Imports System.Diagnostics
- Imports Autodesk.Revit.Attributes
- Imports Autodesk.Revit.DB
- Imports BoundarySegment = Autodesk.Revit.DB.BoundarySegment
- Imports Autodesk.Revit.DB.ExternalService
- Imports Autodesk.Revit.UI
- <Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)> _
- <Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)> _
- <Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)> _
- Public Class extCmd
- Implements IExternalCommand
- Public Function Execute(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Result Implements IExternalCommand.Execute
- Try
- Dim app As Autodesk.Revit.UI.UIApplication = commandData.Application
- Dim doc As Document = app.ActiveUIDocument.Document
- Dim roomCol As FilteredElementCollector = New FilteredElementCollector(app.ActiveUIDocument.Document).OfClass(GetType(SpatialElement))
- Dim s As String = "Finished populating Rooms with Boundary Data" + vbNewLine + vbNewLine
- For Each e As SpatialElement In roomCol
- Dim room As Room = TryCast(e, Room)
- If room IsNot Nothing Then
- Try
- Dim spatialElementBoundaryOptions As New SpatialElementBoundaryOptions()
- spatialElementBoundaryOptions.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish
- Dim calculator1 As New Autodesk.Revit.DB.SpatialElementGeometryCalculator(doc, spatialElementBoundaryOptions)
- Dim results As SpatialElementGeometryResults = calculator1.CalculateSpatialElementGeometry(room)
- ' get the solid representing the room's geometry
- Dim roomSolid As Solid = results.GetGeometry()
- Dim iSegment As Integer = 1
- For Each face As Face In roomSolid.Faces
- Dim subfaceList As IList(Of SpatialElementBoundarySubface) = results.GetBoundaryFaceInfo(face)
- For Each subface As SpatialElementBoundarySubface In subfaceList
- If subface.SubfaceType = SubfaceType.Side Then ' only interested in walls in this example
- Dim ele As Element = doc.GetElement(subface.SpatialBoundaryElement.HostElementId)
- Dim subfaceArea As Double = subface.GetSubface().Area
- Dim netArea As Double = sqFootToSquareM(calwallAreaMinusOpenings(subfaceArea, ele, doc, room))
- s += "Room " + room.Parameter(BuiltInParameter.ROOM_NUMBER).AsString + " : Wall " + ele.Parameter(BuiltInParameter.DOOR_NUMBER).AsString + " : Area " + netArea.ToString + "m2" + vbNewLine
- End If
- Next
- Next
- s += vbNewLine
- Catch ex As Exception
- End Try
- End If
- Next
- Return Result.Succeeded
- Catch ex As Exception
- ex.StackTrace.ToString)
- Return Result.Failed
- End Try
- End Function
- Private Function calwallAreaMinusOpenings(ByVal subfaceArea As Double, ByVal ele As Element, ByVal doc As Document, ByVal room As Room) As Double
- Dim fiCol As FilteredElementCollector = New FilteredElementCollector(doc).OfClass(GetType(FamilyInstance))
- Dim lstTotempDel As New List(Of ElementId)
- 'Now find the familyInstances that are associated to the current room
- For Each fi As FamilyInstance In fiCol
- If fi.Parameter(BuiltInParameter.HOST_ID_PARAM).AsValueString = ele.Id.ToString Then
- If fi.Room IsNot Nothing Then
- If fi.Room.Id = room.Id Then
- lstTotempDel.Add(fi.Id)
- Continue For
- End If
- End If
- If fi.FromRoom IsNot Nothing Then
- If fi.FromRoom.Id = room.Id Then
- lstTotempDel.Add(fi.Id)
- Continue For
- End If
- End If
- If fi.ToRoom IsNot Nothing Then
- If fi.ToRoom.Id = room.Id Then
- lstTotempDel.Add(fi.Id)
- Continue For
- End If
- End If
- End If
- Next
- If lstTotempDel.Count > 0 Then
- Dim t As New Transaction(doc, "tmp Delete")
- Dim wallnetArea As Double = ele.Parameter(BuiltInParameter.HOST_AREA_COMPUTED).AsDouble
- t.Start()
- doc.Delete(lstTotempDel)
- doc.Regenerate()
- Dim wallGrossArea As Double = ele.Parameter(BuiltInParameter.HOST_AREA_COMPUTED).AsDouble
- t.RollBack()
- Dim fiArea As Double = wallGrossArea - wallnetArea
- Return subfaceArea - fiArea
- Else
- Return subfaceArea
- End If
- End Function
- Private Function sqFootToSquareM(ByVal sqFoot As Double) As Double
- Return Math.Round(sqFoot * 0.092903, 2)
- End Function
- End Class
Ответ: Спасибо за код.
Я создал проект на GitHub и поместил туда обе версии: на C# и VB.NET.
В проекте я сделал несколько изменений:
Использовал сравнение AsElementId().IntegerValue.Equals( Id.IntegerValue ) вместо сравнения AsValueString и ele.Id.ToString, так как сравнение чисел работает значительно быстрей, чем сравнение строк.
Использовал фильтр по параметру HOST_ID_PARAM вместо прямого сравнения идентификаторов элементов.
Эти улучшения позволят более быстро и эффективно работать с большими моделями.
В результате код на C# получился таким:
- [Transaction( TransactionMode.Manual )]
- public class Command : IExternalCommand
- {
- /// <summary>
- /// Конвертация квадратных футов в квадратные метры.
- /// </summary>
- double sqFootToSquareM( double sqFoot )
- {
- return Math.Round( sqFoot * 0.092903, 2 );
- }
- /// <summary>
- /// Вычисление площади стен, за исключением проемов.
- /// Временно удаляем двери и окна в транзакции, которую затем откатываем.
- /// </summary>
- /// <param name="subfaceArea">Первоначальная площадь поверхности стены</param>
- /// <param name="wall"></param>
- /// <param name="doc"></param>
- /// <param name="room"></param>
- /// <returns></returns>
- double calwallAreaMinusOpenings(
- double subfaceArea,
- Element wall,
- Room room )
- {
- Document doc = wall.Document;
- Debug.Assert(
- room.Document.ProjectInformation.UniqueId.Equals(
- doc.ProjectInformation.UniqueId ),
- "expected wall and room from same document" );
- // Определяем все проемы в стене.
- FilteredElementCollector fiCol
- = new FilteredElementCollector( doc )
- .OfClass( typeof( FamilyInstance ) );
- List<ElementId> lstTotempDel
- = new List<ElementId>();
- foreach( FamilyInstance fi in fiCol )
- {
- // Экземпляры семейств, располагающихся на стене
- // Могут быть более эффективно получены
- // с помощью фильтра по параметру
- // На больших моделях, это особенно эффективно
- if( fi.get_Parameter(
- BuiltInParameter.HOST_ID_PARAM )
- .AsElementId().IntegerValue.Equals(
- wall.Id.IntegerValue ) )
- {
- if( ( fi.Room != null )
- && ( fi.Room.Id == room.Id ) )
- {
- lstTotempDel.Add( fi.Id );
- }
- else if( ( fi.FromRoom != null )
- && ( fi.FromRoom.Id == room.Id ) )
- {
- lstTotempDel.Add( fi.Id );
- }
- else if( ( fi.ToRoom != null )
- && ( fi.ToRoom.Id == room.Id ) )
- {
- lstTotempDel.Add( fi.Id );
- }
- }
- }
- // Определяем общую площадь проемов
- double openingArea = 0;
- if( 0 < lstTotempDel.Count )
- {
- Transaction t = new Transaction( doc );
- double wallAreaNet = wall.get_Parameter(
- BuiltInParameter.HOST_AREA_COMPUTED )
- .AsDouble();
- t.Start( "tmp Delete" );
- doc.Delete( lstTotempDel );
- doc.Regenerate();
- double wallAreaGross = wall.get_Parameter(
- BuiltInParameter.HOST_AREA_COMPUTED )
- .AsDouble();
- t.RollBack();
- openingArea = wallAreaGross - wallAreaNet;
- }
- return subfaceArea - openingArea;
- }
- public Result Execute(
- ExternalCommandData commandData,
- ref string message,
- ElementSet elements )
- {
- UIApplication app = commandData.Application;
- Document doc = app.ActiveUIDocument.Document;
- SpatialElementBoundaryOptions sebOptions
- = new SpatialElementBoundaryOptions();
- sebOptions.SpatialElementBoundaryLocation
- = SpatialElementBoundaryLocation.Finish;
- Result rc;
- try
- {
- FilteredElementCollector roomCol
- = new FilteredElementCollector( doc )
- .OfClass( typeof( SpatialElement ) );
- string s = "Finished populating Rooms with "
- + "Boundary Data\r\n\r\n";
- foreach( SpatialElement e in roomCol )
- {
- Room room = e as Room;
- if( room != null )
- {
- try
- {
- Autodesk.Revit.DB
- .SpatialElementGeometryCalculator
- calc = new Autodesk.Revit.DB
- .SpatialElementGeometryCalculator(
- doc, sebOptions );
- SpatialElementGeometryResults results
- = calc.CalculateSpatialElementGeometry(
- room );
- Solid roomSolid = results.GetGeometry();
- foreach( Face face in roomSolid.Faces )
- {
- IList<SpatialElementBoundarySubface>
- subfaceList = results.GetBoundaryFaceInfo(
- face );
- foreach( SpatialElementBoundarySubface
- subface in subfaceList )
- {
- if( subface.SubfaceType
- == SubfaceType.Side )
- {
- Element wall = doc.GetElement(
- subface.SpatialBoundaryElement
- .HostElementId );
- double subfaceArea = subface
- .GetSubface().Area;
- double netArea = sqFootToSquareM(
- calwallAreaMinusOpenings(
- subfaceArea, wall, room ) );
- s = s + "Помещение "
- + room.get_Parameter(
- BuiltInParameter.ROOM_NUMBER )
- .AsString()
- + " : Wall " + wall.get_Parameter(
- BuiltInParameter.ALL_MODEL_MARK )
- .AsString()
- + " : Area " + netArea.ToString()
- + " m2\r\n";
- }
- }
- }
- s = s + "\r\n";
- }
- catch( Exception )
- {
- }
- }
- }
- TaskDialog.Show( "Границы помещений", s);
- rc = Result.Succeeded;
- }
- catch( Exception ex )
- {
- TaskDialog.Show( " Границы помещений ",
- ex.Message.ToString() + "\r\n"
- + ex.StackTrace.ToString() );
- rc = Result.Failed;
- }
- return rc;
- }
- }
Давайте посмотрим на результат на простой модели:
Ближняя стена содержит дверь и два окна, поэтому ее площадь должна быть отличной от площади дальней стены.
Надстройка отобразит следующее окно с результатом:
Как вы видите, площадь двери и двух маленьких окон равна чуть меньше 2,5 квадратных метров.
Спасибо Phlip за исследование проблемы и поиска решения.
Источник: http://thebuildingcoder.typepad.com/blog/2015/03/calculating-gross-and-net-wall-areas.html
Обсуждение: http://adn-cis.org/forum/index.php?topic=2627
Опубликовано 11.04.2015Отредактировано 24.04.2015 в 09:47:05