{"id":43,"date":"2019-03-10T08:00:00","date_gmt":"2019-03-10T08:00:00","guid":{"rendered":"http:\/\/wcwp.rf.gd\/2019\/03\/10\/https-landscapearchaeology-org-2019-qgis-shadows\/"},"modified":"2019-03-10T08:00:00","modified_gmt":"2019-03-10T08:00:00","slug":"qgis-shadows","status":"publish","type":"post","link":"https:\/\/landscapeanalysis.org\/?p=43","title":{"rendered":"Enhancing terrain cartography with natural shadows"},"content":{"rendered":"<p><img decoding=\"async\" class=\"alignnone size-medium wp-image-67\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/Algo-window.JPG\" alt=\"Algo-window.JPG\" \/><br \/>\n<em>QGIS algorithm for natural shadow modelling.<\/em><\/p>\n<h2 id=\"introduction\">Introduction<\/h2>\n<p>Modelling natural illumination is quite difficult in software for digital cartography. Yes, we do have hillshading algorithms, but despite the name they do not produce true shade. Hillshade routines colour the terrain according to exposition towards the sun (white for directly exposed and black for unexposed areas),  <em>but they do not model the light path<\/em>. There are no shadows in hillshades. As a result, a canyon floor will be rendered the same way as a wide valley, which impedes the perception of depth. Hillshades often have a feeling of \u201celephant skin\u201d, as if the Earth surface were wrinkled by long term cosmetic abuse.<\/p>\n<p><iframe loading=\"lazy\" frameborder=\"0\" class=\"juxtapose\" width=\"100%\" height=\"750\" src=\"https:\/\/cdn.knightlab.com\/libs\/juxtapose\/latest\/embed\/index.html?uid=1d278554-40e8-11e9-9c6a-0edaf8f81e27\"> <\/iframe><\/p>\n<p><em>Simple hillshade vs natural shadows: slide to compare<\/em><\/p>\n<p>Compare these two images: shadows are not just nice, they enable better understanding of the terrain. Today, cartographers typically turn to 3D software, such as Blender, to obtain the effect of natural lighting (see for example <a href=\"https:\/\/www.scottreinhardmaps.com\/shop\">scottreinhardmaps.com<\/a>). Historically the effect was achieved by hand (figure below).<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-medium wp-image-67\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/jungfraugruppe.jpg\" alt=\"jungfraugruppe.jpg\" \/><br \/>\n<em>Jungfrau range (European Alps) by E. Imhof (see on <a href=\"http:\/\/www.reliefshading.com\/examples\/jungfrau\/\">reliefshading.com<\/a>.<\/em><\/p>\n<p>But, why should we drop all benefits of georeferenced environment and revert to software which can\u2019t handle geographical data? It is not that difficult to simulate natural light, provided that we do not expect the same level of quality as in dedicated 3D doftware.<\/p>\n<p>I have developed a short algorithm to simulate incident light over digital terrain models, namely the shadows it should produce. Rather than simple yes\/no format, <em>shadows are represented as depth values<\/em>. In natural environment, there is a considerable difference between two metres deep shadow, as behind a small mound and hundreds of metres deep shadow, behind a cliff. Taking care of such nuances can help as to get closer to the refined effect of handmade hillshades.<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-medium wp-image-67\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/Puy-de-Dome.jpg\" alt=\"Puy-de-Dome.jpg\" \/><br \/>\n<em>Deep shadow below Puy de D\u00f4me, France (<a href=\"http:\/\/www.baladomes.com\">\u00a9 baladomes.com<\/a>).<\/em><\/p>\n<h2 id=\"algorithm\">Algorithm<\/h2>\n<p>For those interested in the code behind my solution, these are the two crucial lines:<\/p>\n<div class=\"language-python highlighter-rouge\">\n<div class=\"highlight\">\n<pre class=\"highlight\"><code><span class=\"kn\">import<\/span> <span class=\"nn\">numpy<\/span>\n<span class=\"n\">shadows<\/span> <span class=\"o\">=<\/span> <span class=\"n\">elevations<\/span> <span class=\"o\">-<\/span> <span class=\"n\">numpy<\/span><span class=\"p\">.<\/span><span class=\"n\">maximum<\/span><span class=\"p\">.<\/span><span class=\"n\">accumulate<\/span><span class=\"p\">(<\/span> <span class=\"n\">elevations<\/span> <span class=\"p\">)<\/span>\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>The accumulate function takes care that values in the (numpy) array which stores the elevation model would only increase, from the beginning of the array to the end. Imagine walking over a mountain, along a path which is constantly winding upwards and downwards. If you would like to move only upwards, you would need to fill all valleys with soil or water. The accumulate function does just that, it simulates a cascading terrain profile where we can travel either upwards or over a flat terrain. Filled portions of the terrain become shadows if you imagine the sun shining into a flank of a mountain. Shadow depth is calculated by subtracting the cascading model from the actual terrain height.<\/p>\n<div class=\"language-python highlighter-rouge\">\n<div class=\"highlight\">\n<pre class=\"highlight\"><code><span class=\"kn\">from<\/span> <span class=\"nn\">math<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">tan<\/span><span class=\"p\">,<\/span> <span class=\"n\">radians<\/span>\n<span class=\"n\">tilt_factor<\/span> <span class=\"o\">=<\/span> <span class=\"n\">tan<\/span><span class=\"p\">(<\/span><span class=\"n\">radians<\/span><span class=\"p\">(<\/span><span class=\"n\">sun_angle<\/span><span class=\"p\">))<\/span> <span class=\"o\">*<\/span> <span class=\"n\">pixel_size<\/span>\n<span class=\"n\">tilt_matrix<\/span> <span class=\"o\">=<\/span> <span class=\"n\">tilt_factor<\/span> <span class=\"o\">*<\/span> <span class=\"p\">(<\/span><span class=\"n\">indices_x<\/span> <span class=\"o\">+<\/span> <span class=\"n\">indices_y<\/span><span class=\"p\">)<\/span>   \n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>To simulate the vertical angle from which the sun is shining over the terrain, we rather tilt the entire elevation model. That is, we construct a sloping surface and the drape the terrain over it. Indices in the line above can be understood as distance values where the size of pixel is one. They increase from the corner (or the border) closest to the sun position. Note that we decompose distances on x axis and on y axis components: let\u2019s pretend these are vector components (see the previous post on <a href=\"https:\/\/landscapeanalysis.org\/2018\/lidar-hillshade\/\">vectors and hillshades<\/a>).<\/p>\n<p>The rest of the algorithm is rotating the elevation model in different directions to simulate the entire 360 degree orbit. I\u2019ve also included a smoothing function as the raw result tends to be very pixelated. The function is adapted from the <a href=\"https:\/\/landscapeanalysis.org\/2019\/tpi\/\">Topographic position index algorithm<\/a>.<\/p>\n<p>You can see the algo in GitHub repository: <a href=\"https:\/\/github.com\/zoran-cuckovic\/QGIS-raster-shading\">QGIS-raster-sahding<\/a>.<\/p>\n<h2 id=\"using-the-algorithm-and-styling-the-output\">Using the algorithm and styling the output<\/h2>\n<p>The shading algorithm is used as a Processing script for QGIS 3 and is installed as usual (figure below). It will appear under <strong>Scripts\u00a0\u00bb Raster terrain analysis\u00a0\u00bb Natural shading<\/strong> in the Processing toolbox.<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-medium wp-image-67\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/processing-add-script.jpg\" alt=\"processing-add-script.jpg\" \/><\/p>\n<p>The input for the algorithm is a digital elevation model and the output is a \u201cshadow map\u201d. The result will be smoothed by default (otherwise there is a nasty pixelisation effect). Other parameters should be self-explanatory (sun direction and vertical angle).<\/p>\n<p>The most important issue is <strong>limited handling of large datasets<\/strong>, because of numpy raster processing library, which is limited by the available live computer memory. Large rasters, if opened, will saturate live memory (raster size at which this effect may be noticed depends on your machine\u2026).<\/p>\n<p>The output will provide information on the <strong>depth below the closest sun ray for each pixel in a raster DEM.<\/strong> These values can be used to produce gradients of shadow tones, as opposed to uniformly dark surfaces. The best effect is made, as for usual hillshade models, when superimposing several layers in transparency mode.<\/p>\n<p>Natural shadows work best where hillshades tend to produce the \u201celephant skin\u201d effect; my first example is errored and gullied relief in Istria, Croatia. Here, I\u2019m using an elevation model styled with hypsometric colours, overlaid with a hillshade model and a shadow model.<\/p>\n<p><iframe loading=\"lazy\" frameborder=\"0\" class=\"juxtapose\" width=\"100%\" height=\"800\" src=\"https:\/\/cdn.knightlab.com\/libs\/juxtapose\/latest\/embed\/index.html?uid=5d59f3f4-40e9-11e9-9c6a-0edaf8f81e27\"><\/iframe><\/p>\n<p><em>Rugged relief of Istria, Croatia and Slovenia<\/em><\/p>\n<p>See how shadows introduce a feeling of depth? Now, simple black\/grey shadows would be problematic as they could easily occlude detail in valleys. Or, if too transparent, they would just add some mist, without introducing much perceptual improvement. The algorithm for natural shadow modelling, however, provides information on shadow depth, which enables to fine tune the effect. Shallow shadows should be less strong, as we can suppose a stronger impact of light dispersal in such areas. Here, I\u2019ve set different intensities of grey and different levels of opacity for five classes between zero and minus 400 metres. Typically you would also need to completely eliminate shadows less than 2 or 3 metres in depth (opacity = 0%), as they tend to encroach ridgelines and hilltops.<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-medium wp-image-67\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/Style.JPG\" alt=\"Style.JPG\" \/><br \/>\n<em>Shadow intensities are controlled by both colour and transparency for each depth class.<\/em><\/p>\n<p>One last example, Po\u017eega valley in Croatian part of Pannonia:<\/p>\n<p><iframe loading=\"lazy\" frameborder=\"0\" class=\"juxtapose\" width=\"100%\" height=\"500\" src=\"https:\/\/cdn.knightlab.com\/libs\/juxtapose\/latest\/embed\/index.html?uid=b58ce824-40e9-11e9-9c6a-0edaf8f81e27\"><\/iframe><\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>The algorithm for natural shadow modelling gives nice results, but beware that it\u2019s still in the \u201cproof of concept\u201d stage. There are several technical problems to be tackled in the future (such as handling large rasters). Please feel free to report any problems\/inconsistencies that may arise.<\/p>\n<h2 id=\"download\">Download<\/h2>\n<p>The script can be <a href=\"https:\/\/github.com\/zoran-cuckovic\/QGIS-raster-shading\/archive\/script.zip\">downloaded directly<\/a> or from <a href=\"https:\/\/github.com\/zoran-cuckovic\/QGIS-raster-shading\/\">GitHub repository<\/a>. Problems and issues can be signalled in the same repository.<\/p>\n<p><strong>Update, January 2020<\/strong>: the algorithm is now available as <a href=\"http:\/\/www.zoran-cuckovic.from.hr\/QGIS-terrain-shading\/\">native QGIS plugin<\/a>.<\/p>\n<p>Happy mapping!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>QGIS algorithm for natural shadow modelling. Introduction Modelling natural illumination is quite difficult in software for digital cartography. Yes, we do have hillshading algorithms, but despite the name they do not produce true shade. Hillshade routines colour the terrain according to exposition towards the sun (white for directly exposed and black for unexposed areas), but [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[11,12],"class_list":["post-43","post","type-post","status-publish","format-standard","hentry","category-qgis-plugins","tag-qgis","tag-qgis-terrain-shading"],"_links":{"self":[{"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=\/wp\/v2\/posts\/43","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=43"}],"version-history":[{"count":0,"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=\/wp\/v2\/posts\/43\/revisions"}],"wp:attachment":[{"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=43"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=43"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=43"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}