├── README.md ├── presentation ├── big.css ├── big.js ├── img │ ├── dem.png │ ├── dem_sm.png │ ├── example-output.png │ ├── gdal_hillshade.jpg │ ├── hawaii-seafloor.jpg │ ├── ne_hyp_sr.jpg │ ├── ne_hyp_sr_zoomed.jpg │ ├── ne_msr.jpg │ ├── sr_hr_zoomed.png │ ├── sr_hr_zoomed2.jpg │ ├── sr_lr_zoomed.png │ ├── sr_lr_zoomed2.jpg │ ├── usgs-ned.png │ └── usgs-srtm.png └── index.html └── resources ├── ESRI_VE_chart.jpg ├── Web_relief_chart.pdf └── make-hillshades.sh /README.md: -------------------------------------------------------------------------------- 1 | GDAL Hillshade Tutorial 2 | ======================= 3 | Chris Henrick 4 | MapTime NYC 5 | Winter 2015 6 | 7 | ![](presentation/img/example-output.png) 8 | 9 | ## Description 10 | 11 | Participants will learn how to work with [Digital Elevation Model](http://en.wikipedia.org/wiki/Digital_elevation_model) data and use [GDAL](http://en.wikipedia.org/wiki/GDAL) to generate a [Shaded Relief / Hillshade](http://en.wikipedia.org/wiki/Terrain_cartography) for the Kings Canyon National Park area, in the southern Sierra Nevada mountain range, California. The commands in this tutorial are meant to be run in the Bash shell on Mac OS X or a Linux OS but these processes can also be accomplished using [QGIS](http://www2.qgis.org/en/site/). 12 | 13 | ## Assumptions 14 | 15 | Ths tutorial assumes you have GDAL installed and that it is accessible from a [Command Line Interface](http://en.wikipedia.org/wiki/Command-line_interface) such as the [Terminal App](http://guides.macrumors.com/Terminal). Some familiarity with the Unix CLI is beneficial but not required. 16 | 17 | MapBox has a [good tutorial on setting up GDAL](https://www.mapbox.com/tilemill/docs/guides/gdal/) if you need to do so. 18 | 19 | ## The Tutorial 20 | 21 | ### Step 1: Download data: 22 | 1. Make a new folder for this tutorial. Call it something like `gdal_hillshade_tutorial`. 23 | - Then open up your terminal and `cd` to that folder by doing: 24 | `cd /Users/username/gdal_hillshade_tutorial/`. 25 | 26 | - Once you're in your project folder make another directory by doing `mkdir one-arc-second`, and then `cd` to it by doing `cd one-arc-second`. 27 | 28 | 2. Put the following text in a file called url_list.txt: 29 | 30 | ``` 31 | ftp://rockyftp.cr.usgs.gov/vdelivery/Datasets/Staged/NED/1/GridFloat/n37w119.zip 32 | ftp://rockyftp.cr.usgs.gov/vdelivery/Datasets/Staged/NED/1/GridFloat/n38w119.zip 33 | ftp://rockyftp.cr.usgs.gov/vdelivery/Datasets/Staged/NED/1/GridFloat/n37w120.zip 34 | ftp://rockyftp.cr.usgs.gov/vdelivery/Datasets/Staged/NED/1/GridFloat/n38w120.zip 35 | ``` 36 | 37 | 3. Then using the `w-get` utility (available on Linux or via [Homebrew](http://brew.sh/) on a Mac), do: 38 | `wget -i url_list.txt`. Go grab a beer or coffee. 39 | 40 | 4. When the files have finished downloading unzip them by doing `open *.zip`. 41 | 42 | **FYI:** This data comes from the USGS National Elevation Dataset which is available for download via the [National Map Viewer.](http://viewer.nationalmap.gov/viewer/) 43 | 44 | ### Step 2: Process DEM data 45 | 46 | First `cd` up one directory, make a new directory called `processed_data` and `cd` to it. 47 | 48 | 1. Build a VRT file with GDAL: 49 | 50 | ``` 51 | gdalbuildvrt kings_canyon.vrt \ 52 | ../one-arc-second/n37w119/floatn37w119_1.flt \ 53 | ../one-arc-second/n37w120/floatn37w120_1.flt \ 54 | ../one-arc-second/n38w119/floatn38w119_1.flt \ 55 | ../one-arc-second/n38w120/floatn38w120_1.flt 56 | ``` 57 | 58 | 2. Create a GeoTiff from the VRT file: 59 | 60 | `gdal_translate -of GTiff kings_canyon.vrt kings_canyon.tif` 61 | 62 | 3. Project the data to CA State Plane 4 ft / EPSG:2228 and clip it to the surrounding area of the park 63 | 64 | ``` 65 | gdalwarp \ 66 | -s_srs EPSG:4269 \ 67 | -t_srs EPSG:2228 \ 68 | -te 6513124 1921767 6841766 2380645 \ 69 | -r bilinear \ 70 | kings_canyon.tif kings_canyon_2228.tif 71 | ``` 72 | 73 | - **note**: the `-r bilinear` flag is useful to help avoid weird artifacts being created in the data during the reprojection process. 74 | 75 | ### Step 3: Generate the Hillshade, Slope Shade, and Color Relief 76 | #### To Make the Hillshade: 77 | 78 | Using the GDAL DEM tools we can now generate a hillshade: 79 | 80 | `gdaldem hillshade -az 45 -z 1.3 kings_canyon_2228.tif hillshade_az45.tif` 81 | 82 | - **note**: the `-az` flag is for the light direction and the `-v` flag is for vertical exaggeration. These may be changed as desired. 83 | - Generally, the smaller the scale of the map the more vertical exaggeration you would want to use. See the vertical exaggeration chart in the resources directory. 84 | 85 | #### To Make the Color Relief: 86 | 87 | 88 | Make a `color-relief.txt` file with the following values inside it: 89 | 90 | ``` 91 | 0 110 220 110 92 | 925 240 250 160 93 | 1850 230 220 170 94 | 2775 220 220 220 95 | 3700 250 250 250 96 | ``` 97 | 98 | Then generate the color-relief: 99 | 100 | `gdaldem color-relief kings_canyon_2228.tif color-relief.txt kings_canyon_color_relief.tif` 101 | 102 | #### To Make the Slope Shade: 103 | 104 | Generate a slope: 105 | `gdaldem slope kings_canyon_2228.tif slope.tif` 106 | 107 | Make a color-slope.txt file that will be used to generate the slope shade: 108 | `touch color-slope.txt && printf '%s\n%s\n' '0 255 255 255' '90 0 0 0' >> color-slope.txt` 109 | 110 | Finally make the slope shade: 111 | `gdaldem color-relief slope.tif color-slope.txt slope-shade.tif` 112 | 113 | ### Step 4: Combine all 3 layers in QGIS, TileMill or Photoshop. 114 | Open the files in the software of your choice and layer them in the following order: 115 | 116 | 1. Color Relief 117 | 2. Slope Shade 118 | 3. Hillshade 119 | 120 | Then set the opacity for the top two layers to 50-80%. Try experimenting with this. 121 | 122 | - In Tile Mill you can also use the `comp-op: multiply` effect which affects the way the layers are blended. If working in Tile Mill it's best to have your data projected to `EPSG:3785`. The following `CartoCSS` blends data well and you can tweak it by adjusting the opacity value and comp-op parameter: 123 | 124 | ``` 125 | Map { 126 | background-color: #fff; 127 | } 128 | 129 | #hillshadeaz45 { 130 | raster-opacity:0.8; 131 | } 132 | 133 | 134 | #slopeshade { 135 | raster-opacity:0.8; 136 | comp-op:multiply; 137 | } 138 | 139 | 140 | #colorrelief { 141 | raster-opacity:0.8; 142 | comp-op:multiply; 143 | } 144 | ``` 145 | 146 | 147 | - You could also composite these three files in Photoshop, though you'll have to georeference the composited file afterward as Photoshop won't transfer the data that contains the georeference information from the original files. To do this use `gdalinfo` to find the extent of one of the original files and then use `gdal_translate` utility like: 148 | 149 | ``` 150 | gdal_translate -of GTiff \ 151 | -a_ullr \ 152 | -a_srs EPSG:2228 \ 153 | photoshopped_terrain.tif photoshopped_terrain_2228.tif 154 | ``` 155 | 156 | 157 | ### Resampling Terrain Data 158 | If we want our hillshade to be less detailed we can resample our DEM data. Basically all we are doing is making the file a smaller size in pixel measurements (not the actual area it represents) so that each pixel will represent a larger square meter area. This is similar to reducing and resampling the size of a regular image in Photoshop or Gimp. 159 | 160 | `gdalwarp -ts 3000 0 -r bilinear kings_canyon_2228.tif kings_canyon_2228_rs.tif` 161 | 162 | - **note** leaving either the width or height as 0 will let GDAL guess the other dimension based on the input file size's aspect ratio. 163 | 164 | Using `gdalinfo` we can see the resolution of our resampled data. Check the `Pixel Size` value in the output after doing: `gdalinfo kings_canyon_2228_rs.tif`. You'll see that it's larger than the original file. 165 | 166 | You can then do the above steps to generate hillshade, slopeshade, etc. with the resampled data. 167 | 168 | 169 | ## Resources: 170 | Located inside the resources directory. 171 | 172 | - Tom Pattersons' web map shaded relief guide. 173 | - ESRI's vertical exaggeration chart. 174 | - Make-hillshade.sh is a [bash shell script](http://en.wikibooks.org/wiki/Bash_Shell_Scripting) to automate GDAL hillshade process. Takes the file name for a DEM as an argument when running, eg: `./make-hillshade.sh my-dem-data.tif` 175 | 176 | ## Other helpful links 177 | - [Shadedrelief.com](http://www.shadedrelief.com/) 178 | - [Thematic Mapping Blog](http://blog.thematicmapping.org/2012/06/digital-terrain-modeling-and-mapping.html) 179 | - [GDAL Cheat Sheet](https://github.com/dwtkns/gdal-cheat-sheet) 180 | - [EPSG.io](http://epsg.io) 181 | - [Natural Earth Data](http://www.naturalearthdata.com/downloads/) for pre-rendered small scale hillshades. -------------------------------------------------------------------------------- /presentation/big.css: -------------------------------------------------------------------------------- 1 | /*big.css*/ 2 | body { 3 | font-family: 'Helvetica'; 4 | letter-spacing:-1px; 5 | line-height: 120%; 6 | background:#000; 7 | background-size:100%; 8 | color:#fff; 9 | margin:0; 10 | padding:0; 11 | font-weight:600; 12 | } 13 | 14 | h1, h2, h3, p { 15 | margin:0; 16 | letter-spacing: 0; 17 | line-height: 120%; 18 | } 19 | 20 | em, a { 21 | font-style:normal; 22 | color:#8dbd0c; 23 | } 24 | 25 | a { 26 | background: #34d0e7; 27 | color:#000; 28 | text-decoration:none; 29 | } 30 | 31 | img { 32 | width:100%; 33 | } 34 | 35 | ul li { 36 | position: relative; 37 | /*left: 10%;*/ 38 | } 39 | 40 | div { 41 | cursor:pointer; 42 | cursor:hand; 43 | position:absolute; 44 | top:0px; 45 | left:0px; 46 | padding:75px; 47 | line-height:110%; 48 | } 49 | 50 | div.center { 51 | text-align:center; 52 | } 53 | 54 | div.imageText { 55 | text-shadow:0px 0px 5px rgba(0,0,0,0.8); 56 | } -------------------------------------------------------------------------------- /presentation/big.js: -------------------------------------------------------------------------------- 1 | window.onload = function() { 2 | var s = document.getElementsByTagName('div'), cur = 0, ti; 3 | if (!s) return; 4 | function go(n) { 5 | cur = n; 6 | var i = 1e3, e = s[n], t; 7 | for (var k = 0; k < s.length; k++) s[k].style.display = 'none'; 8 | e.style.display = 'inline'; 9 | e.style.fontSize = i + 'px'; 10 | if (e.firstChild.nodeName === 'IMG') { 11 | document.body.style.backgroundImage = 'url(' + e.firstChild.src + ')'; 12 | e.firstChild.style.display = 'none'; 13 | if ('classList' in e) e.classList.add('imageText'); 14 | } else { 15 | document.body.style.backgroundImage = ''; 16 | document.body.style.backgroundColor = e.style.backgroundColor; 17 | } 18 | if (ti !== undefined) window.clearInterval(ti); 19 | t = parseInt(e.getAttribute('time-to-next') || 0, 10); 20 | if (t > 0) ti = window.setTimeout(fwd, (t * 1000)); 21 | while ( 22 | e.offsetWidth > window.innerWidth || 23 | e.offsetHeight > window.innerHeight) { 24 | e.style.fontSize = (i -= 2) + 'px'; 25 | if (i < 0) break; 26 | } 27 | e.style.marginTop = ((window.innerHeight - e.offsetHeight) / 2) + 'px'; 28 | if (window.location.hash !== n) window.location.hash = n; 29 | document.title = e.textContent || e.innerText; 30 | } 31 | document.onclick = function() { go(++cur % (s.length)); }; 32 | function fwd() { go(Math.min(s.length - 1, ++cur)); } 33 | function rev() { go(Math.max(0, --cur)); } 34 | document.onkeydown = function(e) { 35 | if (e.which === 39) fwd(); 36 | if (e.which === 37) rev(); 37 | }; 38 | document.ontouchstart = function(e) { 39 | var x0 = e.changedTouches[0].pageX; 40 | document.ontouchend = function(e) { 41 | var x1 = e.changedTouches[0].pageX; 42 | if (x1 - x0 < 0) fwd(); 43 | if (x1 - x0 > 0) rev(); 44 | }; 45 | }; 46 | function parse_hash() { 47 | return Math.max(Math.min( 48 | s.length - 1, 49 | parseInt(window.location.hash.substring(1), 10)), 0); 50 | } 51 | if (window.location.hash) cur = parse_hash() || cur; 52 | window.onhashchange = function() { 53 | var c = parse_hash(); 54 | if (c !== cur) go(c); 55 | }; 56 | 57 | go(cur); 58 | }; -------------------------------------------------------------------------------- /presentation/img/dem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/dem.png -------------------------------------------------------------------------------- /presentation/img/dem_sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/dem_sm.png -------------------------------------------------------------------------------- /presentation/img/example-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/example-output.png -------------------------------------------------------------------------------- /presentation/img/gdal_hillshade.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/gdal_hillshade.jpg -------------------------------------------------------------------------------- /presentation/img/hawaii-seafloor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/hawaii-seafloor.jpg -------------------------------------------------------------------------------- /presentation/img/ne_hyp_sr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/ne_hyp_sr.jpg -------------------------------------------------------------------------------- /presentation/img/ne_hyp_sr_zoomed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/ne_hyp_sr_zoomed.jpg -------------------------------------------------------------------------------- /presentation/img/ne_msr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/ne_msr.jpg -------------------------------------------------------------------------------- /presentation/img/sr_hr_zoomed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/sr_hr_zoomed.png -------------------------------------------------------------------------------- /presentation/img/sr_hr_zoomed2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/sr_hr_zoomed2.jpg -------------------------------------------------------------------------------- /presentation/img/sr_lr_zoomed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/sr_lr_zoomed.png -------------------------------------------------------------------------------- /presentation/img/sr_lr_zoomed2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/sr_lr_zoomed2.jpg -------------------------------------------------------------------------------- /presentation/img/usgs-ned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/usgs-ned.png -------------------------------------------------------------------------------- /presentation/img/usgs-srtm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/presentation/img/usgs-srtm.png -------------------------------------------------------------------------------- /presentation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
use the
◀ & ▶ keys to navigate
11 |
Maptime NYC
Hillshade Tutorial
12 |
Follow along with this presentation at bit.ly/hillshade
13 |
Hillshades!
14 |
What are they?
15 |
16 |
17 |
18 |
What kind of data is used?
19 |
Digital Elevation Model (DEM)

20 |
Why make a hillshade?
21 |
Because good cartography = customization
22 | 23 |
24 |
Free & Open Source Software for making hillshades
25 |
26 |
    27 |
  • GDAL
    (command line software)

  • 28 |
  • QGIS
    (desktop GIS software)
  • 29 |
30 |
31 |
Open Data Sources
32 |
33 |
    34 |
  • National Elevation Dataset (NED)
    (hi-res / large scale)

  • 35 |
  • Shuttle Radar Topography Mission (STRM)
    (low-res / small scale)
  • 36 |
37 |
38 |
39 |
40 |
The Demo...
41 |
1. Download DEM data from the USGS.
42 |
2. Process the data.
43 |
3. Generate the terrain layers.
44 |
4. Composite the layers in QGIS, Tile Mill or Photoshop.
45 |
All done. Yay!
46 | 47 | 48 | -------------------------------------------------------------------------------- /resources/ESRI_VE_chart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/resources/ESRI_VE_chart.jpg -------------------------------------------------------------------------------- /resources/Web_relief_chart.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clhenrick/gdal_hillshade_tutorial/1d2361d54fab2d82622dc7e5569e65163b1bf7f2/resources/Web_relief_chart.pdf -------------------------------------------------------------------------------- /resources/make-hillshades.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Takes a Digital Elevation Model (DEM) and generates hillshades from 4 different light angles and a slope shade 3 | # These may then be composited in QGIS, TileMill, Photoshop, etc. 4 | # Requires a color-slope.txt file containing the following: 0 255 255 255 \n 90 0 0 0 5 | # Note: Process DEM prior to running this script (mosaic, clip, resample, reproject, etc) 6 | 7 | GFLT=$1 #must be a raster DEM file type supported by GDAL 8 | Z=1.3 # vertical exaggeration factor. apply greater value for smaller scale / larger areas 9 | 10 | echo "Making color-slope.txt..." 11 | touch color-slope.txt && printf '%s\n%s\n' '0 255 255 255' '90 0 0 0' >> color-slope.txt 12 | 13 | echo "Generating hillshade from $GFLT with sunlight angle at 45˚..." 14 | gdaldem hillshade -of 'GTiff' -z $Z -az 45 $GFLT hillshade_az45.tif 15 | 16 | echo "Generating hillshade from $GFLT -z $Z with sunlight angle at 135˚..." 17 | gdaldem hillshade -of 'GTiff' -z $Z -az 135 $GFLT hillshade_az135.tif 18 | 19 | echo "Generating hillshade from $GFLT -z $Z with sunlight angle at 225˚..." 20 | gdaldem hillshade -of 'GTiff' -z $Z -az 225 $GFLT hillshade_az225.tif 21 | 22 | echo "Generating hillshade from $GFLT -z $Z with sunlight angle at 315˚..." 23 | gdaldem hillshade -of 'GTiff' -z $Z -az 315 $GFLT hillshade_az315.tif 24 | 25 | echo "Generating Slope from $GFLT..." 26 | gdaldem slope $GFLT slope.tif 27 | 28 | echo "Creating slope shade..." 29 | gdaldem color-relief slope.tif color-slope.txt slopeshade.tif 30 | --------------------------------------------------------------------------------