😺

BIM Data Visualization with pyRevit & Streamlit

8 min read

1. What is pyRevit

pyRevit (with lowercase py) is a Rapid Application Prototyping (RAD) environment for Autodesk Revit®. It helps you quickly sketch out your automation and add-on ideas, in whichever language that you are most comfortable with, inside the Revit environment and using its APIs. It also ships with an extensive set of powerful tools that showcase its capabilities as a development environment. Download and install pyRevit, launch Revit, and note the new pyRevit tab that includes these tools. pyRevit also ships with a handy CLI utility for customized configuration and deployment of your tools, and a telemetry server to monitor pyRevit usage across your teams. - pyRevit Official Doc

How to Install

2. What is Streamlit

Streamlit allows you to develop the web data visualizization tool without front-end knowledge.
The command to install and execute streamlit in a sec.
As of May 2021, streamlit suports 3.6-3.8 verion of Python.
Before you run the streamlit command, make sure your python version with python3 --version in your terminal.

    pip install streamlit
    streamlit hello

3. What is pyRevit Routes

Let's say we want to create a web application that would display a list of doors in a model. The web application would be split into two parts.

  • Front-end: the part that runs in the browser and acts as the user interface, and
  • Back-end: the part that the front-end contacts to send and receive data

While you are free to select whatever toolchain and GUI framework (React, Vue.js, etc) you are comfortable with for the front-end, the challenge has always been on how to create a back-end that has access to Revit contexts and can query or modify Revit documents. What we really needed is to run a HTTP web server over a running Revit instance and manage the HTTP calls in a way that would be executed in Revit context.

The new Routes python module, is an HTTP micro-framework to create web APIs running over Revit instances. This means that you can create functionality that could be triggerd from remote. This framework provides the necessary mechanism to create a back-end that has access to Revit context. - pyRevit Developer Docs

4. pyRevit Route Step-by-step

folder structure for pyRevit Extension

pyRevit already has a fuction to create your custom addin with creating folder as below. After creating the folder, set up the file, whose name should be startup.py.

pyRevit-Sample-Extensions/
    |----pyRevitRoute.Extension/
        |----startup.py

5. Show Project Name in your browser

5.1 pyRevit-side

At first, we would like to get the project title under document class.

doc = __revit__.ActiveUIDocument.Document
title = doc.Title

After that, we need to specify the location to store the information.
At this time, we'd like to use /title under route-sample.
pyRevit Roputes is like flask that is web application library for python. If you've already experienced, it's more easy to understand what the script does.

from pyrevit import routes

api = routes.API('route-sample')


@api.route('/title')
def get_title(doc):
	return routes.make_response(data=doc.Title)

This is pretty difference from pyRevit Addin development but we need to reload in each time.
Then let's try to open your browser and url is http://localhost:48884/route-sample/title.
If your browser shows like below, it's all you have to do from pyRevit side.
Let's move to streamlit side.

5.2 Streamlit side

Let's read the Json data from pyRevit.
I'd like to use requests library to get the data.
Before you develop the streamlit app, please try the command to use requests library.

pip requests

To create Streamlit app, you need to prepare fot the app and create the file, app.py
The script for the app, it looks like below.

import streamlit as st
import requests 

BASE_URL = 'http://localhost:48884/route-sample/'

def get_title():
	url = BASE_URL + 'title'
	res= requests.get(url)
	if res.status_code == 200:
		return res.json()

st.title(get_title())

To run your application, please type the below command.

streamlit run app.py

If success, your browser shows like this.

6. Create Level table with pandas

6.1 pyRevit side

To create the table, we need to get the all levels in the projefct.
At this moment, I'm using Revit Python Wrapper to get all levels with ease.
And it's better to sort the revels by elevation using sorted method.

One minor note: I would like to use metric units world so I converted the Elevation unit from feet to mm. If you customize the unit, please change this line int the one you wish. level_dict['Elevation']= level.Elevation * 304.8

@api.route('/levels')
def get_levels(doc):
	level_list = []
	levels = db.Collector(
		of_category='Levels',
		is_not_type=True).get_elements()
	for level in levels:
		level_dict = {}
		level_dict['name'] = level.name
		level_dict['Elevation']= level.Elevation * 304.8
		level_list.append(level_dict)
	sorted_level_list = sorted(level_list, key=lambda x:x['Elevation'])
	return routes.make_response(data=sorted_level_list)

##6.2 Streamlit side
To show the table in your browser, the scripts need to update like below.

def get_levels():
	name_list = []
	elevation_list = []
	url = BASE_URL + 'levels'
	res= requests.get(url)
	if res.status_code == 200:
		for l in res.json():
			name_list.append(l['name'])
			elevation_list.append(l['Elevation'])
		level_tuple = list(zip(name_list, elevation_list))
		chart_data = pd.DataFrame(
			level_tuple,
			columns = ['Level Name','Elevation'])
		return chart_data

st.subheader(" Levels ")
st.dataframe(get_levels())

If success, your browser shows like this.

7. Making Bar chart to count furniture family instance by family type

pyRevit side

Let's make bar chart for furniture family
To prepare the data, pyRevit side script should update like below.

@api.route('/furniture')
def get_furnitures(doc):
	furnitures = db.Collector(
		of_category='Furniture',
		is_not_type=True).get_elements()
	furniture_dict = {}
	for w in furnitures:
		if w.Symbol.FamilyName in furniture_dict.keys():
			furniture_dict[w.Symbol.FamilyName] += 1 
		else:
			furniture_dict[w.Symbol.FamilyName] = 1
	return routes.make_response(data=furniture_dict)

Streamlit Side

Let's use st.bar_chart to show the bar chart.

def get_furnitures():
	url = BASE_URL + 'furniture'
	res= requests.get(url)
	if res.status_code == 200:
		furniture_dict = res.json()
		chart_data = pd.DataFrame(
			furniture_dict.values(),
			index=furniture_dict.keys())
		return chart_data

st.subheader(" Furniture Type ")
st.bar_chart(get_furnitures())

If success, your browser shows like this.

Thank you to read the article, I'm very happy to hear your comment/question.
Please feel free to reach out to me.