From 06883d47679fb0e0476e2d389a35d9b50579db3d Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Tue, 29 Apr 2025 15:07:53 -0400 Subject: [PATCH] movement tutorial: attempt to correct to rendered content --- .../GRASS_movement/execute-results/html.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_freeze/content/tutorials/modeling_movement/GRASS_movement/execute-results/html.json b/_freeze/content/tutorials/modeling_movement/GRASS_movement/execute-results/html.json index 2ca18bd..3642d0b 100644 --- a/_freeze/content/tutorials/modeling_movement/GRASS_movement/execute-results/html.json +++ b/_freeze/content/tutorials/modeling_movement/GRASS_movement/execute-results/html.json @@ -1,10 +1,10 @@ { - "hash": "d059b5339281a67e94a6ace4e0d3f99e", + "hash": "cd674d5718051958d08a9fc939b70602", "result": { "engine": "jupyter", - "markdown": "---\ntitle: \"Modeling Movement in GRASS\"\nauthor: \"Michael Barton & Anna Petrasova\"\ndate: 2025-02-10\ndate-modified: today\nformat: \n html:\n embed-resources: true\n toc: true\n code-tools: true\n code-copy: true\n code-fold: false\npage-layout: article\ncategories: [intermediate, advanced, GUI, raster, cost surface, least cost path]\ndescription: Generating a cumulative cost surface and least cost path with *r.walk* and *r.path* to model movement by walking across a landscape.\nengine: jupyter\nexecute: \n eval: false\nJupyter: python3\ncopyright: \n holder: Michael Barton & Anna Petrasova\n year: 2025\nfunding: \"Creation of this tutorial was supported in part by US National Science Foundation grant FAIN 2303651.\"\n\n---\n\n\nGRASS has sophisticated tools to model movement across terrain, including [r.cost](https://grass.osgeo.org/grass-stable/manuals/r.cost.html), [r.walk](https://grass.osgeo.org/grass-stable/manuals/r.walk.html), [r.drain](https://grass.osgeo.org/grass-stable/manuals/r.drain.html), and [r.path](https://grass.osgeo.org/grass-stable/manuals/r.path.html). In this tutorial, we will use ***r.walk*** and ***r.path*** to determine the best walking path to reach a destination, like a hospital.\n\n::: {.callout-note title=\"Dataset\"}\nThis tutorial uses one of the standard GRASS sample data sets: flagstaff_arizona_usa. We will refer to place names in that data set, but it can be completed with any of the [standard sample data sets](https://grass.osgeo.org/download/data/) for any region--for example the [North Carolina data set](https://grass.osgeo.org/sampledata/north_carolina/nc_basic_spm_grass7.zip). We will use the Flagstaff *elevation* DEM, the *hospitals* vector points file (any other vector point file will serve), and the *landuse* raster map.\n\nThis tutorial is designed so that you can complete it using the **GRASS GUI**, GRASS commands from the **console or terminal**, or using GRASS commands in a **Jupyter Notebook** environment.\n:::\n\n::: {.callout-note title=\"Don't know how to get started?\"}\nIf you are not sure how to get started with GRASS using its graphical user interface or using Python, checkout the tutorials [Get started with GRASS GIS GUI](../get_started/fast_track.qmd) and [Get started with GRASS & Python in Jupyter Notebooks](../get_started/fast_track_grass_and_python.qmd).\n:::\n\n## What is a cost surface?\n\n::::: grid\n::: g-col-6\n\n- A **cost surface** is a raster in which each cell represents the “cost” or difficulty to move across landscape.\n\n- A **cumulative cost surface** shows the total accumulated cost of moving from a starting point to a location. Cumulative cost surfaces are also used to find a **least cost path** between a location and the starting point.\n\n- GRASS [r.walk](https://grass.osgeo.org/grass-stable/manuals/r.walk.html) tool generates a cumulative cost surface using Naismith's rule for walking times where each cell has the value in **seconds** of the time it takes to walk from the start point to that cell.\n:::\n\n::: g-col-6\n![cost surface](img_movement/GRASS_movement_0.webp){fig-align=\"right\" width=\"100%\"}\n:::\n:::::\n\n# Modeling movement with a cumulative cost surface\n\n## Overview\n\n::::: grid\n::: g-col-6\n\n1. Start with a **DEM of elevation** for topography to determine movement costs.\n\n2. Create a **friction map** with a value of 0 (or other values for additional movement costs).\n\n3. Select **start point(s)**.\n\n4. Create a **cost surface**.\n\n5. A **least cost** path can then be calculated between any point on the cost surface and the start point.\n:::\n\n::: g-col-6\n![DEM](img_movement/GRASS_movement_1.webp)\n:::\n:::::\n\n## Friction map\n\n- The GRASS [map calculator](https://grass.osgeo.org/grass-stable/manuals/r.mapcalc.html) lets you create a map, alter a map, or combine maps using **map algebra**. The map calculator can be used from the GUI or as a command.\n\n- Before you make a friction map, make sure the [**computational region**](https://grass.osgeo.org/grass-stable/manuals/g.region.html) matches the elevation map.\n\n- Create the *friction0* map with 0 in every cell.\n\n:::::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n::::: grid\n::: g-col-6\n\n1. Add the *elevation* raster to the Layer Manager.\n\n2. Right click *elevation* in Layer Manager.\n\n3. Select **Set computational region from selected map**.\n\n4. Open the **Raster Map Calculator** from the top toolbar (\"abacus\" icon).\n\n5. Fill in the output map name as *friction0* and enter 0 in the expression field.\n\n6. Press Run.\n:::\n\n::: g-col-6\n![Raster map calculator](img_movement/GRASS_movement_2.webp)\n:::\n:::::\n\n#### Command line\n\n1. Set the region to match the elevation map\n\n2. Create a friction map with a value of 0 in all cells that matches the extent and resolution of the elevation map\n\n::: {#7decd308 .cell execution_count=1}\n``` {.python .cell-code}\ng.region raster=elevation\nr.mapcalc \"friction0 = 0\"\n```\n:::\n\n\n#### Python\n\n1. Set the region to match the elevation map\n\n2. Create a friction map with a value of 0 in all cells that matches the extent and resolution of the elevation map\n\n::: {#af74724c .cell execution_count=2}\n``` {.python .cell-code}\ngs.run_command(\"g.region\", raster=\"elevation\")\ngs.mapcalc(\"friction0 = 0\")\n```\n:::\n\n\n::::::\n\n## Choose a start point\n\n- Now that we have a DEM map for terrain and a friction map, all we need for a cumulative cost surface is a start point: the point from which to calculate movement costs.\n\n- A start point can be a vector point, a raster cell, or even a pair of coordinates.\n\n- A cost surface in *r.walk* can have multiple start points.\n\n- We’ll make a start point from the Flagstaff Medical Center, found in the *hospitals* vector points file.\n\n:::::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n::::: grid\n::: g-col-6\nUse the **Attribute Table Manager** for *hospitals*:\n\n1. Display *hospitals* map by adding it to the Layer Manager from Data Catalog.\n\n2. Right click and open the Attribute Table Manager.\n\n3. Select \"Flagstaff Medical Center\" record.\n\n4. Right click and select **Extract selected features** from the context menu.\n\n5. Name the new map *FMC*\n:::\n\n::: g-col-6\n![data table tool for hospitals vector points map](img_movement/GRASS_movement_3.webp)\n\n![table row context menu](img_movement/GRASS_movement_4.webp){width=\"60%\"}\n:::\n:::::\n\n#### Command line\n\nUse the v.extract tool to create a vector point map named *FMC* from the *hospitals* map.\n\n::: {#0a7ae3ee .cell execution_count=3}\n``` {.python .cell-code}\nv.extract input=hospitals type=point where=\"FACILITY_N = 'FLAGSTAFF MEDICAL CENTER'\" output=FMC\n```\n:::\n\n\n#### Python\n\nUse the v.extract tool to create a vector point map named *FMC* from the *hospitals* map.\n\n::: {#8bd3b8ce .cell execution_count=4}\n``` {.python .cell-code}\ngs.run_command(\"v.extract\", input=\"hospitals\", type=\"point\", where=\"FACILITY_N = 'FLAGSTAFF MEDICAL CENTER'\", output=\"FMC\")\n```\n:::\n\n\n::::::\n\n- Use the *d.vect* tool by double clicking FMC in the layer manager to display the point with a color and size to see it better\n\n![](img_movement/GRASS_movement_5.webp)\n\n## Generate the cumulative cost surface {#generate-cumulative-cost-surface}\n\n::::::::::::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n- Use the *r.walk* tool from the **Raster/Terrain Analysis** menu\n\n::::: grid\n::: g-col-6\n\n1. Enter the **elevation map** ( *elevation* ), **friction map** ( *friction0* ), and **name of the cost surface** to create (FMC_cost_seconds)\n\n![](img_movement/GRASS_movement_6.webp){fig-align=\"left\" width=\"100%\"}\n:::\n\n::: g-col-6\n2. Enter the name of a **directions map** ( *FMC_directions* ) to use for creating a least cost path.\n\n![](img_movement/GRASS_movement_7.webp){fig-align=\"left\" width=\"100%\"}\n:::\n:::::\n\n::::: grid\n::: g-col-6\n3. Enter the **start point** ( *FMC* ).\n\n![](img_movement/GRASS_movement_8.webp){fig-align=\"left\" width=\"100%\"}\n:::\n\n::: g-col-6\n4. Optional: control the **spatial extent** of cost surface.\n\n![](img_movement/GRASS_movement_9.webp)\n:::\n:::::\n\n::::: grid\n::: g-col-6\n5. Optional: adjust **Movement parameter** settings.\n\n![](img_movement/GRASS_movement_10.webp){fig-align=\"left\" width=\"100%\"}\n:::\n\n::: g-col-6\n6. Recommended: select **knight's move** for calculating cost and direction.\n\n![](img_movement/GRASS_movement_11.webp){fig-align=\"left\" width=\"100%\"}\n:::\n:::::\n\n::: {.callout-note title=\"Tip\"}\nClick the \"copy\" button to copy the GRASS command. You can save it in a text file for later reuse or to document your work.\n:::\n\n#### Command line\n\n- Use the r.walk command to generate the cumulative cost surface.\n\n::: {#7612a174 .cell execution_count=5}\n``` {.python .cell-code}\nr.walk elevation=elevation friction=friction0 output=FMC_cost_seconds outdir=FMC_directions start_points=FMC -k\n```\n:::\n\n\n#### Python\n\n- Use the r.walk command to generate the cumulative cost surface.\n\n::: {#022588c8 .cell execution_count=6}\n``` {.python .cell-code}\ngs.run_command(\"r.walk\",\n elevation=\"elevation\",\n friction=\"friction0\",\n output=\"FMC_cost_seconds\",\n outdir=\"FMC_directions\",\n start_points=\"FMC\",\n flags=\"k\")\n```\n:::\n\n\n:::::::::::::\n\n## Cumulative cost surface map\n\n- Each raster cell value in the cost surface is the time in seconds to walk from FMC to that cell over the terrain of the Flagstaff DEM\n\n![](img_movement/GRASS_movement_12.webp)\n\n::: {.callout-note title=\"Tip\"}\nTip: You can display the cumulative cost surface over a shaded relief map in the **layer manager** using the [d.shade](https://grass.osgeo.org/grass-stable/manuals/d.shade.html) tool and a relief map of *elevation* made with [r.relief](https://grass.osgeo.org/grass-stable/manuals/d.shade.html).\n:::\n\n## Movement across a cumulative cost surface {#movement-across-cost-surface}\n\n- You can transform the walking time in seconds to hours by dividing the map by 3600 using the map calculator\n\n:::::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n::::: grid\n::: g-col-6\n\n1. Open the map calculator.\n\n2. Enter the name of the new map of walking time in hours: *FMC_cost_hours*.\n\n3. Enter FMC_cost_seconds / 3600 into the expressions field\n\n4. Press Run.\n:::\n\n::: g-col-6\n![](img_movement/GRASS_movement_13.webp){fig-align=\"left\" width=\"100%\"}\n:::\n:::::\n\n#### Command line\n\n::: {#d116f2f1 .cell execution_count=7}\n``` {.python .cell-code}\nr.mapcalc \"FMC_cost_hours = FMC_cost_seconds / 3600\"\n```\n:::\n\n\n#### Python\n\n::: {#d8e8862d .cell execution_count=8}\n``` {.python .cell-code}\ngs.mapcalc(\"FMC_cost_hours = FMC_cost_seconds / 3600\")\n```\n:::\n\n\n::::::\n\n------------------------------------------------------------------------\n\n::::: grid\n::: g-col-6\n\n- We can query or filter this hourly cumulative cost surface to areas of equivalent walking time.\n\n- For example, in the **layer manager** we can used the *d.rast* tool to show the area within a 2 hour walk of FMC and then set the opacity of the cost surface to 50% to see the underlying terrain\\\n:::\n\n::: g-col-6\n![](img_movement/GRASS_movement_15.webp)\n:::\n:::::\n\n![Shaded area represents terrain reached within a 2 hour walk from FMC](img_movement/GRASS_movement_14.webp)\n\n# Least cost paths {#least-cost-path}\n\n## Overview\n\n- We can also plot a **least cost path** (LCP), which is the least costly (shortest time) route between any point on the cumulative cost surface and FMC.\n\n- Imagine a stranded hiker NE of Flagstaff who has to walk to FMC.\n\n- What path would take the least time?\n\n![](img_movement/GRASS_movement_16.webp)\n\n## Generating a least cost path\n\nTo create a LCP in GRASS, we will use [r.path](https://grass.osgeo.org/grass-stable/manuals/r.path.html) (also under the Raster/Terrain analysis menu).\n\n:::::::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n::::: grid\n::: g-col-6\n\n1. Input the **directions map** (*FMC_directions*) we also made when we created the cumulative cost surface.\\\n ![](img_movement/GRASS_movement_17.webp)\n:::\n\n::: g-col-6\n2. Input the **coordinates of the hiker** as the starting point for the LCP. (We could also use a pre-defined vector point).\n\n![](img_movement/GRASS_movement_18.webp)\n:::\n:::::\n\n:::: grid\n::: g-col-6\n3. Specify the **name of the output vector path map** (*LCP_cumulative*).\n\n![](img_movement/GRASS_movement_19.webp)\n:::\n::::\n\n#### Command line\n\n::: {#2232e632 .cell execution_count=9}\n``` {.python .cell-code}\nr.path input=FMC_directions format=auto vector_path=LCP_cumulative start_coordinates=477476,13914951\n```\n:::\n\n\n#### Python\n\n::: {#1e2187a8 .cell execution_count=10}\n``` {.python .cell-code}\ngs.run_command(\"r.path\",\n input=\"FMC_directions\",\n format=\"auto\",\n vector_path=\"LCP_cumulative\",\n start_coordinates=[477476, 13914951])\n```\n:::\n\n\n::::::::\n\n## Least cost path generated\n\n- Here is the LCP result.\n\n![](img_movement/GRASS_movement_20.webp)\n\n# Adding a friction map to movement costs\n\n## Overview\n\n- A **friction map** can be used to incorporate other factors than just terrain into creating a cumulative cost surface and modeling movement\n\n- The value of each cell in a friction map raster is the amount of walking time, in seconds/meter, *in addition to* the time needed because of terrain\n\n- We can **reclassify** the *landuse* map to create a friction map of the amount of extra time it would take to walk through different kinds of land cover\n\n![Reclassification of *landuse* to show major land cover types](img_movement/GRASS_movement_21.webp)\n\n## Reclassifying *landuse* to create a friction map\n\nA standard walking speed across flat terrain is about 5 km/hr = 0.72 sec/m.\n\nWe might then estimate that it would take an additional\n\n- 3 sec/m to walk through dense pinyon-juniper woodland\n\n- 1 sec/m to cross conifer forest\n\n- 2 sec/m to wander through urban Flagstaff\n\n- 5 sec/m to clamber over lava fields\n\n- 10 sec/m to try and cross water\n\nWe can create this friction map by reclassifying the *landuse* map using [*r.reclass*](https://grass.osgeo.org/grass-stable/manuals/r.reclass.html) to assign new friction values to the existing land cover categories. \n\n:::::: {.panel-tabset group=\"language\"}\n\n## GUI\n\nThe [r.reclass](https://grass.osgeo.org/grass-stable/manuals/r.reclass.html) tool is found under the Raster/Change category… menu.\n\n::::: grid\n::: g-col-6\n\n1. Enter *landuse* for the raster to be reclassified\n\n2. Enter *friction_reclassified* for the output reclassified map\n\n3. Enter the reclass rules directly in the text box or from a saved text file. Use and \\* symbol to represent everything not covered by the specific reclass rules\n\n > 11 90 95 = 10 water \n > 21 thru 24 = 2 urban \n > 31 = 5 lava \n > 41 thru 43 = 1 conifer forest \n > 52 = 3 pinyon juniper woodland \n > \\* = 0 no friction \n \n:::\n\n::: g-col-6\n\n- This creates a new friction map named *friction_landcover*\n\n![](img_movement/GRASS_movement_22.webp)\n:::\n:::::\n\n## Command line\n\n::: {#0b8abf49 .cell execution_count=11}\n``` {.python .cell-code}\nr.reclass input=landuse output=friction_landcover rules=- << EOF\n11 90 95 = 10 water\n21 thru 24 = 2 urban\n31 = 5 lava\n41 thru 43 = 1 conifer forest\n52 = 3 pinyon juniper woodland\n* = 0 no friction\nEOF\n```\n:::\n\n\n## Python\n\n::: {#e02e73c2 .cell execution_count=12}\n``` {.python .cell-code}\nrules = \"\"\"\\\n11 90 95 = 10 water\n21 thru 24 = 2 urban\n31 = 5 lava\n41 thru 43 = 1 conifer forest\n52 = 3 pinyon juniper woodland\n* = 0 no friction\n\"\"\"\ngs.write_command(\"r.reclass\",\n input=\"landuse\",\n output=\"friction_landcover\",\n rules=\"-\",\n stdin=rules)\n```\n:::\n\n\n::::::\n\n![The reclassified friction map (*friction_landcover*)](img_movement/GRASS_movement_23.webp)\n\n## Modifying a cumulative cost surface with a friction map\n\nWe can now create a new cumulative cost surface using this new friction map and convert it from seconds to hours, as we did before:\n\n::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n1. Follow the procedures in the [Generate the cumulative cost surface\nsection](#generate-cumulative-cost-surface) and substitute the new *friction_landcover* map for the *friction0* map used previously.\n\n2. Convert the cumulative cost surface to hours instead of seconds following the procedures in the [Movement across a cumulative cost surface section]({#movement-across-cost-surface})\n\n#### Command line\n\n::: {#f942dc5e .cell execution_count=13}\n``` {.python .cell-code}\nr.walk elevation=elevation friction=friction_landcover output=FMC_vegcost_seconds outdir=FMC_vegcost_directions start_points=FMC -k\nr.mapcalc \"FMC_vegcost_hours = FMC_vegcost_seconds / 3600\"\n```\n:::\n\n\n#### Python\n\n::: {#e69d55df .cell execution_count=14}\n``` {.python .cell-code}\ngs.run_command(\"r.walk\",\n elevation=\"elevation\",\n friction=\"friction_landcover\",\n output=\"FMC_vegcost_seconds\",\n outdir=\"FMC_vegcost_directions\",\n start_points=\"FMC\",\n flags=\"k\")\ngs.mapcalc(\"FMC_vegcost_hours = FMC_vegcost_seconds / 3600\")\n```\n:::\n\n\n:::\n\n![cumulative cost surface with additional friction from land cover](img_movement/GRASS_movement_24.webp)\n\n## Least cost paths and friction\n\n- We can create a new LCP from the stranded hiker to FMC over terrain where land cover also affects the cost of movement\n\n::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n1. Follow the procedures in the [Least cost path section](#least-cost-path), substituting the new direction map made along with the cumulative cost surface with land cover friction map.\n\n2. Give this new LCP the name *LCP_vegcost* to distinguish from the previous LCP.\n\n#### Command line\n\n::: {#3f34018c .cell execution_count=15}\n``` {.python .cell-code}\nr.path input=FMC_vegcost_directions format=auto vector_path=LCP_vegcost start_coordinates=477476,13914951\n```\n:::\n\n\n#### Python\n\n::: {#2495f947 .cell execution_count=16}\n``` {.python .cell-code}\ngs.run_command(\"r.path\",\n input=\"FMC_vegcost_directions\",\n format=\"auto\",\n vector_path=\"LCP_vegcost\",\n start_coordinates=[477476, 13914951])\n```\n:::\n\n\n:::\n\n- It takes more time to reach FMC if the hiker has to navigate dense vegetation as well as terrain.\n\n- In the map below, terrain colored by land cover, the original LCP with terrain only is shown by the blue line. The LCP with a land cover friction map is shown by the heavier yellow line\n\n![comparing LCP with terrain only and with land cover friction](img_movement/GRASS_movement_25.webp)\n\n", + "markdown": "---\ntitle: \"Modeling Movement in GRASS\"\nauthor: \"Michael Barton & Anna Petrasova\"\ndate: 2025-02-10\ndate-modified: today\nformat: \n html:\n embed-resources: true\n toc: true\n code-tools: true\n code-copy: true\n code-fold: false\npage-layout: article\ncategories: [intermediate, advanced, GUI, raster, cost surface, least cost path]\ndescription: Generating a cumulative cost surface and least cost path with *r.walk* and *r.path* to model movement by walking across a landscape.\nengine: jupyter\nexecute: \n eval: false\nJupyter: python3\ncopyright: \n holder: Michael Barton & Anna Petrasova\n year: 2025\nfunding: \"Creation of this tutorial was supported in part by US National Science Foundation grant FAIN 2303651.\"\n\n---\n\n\n\n\nGRASS has sophisticated tools to model movement across terrain, including [r.cost](https://grass.osgeo.org/grass-stable/manuals/r.cost.html), [r.walk](https://grass.osgeo.org/grass-stable/manuals/r.walk.html), [r.drain](https://grass.osgeo.org/grass-stable/manuals/r.drain.html), and [r.path](https://grass.osgeo.org/grass-stable/manuals/r.path.html). In this tutorial, we will use ***r.walk*** and ***r.path*** to determine the best walking path to reach a destination, like a hospital.\n\n::: {.callout-note title=\"Dataset\"}\nThis tutorial uses one of the standard GRASS sample data sets: flagstaff_arizona_usa. We will refer to place names in that data set, but it can be completed with any of the [standard sample data sets](https://grass.osgeo.org/download/data/) for any region--for example, the [North Carolina data set](https://grass.osgeo.org/sampledata/north_carolina/nc_basic_spm_grass7.zip). We will use the Flagstaff *elevation* DEM, the *hospitals* vector points file (any other vector point file will serve), and the *landuse* raster map.\n\nThis tutorial is designed so that you can complete it using the **GRASS GUI**, GRASS commands from the **console or terminal**, or using GRASS commands in a **Jupyter Notebook** environment.\n:::\n\n::: {.callout-note title=\"Don't know how to get started?\"}\nIf you are not sure how to get started with GRASS using its graphical user interface or using Python, checkout the tutorials [Get started with GRASS GIS GUI](../get_started/fast_track.qmd) and [Get started with GRASS & Python in Jupyter Notebooks](../get_started/fast_track_grass_and_python.qmd).\n:::\n\n## What is a cost surface?\n\n::::: grid\n::: g-col-6\n\n- A **cost surface** is a raster in which each cell represents the “cost” or difficulty to move across landscape.\n\n- A **cumulative cost surface** shows the total accumulated cost of moving from a starting point to a location. Cumulative cost surfaces are also used to find a **least cost path** between a location and the starting point.\n\n- GRASS [r.walk](https://grass.osgeo.org/grass-stable/manuals/r.walk.html) tool generates a cumulative cost surface using Naismith's rule for walking times where each cell has the value in **seconds** of the time it takes to walk from the start point to that cell.\n:::\n\n::: g-col-6\n![cost surface](img_movement/GRASS_movement_0.webp){fig-align=\"right\" width=\"100%\"}\n:::\n:::::\n\n# Modeling movement with a cumulative cost surface\n\n## Overview\n\n::::: grid\n::: g-col-6\n\n1. Start with a **DEM of elevation** for topography to determine movement costs.\n\n2. Create a **friction map** with a value of 0 (or other values for additional movement costs).\n\n3. Select **start point(s)**.\n\n4. Create a **cost surface**.\n\n5. A **least cost** path can then be calculated between any point on the cost surface and the start point.\n:::\n\n::: g-col-6\n![DEM](img_movement/GRASS_movement_1.webp)\n:::\n:::::\n\n## Friction map\n\n- The GRASS [map calculator](https://grass.osgeo.org/grass-stable/manuals/r.mapcalc.html) lets you create a map, alter a map, or combine maps using **map algebra**. The map calculator can be used from the GUI or as a command.\n\n- Before you make a friction map, make sure the [**computational region**](https://grass.osgeo.org/grass-stable/manuals/g.region.html) matches the elevation map.\n\n- Create the *friction0* map with 0 in every cell.\n\n:::::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n::::: grid\n::: g-col-6\n\n1. Add the *elevation* raster to the Layer Manager.\n\n2. Right click *elevation* in Layer Manager.\n\n3. Select **Set computational region from selected map**.\n\n4. Open the **Raster Map Calculator** from the top toolbar (\"abacus\" icon).\n\n5. Fill in the output map name as *friction0* and enter 0 in the expression field.\n\n6. Press Run.\n:::\n\n::: g-col-6\n![Raster map calculator](img_movement/GRASS_movement_2.webp)\n:::\n:::::\n\n#### Command line\n\n1. Set the region to match the elevation map\n\n2. Create a friction map with a value of 0 in all cells that matches the extent and resolution of the elevation map\n\n::: {#afea62e7 .cell execution_count=1}\n``` {.python .cell-code}\ng.region raster=elevation\nr.mapcalc \"friction0 = 0\"\n```\n:::\n\n\n#### Python\n\n1. Set the region to match the elevation map\n\n2. Create a friction map with a value of 0 in all cells that matches the extent and resolution of the elevation map\n\n::: {#535d7108 .cell execution_count=2}\n``` {.python .cell-code}\ngs.run_command(\"g.region\", raster=\"elevation\")\ngs.mapcalc(\"friction0 = 0\")\n```\n:::\n\n\n::::::\n\n## Choose a start point\n\n- Now that we have a DEM map for terrain and a friction map, all we need for a cumulative cost surface is a start point: the point from which to calculate movement costs.\n\n- A start point can be a vector point, a raster cell, or even a pair of coordinates.\n\n- A cost surface in *r.walk* can have multiple start points.\n\n- We’ll make a start point from the Flagstaff Medical Center, found in the *hospitals* vector points file.\n\n:::::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n::::: grid\n::: g-col-6\nUse the **Attribute Table Manager** for *hospitals*:\n\n1. Display *hospitals* map by adding it to the Layer Manager from Data Catalog.\n\n2. Right click and open the Attribute Table Manager.\n\n3. Select \"Flagstaff Medical Center\" record.\n\n4. Right click and select **Extract selected features** from the context menu.\n\n5. Name the new map *FMC*\n:::\n\n::: g-col-6\n![data table tool for hospitals vector points map](img_movement/GRASS_movement_3.webp)\n\n![table row context menu](img_movement/GRASS_movement_4.webp){width=\"60%\"}\n:::\n:::::\n\n#### Command line\n\nUse the v.extract tool to create a vector point map named *FMC* from the *hospitals* map.\n\n::: {#ccda7731 .cell execution_count=3}\n``` {.python .cell-code}\nv.extract input=hospitals type=point where=\"FACILITY_N = 'FLAGSTAFF MEDICAL CENTER'\" output=FMC\n```\n:::\n\n\n#### Python\n\nUse the v.extract tool to create a vector point map named *FMC* from the *hospitals* map.\n\n::: {#5eb459f9 .cell execution_count=4}\n``` {.python .cell-code}\ngs.run_command(\"v.extract\", input=\"hospitals\", type=\"point\", where=\"FACILITY_N = 'FLAGSTAFF MEDICAL CENTER'\", output=\"FMC\")\n```\n:::\n\n\n::::::\n\n- Use the *d.vect* tool by double clicking FMC in the layer manager to display the point with a color and size to see it better\n\n![](img_movement/GRASS_movement_5.webp)\n\n## Generate the cumulative cost surface {#generate-cumulative-cost-surface}\n\n::::::::::::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n- Use the *r.walk* tool from the **Raster/Terrain Analysis** menu\n\n::::: grid\n::: g-col-6\n\n1. Enter the **elevation map** ( *elevation* ), **friction map** ( *friction0* ), and **name of the cost surface** to create (FMC_cost_seconds)\n\n![](img_movement/GRASS_movement_6.webp){fig-align=\"left\" width=\"100%\"}\n:::\n\n::: g-col-6\n2. Enter the name of a **directions map** ( *FMC_directions* ) to use for creating a least cost path.\n\n![](img_movement/GRASS_movement_7.webp){fig-align=\"left\" width=\"100%\"}\n:::\n:::::\n\n::::: grid\n::: g-col-6\n3. Enter the **start point** ( *FMC* ).\n\n![](img_movement/GRASS_movement_8.webp){fig-align=\"left\" width=\"100%\"}\n:::\n\n::: g-col-6\n4. Optional: control the **spatial extent** of cost surface.\n\n![](img_movement/GRASS_movement_9.webp)\n:::\n:::::\n\n::::: grid\n::: g-col-6\n5. Optional: adjust **Movement parameter** settings.\n\n![](img_movement/GRASS_movement_10.webp){fig-align=\"left\" width=\"100%\"}\n:::\n\n::: g-col-6\n6. Recommended: select **knight's move** for calculating cost and direction.\n\n![](img_movement/GRASS_movement_11.webp){fig-align=\"left\" width=\"100%\"}\n:::\n:::::\n\n::: {.callout-note title=\"Tip\"}\nClick the \"copy\" button to copy the GRASS command. You can save it in a text file for later reuse or to document your work.\n:::\n\n#### Command line\n\n- Use the r.walk command to generate the cumulative cost surface.\n\n::: {#692e50a8 .cell execution_count=5}\n``` {.python .cell-code}\nr.walk elevation=elevation friction=friction0 output=FMC_cost_seconds outdir=FMC_directions start_points=FMC -k\n```\n:::\n\n\n#### Python\n\n- Use the r.walk command to generate the cumulative cost surface.\n\n::: {#9b7dc678 .cell execution_count=6}\n``` {.python .cell-code}\ngs.run_command(\"r.walk\",\n elevation=\"elevation\",\n friction=\"friction0\",\n output=\"FMC_cost_seconds\",\n outdir=\"FMC_directions\",\n start_points=\"FMC\",\n flags=\"k\")\n```\n:::\n\n\n:::::::::::::\n\n## Cumulative cost surface map\n\n- Each raster cell value in the cost surface is the time in seconds to walk from FMC to that cell over the terrain of the Flagstaff DEM\n\n![](img_movement/GRASS_movement_12.webp)\n\n::: {.callout-note title=\"Tip\"}\nTip: You can display the cumulative cost surface over a shaded relief map in the **layer manager** using the [d.shade](https://grass.osgeo.org/grass-stable/manuals/d.shade.html) tool and a relief map of *elevation* made with [r.relief](https://grass.osgeo.org/grass-stable/manuals/d.shade.html).\n:::\n\n## Movement across a cumulative cost surface {#movement-across-cost-surface}\n\n- You can transform the walking time in seconds to hours by dividing the map by 3600 using the map calculator\n\n:::::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n::::: grid\n::: g-col-6\n\n1. Open the map calculator.\n\n2. Enter the name of the new map of walking time in hours: *FMC_cost_hours*.\n\n3. Enter FMC_cost_seconds / 3600 into the expressions field\n\n4. Press Run.\n:::\n\n::: g-col-6\n![](img_movement/GRASS_movement_13.webp){fig-align=\"left\" width=\"100%\"}\n:::\n:::::\n\n#### Command line\n\n::: {#28a29d1b .cell execution_count=7}\n``` {.python .cell-code}\nr.mapcalc \"FMC_cost_hours = FMC_cost_seconds / 3600\"\n```\n:::\n\n\n#### Python\n\n::: {#6112bfb5 .cell execution_count=8}\n``` {.python .cell-code}\ngs.mapcalc(\"FMC_cost_hours = FMC_cost_seconds / 3600\")\n```\n:::\n\n\n::::::\n\n------------------------------------------------------------------------\n\n::::: grid\n::: g-col-6\n\n- We can query or filter this hourly cumulative cost surface to areas of equivalent walking time.\n\n- For example, in the **layer manager** we can used the *d.rast* tool to show the area within a 2 hour walk of FMC and then set the opacity of the cost surface to 50% to see the underlying terrain\\\n:::\n\n::: g-col-6\n![](img_movement/GRASS_movement_15.webp)\n:::\n:::::\n\n![Shaded area represents terrain reached within a 2 hour walk from FMC](img_movement/GRASS_movement_14.webp)\n\n# Least cost paths {#least-cost-path}\n\n## Overview\n\n- We can also plot a **least cost path** (LCP), which is the least costly (shortest time) route between any point on the cumulative cost surface and FMC.\n\n- Imagine a stranded hiker NE of Flagstaff who has to walk to FMC.\n\n- What path would take the least time?\n\n![](img_movement/GRASS_movement_16.webp)\n\n## Generating a least cost path\n\nTo create a LCP in GRASS, we will use [r.path](https://grass.osgeo.org/grass-stable/manuals/r.path.html) (also under the Raster/Terrain analysis menu).\n\n:::::::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n::::: grid\n::: g-col-6\n\n1. Input the **directions map** (*FMC_directions*) we also made when we created the cumulative cost surface.\\\n ![](img_movement/GRASS_movement_17.webp)\n:::\n\n::: g-col-6\n2. Input the **coordinates of the hiker** as the starting point for the LCP. (We could also use a pre-defined vector point).\n\n![](img_movement/GRASS_movement_18.webp)\n:::\n:::::\n\n:::: grid\n::: g-col-6\n3. Specify the **name of the output vector path map** (*LCP_cumulative*).\n\n![](img_movement/GRASS_movement_19.webp)\n:::\n::::\n\n#### Command line\n\n::: {#11be286b .cell execution_count=9}\n``` {.python .cell-code}\nr.path input=FMC_directions format=auto vector_path=LCP_cumulative start_coordinates=477476,13914951\n```\n:::\n\n\n#### Python\n\n::: {#1a5cb186 .cell execution_count=10}\n``` {.python .cell-code}\ngs.run_command(\"r.path\",\n input=\"FMC_directions\",\n format=\"auto\",\n vector_path=\"LCP_cumulative\",\n start_coordinates=[477476, 13914951])\n```\n:::\n\n\n::::::::\n\n## Least cost path generated\n\n- Here is the LCP result.\n\n![](img_movement/GRASS_movement_20.webp)\n\n# Adding a friction map to movement costs\n\n## Overview\n\n- A **friction map** can be used to incorporate other factors than just terrain into creating a cumulative cost surface and modeling movement\n\n- The value of each cell in a friction map raster is the amount of walking time, in seconds/meter, *in addition to* the time needed because of terrain\n\n- We can **reclassify** the *landuse* map to create a friction map of the amount of extra time it would take to walk through different kinds of land cover\n\n![Reclassification of *landuse* to show major land cover types](img_movement/GRASS_movement_21.webp)\n\n## Reclassifying *landuse* to create a friction map\n\nA standard walking speed across flat terrain is about 5 km/hr = 0.72 sec/m.\n\nWe might then estimate that it would take an additional\n\n- 3 sec/m to walk through dense pinyon-juniper woodland\n\n- 1 sec/m to cross conifer forest\n\n- 2 sec/m to wander through urban Flagstaff\n\n- 5 sec/m to clamber over lava fields\n\n- 10 sec/m to try and cross water\n\nWe can create this friction map by reclassifying the *landuse* map using [*r.reclass*](https://grass.osgeo.org/grass-stable/manuals/r.reclass.html) to assign new friction values to the existing land cover categories. \n\n:::::: {.panel-tabset group=\"language\"}\n\n## GUI\n\nThe [r.reclass](https://grass.osgeo.org/grass-stable/manuals/r.reclass.html) tool is found under the Raster/Change category… menu.\n\n::::: grid\n::: g-col-6\n\n1. Enter *landuse* for the raster to be reclassified\n\n2. Enter *friction_reclassified* for the output reclassified map\n\n3. Enter the reclass rules directly in the text box or from a saved text file. Use and \\* symbol to represent everything not covered by the specific reclass rules\n\n > 11 90 95 = 10 water \n > 21 thru 24 = 2 urban \n > 31 = 5 lava \n > 41 thru 43 = 1 conifer forest \n > 52 = 3 pinyon juniper woodland \n > \\* = 0 no friction \n \n:::\n\n::: g-col-6\n\n- This creates a new friction map named *friction_landcover*\n\n![](img_movement/GRASS_movement_22.webp)\n:::\n:::::\n\n## Command line\n\n::: {#8e531097 .cell execution_count=11}\n``` {.python .cell-code}\nr.reclass input=landuse output=friction_landcover rules=- << EOF\n11 90 95 = 10 water\n21 thru 24 = 2 urban\n31 = 5 lava\n41 thru 43 = 1 conifer forest\n52 = 3 pinyon juniper woodland\n* = 0 no friction\nEOF\n```\n:::\n\n\n## Python\n\n::: {#8285abd7 .cell execution_count=12}\n``` {.python .cell-code}\nrules = \"\"\"\\\n11 90 95 = 10 water\n21 thru 24 = 2 urban\n31 = 5 lava\n41 thru 43 = 1 conifer forest\n52 = 3 pinyon juniper woodland\n* = 0 no friction\n\"\"\"\ngs.write_command(\"r.reclass\",\n input=\"landuse\",\n output=\"friction_landcover\",\n rules=\"-\",\n stdin=rules)\n```\n:::\n\n\n::::::\n\n![The reclassified friction map (*friction_landcover*)](img_movement/GRASS_movement_23.webp)\n\n## Modifying a cumulative cost surface with a friction map\n\nWe can now create a new cumulative cost surface using this new friction map and convert it from seconds to hours, as we did before:\n\n::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n1. Follow the procedures in the [Generate the cumulative cost surface\nsection](#generate-cumulative-cost-surface) and substitute the new *friction_landcover* map for the *friction0* map used previously.\n\n2. Convert the cumulative cost surface to hours instead of seconds following the procedures in the [Movement across a cumulative cost surface section]({#movement-across-cost-surface})\n\n#### Command line\n\n::: {#539105a8 .cell execution_count=13}\n``` {.python .cell-code}\nr.walk elevation=elevation friction=friction_landcover output=FMC_vegcost_seconds outdir=FMC_vegcost_directions start_points=FMC -k\nr.mapcalc \"FMC_vegcost_hours = FMC_vegcost_seconds / 3600\"\n```\n:::\n\n\n#### Python\n\n::: {#a931373e .cell execution_count=14}\n``` {.python .cell-code}\ngs.run_command(\"r.walk\",\n elevation=\"elevation\",\n friction=\"friction_landcover\",\n output=\"FMC_vegcost_seconds\",\n outdir=\"FMC_vegcost_directions\",\n start_points=\"FMC\",\n flags=\"k\")\ngs.mapcalc(\"FMC_vegcost_hours = FMC_vegcost_seconds / 3600\")\n```\n:::\n\n\n:::\n\n![cumulative cost surface with additional friction from land cover](img_movement/GRASS_movement_24.webp)\n\n## Least cost paths and friction\n\n- We can create a new LCP from the stranded hiker to FMC over terrain where land cover also affects the cost of movement\n\n::: {.panel-tabset group=\"language\"}\n\n#### GUI\n\n1. Follow the procedures in the [Least cost path section](#least-cost-path), substituting the new direction map made along with the cumulative cost surface with land cover friction map.\n\n2. Give this new LCP the name *LCP_vegcost* to distinguish from the previous LCP.\n\n#### Command line\n\n::: {#42a0d371 .cell execution_count=15}\n``` {.python .cell-code}\nr.path input=FMC_vegcost_directions format=auto vector_path=LCP_vegcost start_coordinates=477476,13914951\n```\n:::\n\n\n#### Python\n\n::: {#76c25b63 .cell execution_count=16}\n``` {.python .cell-code}\ngs.run_command(\"r.path\",\n input=\"FMC_vegcost_directions\",\n format=\"auto\",\n vector_path=\"LCP_vegcost\",\n start_coordinates=[477476, 13914951])\n```\n:::\n\n\n:::\n\n- It takes more time to reach FMC if the hiker has to navigate dense vegetation as well as terrain.\n\n- In the map below, terrain colored by land cover, the original LCP with terrain only is shown by the blue line. The LCP with a land cover friction map is shown by the heavier yellow line\n\n![comparing LCP with terrain only and with land cover friction](img_movement/GRASS_movement_25.webp)\n\n", "supporting": [ - "GRASS_movement_files" + "GRASS_movement_files/figure-html" ], "filters": [], "includes": {}