Skip to content

Commit

Permalink
gec002 (#5)
Browse files Browse the repository at this point in the history
* Change to using the ezdxf package and Constructive solid geometry for mesh generation
* Change to lambert conformal conic projection
* Adjust pin hole size
* Correct north and south faces
* Add quad lookup. 
* File sea level drop.
  • Loading branch information
gecrooks authored May 11, 2024
1 parent ac56309 commit abd5012
Show file tree
Hide file tree
Showing 10 changed files with 1,096 additions and 716 deletions.
2 changes: 2 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[flake8]
max-line-length = 99
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,9 @@ cython_debug/


cache
*.stl
junk
proto



129 changes: 62 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# landscape2stl: High resolution terrain models for 3D printing
# landscape2stl: Modular, high resolution terrain models for 3D printing


[Source](https://github.com/gecrooks/landscape2stl)


Gavin E. Crooks (2023)
Gavin E. Crooks (2024)


![Yosemite](images/yosemite_r2d2.jpeg)
Yosemite valley and environs, scale about 1" to 1 mile, overall size 24"x21". R2D2 for scale.
![Yosemite](images/yosemite.jpeg)
Yosemite valley and environs, scale about 1" to 1 mile, overall size 42"x43" (a little over 1 square meter).

![Magnets](images/landscape_magnets.jpeg)
Yosemite was printed in 9 segments, held together by magnets installed along the base.
Yosemite was printed in 30 segments, held together by magnets installed along the base.



The python script landscape2stl.py generates large scale terrain models as STL files suitable for 3D printing. I have
incorporated a number of innovations, namely a high resolution data source, novel map projection, and base alignment magnets.
incorporated a number of innovations, notably a high resolution data source and base alignment magnets.

Install dependencies,

Expand All @@ -35,99 +35,105 @@ dataset resolution isn't fine enough the model starts looking like Minecraft.)

The downside is that this dataset is only available for the contiguous 48 states, Hawaii, and some parts of Alaska.

## There is no projection

When you print a 2D map of a portion of the world you have to choose a map projection, since the world is spherical and the map is flat. There are many possible projections, and no projection is ideal since they must all inevitable distort the spherical reality to the flat plane.

While I was pondering the problem of which projection would be best for terrain models, a flash of inspiration hit me: I don't need a projection at all! With a 3D printer I can print in 3D! Each terrain segment created by landscape2stl.py is a gently curved wedge of the Earth. The sides slope inwards slightly, and the east-west lines of longitude are curved. At large scales this effect is subtle, but not insignificant even at a scale of 1 miles: 1 inch.


## Base alignment magnets

Since print beds are limited in size large landscapes have to be printed as a collection of segments. However, getting neighboring segments to line up satisfactorily can be tricky.

Inspired by the gridfinity project, I solved the alignment problem by adding holes along the base into which you can press-fit 6mm X 2mm
magnets. (I'm using DIYMAG brand from Amazon, which cost about 3¢ each.) Neighboring segments snap together with a satisfying click, with nigh on perfect alignment, and can be broken apart again for storage or transport.
magnets. (I'm using DIYMAG brand from Amazon, which cost about 5¢ each.) Neighboring segments snap together with a satisfying click, with nigh on perfect alignment, and can be broken apart again for storage or transport.

I've also added smaller holes along the sides for alignment pins (Use metal pins, or short lengths of filament).


## CLI: landscape2stl.py

```
> python -m landscape2stl.py --help
> python -m landscape2stl --help
usage: landscape2stl.py [-h]
[--preset REGION]
[--scale SCALE] [--exaggeration EXAGGERATION] [--magnets MAGNETS]
[N W S E ...]
usage: landscape2stl.py [-h]
[--preset {half_dome,west_of_half_dome,whitney,grand_canyon,shasta,shasta_west,joshua_tree,owens_valley,denali}] [--quad QUAD]
[--state STATE]
[--scale SCALE]
[--exaggeration EXAGGERATION]
[--resolution {10,30}]
[--magnets MAGNETS]
[--name NAME] [-v]
[S W N E ...]
Create landscape STLs
Create quadrangle landscape STLs
positional arguments:
N W S E Latitude/longitude coordinates for slab (Order North edge, west edge, south edge, east edge)
S W N E Latitude/longitude coordinates for quadrangle (Order south edge, west edge, north edge, east edge)
options:
-h, --help show this help message and exit
--preset REGION
--preset {half_dome,west_of_half_dome,whitney,grand_canyon,shasta,shasta_west,joshua_tree,owens_valley,denali}
--quad QUAD
--state STATE
--scale SCALE Map scale
--exaggeration EXAGGERATION
Vertical exaggeration
--resolution {10,30} DEM resolution
--magnets MAGNETS Magnet spacing (in degrees)
--name FILENAME
--name NAME Filename for model
-v, --verbose
```

### N W S E
### S W N E

Latitude/longitude coordinates for slab (Order south edge, west edge, north edge, east edge)


Latitude/longitude coordinates for slab (Order North edge, west edge, south edge, east edge)

### Preset
A collection of pre-selected regions that override explicit selection of coordinates. These regions
were used for development and illustration. Take a look at the code to see what the options are.

### Quad
Alternatively give the name of a USGS 7.5 minute quadrangle map. This will print at 1:62_500 scale.

One resource to find the name of quadrangle maps is https://livingatlas.arcgis.com/topomapexplorer/
(Select 1:24_000 scale for 7.5 minute maps.)

### Scale
Common scales for US maps include:
* 1:24_000, 1" = 2000', about 2.5" to 1 mile
* 1:62_500, about 1" to 1 mile
* 1:125_000, about 1" to 2 miles
* 1:1_000_000, about 1" to 16 miles

* 1:1_000_000 Approx 1 inch: 16 miles 2° x 2° 144 x 112 miles
* 1:250_000 Approx 1 inch: 4 miles 1/2° x 1/2°
* 1:62_500 Approx 1 inch: 1 mile 1/8° x 1/8° 9 x 7 miles
* 1:24_000 1" = 2000', about 2.5" to 1 mile
* 1:15_625 Approx 4 inches: 1 mile 1/32° x 1/32° 2 1/4 x 1 3/4 miles

Also listed are the approximate scale in inches per mile, the latitude and longitude extend of the quadrangle that will fit on a 256mm x 256mm print bed, and the approximate scaled size in miles.


### Exaggeration

Low scale models require vertical exaggeration else the landscape looks flat and uninteresting. Exaggeration isn't needed at scales of 1:125_000 or higher. Opinions vary as to appropriate exaggeration, but at 1:1_000_000 an exaggeration of 2:1 looks OK. (Just don't go the way of NASA when showing pictures of Maat Mons)
Low scale models require vertical exaggeration else the landscape looks flat and uninteresting. Exaggeration isn't needed at scales of 1:125_000 or higher. Opinions vary as to appropriate exaggeration, but at 1:1_000_000 an exaggeration of 3:1 looks OK. (Just don't go the way of NASA when showing pictures of Maat Mons) If not set explicitly, the exaggeration is set using a heuristic based on the scale.


### Magnet spacing
The spacing between magnets in degrees. If you use a standard scale a reasonable magnet spacing will be chosen for you. Set to 0.0
to disable magnets.
The spacing between magnets in degrees. If you use a standard scale a reasonable magnet spacing will be chosen for you.



## Slicing and Printing

* Printer: Ender 3 pro
* Printer: Bambu Lab P1S
* Nozzle: 0.4mm
* Layer height: 0.12mm
* Infill: 5%
* Filament: eSUN PLA PRO (PLA+), Cool White (print at 210c, 70c plate temperature)
* Layer height: 0.08mm
* Infill: 8%
* Filament: eSUN PLA PRO (PLA+), Cool White (Sometimes described as Cold White)
* Brim (for bed adhesion)

The filament I used is a bright, opaque white that highlights details by throwing valleys and folds into shadow.

With a layer height of 0.12mm contour lines are approximately 25' apart at a scale of 1" : 1 mile.

Bed adhesion is important since there is a tendency for larger prints to warp upwards at the corners.
Upper layers may self-correct, but the base and magnets will be out of alignment. After some frustration here are
the steps I took to ensure success.
With a layer height of 0.08mm contour lines are approximately 15' apart at a scale of 1" : 1 mile.

1) Carefully level the bed.
2) Print with a brim (easily removable with a deburring tool).
3) Increase plate temperature (I'm currently using 70c)
4) Use a glass build plate and spray adhesive (3DLAC Printer Adhesive).
5) Monitor the first layers, and abort if there is a failure to adhere.
After printing, use a deburring tool to clean up bottom edges, lightly sand the sides, and press-fit the magnets. Make sure the magnet orientations are consistent. I use the same orientation on North and East edges, and flip the orientation for the South and West edges. Make sure to use the same magnet orientations for each additional slab.

After printing, use a deburring tool to clean up bottom edges, lightly sand the sides, and press-fit the magnets. Make sure the magnet orientations are consistent. I use the same orientation on North and East edges, and flip the orientation for the south and west edges. Make sure to use the same magnet orientations for each additional slab.

To fit the magnets, take a stack of magnets, place over the magnet hole (in the correct orientation), and give the top of the stack a sharp tap with a mallet. If the magnet won't stay in its socket, add a small spot of superglue.
To fit the magnets, take a stack of magnets, place over the magnet hole (in the correct orientation), and give the top of the stack a few lights tap with a mallet. If the magnet won't stay in its socket, add a small spot of superglue.


## Known Issues
Expand All @@ -136,34 +142,23 @@ To fit the magnets, take a stack of magnets, place over the magnet hole (in the

This is an experimental prototype package of beta code for my own amusement. There is no support. Use at your own risk. Caveat emptor.

### Non-manifold models
The generated models have bugs. The Bambu Labs slicer will complain about "Non-manifold edges". Often these errors can be ignored, but sometimes the slicer gets upset, and the model needs to be repaired (Unfortunately Bambu only provides the repair functionality on Windows, not on Mac OS) The Cura slicer is more forgiving.

Some of these errors are being generated by hacks in my code that are there to get adequate performance out of the Computational Solid Geometry package in ezdxf, and some seem to be being generated by ezdxf itself.

### Limited data coverage

The 3DEP 1/3 arc second data set only covers the USA (And not even all of Alaska). Ideally we would fall back to other lower resolution datasets for the rest of the world.

### Projection

### Model arcs

Large enough models arc upwards due to the curvature of Earth's surface. The magnets are not strong enough to hold the arc themselves, so it may be necessary to add shims to support the middle segments of the arch.


### Flat base edges

The method for adding the magnet holes is an ugly hack. As a simplification, the surface that the magnets are embedded into is flat. That's correct for the North-South edges, but the East-West edges of the terrain actually curve, creating an odd break between the base and the heights. This can be seem in the Denali preset (Since Denali is far north the curvature of lines of longitude is more pronounced).

In practice, this mismatch between base and heights doesn't seem to be a problem, but the current solution is inelegant and should be rewritten using proper computational geometry.

I'm using a Lambert conformal conic projection with standard parallels of 33 and 45 degrees. This projection works well for the contiguous United States, but other projections would be better for Hawaii and Alaska, and for other regions of the world.

### Shorelines

Shorelines are generally not visible in the models.

(A possible fix would be to reduce the height of the ocean by some number of meters. Unfortunately the 3DEP dataset does not make it clear where land ends and the sea begins. You might think everything above sea level is land, but that produces bad looking coast lines. Setting sea level to 1m oddly produces better results, but not ideal. And also some areas of California, notable Death Valley, are below sea level.)


### Circular Contours
We drop ocean areas by a small amount to make shorelines more visible in the models. Unfortunately the 3DEP dataset does not make it clear where land ends and the sea begins. You might think that sea is at sea level, and everything above sea level is land, but that produces bad looking coast lines. Setting sea level to 1m oddly produces better results, but not ideal. Also some areas of California, notable Death Valley, are below sea level. And the sea is not always at zero meters (no not obviously good reason). I've added some heuristics to generate shores lines, but they are under-tested. Use at your own risk.

At the center of each terrain segment up is vertical, but at the edges of the segment up is slightly outwards because each segment is curved. This means the contour lines created by the successive layers of the 3D print are not actually horizontal. This is visible if you create a model of a very flat area, such as a patch of ocean. You get circular contour lines centered at the center of the segment. However, this effect isn't noticeable for any interestingly rugged piece of terrain.



26 changes: 26 additions & 0 deletions example_oahu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python

from landscape2stl import create_stl, STLParameters


params = STLParameters(
scale=250_000,
magnet_spacing=0.05,
exaggeration=2,
magnet_holes=True,
bolt_holes=False,
pin_holes=False,
)

ee = -158.3
nn = 21.30
delta = 0.1

# Oahu
for e in range(0, 7):
for n in range(0, 6):
name = f"oahu_250000_{e}{n}"
north = nn + n * delta
east = ee + e * delta
print(name, north, east, north-delta, east+delta)
create_stl(params, (north, east, north-delta, east+delta), name=name, verbose=True)
47 changes: 47 additions & 0 deletions example_yosemite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python

from landscape2stl import create_quad_stl


# Names of 7.5 minute quadrangles, north to south, blocks east to west.
yosemite_quadrangles = (
"Kibbie Lake",
"Tiltill Mountain",
"Piute Mountain",
"Matterhorn Peak",
"Dunderberg Peak",
"Lundy",
# "Negit Island",
"Lake Eleanor",
"hetch_hetchy_reservoir",
"ten_lakes",
"falls_ridge",
"tioga_pass",
"mount_dana",
# "lee_vining",
"Ackerson Mountain",
"tamarack_flat",
"yosemite_falls",
"tenaya_lake",
"vogelsang_peak",
"koip_peak",
# "june_lake",
"El Portal",
"el_capitan",
"half_dome",
"merced_peak",
"mount_lyell",
"mount_ritter",
# "mammoth_mountain",
"Buckingham Mountain",
"Wawona",
"Mariposa grove",
"Sing Peak",
"Timber Knob",
"Cattle Mountain",
# "Crystal Crag",
)


for name in yosemite_quadrangles:
create_quad_stl(name, "CA", verbose=True)
Binary file removed images/yosemite_r2d2.jpeg
Binary file not shown.
Loading

0 comments on commit abd5012

Please sign in to comment.