1540/2078

Elasticsearch-Head, RevitLookup and Area Schemes

Originally Published in

I ran the first query on the collection of tbc ↗ blog posts imported into Elasticsearch to experiment for the question answering system Q4R4 Question Answering for Revit API.

No spectacular results to report so far, but at least it works.

I installed the elasticsearch-head ↗ web front end to better explore and understand my local Elasticsearch cluster.

Alexander made a small correction to the latest RevitLookup enhancements, reverting one of the changes made yesterday.

Lots of interesting solutions on the Revit API discussion forum ↗, including a nice little filtering sample that I picked up:

Elasticsearch Text Field Mapping

Yesterday, I described the q4r4 tbc import script tbcimport.py that I implemented to import all The Building Coder blog posts into Elasticsearch ↗ to start experimenting with queries on them.

By default, the blog post text field was apparently imported and populated as a type keyword field:

$ curl -XGET 'localhost:9200/tbc/_mapping?pretty'
{
  "tbc" : {
    "mappings" : {
      "blogpost" : {
        "properties" : {
          "date" : {
            "type" : "date"
          },
          "nr" : {
            "type" : "long"
          },
          "text" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          },
          "title" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          },
          "url" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    }
  }
}

Since I want to run a full text search on the blog post text, I need to change that mapping. Actually, I might as well change it for the title field as well:

$ curl -XPUT 'localhost:9200/tbc?pretty' -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "blogpost": {
      "properties": {
        "text": { "type":  "text" },
        "title": { "type":  "text" }
      }
    }
  }
}
'

Now the mapping looks more suitable:

$ curl -XGET 'localhost:9200/tbc/_mapping?pretty'
{
  "tbc" : {
    "mappings" : {
      "blogpost" : {
        "properties" : {
          "date" : {
            "type" : "date"
          },
          "nr" : {
            "type" : "long"
          },
          "text" : {
            "type" : "text"
          },
          "title" : {
            "type" : "text"
          },
          "url" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    }
  }
}

Elasticsearch-Head Web Front End

Typing curl scripts on the command line is probably not the most effective way to explore my local Elasticsearch cluster.

I installed the elasticsearch-head ↗ web front end to improve and simplify my interactive access.

As explained in its readme documentation, I must add two CORS settings to the elasticsearch config file elasticsearch.yml to allow elastic-head to connect to it:

  http.cors.enabled: true
  http.cors.allow-origin: "*"

With those settings in place, I can browse the cluster contents and start experimenting with queries on them:

Elasticsearch-head

More RevitLookup Updates

Two minor RevitLookup updates today, each encapsulated in an own new version.

Alexander Ignatovich, @CADBIMDeveloper ↗, aka Александр Игнатович, disagreed with one of the changes made yesterday and reverted that in his pull request #31 try-catch for each element in cycle is bad idea and looks ugly ↗.

Many thanks to Alexander for paying attention and fixing this!

That change is merged in RevitLookup release 2017.0.0.21 ↗.

I was unhappy with a couple of warning messages during compilation and fixed those in RevitLookup release 2017.0.0.22 ↗.

The most up-to-date version is always provided in the master branch of the RevitLookup GitHub repository ↗.

If you would like to access any part of the functionality that was removed when switching to the Reflection based approach, please grab it from release 2017.0.0.13 ↗ or earlier.

I am also happy to restore any other code that was removed and that you would like preserved. Simply create a pull request for that, explain your need and motivation, and I will gladly merge it back again.

Get Area Scheme from an Area

A Revit API discussion forum ↗ question on getting the area scheme from an area ↗ turned out to be a pretty trivial matter of accessing and evaluating a simple series of parameter values on the area and area scheme:

Question: I’m not sure if this is possible but I’ve been trying to get the AREA_SCHEME_NAME from a collection of areas.

I’ve tried several ways without luck.

Can an area report what Area Scheme (Gross, Rentable) it belongs to?

Answer: Have you tried this?

  Area area;

  AreaScheme scheme = doc.GetElement(
    area.get_Parameter(
      BuiltInParameter.AREA_SCHEME_ID ).AsElementId() )
        as AreaScheme;

  string areaSchemeName = scheme.get_Parameter(
    BuiltInParameter.AREA_SCHEME_NAME ).AsString();

Response: Thanks.

Here’s the code for a little test to get the areas that are on the ‘Gross Building’ scheme:

IList<SpatialElement> areas = new FilteredElementCollector( doc )
  .OfCategory( BuiltInCategory.OST_Areas )
  .OfClass( typeof( SpatialElement ) )
  .Cast<SpatialElement>()
  .ToList();

foreach( Element e in areas )
{
  AreaScheme _scheme = doc.GetElement( 
    e.get_Parameter( BuiltInParameter.AREA_SCHEME_ID )
      .AsElementId() ) as AreaScheme;

  string _AreaSchemeName = _scheme.get_Parameter( 
    BuiltInParameter.AREA_SCHEME_NAME ).AsString();

  if( _AreaSchemeName.ToString() == "Gross Building" )
  {
    TaskDialog.Show( "Revit", _AreaSchemeName );
    double ox = e.LookupParameter( "Area" ).AsDouble();
    TaskDialog.Show( "Revit", ox.ToString() );
  }
  else { continue; }
}

Answer: First, there are a couple of unnecessary inefficiencies in the sample code snippet.

There is no need for the Cast<>, and more importantly, ToList adds no value for this use case and consumes both time and memory, cf.:

I refactored the parameter accessing code as a separate little method to retrieve the area scheme name from the area element like this:

/// <summary>
/// Return the area scheme name of a given area element
/// using only generic Element Parameter access.
/// </summary>
static string GetAreaSchemeNameFromArea( Element e )
{
  if( !( e is Area ) )
  {
    throw new ArgumentException(
      "Expected Area element input argument." );
  }

  Document doc = e.Document;

  Parameter p = e.get_Parameter(
    BuiltInParameter.AREA_SCHEME_ID );

  if( null == p )
  {
    throw new ArgumentException(
      "element lacks AREA_SCHEME_ID parameter" );
  }

  Element areaScheme = doc.GetElement( p.AsElementId() );

  p = areaScheme.get_Parameter(
    BuiltInParameter.AREA_SCHEME_NAME );

  if( null == p )
  {
    throw new ArgumentException(
      "area scheme lacks AREA_SCHEME_NAME parameter" );
  }

  return p.AsString();
}

With that in hand, the retrieval of all areas matching a given area scheme can we rewritten like this:

/// <summary>
/// Retrieve all areas belonging to 
/// a specific area scheme.
/// </summary>
public IEnumerable<Element> GetAreasInAreaScheme(
  Document doc,
  string areaSchemeName )
{
  return new FilteredElementCollector( doc )
    .OfCategory( BuiltInCategory.OST_Areas )
    .OfClass( typeof( SpatialElement ) )
    .Where<Element>( e => areaSchemeName.Equals(
      GetAreaSchemeNameFromArea( e ) ) );
}

I added these two methods to The Building Coder samples ↗ in release 2017.0.132.10 ↗.

You can see the new code by comparing with the preceding release 2017.0.132.9 ↗.