Mapping a Floor Plan CAD file in Tableau
Skip preamble and see Github repo, Google Colab notebook, and video walkthrough.
Background Motivation
Engineers and architects use CAD files to create, manage, and store building designs and floor plans. CAD files are rich with data, but if you hand a CAD file to a Tableau user without access to a CAD program or GIS tool, you’re out of luck. That is a shame because a Tableau user could take the floor plan data, overlay it with data living in spreadsheets or databases, and do some pretty cool things, such as:
track the flow of patients through a hospital to identify bottlenecks
-or-
help retailers improve store layouts by overlaying sales data onto the floor plan
This post will hopefully help unlock these types of use cases. I will walk through a Google Colab notebook that uses open source Python libraries to extract geographic data from a CAD file floor plan, converts the data into latitude and longitude, and maps it in Tableau.
Link to Github repo, Google Colab notebook, and video walkthrough
The Colab notebook and video go into detail on each step, so I won’t rehash that here, but I will outline the broad steps, along with commentary.
Upload CAD file
When I started this project, I originally wanted to read a DWG file. However, I hit so many roadblocks trying to read a DWG file into free Python libraries that I eventually gave up and switched to DXF. DWG is a proprietary AutoCAD format. AutoCAD let’s you export your DWG files as DXF. I’m not a CAD expert, so I’m not aware of any downsides of exporting to DXF, but for our floor plan mapping use case, I didn’t run into any issues using the DXF export. There’s some interesting commentary on Wikipedia about "DWG support in freemium and free software."
Read CAD file into GeoDataFrame
floorplan_gdf = gpd.read_file(file_path)
GeoPandas makes it so easy to read DXF files. I originally was using the ezdxf Python package, which worked fine, but after stumbling on this tutorial from Hatari Labs, I realized how much easier GeoPandas would be for this use case. If your CAD file was already georeferenced (e.g. it already had real world coordinates associated with the entities) -or- you are satisfied with plotting your floor plan as X, Y coordinates on a scatter plot, then you can stop at this step and export to CSV!
*Note if you stop at this step, you will need to extract the X, Y coordinates from the Geometries.
gdf['X'] = gdf['geometry'].x gdf['Y'] = gdf['geometry'].y
The X, Y for Points can be easily visualized as a scatter plot. For the lines, you will need to structure the data so that Tableau can understand and plot the paths.
Split GeoDataFrame into multiple GeoDataFrames
Tableau doesn’t handle multiple geometry types stored in a single geometry column. Maybe this applies to other software too? I’m not sure. I thought it’d be simpler to split out the GeoDataFrame into multiple GeoDataFrames based on geometry type.
Find control points
This step entails mapping our CAD file’s local X, Y coordinates to real world longitude and latitude. I end up using 5 control points. Check out the video discussing this step starting at 3:39. Since longitude and latitude aren’t linear, it may have been more mathematically sound to map our local X, Y to a planar coordinate system.
Create and apply linear transformation
I originally was looking at geospatial libraries that map local X, Y coordinates to lat, lon, but I eventually settled on a simple linear regression. This is not a complicated multi-factor regression, it’s simply good ol’ “y = mx + b”.
We use 2 linear regressions - one for X and one for Y.
I evaluate the R^2 value to confirm the transformation works. If you’re following along and your R^2 isn’t very close to 1, then something has gone wrong.
If you want to look at a different approach, check out the Hatari Labs tutorial that I referenced before.
Output the GeoDataFrame that now contains Lat, Lon to GeoJSON
point_gdf.to_file("points_floorplan.geojson", driver='GeoJSON')
In my example, I create 2 GeoJSON’s. One for points and another for linestrings.
Confirm everything is correct by visualizing in Tableau.
After many failed attempts at this, it was so satisfying to see the points line up properly on the map.
Thanks for reading, and I hope this post helps you build something cool and useful!