ADN Open CIS
Сообщество программистов Autodesk в СНГ

11/04/2015

Вычисление общей и полезной площади пверхности стен

Вопрос: мне нужно определить площадь, занимаемая окнами и дверями в стене. Я думал это можно легко сделать просто, взяв значение параметра HOST_AREA_COMPUTED, так как при первоначальном тестировании значение параметра показывало верную цифру. Но, при дальнейшем тестирование это оказалось не так.

В тестовом файле есть стена с двумя дверями. Внешне они выглядят одинаково, но для одной площадь равна 4м2, для другой – 2м2.

Почему так? Что же на самом деле возвращает параметр HOST_AREA_COMPUTED? Какой самый правильный способ определения площади окон и дверей в стене?

Ответ: не могу сказать ничего конкретного по поводу того, какой же значение находится в этом параметре и как он рассчитывается. На сколько мне известно, его значение видно в пользовательском интерфейсе, и возможно будет лучше спросить у проектировщиков, что они знают о значении этого параметра.

Что касается API, я полагаю вы знаете о примере из Revit SDK для работы с материалами.

В этих примерах нет того, что вам нужно?

Ответ: Спасибо за ответ и за ссылку.

Я попробую пояснить что же все-таки хочу получить. Я хочу найти площадь поверхностей стен, которые являются границами помещений. Для этого я решил воспользоваться классом SpatialElementGeometryCalculator, так как он корректно считает площадь стен, перекрытий и потолков, даже если они наклонены. Но проблема в том, что этот класс не вычитает из итоговой площади площадь окон и стен.

Я знаю, что двери и окна связаны с помещением, точно так же, как и стена. Поэтому я мог бы получить площадь стен за вычетом проемов, затем временно удалить окна и двери, снова посчитать площадь. Разница между двумя значениями и будет являться площадью, занимаемой дверями и окнами.

Ответ: Идея выглядит интересной. На сайте The Building Coder пока нет подобных примеров, за исключением сложной статьи о расчете тепловой нагрузки (на англ.)

Ответ: Спасибо за ссылку. Хорошая новость в том, что временное удаление объектов вроде работает как надо. Но я так и не понял, почему значение параметра Площадь все же различно для похожих элементов, хотя это больше и не имеет значение.

Я посмотрел ваш пример. Это не совсем то, что мне нужно, так как мне нужно считать именно площадь поверхности стены, граничащей с помещением, а стена может быть границей нескольких помещений.

Поэтому мы и решили использовать класс SpatialElementGeometryCalculator, с которым после нескольких неудачных попыток все же удалось получить требуемый результат. И я буду рад поделиться результатом.

Ответ: Я думаю, что в некоторых случаях при расчете площади двери что-то считается дважды.

Безусловно, SpatialElementGeometryCalculator более мощный и более подходит в вашем случае.

Ответ: Вот пример код по вычислению поверхностей стен минус двери и окна.

Код на VB.NET, но при необходимсоти можно его конвертировать в C#.

Код - VB.NET: [Выделить]
  1. Imports Autodesk.Revit.ApplicationServices
  2. Imports System.IO
  3. Imports Autodesk.Revit.DB.Architecture
  4. Imports System.Collections.Generic
  5. Imports System.Diagnostics
  6. Imports Autodesk.Revit.Attributes
  7. Imports Autodesk.Revit.DB
  8. Imports BoundarySegment = Autodesk.Revit.DB.BoundarySegment
  9. Imports Autodesk.Revit.DB.ExternalService
  10. Imports Autodesk.Revit.UI
  11.  
  12. <Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)> _
  13. <Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)> _
  14. <Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)> _
  15. Public Class extCmd
  16.   Implements IExternalCommand
  17.  
  18.   Public Function Execute(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Result Implements IExternalCommand.Execute
  19.     Try
  20.  
  21.       Dim app As Autodesk.Revit.UI.UIApplication = commandData.Application
  22.       Dim doc As Document = app.ActiveUIDocument.Document
  23.  
  24.       Dim roomCol As FilteredElementCollector = New FilteredElementCollector(app.ActiveUIDocument.Document).OfClass(GetType(SpatialElement))
  25.       Dim s As String = "Finished populating Rooms with Boundary Data" + vbNewLine + vbNewLine
  26.       For Each e As SpatialElement In roomCol
  27.         Dim room As Room = TryCast(e, Room)
  28.         If room IsNot Nothing Then
  29.           Try
  30.             Dim spatialElementBoundaryOptions As New SpatialElementBoundaryOptions()
  31.             spatialElementBoundaryOptions.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish
  32.             Dim calculator1 As New Autodesk.Revit.DB.SpatialElementGeometryCalculator(doc, spatialElementBoundaryOptions)
  33.             Dim results As SpatialElementGeometryResults = calculator1.CalculateSpatialElementGeometry(room)
  34.  
  35.             ' get the solid representing the room's geometry
  36.             Dim roomSolid As Solid = results.GetGeometry()
  37.             Dim iSegment As Integer = 1
  38.             For Each face As Face In roomSolid.Faces
  39.               Dim subfaceList As IList(Of SpatialElementBoundarySubface) = results.GetBoundaryFaceInfo(face)
  40.               For Each subface As SpatialElementBoundarySubface In subfaceList
  41.                 If subface.SubfaceType = SubfaceType.Side Then ' only interested in walls in this example
  42.                   Dim ele As Element = doc.GetElement(subface.SpatialBoundaryElement.HostElementId)
  43.                   Dim subfaceArea As Double = subface.GetSubface().Area
  44.                   Dim netArea As Double = sqFootToSquareM(calwallAreaMinusOpenings(subfaceArea, ele, doc, room))
  45.                   s += "Room " + room.Parameter(BuiltInParameter.ROOM_NUMBER).AsString + " : Wall " + ele.Parameter(BuiltInParameter.DOOR_NUMBER).AsString + " : Area " + netArea.ToString + "m2" + vbNewLine
  46.                 End If
  47.  
  48.               Next
  49.             Next
  50.             s += vbNewLine
  51.           Catch ex As Exception
  52.           End Try
  53.  
  54.         End If
  55.  
  56.       Next
  57.       MsgBox(s, MsgBoxStyle.Information, "Room Boundaries")
  58.       Return Result.Succeeded
  59.     Catch ex As Exception
  60.       MsgBox(ex.Message.ToString + vbNewLine +
  61.        ex.StackTrace.ToString)
  62.       Return Result.Failed
  63.     End Try
  64.   End Function
  65.  
  66.  
  67.   Private Function calwallAreaMinusOpenings(ByVal subfaceArea As Double, ByVal ele As Element, ByVal doc As Document, ByVal room As Room) As Double
  68.     Dim fiCol As FilteredElementCollector = New FilteredElementCollector(doc).OfClass(GetType(FamilyInstance))
  69.     Dim lstTotempDel As New List(Of ElementId)
  70.     'Now find the familyInstances that are associated to the current room
  71.     For Each fi As FamilyInstance In fiCol
  72.       If fi.Parameter(BuiltInParameter.HOST_ID_PARAM).AsValueString = ele.Id.ToString Then
  73.         If fi.Room IsNot Nothing Then
  74.           If fi.Room.Id = room.Id Then
  75.             lstTotempDel.Add(fi.Id)
  76.             Continue For
  77.           End If
  78.         End If
  79.         If fi.FromRoom IsNot Nothing Then
  80.           If fi.FromRoom.Id = room.Id Then
  81.             lstTotempDel.Add(fi.Id)
  82.             Continue For
  83.           End If
  84.         End If
  85.         If fi.ToRoom IsNot Nothing Then
  86.           If fi.ToRoom.Id = room.Id Then
  87.             lstTotempDel.Add(fi.Id)
  88.             Continue For
  89.           End If
  90.         End If
  91.       End If
  92.     Next
  93.  
  94.     If lstTotempDel.Count > 0 Then
  95.       Dim t As New Transaction(doc, "tmp Delete")
  96.       Dim wallnetArea As Double = ele.Parameter(BuiltInParameter.HOST_AREA_COMPUTED).AsDouble
  97.       t.Start()
  98.       doc.Delete(lstTotempDel)
  99.       doc.Regenerate()
  100.       Dim wallGrossArea As Double = ele.Parameter(BuiltInParameter.HOST_AREA_COMPUTED).AsDouble
  101.       t.RollBack()
  102.       Dim fiArea As Double = wallGrossArea - wallnetArea
  103.       Return subfaceArea - fiArea
  104.     Else
  105.       Return subfaceArea
  106.     End If
  107.   End Function
  108.  
  109.  
  110.  
  111.   Private Function sqFootToSquareM(ByVal sqFoot As Double) As Double
  112.     Return Math.Round(sqFoot * 0.092903, 2)
  113.   End Function
  114.  
  115. End Class

Ответ: Спасибо за код.

Я создал проект на GitHub и поместил туда обе версии: на C# и VB.NET.

В проекте я сделал несколько изменений:

Использовал сравнение AsElementId().IntegerValue.Equals( Id.IntegerValue ) вместо сравнения AsValueString и ele.Id.ToString, так как сравнение чисел работает значительно быстрей, чем сравнение строк.

Использовал фильтр по параметру HOST_ID_PARAM вместо прямого сравнения идентификаторов элементов.

Эти улучшения позволят более быстро и эффективно работать с большими моделями.

В результате код на C# получился таким:

Код - C#: [Выделить]
  1. [Transaction( TransactionMode.Manual )]
  2. public class Command : IExternalCommand
  3. {
  4.   /// <summary>
  5.   /// Конвертация квадратных футов в квадратные метры.
  6.   /// </summary>
  7.   double sqFootToSquareM( double sqFoot )
  8.   {
  9.     return Math.Round( sqFoot * 0.092903, 2 );
  10.   }
  11.  
  12.   /// <summary>
  13.   /// Вычисление площади стен, за исключением проемов.
  14.  /// Временно удаляем двери и окна в транзакции, которую затем откатываем.
  15.   /// </summary>
  16.   /// <param name="subfaceArea">Первоначальная площадь поверхности стены</param>
  17.   /// <param name="wall"></param>
  18.   /// <param name="doc"></param>
  19.   /// <param name="room"></param>
  20.   /// <returns></returns>
  21.   double calwallAreaMinusOpenings(
  22.     double subfaceArea,
  23.     Element wall,
  24.     Room room )
  25.   {
  26.     Document doc = wall.Document;
  27.  
  28.     Debug.Assert(
  29.       room.Document.ProjectInformation.UniqueId.Equals(
  30.         doc.ProjectInformation.UniqueId ),
  31.       "expected wall and room from same document" );
  32.  
  33.     // Определяем все проемы в стене.
  34.  
  35.     FilteredElementCollector fiCol
  36.       = new FilteredElementCollector( doc )
  37.         .OfClass( typeof( FamilyInstance ) );
  38.  
  39.     List<ElementId> lstTotempDel
  40.       = new List<ElementId>();
  41.  
  42.     foreach( FamilyInstance fi in fiCol )
  43.     {
  44.       // Экземпляры семейств, располагающихся на стене
  45.       // Могут быть более эффективно получены
  46.       // с помощью фильтра по параметру
  47.       // На больших моделях, это особенно эффективно
  48.  
  49.       if( fi.get_Parameter(
  50.         BuiltInParameter.HOST_ID_PARAM )
  51.           .AsElementId().IntegerValue.Equals(
  52.             wall.Id.IntegerValue ) )
  53.       {
  54.         if( ( fi.Room != null )
  55.           && ( fi.Room.Id == room.Id ) )
  56.         {
  57.           lstTotempDel.Add( fi.Id );
  58.         }
  59.         else if( ( fi.FromRoom != null )
  60.           && ( fi.FromRoom.Id == room.Id ) )
  61.         {
  62.           lstTotempDel.Add( fi.Id );
  63.         }
  64.         else if( ( fi.ToRoom != null )
  65.           && ( fi.ToRoom.Id == room.Id ) )
  66.         {
  67.           lstTotempDel.Add( fi.Id );
  68.         }
  69.       }
  70.     }
  71.  
  72.     // Определяем общую площадь проемов
  73.  
  74.     double openingArea = 0;
  75.  
  76.     if( 0 < lstTotempDel.Count )
  77.     {
  78.       Transaction t = new Transaction( doc );
  79.  
  80.       double wallAreaNet = wall.get_Parameter(
  81.         BuiltInParameter.HOST_AREA_COMPUTED )
  82.           .AsDouble();
  83.  
  84.       t.Start( "tmp Delete" );
  85.       doc.Delete( lstTotempDel );
  86.       doc.Regenerate();
  87.       double wallAreaGross = wall.get_Parameter(
  88.         BuiltInParameter.HOST_AREA_COMPUTED )
  89.           .AsDouble();
  90.       t.RollBack();
  91.  
  92.       openingArea = wallAreaGross - wallAreaNet;
  93.     }
  94.  
  95.     return subfaceArea - openingArea;
  96.   }
  97.  
  98.   public Result Execute(
  99.     ExternalCommandData commandData,
  100.     ref string message,
  101.     ElementSet elements )
  102.   {
  103.     UIApplication app = commandData.Application;
  104.     Document doc = app.ActiveUIDocument.Document;
  105.  
  106.     SpatialElementBoundaryOptions sebOptions
  107.       = new SpatialElementBoundaryOptions();
  108.  
  109.     sebOptions.SpatialElementBoundaryLocation
  110.       = SpatialElementBoundaryLocation.Finish;
  111.  
  112.     Result rc;
  113.  
  114.     try
  115.     {
  116.       FilteredElementCollector roomCol
  117.         = new FilteredElementCollector( doc )
  118.           .OfClass( typeof( SpatialElement ) );
  119.  
  120.       string s = "Finished populating Rooms with "
  121.         + "Boundary Data\r\n\r\n";
  122.  
  123.       foreach( SpatialElement e in roomCol )
  124.       {
  125.         Room room = e as Room;
  126.  
  127.         if( room != null )
  128.         {
  129.           try
  130.           {
  131.             Autodesk.Revit.DB
  132.               .SpatialElementGeometryCalculator
  133.                 calc = new Autodesk.Revit.DB
  134.                   .SpatialElementGeometryCalculator(
  135.                     doc, sebOptions );
  136.  
  137.             SpatialElementGeometryResults results
  138.               = calc.CalculateSpatialElementGeometry(
  139.                 room );
  140.  
  141.             Solid roomSolid = results.GetGeometry();
  142.  
  143.             foreach( Face face in roomSolid.Faces )
  144.             {
  145.               IList<SpatialElementBoundarySubface>
  146.                 subfaceList = results.GetBoundaryFaceInfo(
  147.                   face );
  148.  
  149.               foreach( SpatialElementBoundarySubface
  150.                 subface in subfaceList )
  151.               {
  152.                 if( subface.SubfaceType
  153.                   == SubfaceType.Side )
  154.                 {
  155.                   Element wall = doc.GetElement(
  156.                     subface.SpatialBoundaryElement
  157.                       .HostElementId );
  158.  
  159.                   double subfaceArea = subface
  160.                     .GetSubface().Area;
  161.  
  162.                   double netArea = sqFootToSquareM(
  163.                     calwallAreaMinusOpenings(
  164.                       subfaceArea, wall, room ) );
  165.  
  166.                   s = s + "Помещение "
  167.                     + room.get_Parameter(
  168.                       BuiltInParameter.ROOM_NUMBER )
  169.                         .AsString()
  170.                     + " : Wall " + wall.get_Parameter(
  171.                       BuiltInParameter.ALL_MODEL_MARK )
  172.                         .AsString()
  173.                     + " : Area " + netArea.ToString()
  174.                     + " m2\r\n";
  175.                 }
  176.               }
  177.             }
  178.             s = s + "\r\n";
  179.           }
  180.           catch( Exception )
  181.           {
  182.           }
  183.         }
  184.       }
  185.       TaskDialog.Show( "Границы помещений", s);
  186.  
  187.       rc = Result.Succeeded;
  188.     }
  189.     catch( Exception ex )
  190.     {
  191.       TaskDialog.Show( " Границы помещений ",
  192.         ex.Message.ToString() + "\r\n"
  193.         + ex.StackTrace.ToString() );
  194.  
  195.       rc = Result.Failed;
  196.     }
  197.     return rc;
  198.   }
  199. }

Давайте посмотрим на результат на простой модели:

 

Ближняя стена содержит дверь и два окна, поэтому ее площадь должна быть отличной от площади дальней стены.

 

Надстройка отобразит следующее окно с результатом:

 

Как вы видите, площадь двери и двух маленьких окон равна чуть меньше 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