{"id":59,"date":"2018-07-01T19:17:28","date_gmt":"2018-07-01T19:17:28","guid":{"rendered":"http:\/\/wcwp.rf.gd\/2018\/07\/01\/https-landscapearchaeology-org-2018-tanaka-contour-lines\/"},"modified":"2018-07-01T19:17:28","modified_gmt":"2018-07-01T19:17:28","slug":"tanaka-contour-lines","status":"publish","type":"post","link":"https:\/\/landscapeanalysis.org\/?p=59","title":{"rendered":"Tanaka method or how to make shaded contour lines"},"content":{"rendered":"<p>Contour lines have become a commonplace, unassuming companion of our cartographic outputs &#8211; GIS has made us forget their visual potential. Already in the 19th century cartographers experimented with shaded contours, but the technique has become known as the Tanaka method, after Japanese cartographer Kitiro Tanaka (Kennelly 2016, wiki.gis 2017). Even the cover page of Imhof\u2019s seminal book sports a pile of shaded contours!<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/imhof.jpg\" alt=\"\" \/><\/p>\n<p>Shaded contour thickness and colour vary according to difference between the line direction and the light source direction. Lines perpendicular to the light are thicker than those parallel to it, while lines on \u201csunlit\u201d slopes should be brighter than those on slopes under shadow. Such rendering adds the feel of the third dimension to the contoured map &#8211; without any actual 3D model.<\/p>\n<p>A solution for achieving this effect has already been proposed by A. Graser a couple of years ago (link below). I\u2019ve adapted the method for the newer versions of QGIS, and added a logic that permits to change light angle.<\/p>\n<h3 id=\"contours\">Contours<\/h3>\n<p>Contour shading requires consistent contour direction, that is, they should all be either clockwise or counter-clockwise, but not both. (There is one exception, the bottom level contours should be in an opposite direction, but that\u2019s a detail.)<\/p>\n<p>I\u2019ve tested several algorithms:<\/p>\n<figure>\n    <img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/Saga-double-direction.png\" \/><figcaption>SAGA: definitely not consistent.<\/figcaption><\/figure>\n<figure>\n    <img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/Grass-contours.png\" \/><figcaption>GRASS r.contour: not consistent.<\/figcaption><\/figure>\n<figure>\n    <img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/Gdal-Contours.png\" \/><figcaption>GDAL: finally OK !<\/figcaption><\/figure>\n<p>The only algorithm that produces a consistent result is the one implemented in GDAL. It can be accessed through QGIS main interface: go to <strong>Raster\u00a0\u00bb Extraction\u00a0\u00bb Contour<\/strong>.<\/p>\n<p>In fact GDAL is expressly tweaked to produce tidied contours (\u201c<em>Starting from version 1.7 the contour line-strings will be oriented consistently. The high side will be on the right, i.e. a line string goes clockwise around a top<\/em>.\u201d says the <a href=\"http:\/\/www.gdal.org\/gdal_contour.html\">documentation<\/a>). In my experience, GDAL is sometimes producing counter-clockwise contours, but they are still consistent (this might happen when all raster edges have the same value). In that case we simply readjust the lighting, as described below.<\/p>\n<h3 id=\"applying-the-style\">Applying the style<\/h3>\n<p>The problem with rendering Tanaka lines is to apply a specific style for each line segment. Using older functions, this is feasible only with an \u201cexploded\u201d version of the contour file, that is, a file where all two-node components of polylines are registered as separate lines, which can then be styled individually (cf. the approach of A. Graser). However, beginning with the version 2.14, QGIS features \u201con the fly\u201d line breaking routine. This means that all we need is an expression embedded to the style definition, no more additional files with dissolved geometries.<\/p>\n<p><strong&gt;TL;DR<\/strong>: You can just download the style and apply it to your contour lines (link at the bottom). But do take a look at the explanation of its functioning\u2026 \ud83d\ude42<\/p>\n<p>1) To break polylines down to simple (two-node) elements we use the <strong>Geometry generator<\/strong> as Symbol layer type and the <code class=\"language-python highlighter-rouge\"><span class=\"n\">segments_to_lines<\/span><span class=\"p\">(<\/span> <span class=\"err\">$<\/span><span class=\"n\">geometry<\/span><span class=\"p\">)<\/span><\/code> expression to do the splitting.<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/qgis-segments_to_lines.png\" alt=\"\" \/><\/p>\n<p>2) Now we enter our styles as an expression (choose <strong>Edit<\/strong>).<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/qgis-windows.png\" alt=\"\" \/><\/p>\n<p>3) For the colour, the style definition looks like this:<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/qgis-colour.png\" alt=\"\" \/><\/p>\n<div class=\"language-python highlighter-rouge\">\n<div class=\"highlight\">\n<pre class=\"highlight\"><code><span class=\"n\">color_hsl<\/span><span class=\"p\">(<\/span> <span class=\"mi\">0<\/span><span class=\"p\">,<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span>\n  <span class=\"n\">scale_linear<\/span><span class=\"p\">(<\/span>\n    <span class=\"nb\">abs<\/span><span class=\"p\">(<\/span>\n      <span class=\"p\">(<\/span><span class=\"mi\">135<\/span> <span class=\"o\">+<\/span>\n        <span class=\"n\">degrees<\/span><span class=\"p\">(<\/span>\n          <span class=\"n\">azimuth<\/span><span class=\"p\">(<\/span>\n             <span class=\"n\">end_point<\/span><span class=\"p\">(<\/span><span class=\"n\">geometry_n<\/span><span class=\"p\">(<\/span><span class=\"err\">$<\/span><span class=\"n\">geometry<\/span><span class=\"p\">,<\/span><span class=\"o\">@<\/span><span class=\"n\">geometry_part_num<\/span><span class=\"p\">)),<\/span>\n            <span class=\"n\">start_point<\/span><span class=\"p\">(<\/span><span class=\"n\">geometry_n<\/span><span class=\"p\">(<\/span><span class=\"err\">$<\/span><span class=\"n\">geometry<\/span><span class=\"p\">,<\/span><span class=\"o\">@<\/span><span class=\"n\">geometry_part_num<\/span><span class=\"p\">))<\/span>\n     <span class=\"p\">)))<\/span>\n      <span class=\"o\">%<\/span> <span class=\"mi\">360<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">180<\/span><span class=\"p\">),<\/span>\n  <span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"mi\">180<\/span> <span class=\"p\">,<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"mi\">100<\/span><span class=\"p\">))<\/span>\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>4) And for the thickness:<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/qgis-thickness.png\" alt=\"\" \/><\/p>\n<div class=\"language-python highlighter-rouge\">\n<div class=\"highlight\">\n<pre class=\"highlight\"><code><span class=\"n\">scale_linear<\/span><span class=\"p\">(<\/span>\n  <span class=\"nb\">abs<\/span><span class=\"p\">(<\/span>\n    <span class=\"p\">(<\/span><span class=\"mi\">135<\/span> <span class=\"o\">+<\/span>\n      <span class=\"n\">degrees<\/span><span class=\"p\">(<\/span>\n        <span class=\"n\">azimuth<\/span><span class=\"p\">(<\/span>\n          <span class=\"n\">end_point<\/span><span class=\"p\">(<\/span><span class=\"n\">geometry_n<\/span><span class=\"p\">(<\/span><span class=\"err\">$<\/span><span class=\"n\">geometry<\/span><span class=\"p\">,<\/span><span class=\"o\">@<\/span><span class=\"n\">geometry_part_num<\/span><span class=\"p\">)),<\/span>\n          <span class=\"n\">start_point<\/span><span class=\"p\">(<\/span><span class=\"n\">geometry_n<\/span><span class=\"p\">(<\/span><span class=\"err\">$<\/span><span class=\"n\">geometry<\/span><span class=\"p\">,<\/span><span class=\"o\">@<\/span><span class=\"n\">geometry_part_num<\/span><span class=\"p\">))<\/span>\n      <span class=\"p\">)<\/span> <span class=\"p\">))<\/span>\n    <span class=\"o\">%<\/span> <span class=\"mi\">180<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">90<\/span><span class=\"p\">),<\/span>\n <span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"mi\">90<\/span> <span class=\"p\">,<\/span><span class=\"mf\">0.2<\/span><span class=\"p\">,<\/span> <span class=\"mf\">0.7<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n<\/div>\n<\/div>\n<h3 id=\"adjusting-line-thickness-and-light-direction\">Adjusting line thickness and light direction<\/h3>\n<p>Super complicated ? Yes it is, indeed\u2026 This is a difficult nut to crack without real programming (such as in a Python function). This is what you need to know:<\/p>\n<p>Line brightness and thickness values are extracted from the <code class=\"language-python highlighter-rouge\"><span class=\"n\">scale_linear<\/span><\/code> function. First two values are the input, and the last two are minimum and maximum output. For example, in <code class=\"language-python highlighter-rouge\"><span class=\"n\">scale_linear<\/span><span class=\"p\">(<\/span><span class=\"s\">\"our_file\"<\/span><span class=\"p\">,<\/span> <span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"mi\">180<\/span><span class=\"p\">,<\/span> <span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"mi\">100<\/span><span class=\"p\">)<\/span><\/code> original values of 0 &#8211; 180 are projected to the range 0 &#8211; 100. (The scale_linear function has a good explanation in QGIS expression builder).<\/p>\n<p>Thus to change <strong>brightness<\/strong> you have to play with the last two values of the colour definition, where 0 is black and 100 is white (100% brightness). Likewise, to change the <strong>line thickness<\/strong>, the last two values of the thickness definition need to be changed (0.2 and 0.7 for min\/max thickness).<\/p>\n<p>Light angle can also be tweaked. The function is squeezing the full angle range to a smaller interval using modulo operator (%). The default light angle (0) is from the west and values for light angles are increasing counter-clockwise (don\u2019t ask me why\u2026).<\/p>\n<div class=\"language-python highlighter-rouge\">\n<div class=\"highlight\">\n<pre class=\"highlight\"><code>              <span class=\"mi\">90<\/span> \n       <span class=\"mi\">135<\/span>          <span class=\"mi\">45<\/span>\n\n     <span class=\"mi\">180<\/span>               <span class=\"mi\">0<\/span>\n\n       <span class=\"mi\">225<\/span>          <span class=\"mi\">315<\/span> \n             <span class=\"mi\">270<\/span> \n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>To adjust the lighting, we can push the beginning of the range forward, but we cannot pull it into the negative range. So, for the north-western lighting, on clockwise oriented contours, we set the angle to 135. To have the same effect on counter-clockwise contours, we need to push to 315.<\/p>\n<p>You can easily spot the 135 value in the expression: that is the one to be changed in order to adjust the lighting (! this has to be done for both, colour and line thickness !).<\/p>\n<p>Finally, the result is much nicer when line terminals are rounded (<strong>Cap stlye : Round<\/strong>) [Thanks to Nyall Dawson for the reminder.]<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/cap-style.png\" alt=\"\" \/><\/p>\n<h3 id=\"verdict\">Verdict<\/h3>\n<p>Original Tanaka\u2019s examples of shaded contours are mostly used to represent natural topography. I find that approach visually too heavy; it is difficult to add additional information without overcrowding the map (but that\u2019s just an impression\u2026). What works better are all those heatmaps that are otherwise colourful splatters: Tanaka contours bring them to life! Just take a look at the gorgeous depth-map below\u2026<\/p>\n<h3 id=\"download\">Download<\/h3>\n<p><a href=\"https:\/\/plugins.qgis.org\/styles\/68\/\"><strong>Download the style from QGIS style library.<\/strong><\/a><\/p>\n<h3 id=\"bibliography\">Bibliography<\/h3>\n<p>Graser 2015, How to create illuminated contours, Tanaka-style accessed at <a href=\"http:\/\/anitagraser.com\/2015\/05\/24\/how-to-create-illuminated-contours-tanaka-style\/\">http:\/\/anitagraser.com\/2015\/05\/24\/how-to-create-illuminated-contours-tanaka-style\/<\/a>, December 2017.<\/p>\n<p>Kennelly 2016, Complexities of designing terrain maps illustrated with horizontal hachures, <em>International Journal of Cartography<\/em>, DOI: <a href=\"http:\/\/doi.org\/10.1080\/23729333.2016.1158491\">10.1080\/23729333.2016.1158491<\/a><\/p>\n<p>wiki.gis 2017, Tanaka contours, accessed at <a href=\"http:\/\/wiki.gis.com\/wiki\/index.php\/Tanaka_contours\">http:\/\/wiki.gis.com\/wiki\/index.php\/Tanaka_contours<\/a>, December 2017.<\/p>\n<figure>\n    <img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/Tanaka-up.png\" \/><figcaption>Shaded contours &#8211; upward direction<\/figcaption><\/figure>\n<p>.<\/p>\n<figure>\n    <img decoding=\"async\" src=\"http:\/\/landscapearchaeology.org\/wp-content\/uploads\/figures-jekyll\/2018\/06\/Tanaka-down.png\" \/><figcaption>Depth effect obtained by reversing the light angle (set to southeast)<\/figcaption><\/figure>\n<p>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Contour lines have become a commonplace, unassuming companion of our cartographic outputs &#8211; GIS has made us forget their visual potential. Already in the 19th century cartographers experimented with shaded contours, but the technique has become known as the Tanaka method, after Japanese cartographer Kitiro Tanaka (Kennelly 2016, wiki.gis 2017). Even the cover page of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[11],"class_list":["post-59","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-qgis"],"_links":{"self":[{"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=\/wp\/v2\/posts\/59","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=59"}],"version-history":[{"count":0,"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=\/wp\/v2\/posts\/59\/revisions"}],"wp:attachment":[{"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=59"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=59"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/landscapeanalysis.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=59"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}