We already looked at some examples of confusion due to the interaction of phases with the room property on family instances. Here is a new aspect of this encountered and solved by Patrick Rosendahl of SOLAR-COMPUTER GmbH, who kindly explains the issue and its resolution like this:
Problem: I am having a problem with the FamilyInstance Room property. I looked at the discussion of issues with the Room property and its phase dependency but have not been able to solve it yet.
This is the situation:
- Customer makes use of design options and phasing.
- Most rooms are in the main model, some rooms are in design options.
- There are three phases: phase_A, phase_B, phase_C.
- Phase_C is not used, have not checked the impact if deleted.
- Most architecture is created in phase_A and located in the main model.
- Some architecture is created in phase_B and located in design options.
- All family instances and all rooms are created in phase_B.
- Room volume computation is turned on.
- Revit's schedules work correctly, i.e. show the correct room-wise list of family instances.
These are the problems encountered:
- Sometimes, the family instance Room property returns null, even though the family instance is clearly within a room.
- Even worse: sometimes, the family instance method get_Room( familyinstance.CreationPhase ) returns null.
Questions:
- Might there be a problem with the design options?
- Is there any better workaround than the two approaches mentioned above?
- What function is the Revit schedule using to successfully determine the room property of family instances?
As a workaround, I thought of calling the get_Room method on the family instance in a loop over all available phases in the project, and making use of the document PointInRoom method if that fails.
Solution: Here is a promising start for a solution that I am currently exploring:
- Obviously, first try using the family instance Room property directly.
- If that fails, try
foreach(Phase p) { familyinstance.get_Room(p) }
- Hard part: determine primary options;
foreach(Room r where r.DesignOption.IsPrimary) { r.IsPointInRoom( fi.Location ) }
- Emergency:
foreach( Room r ) { r.IsPointInRoom( fi.Location ) }
Here is the actual method implementating this that I am currently working with, including some todo items left as an exercise for the reader ;)
public static Room DetermineRoom( Element el ) { FamilyInstance fi = el as FamilyInstance; if( fi == null ) { return null; } // As simple as that? try { if( fi.Room != null ) { //Debug.WriteLine("fi.Room != null"); return fi.Room; } } catch { } // Try phasing Room r = null; foreach( Phase p in el.Document.Phases ) { try { // TODO should check fi.GetPhaseStatus // instead of provoking an exception r = fi.get_Room( p ); if( r != null ) { //Debug.WriteLine("fi.get_Room( " // + p.Name + ") != null"); return r; } } catch { } } LocationPoint lp = el.Location as LocationPoint; if( lp != null ) { // Try design options List<Element> roomlst = get_Elements( el.Document, typeof( Room ) ); // Try rooms from primary design option foreach( Element roomel in roomlst ) { Room priroom = roomel as Room; if( priroom == null ) continue; if( priroom.DesignOption == null ) continue; if( priroom.DesignOption.IsPrimary ) { // TODO should check whether priroom // and el phasing overlaps if( priroom.IsPointInRoom( lp.Point ) ) { //Debug.WriteLine( // "priroom.IsPointInRoom != null"); return priroom; } } } // Emergency: try any room foreach( Element roomel in roomlst ) { Room room = roomel as Room; if( room == null ) continue; // TODO should check whether room // and el phasing overlaps if( room.IsPointInRoom( lp.Point ) ) { //Debug.WriteLine( // "room.IsPointInRoom != null"); return room; } } } // Nothing found return null; }
I think this approach provides is a good starting point. A more complete solution might possibly take the phase and design option as arguments. In my context, the function above rarely returns a non-primary design option room.
Many thanks to Patrick for his research and sharing this solution!