Code inspired from a project by Milan Janosov to create a stylized elevation map of the world.
You can read about his work creating a Lego elevation map of Budapest here on his Substack.
Lego has previously made a world map (see below), but I think I could do a little better!
Downloading DEMs
Merging DEMs
Create Grid
Map Elevation Per Grid Cell
Discretize Elevation into Bins
Quantize Surface Color into Bins
Discretize color image and make patches for building
Resize image and apply grid
Overview of the patches
Save patches
Future project goals
Project challenges and solutions
I went to USGS' EarthExplorer to download all the GeoTIFF tiles from the Global 30 Arc-Second Elevation (GTOPO30) dataset:
I manually downloaded all 33 tiles in the dataset. Here's an example of what the raw GeoTiff looks like:
The challenge here was finding the right process to merge the DEMs, as each one was on the order of 50 MB. What worked was to create a VRT, or Virtual Raster Table from the GDAL library, which helps relieve the memory burden for the system.
Using matplotlib, it was easy to save the resulting .tif file as a JPG for pretty viewing:
Now we have to rasterize the elevation map. This is crucial as we'll be eventually making this map in Lego, which are bricks (duh).
We need to have a look at the boundaries of the world. I have a file downloaded already. Since the file has an entry for every country, we will get distracting boundary lines in our polygon. Using the union_all
method, I can merge all the polygons into a single massive polygon. Let's take a look at the result:
We'll have to figure out what the dimensions of the Lego set are (in Lego studs), as that is a good reference size for our eventual Lego map.
- The map is 5 x 16 or 80 studs in height and
- 8 x 16 or 128 studs in width
Using a function from Milan, we can create a grid over the polygon. Let's look at the contiguous USA:
Using zonal statistics, I calculated the mean elevation for each grid cell.
Here, I've shown 7 bins of elevation using the quantile binning method that allows for roughly equal representation of the amount of data into each bin.
We all know maps with the surface color of Earth, like this:
But that file has over 200,000 individual colors, waaay more than what Lego possesses. I first brightened the image and then used the median cut algorithm to lower the colorspace of the image down to 12 colors. You'll notice that half the colors are of the blue ocean, but that's ok. The important thing is to have enough colors to make the land surface interesting.
I resized the image to be 160 units by 80 units becuase that was faithful to the Blue Marble image. I also plotted the colors contained within the image, which would be helpful for someone actually building the map:
I created an image with a "patched" version of the map to show all 50 patches needed to build the map:
I saved the patches for future use. Here is what a single patch looks like:
And that completes the project!
Now where did that last Lego piece go...
- Make the project into a function
- Post it to my website so others can make their own maps
- Challenge: How to merge DEMs without overloading system memory?
Solution: Use virtual raster tables
- Challenge: Artifacts appear when resizing the DEM
Solution: First troubleshoot on which layer (DEM, world shoreline polygon, etc.) is giving me trouble, then re-do the grid-extract-calculate process to do it all in one step