Apply parameter depending on zone

Apply parameter depending on zone

H.echeva
Advocate Advocate
1,636 Views
12 Replies
Message 1 of 13

Apply parameter depending on zone

H.echeva
Advocate
Advocate

Hello all,

 

I am Revit API begginer and I am trying to make a macro that applies a value to an elements parameter deppending in which area it is.

For example if I have a door in one area that I called "zone 01"  I want the parameter to be that.

If there is a wall that goes through zone 01 and zone 02 I want the parameter to be "zone 01, zone 02"

 

At the moment my strategy is to generate volumes that represent this areas and the filter the elements that intersect them.

However I can not make my code to work.

When the element is in zone 01 it works and when it goes throug zone 1 and zone 2 it works as well.

However if the element is only in zone 02 it doesnt work, the parameter remains empty.

 

The model is just a couple of walls and doors and the volumes intersect propperly.

If I "comment" filter1 and filter12 it works for zone 2!

 

I pretend to make this or 6 areas in total.

 

Any help will be appreciated.

 

Here is my attempt:

 

public void ConstructionArea()
		{
			UIDocument uidoc = this.ActiveUIDocument;
			Document doc = uidoc.Document;
			//get selection from revit			
			ICollection<ElementId> myids = uidoc.Selection.GetElementIds();
			
			
			//collector
			FilteredElementCollector collector = new FilteredElementCollector(doc,myids);
			
			//set zone elements IDs
			ElementId z1Id = new ElementId(197634); Element z1 = doc.GetElement(z1Id);
			ElementId z2Id = new ElementId(197723); Element z2 = doc.GetElement(z2Id);
			
			
			//filters
			ElementIntersectsElementFilter filter1 = new ElementIntersectsElementFilter(z1);
			ElementIntersectsElementFilter filter2 = new ElementIntersectsElementFilter(z2);
			
			
			//logical filters			
			IList<ElementFilter> flist12 = new List<ElementFilter>{filter1,filter2};
			LogicalAndFilter filter12 = new LogicalAndFilter(flist12);
			
			
			//collector filter
			ICollection<Element> elements1 = collector.WherePasses(filter1).ToElements();
			ICollection<Element> elements2 = collector.WherePasses(filter2).ToElements();
			IList<Element> elements12 = collector.WherePasses(filter12).ToElements();
			
			using (Transaction t = new Transaction(doc,"construction area"))
			{
				t.Start();
				//iterate 
				
				foreach (Element e in elements1)
					{
						Parameter mypara = e.LookupParameter("test");
						mypara.Set("zone 01");
					}
					
				
				foreach (Element e in elements2)
					{
						Parameter mypara = e.LookupParameter("test");
						mypara.Set("zone 02");
					}
				
				foreach (Element e in elements12)
					{
						Parameter mypara = e.LookupParameter("test");
						mypara.Set("zone 01, zone 02");
					}
					
				t.Commit();
			}
			
			
		}

 

0 Likes
Accepted solutions (2)
1,637 Views
12 Replies
Replies (12)
Message 2 of 13

Mustafa.Salaheldin
Collaborator
Collaborator

Your code seems fine. Can you please provide sample file for the Revit prject?


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Message 3 of 13

H.echeva
Advocate
Advocate

Hello Mustafa,

 

Thank you for your quick response.

The file is this https://drive.google.com/file/d/1URkY1Fqy2wkddFCMNzz6OeN2xgs8R4o0/view?usp=sharing

 

¨You can see that there are 4 volumes. At the moment I am just using two. Top-left is zone 01 Top-right is zone 02.

 

Regards

0 Likes
Message 4 of 13

Mustafa.Salaheldin
Collaborator
Collaborator

Actually I'm a little bit confused, what are you trying to do?

The file doesn't have any Area/Room/Zone so I guess you are using the generic model family as your zone, isn't?

If you are using the generic model as a "zone" then you have to give it a name, that will make it easier to fill the parameter for each element inside the volume.

If I'm understanding right then I can help you implementing the code.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Message 5 of 13

H.echeva
Advocate
Advocate

That´s right Mustafa.

 

I don´t want to use any room or revit area/zone. I want to use this In-place generic models as zones. In the real project there are "virtual" construction zones that don´t match with any room or space, they just divide the building.

 

What do you mean to give them a name? they are in-place generic models and I gave them a name when I created them. Maybe I should use a family instead of a in-place component?

 

Do you know why it doen´t work? any help is welcome 🙂

 

Many thanks!

0 Likes
Message 6 of 13

FAIR59
Advisor
Advisor
Accepted solution

as I understand, when you use a filter on a collector, the collector itself also changes to the filtered elements. For every zone and combination of zones you need to start with a fresh collector.

Message 7 of 13

H.echeva
Advocate
Advocate

FAIR 59, that worked!

 

Here is the final code:

public void ConstructionArea()
		{
			UIDocument uidoc = this.ActiveUIDocument;
			Document doc = uidoc.Document;
			//get selection from revit			
			ICollection<ElementId> myids = uidoc.Selection.GetElementIds();
			
			
			//collector
			FilteredElementCollector collector1 = new FilteredElementCollector(doc,myids);
			FilteredElementCollector collector2 = new FilteredElementCollector(doc,myids);
			FilteredElementCollector collector12 = new FilteredElementCollector(doc,myids);
			
			//set zone elements IDs
			ElementId z1Id = new ElementId(197634); Element z1 = doc.GetElement(z1Id);
			ElementId z2Id = new ElementId(197723); Element z2 = doc.GetElement(z2Id);
			
			
			//filters
			ElementIntersectsElementFilter filter1 = new ElementIntersectsElementFilter(z1);
			ElementIntersectsElementFilter filter2 = new ElementIntersectsElementFilter(z2);
			
			
			//logical filters	
						
			IList<ElementFilter> flist12 = new List<ElementFilter>{filter1,filter2};
			LogicalAndFilter filter12 = new LogicalAndFilter(flist12);
			
			//collector filter
			ICollection<Element> elements1 = collector1.WherePasses(filter1).ToElements();
			ICollection<Element> elements2 = collector2.WherePasses(filter2).ToElements();
			IList<Element> elements12 = collector12.WherePasses(filter12).ToElements();
			
			using (Transaction t = new Transaction(doc,"construction area"))
			{
				t.Start();
				//iterate 
				
				foreach (Element e in elements1)
					{
						Parameter mypara = e.LookupParameter("test");
						mypara.Set("zone 01");
					}
					
				
				foreach (Element e in elements2)
					{
						Parameter mypara = e.LookupParameter("test");
						mypara.Set("zone 02");
					}
				
				foreach (Element e in elements12)
					{
						Parameter mypara = e.LookupParameter("test");
						mypara.Set("zone 01, zone 02");
					}
					
					
				t.Commit();
			}
			
			
		}
0 Likes
Message 8 of 13

Mustafa.Salaheldin
Collaborator
Collaborator
Yes, It would be better if you create a Generic model loadable family, instead of In-Place, so that you don't need to create multiple time with different names. Just create one type and let the instances have distinct names. This is even better for performance than creating individual In-place components.
That also will allow you to automatically find the intersections and apply the naming without creating ElementIds.

¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

Message 9 of 13

H.echeva
Advocate
Advocate

Hi Mustafa,

 

What do you mean to put names to the instances? if there is just one type how do you put a name to the different instances? a custom parameter called name?

 

How do you apply this to avoid using IDs?

 

Many thanks

0 Likes
Message 10 of 13

Mustafa.Salaheldin
Collaborator
Collaborator

Yes I mean by using parameter like "Mark", so you create one family type and then create multiple instances each one with a unique name(Mark), and the name will be incremented automatically. So you can automatize the naming process once you get the intersection.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

0 Likes
Message 11 of 13

Mustafa.Salaheldin
Collaborator
Collaborator
Accepted solution

Actually I was thinking about using the Filled Regions instead of Generic Models. I have made a slight update to your file and rewritten the code.

 

#region Namespaces

using System;
using System.Text;
using System.Linq;
using System.Xml;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using System.IO;

using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;

using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Plumbing;

using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.UI.Events;

//using Autodesk.Revit.Collections;
using Autodesk.Revit.Exceptions;
using Autodesk.Revit.Utility;

using RvtApplication = Autodesk.Revit.ApplicationServices.Application;
using RvtDocument = Autodesk.Revit.DB.Document;

#endregion

namespace Toolbox
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class ReloadGroups : IExternalCommand
    {
        #region Cached Variables

        private static ExternalCommandData _cachedCmdData;

        public static UIApplication CachedUiApp
        {
            get
            {
                return _cachedCmdData.Application;
            }
        }

        public static RvtApplication CachedApp
        {
            get
            {
                return CachedUiApp.Application;
            }
        }

        public static RvtDocument CachedDoc
        {
            get
            {
                return CachedUiApp.ActiveUIDocument.Document;
            }
        }

        #endregion

        #region IExternalCommand Members         

        public Result Execute(ExternalCommandData cmdData, ref string msg, ElementSet elemSet)
        {
            _cachedCmdData = cmdData;

            try
            {

                FilteredElementCollector col = new FilteredElementCollector(CachedDoc).OfClass(typeof(FilledRegion));

                List<FilledRegion> myids = col.Cast<FilledRegion>().ToList();

                foreach (FilledRegion item in myids)
                {
                    BoundingBoxXYZ bb = item.get_BoundingBox(CachedUiApp.ActiveUIDocument.ActiveView);
                    Outline ol = new Outline(bb.Min, bb.Max);

                    BoundingBoxIntersectsFilter filterIntersect = new BoundingBoxIntersectsFilter(ol);
                    BoundingBoxIsInsideFilter filterInside = new BoundingBoxIsInsideFilter(ol);
                    LogicalOrFilter filter = new LogicalOrFilter(filterIntersect, filterInside);


                    FilteredElementCollector collector = new FilteredElementCollector(CachedDoc);
                    collector.WherePasses(filter).WhereElementIsNotElementType();

                    List<Element> elements = collector.Cast<Element>().ToList();
                    List<Element> filteredElms = elements.Where(e => null != e.Category && e.Category.AllowsBoundParameters == true && e.Category.HasMaterialQuantities).ToList();

                    using (Transaction t = new Transaction(CachedDoc, "construction area"))
                    {
                        t.Start();
                        //iterate 

                        foreach (Element e in filteredElms)
                        {
                            Parameter mypara = e.LookupParameter("test");

                            if (mypara != null)
                            {
                                string value = mypara.AsString();
                                string pv = item.GetParameters("Mark").FirstOrDefault().AsString();

                                if (value == string.Empty)
                                {
                                    mypara.Set(pv);
                                }
                                else
                                {
                                    mypara.Set(value + ", " + pv);
                                }
                            }

                        }


                        t.Commit();
                    }
                }

                return Result.Succeeded;
            }
            catch (Exception ex)
            {
                msg = ex.ToString();
                return Result.Failed;
            }
        }
        #endregion
    }
}

Now we are finding if the objects are contained or intersected with the bounding boxes of the filled regions. Then we rename the elements based on the "Mark" value of each filled region. This is a fully automatized solution.

 

The new sample file is in Revit 2017.

Please don't forget to mark the reply as an answer if it satisfies your need.

 

 


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

0 Likes
Message 12 of 13

H.echeva
Advocate
Advocate

Thanks Mustafa,

 

That looks great however is too complicated to understand for me with my programming knowledge at the moment. I don't even know how to use linq.

I will definitely try to study it and learn from it.

 

Again many thanks for your help.

0 Likes
Message 13 of 13

Mustafa.Salaheldin
Collaborator
Collaborator

You are welcome. For me too linq was a mystery but after a quite little of time I get used to it and I started to use it everywhere. If you still need help in anything just send me a message.


¯\_(ツ)_/¯
Let it work like a charm.

Mustafa Salaheldin


EESignature




Digital Integration Manager, DuPod

Facebook | Twitter | LinkedIn

0 Likes