Set Elevation Level for Elements in Model

FlorisvdG
Advocate
Advocate

Set Elevation Level for Elements in Model

FlorisvdG
Advocate
Advocate

Hello everyone,

 

I've been asked to create an addin that checks all elements within a model for a correct value in their 'level' property.

for example:

An lighting element is set to Level = 1st Floor, with an offset to 8000. It's location is actually on the 2nd floor.
In this case, the addin will change the level to 2nd floor, and compensate the offset to make sure the element stays in the same place.

 

It sounds easy enough, but my lack of Revit Knowledge is getting in the way.

I've encountered a few minor problems here, on which I'd like some advice.

 

1: Collect all the elements that actually have this 'Level' property.

Option 1: I can collect the elements based on several built-in Categories, but this seems like error-sensitive to me. (What if I overlook a category, or they are changed in the newer Revit versions etc.) It also returns more than I need and will require additional filtering. (Which is fine when needed, but prefer preventing it).
Option 2:
Well, I'm not sure how to configure my FilteredElementCollector other than option one, even though I'm pretty sure there must be a better way. I'm thinking collecting all FamilyInstances will not be enough.

2: Determining the location of the element.
So far i've checked for:

element.Location as Point, & element.Location as Curve. As well as checking for -GetPath(), and -GetEndpoint();


I use the Z value as the vertical location for the element.
I am very unsure of whether this the correct way of retrieving element locations.
Googling about this left me more confused then I was before. 
When to use Location property? When to use bounding box? When to use yet another different approach?
I am in need of some guidance. Like I mentioned before, I am noticing a lack of understanding Revit's internal structures.

I believe that once these two bumps are cleared, I'm good to go.
Any help would be much appreciated.

Also, I found this Dynamo script, which actually helped me to get where I am now:
https://www.youtube.com/watch?v=GoPgGq4EXZQ&feature=youtu.be
https://bimstallatie.sites.google.com/site/bimstallatie/dynamo/referentie-levels-aanpassen/Levels%20...

0 Likes
Reply
Accepted solutions (1)
5,481 Views
9 Replies
Replies (9)

naveen.kumar.t
Autodesk Support
Autodesk Support

Hi @FlorisvdG ,

try using the below code

 // Find the level whose Name is "Level 1"
                FilteredElementCollector collector = new FilteredElementCollector(doc);
                ICollection<Element> levels = collector.OfClass(typeof(Level)).ToElements();
                var query = from element in collector where element.Name == "Level 1" select element;// Linq query

                // Get the level id which will be used to match elements
                List<Element> level1 = query.ToList<Element>();
                ElementId levelId = level1[0].Id;

                // Find all walls on level one
                ElementLevelFilter level1Filter = new ElementLevelFilter(levelId);
                collector = new FilteredElementCollector(doc);
                ICollection<Element> allInstanceOnLevel1 = collector.OfClass(typeof(FamilyInstance)).WherePasses(level1Filter).ToElements();

for example:

Let's say you have an element A  present in level 1 with an offset from height value of 100 and element B present in level 2 with an offset value of 0.

let's assume the location point of element A  present in level 1 with an offset from height value of 100 is greater than level 2

The above code helps to retrieve elements based on the level and it won't care about the offset from height value.

Since your element's level property is set to "level 1" then your element A will be retrieved. 

I hope this helps.

 

 


Naveen Kumar T
Developer Technical Services
Autodesk Developer Network

0 Likes

eason.kangEDLV4
Autodesk Support
Autodesk Support

Hi,

 

For the Q1, please check Jeremy's answer here, he provided several blogs talking about how to use FilteredElementCollector with parameter filters in details: https://forums.autodesk.com/t5/revit-api-forum/filter-elements-by-parameter-value/m-p/8037351/highli... 

 

and check the blog here: https://thebuildingcoder.typepad.com/blog/2018/06/forge-tutorials-and-filtering-for-a-parameter-valu...

 

In addition, please don't use hard code naming of element or parameter name to filter elements, use enum BuiltinParameter or BuiltinCategory instead to avoid Revit software language issue. For starters, I would advise you to use RevitLookup to assist you to find proper enum type for element category or parameter.

 

For the Q2, Element.Location is the placement location of that element. It's enough to tell the position of the element in the 3D world with my experience.

  • LocationPoint: a single point represents Revit element's position like column, light fixture, window, door, sprinkler, pump and generator, electrical panel, MEP fittings, models created via the Generical Model template, and etc.
  • LocationCurve: a curve segment with two points represents the Revit element's position such as structural framing, cable tray, duct, pipe, models created via the Line based Generical Model template and so on.

Of course, you can use the bounding box, too. It's the minimum or smallest bounding or enclosing box that completely contains the Revit element. In most case, the bounding box is for seeking the coarse location of the element, and the Element.Location is for the accurate element's location with my experience. They also could be used together to determine the elements' location. Therefore, it depends on what you would like to achieve.

 

Hope it helps!

 

Cheers,

 

 


Eason Kang
Developer Advocate
Developer Advocacy & Support Service
Autodesk Platform Service (formerly Forge)

0 Likes

FlorisvdG
Advocate
Advocate

Thank you both for this. Very Helpful!

 

Since there doesn't seem to be a way to create a collector for all levels(correct me if I'm wrong), I created one collector for every level and combined them into 1 list. (Modellevel is a wrapper class I made for Revit's Level class).

 

 

public List<object> GetElementsWithLevel()
{
GetLevels(); var lstElementsWithLevel = new List<object>(); foreach (var modelLevel in _listLevels) { ElementLevelFilter levelFilter = new ElementLevelFilter(modelLevel.Level.Id); FilteredElementCollector collector = new FilteredElementCollector(_doc); lstElementsWithLevel.AddRange ( collector .WherePasses(levelFilter) .ToList() ); } return lstElementsWithLevel;
}

This works very well.
I am now wondering though How this ElementLevelFilter works. I am only getting FamilyInstances returned.
Is it not possible for Non-FamilyInstances to have a Level associated with them?
Do I need to get them in a different way?




 

0 Likes

eason.kangEDLV4
Autodesk Support
Autodesk Support

Hi,

 

As I know, some specific elements' level info. are stored in their parameter. Therefore, ElementLevelFilter won't work in this case. Please use ElementParameterFilter instead and check this blog here for the detailed tutorial:

 

https://thebuildingcoder.typepad.com/blog/2010/06/parameter-filter.html#1

 

Cheers,


Eason Kang
Developer Advocate
Developer Advocacy & Support Service
Autodesk Platform Service (formerly Forge)

0 Likes

FlorisvdG
Advocate
Advocate

Thanks. That makes sense, I thought it would be something like that.

I can add  the parameter INSTANCE_REFERENCE_LEVEL_PARAM as a filter, and add those elements as well.

 

Though this isn't it, is it?

 

I don't know which element Types I am looking for, let alone know what parameter is used to store the level for that type. There are a lot of built-in parameters that somehow refer to some form of level reference. I'm guessing I won't be needing all of them.

 

All I know is I need "All elements that are somehow connected to a Level".
How can do I figger out what parameters to use, and when can I be sure I am actually retrieving all the different types of elements and I am not forgetting any?

 

0 Likes

eason.kangEDLV4
Autodesk Support
Autodesk Support

Hi,

 

As I mentioned before, you can use RevitLookup to assist you to find proper BuiltInParameter enum type fo element parameters. here is an example for the structural framing:

BuiltInParameter.jpg

 

To filter all elements' level parameter, you would have to create multiple FilteredElementCollector instances and pass the corresponding parameter filter to them to obtain what you want.

 

However, the ElementParameterFilter is a slow filter. It will hurt to performance of your Addin. Therefore, I would like to advise you to use LINQ instead. Here is an example for you:

private ElementId GetLevelId(Element e)
{
	var builtinParams = new List<BuiltInParameter>{
		BuiltInParameter.LEVEL_PARAM,
    	BuiltInParameter.INSTANCE_REFERENCE_LEVEL_PARAM,
    	BuiltInParameter.FACEROOF_LEVEL_PARAM,
    	BuiltInParameter.RBS_START_LEVEL_PARAM,
    	BuiltInParameter.SCHEDULE_LEVEL_PARAM
    };
	
	if(e.LevelId != null)
	{
		return e.LevelId;
	}
	else
	{
		try
		{
			ElementId id = null;
			foreach(var bip in builtinParams)
            {
				Parameter param = e.get_Parameter(bip);
				if(param != null)
				{
					id = param.AsElementId();
					break;
				}
			}
			
			return id;
		}
		catch(Exception ex)
		{
			return null;
		}
	}
}

public void Run()
{
	
    FilteredElementCollector collector = new FilteredElementCollector(this.Document);
    ICollection<Element> levels = collector.OfClass(typeof(Level)).ToElements();
    var query = from element in collector
    			where element.Name == "02 - Floor"
    			select element;// Linq query

    // Get the level id which will be used to match elements
    var level = query.FirstOrDefault();
    ElementId levelId = level.Id;
    
    var builtinParams = new List<BuiltInParameter>{
    	BuiltInParameter.INSTANCE_REFERENCE_LEVEL_PARAM,
    	BuiltInParameter.FACEROOF_LEVEL_PARAM,
    	BuiltInParameter.RBS_START_LEVEL_PARAM,
    	BuiltInParameter.SCHEDULE_LEVEL_PARAM
    };
    
    var elements = new FilteredElementCollector(this.Document)
    					.OfClass(typeof(FamilyInstance))
		            	.Where(e => {
    	       				var lvlId = this.GetLevelId(e);
    	       				if(lvlId == null || lvlId != levelId)
    	       					return false;
    	       				
    	       				return true;
		            	});
    
    TaskDialog.Show("Revit", elements.Count().ToString());
}

Hope it helps.

 

Cheers,

 

 


Eason Kang
Developer Advocate
Developer Advocacy & Support Service
Autodesk Platform Service (formerly Forge)

0 Likes

FlorisvdG
Advocate
Advocate

Again thank you very much. Your help is much appreciated.

 

I thought slowfilters were still a little faster then using LINQ on a big batch of elements, I will look into that!

I'm aware of revit lookup, and the ability to look up the parameters there. Thanks though for making it so very clear.

But as I mentioned, I do not know what elements I'm looking for. So there's no Elements for me to Lookup.

 

From your example I get that those parameters all refer to the Level for some instances, So I'll definately add those to my list. Big Thanks.

I'm sorry to be dragging this on so much, but part of my question still remains unanswered, despite all the help I've gotten so far (which I am very grateful for obviously).
When the element types are unknown, is there any way to find out which of the built in Parameters refer to the Level? Apart from knowing from experience?  There are 92 Built-in parameters 'level' in their name (2017 API). I can't make up from their names which ones actually refer to the Level the element is on.

Perhaps the 5 parameters you've shown in your example are the only five I need. But again, how to be sure of this?





 

 

 

 

0 Likes

eason.kangEDLV4
Autodesk Support
Autodesk Support
Accepted solution

Hi,

 

That will depend on what kind of professional area and what kind of family templates you're working for. Not all of them are reporting the Level info we saw on the Revit Property panel with my experience.

 

And it's hard to list them out to me without further information, the amount of element type is not a small number. Therefore, I would like to advise you to record the element types if their level info is not in these 5 BuiltinParameter I listed for you, then find their BuiltinParameter enum, and modify your Addin step-by-step.

 

Cheers,


Eason Kang
Developer Advocate
Developer Advocacy & Support Service
Autodesk Platform Service (formerly Forge)

0 Likes

FlorisvdG
Advocate
Advocate

Thank you for all the help.

I started with a very general collector, filtering just a few classes and categories, worked my way up from there with the help from one of our engineers. So far I haven't encountered a different Level-Parameter then those 5.

I accepted your latest reply as the solution for this topic.

0 Likes