About

At 2,301 feet, Eagle Mountain is the highest point in the state of Minnesota. While it lacks prolific elevation, Eagle Mountain has 437 miles of isolation from the nearest higher point, which ranks it 10th among the most isolated peaks in the United States and its territories.

I’ve had the pleasure of hiking Eagle Mountain twice, and both times I was struck by the beauty of the terrain. The peak, in Superior National Forest, offers a cliff overlook (a feature only found in the northeast of the state) with views of a spattering of lakes (which charactertizes all of Minnesota). Physical topographic mapping provides the perfect opportunity to practice technical and artistic skills; given its status, Eagle Mountain makes the perfectsubject.

Materials

To build a wooden map, I used the following materials:

  • 1/8 inch wood panelling
  • wood glue
  • blue watercolor paints

Tools:

  • projector
  • pencil
  • razor
  • jigsaw
  • dremel tool with coarse sanding bit
  • paint brush
  • clamps
  • table saw (optional)

And data:

  • geographic features from OpenStreetMap (in osm format)
  • elevation data from NASA SRTM (in hgt / raster format)

Procedure

Computing map data

Trail and lake data are taken from the OpenStreetMap exporter UI in osm format:

extent <- list(xmin=-90.6043,
               xmax=-90.5383,
               ymin=47.8871,
               ymax=47.9180)
osm_path <- 'eagle_mountain_tile.osm'
em_lakes <- readOGR(osm_path, 'multipolygons')
## OGR data source with driver: OSM 
## Source: "/Users/kholub/snippets/national_forests/contours/eagle_mountain_tile.osm", layer: "multipolygons"
## with 70 features
## It has 25 fields
em_lines <- readOGR(osm_path , 'lines')
## OGR data source with driver: OSM 
## Source: "/Users/kholub/snippets/national_forests/contours/eagle_mountain_tile.osm", layer: "lines"
## with 209 features
## It has 9 fields
em_trail <- em_lines[em_lines$name %in% c("Eagle Mountain Hiking Trail"), ] 

The elevation is sourced from ViewFinderPanoramas and is simply read in as raster data:

em_elevation_raster <- raster('N47W091.hgt') %>%
  crop(unlist(extent))

Contours are computed using an undocumented algorithm in base R contourLines. I believe it is doing something akin to Marching Squares.

em_contours <- rasterToContour(em_elevation_raster, nlevels=15)

For building physical contour layers, contour layers of each elevation must be plotted separately. To these ends, a plotting function which shows contours, lakes (at base elevation), and any trail intersections:

plot_level <- function(elevation, contours, lakes, trail, plot_extent = extent, min_elevation='560') {
  relevant_contours <- contours[contours$level == elevation,]
  
  plot(relevant_contours,
       xlim=c(plot_extent$xmin, plot_extent$xmax),
       ylim=c(plot_extent$ymin, plot_extent$ymax),
       axes=TRUE,
       lwd=5,
       main=elevation)
  
  # TODO this is not geopgrahically accurate, since some lakes are at elevation
  # could derive the heights and include in contours... but there are clearly
  # some accuracy errors in contour line placement and lake position. so
  # probably best to artistically eyeball
  if (elevation == min_elevation) {
    plot(lakes, col = 'blue',
         xlim=c(plot_extent$xmin, plot_extent$xmax),
         ylim=c(plot_extent$ymin, plot_extent$ymax),
         add=TRUE)
    plot(trail,
         col = 'green',
         xlim=c(plot_extent$xmin, plot_extent$xmax),
         ylim=c(plot_extent$ymin, plot_extent$ymax),
         add=TRUE)
  }
  
  trail_intersections <- gIntersection(trail, relevant_contours)
  
  if (!is.null(trail_intersections)) {
    plot(trail_intersections, 
         col='red',
         lwd=5,
         add=TRUE)
  }
  
  axis(side = 1, lwd = 5)
  axis(side = 2, lwd = 5)
  box(lwd=5)
}

which can be visualized:

for (ele in levels(em_contours$level)) {
  jpeg(paste0("static/contours/", ele, ".jpg"), width=1500, height=1500)
  plot_level(ele, em_contours, em_lakes, em_trail)
  dev.off()
}