{"id":1310,"date":"2019-09-16T21:23:44","date_gmt":"2019-09-16T19:23:44","guid":{"rendered":"https:\/\/hannes.enjoys.it\/blog\/?p=1310"},"modified":"2021-06-21T21:11:51","modified_gmt":"2021-06-21T19:11:51","slug":"dynamic-elevation-profile-lines-as-qgis-geometry-generator","status":"publish","type":"post","link":"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/","title":{"rendered":"Dynamic elevation profile lines as QGIS geometry generator"},"content":{"rendered":"\n<p>Load a raster layer in QGIS.<\/p>\n\n\n\n<p>Add a new Scratch Layer of Polygon type (in any CRS). Set its Symbology to Inverted Polygons. Use a Geometry Generator as symbol layer type. Set it to LineString\/MultiLineString. Enter the expression below and adjust the layer ID (best enter the expression editor and find it in the &#8220;layers&#8221; tree). Then adjust the scaling factor at the very bottom.<\/p>\n\n\n\n<figure class=\"wp-block-gallery columns-3 is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\"><ul class=\"blocks-gallery-grid\"><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/7.png\" alt=\"\" data-id=\"1323\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/attachment\/7\/\" class=\"wp-image-1323\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/7.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/7-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/7-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/7-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/3.png\" alt=\"\" data-id=\"1317\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/attachment\/3\/\" class=\"wp-image-1317\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/3.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/3-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/3-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/3-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/9.png\" alt=\"\" data-id=\"1325\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/attachment\/9\/\" class=\"wp-image-1325\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/9.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/9-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/9-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/9-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/5.png\" alt=\"\" data-id=\"1319\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/attachment\/5\/\" class=\"wp-image-1319\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/5.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/5-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/5-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/5-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/11.png\" alt=\"\" data-id=\"1329\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/attachment\/11\/\" class=\"wp-image-1329\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/11.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/11-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/11-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/11-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/6.png\" alt=\"\" data-id=\"1322\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/attachment\/6\/\" class=\"wp-image-1322\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/6.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/6-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/6-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/6-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/2.png\" alt=\"\" data-id=\"1316\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/attachment\/2\/\" class=\"wp-image-1316\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/2.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/2-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/2-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/2-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/8.png\" alt=\"\" data-id=\"1324\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/attachment\/8\/\" class=\"wp-image-1324\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/8.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/8-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/8-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/8-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/1.png\" alt=\"\" data-id=\"1315\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/attachment\/1\/\" class=\"wp-image-1315\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/1.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/1-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/1-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/1-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/10-1.png\" alt=\"\" data-id=\"1328\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/10-2\/\" class=\"wp-image-1328\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/10-1.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/10-1-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/10-1-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/10-1-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><li class=\"blocks-gallery-item\"><figure><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"838\" src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/4.png\" alt=\"\" data-id=\"1318\" data-link=\"https:\/\/hannes.enjoys.it\/blog\/2019\/09\/dynamic-elevation-profile-lines-as-qgis-geometry-generator\/attachment\/4\/\" class=\"wp-image-1318\" srcset=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/4.png 955w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/4-300x263.png 300w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/4-768x674.png 768w, https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/4-624x548.png 624w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure><\/li><\/ul><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>-- UPPER CASE comments below are where you can change things\n\nwith_variable(\n\t'raster_layer',\n\t'long_and_complicated_layer_id',  -- RASTER LAYER to sample from\n\t-- this collects all the linestrings generated below into one multilinestring\n\tcollect_geometries(\n\t\t-- a loop for each y value of the grid\n\t\tarray_foreach(\n\t\t\t-- array_foreach loops over all elements of the series generated below\n\t\t\t-- which is a range of numbers from the bottom to the top of y values\n\t\t\t-- of the map canvas extent coordinates.\n\t\t\t-- the result will be an array of linestrings\n\t\t\tgenerate_series(\n\t\t\t\ty(@map_extent_center)-(@map_extent_height\/2), -- bottom y\n\t\t\t\ty(@map_extent_center)+(@map_extent_height\/2),  -- top y\n\t\t\t\t@map_extent_height\/50  -- stepsize -&gt; HOW MANY LINES\n\t\t\t),\n\t\t\t\n\t\t\t-- we want to enter another loop so we assign the name 'y' to\n\t\t\t-- the current element of the array_foreach loop\n\t\t\twith_variable(\n\t\t\t\t'y',\n\t\t\t\t@element,\n\t\t\t\t\n\t\t\t\t-- now we are ready to generate the line for this y value\n\t\t\t\tmake_line(\n\t\t\t\t\t-- another loop, this time for the x values. same logic as before\n\t\t\t\t\t-- the result will be an array of points\n\t\t\t\t\tarray_foreach(\n\t\t\t\t\t\tgenerate_series(\n\t\t\t\t\t\t\tx(@map_extent_center)-(@map_extent_width\/2), -- left x\n\t\t\t\t\t\t\tx(@map_extent_center)+(@map_extent_width\/2),  -- right x\n\t\t\t\t\t\t\t@map_extent_width\/50  -- stepsize -&gt; HOW MANY POINTS PER LINE\n\t\t\t\t\t\t),\n\t\t\t\t\t\t-- and here we create each point of the line\n\t\t\t\t\t\tmake_point(\n\t\t\t\t\t\t\t@element,  -- the current value from the loop over the x value range\n\t\t\t\t\t\t\t@y  -- the y value from the outer loop\n\t\t\t\t\t\t\t+   -- will get an additional offset to generate the effect\n\t\t\t\t\t\t\t-- we look for values at _this point_ in the raster, and since\n\t\t\t\t\t\t\t-- the raster might not have any value here, we must use coalesce\n\t\t\t\t\t\t\t-- to use a replacement value in those cases\n\t\t\t\t\t\t\tcoalesce(  -- coalesce to catch raster null values\n\t\t\t\t\t\t\t\traster_value(\n\t\t\t\t\t\t\t\t\t@raster_layer,\n\t\t\t\t\t\t\t\t\t1,  -- band 1, *snore*\n\t\t\t\t\t\t\t\t\t-- to look up the raster value we need to look in the right position\n\t\t\t\t\t\t\t\t\t-- so we make a sampling point in the same CRS as the raster layer\n\t\t\t\t\t\t\t\t\ttransform( \n\t\t\t\t\t\t\t\t\t\tmake_point(@element, @y),\n\t\t\t\t\t\t\t\t\t\t@map_crs,\n\t\t\t\t\t\t\t\t\t\tlayer_property(@raster_layer,'crs')\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t0  -- coalesce 0 if raster_value gave null\n\t\t\t\t\t\t\t-- here is where we set the scaling factor for the raster -&gt; y values\n\t\t\t\t\t\t\t-- if things are weird, set it to 0 and try small multiplications or divisions\n\t\t\t\t\t\t\t-- to see what happens.\n\t\t\t\t\t\t\t-- for metric systems you will want to multiply\n\t\t\t\t\t\t\t-- for geographic coordinates you will want to divide\n\t\t\t\t\t\t\t)*10  -- user-defined factor for VERTICAL EXAGGERATION\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t)\n)  -- wee<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-video aligncenter\"><video height=\"400\" style=\"aspect-ratio: 464 \/ 400;\" width=\"464\" controls src=\"https:\/\/hannes.enjoys.it\/blog\/wp-content\/uploads\/elevation-lines-geometry-generator-20190916.mp4\"><\/video><\/figure>\n\n\n\n<p>If you don&#8217;t have your raster data on a SSD this can be a bit slow.<\/p>\n\n\n\n<p>Yes, this works if you change your CRS!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Load a raster layer in QGIS. Add a new Scratch Layer of Polygon type (in any CRS). Set its Symbology to Inverted Polygons. Use a Geometry Generator as symbol layer type. Set it to LineString\/MultiLineString. Enter the expression below and adjust the layer ID (best enter the expression editor and find it in the &#8220;layers&#8221; [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,31],"tags":[],"class_list":["post-1310","post","type-post","status-publish","format-standard","hentry","category-cartography","category-qgis"],"_links":{"self":[{"href":"https:\/\/hannes.enjoys.it\/blog\/wp-json\/wp\/v2\/posts\/1310","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hannes.enjoys.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hannes.enjoys.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hannes.enjoys.it\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hannes.enjoys.it\/blog\/wp-json\/wp\/v2\/comments?post=1310"}],"version-history":[{"count":9,"href":"https:\/\/hannes.enjoys.it\/blog\/wp-json\/wp\/v2\/posts\/1310\/revisions"}],"predecessor-version":[{"id":1601,"href":"https:\/\/hannes.enjoys.it\/blog\/wp-json\/wp\/v2\/posts\/1310\/revisions\/1601"}],"wp:attachment":[{"href":"https:\/\/hannes.enjoys.it\/blog\/wp-json\/wp\/v2\/media?parent=1310"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hannes.enjoys.it\/blog\/wp-json\/wp\/v2\/categories?post=1310"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hannes.enjoys.it\/blog\/wp-json\/wp\/v2\/tags?post=1310"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}