Elasticsearch-Head, RevitLookup and Area Schemes

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( typeofSpatialElement ) )
  .Cast<SpatialElement>()
  .ToList();

foreachElement 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 );

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

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

  p = areaScheme.get_Parameter(
    BuiltInParameter.AREA_SCHEME_NAME );

  ifnull == 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( typeofSpatialElement ) )
    .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.