The downloaded flood extent covered a larger area in Rio Grande do Sul. However, the area of interest containing the urban dense cities such as Porto Alegre was smaller. Therefore, a series of operations reduced the large area focusing on Porto Alegre, while improving the query performance.
A positive aspect of these set of operations in the methodology is to allow demanding analysis without expensive hardware (See computational environment), which indirectly means a lower consume of energy and environment impact. If a still larger area of interest is required, we carried out tests with duckdb and its extension spatial 1 with sucess.
Visualization of flood extent
library(sf)library(DBI)# Load the flood extent and remove Z dimensionflooding_subdivided_porto <-st_zm(sf::st_read(connection, DBI::Id(schema="agile_gis_2025_rs",table="flooding_subdivided_porto")))core_metropolitan_area <-st_zm(sf::st_read(connection, DBI::Id(schema="agile_gis_2025_rs",table="core_metropolitan_area")))# Create leaflet productlibrary(leaflet.extras)library(leaflet)leaflet() |>addTiles() |>addProviderTiles("OpenStreetMap", group ="OpenStreetMap") |>addProviderTiles("Esri.WorldImagery", group ="Esri.WorldImagery") |>addPolygons(data=flooding_subdivided_porto,fillColor ="#dec8b7ff",fillOpacity =0.6,weight =1,color ="#4b2609",dashArray ="1",group="Flood extent") |>addPolygons(data=core_metropolitan_area,fillColor ="yellow",fillOpacity =0.05,weight =2,color ="red",dashArray ="1",group="Core Metropolitan Area") |>addLayersControl(baseGroups =c("OpenStreetMap", "Esri.WorldImagery"),overlayGroups =c("Flood extent","Core Metropolitan Area"),options =layersControlOptions(collapsed =FALSE) )
These set of operations are grouped into the section subdivide the flood extent and simplify geometry.
1.1 Subdivide the flood extent
The code chunk “[AoI-6] Dect flood extent” includes one query with several CTE to create the table flooding_subdivided_porto. Firstly, we subdivided the extent of the flooding following the post published by the co-founder of PostGIS 2, Paul Ramsay.
[AoI-6] Detect flood extent
CREATETABLE flooding_subdivided_porto ASWITH porto_alegre_ghs AS(SELECT ghs.*FROM urban_center_4326 AS ghsJOIN nutsON st_intersects(nuts.wkb_geometry, ghs.geom)WHERE nuts.shapename ='Porto Alegre'),--- Select core metropolitan areas in the municipalities that intersects with ghscore_metropolitan_area_ghs AS (SELECT st_union(wkb_geometry) AS core_geomFROM nutsJOIN porto_alegre_ghsON st_intersects(nuts.wkb_geometry, porto_alegre_ghs.geom)),---Subdivide the flood extent to increase spatial indexes performanceflooding_sul_subdivided AS (SELECT st_subdivide(geom) as the_geomFROM flooding_raw)/* Select the flooding subunits that intersects withthe previous bounding box */SELECT flooding_sul_subdivided.*FROM flooding_sul_subdivided, core_metropolitan_area_ghsWHERE st_intersects(flooding_sul_subdivided.the_geom, core_metropolitan_area_ghs.core_geom);--- time: 39099.137 ms
1.2 Simplify geometry
The reason for simplifying the geometry by removing the slivers polygons was that spatial operations such as st_difference, which are required to obtain the post-disaster network, are computationally expensive. Based on the Carto’s post, we removed the slivers polygons using the calculated area 3. Another technique in PostGIS to remove these slivers polygons is via dilation and erosion 45, which could be used in future studies. The table flooding_cleaned_porto_union_simple is a lighter version of the flood extent
[AoI-7] Simplifying geometry
# Reduce geometryCREATEINDEX idx_flooding_subdivided_porto ON flooding_subdivided_porto USING gist(the_geom);EXPLAINANALYZECREATETABLE flooding_cleaned_porto ASSELECT ST_Force2D((ST_Dump(the_geom)).geom)::geometry(Polygon, 4326) AS geomFROM flooding_subdivided_porto;--- time: 25.746 ms# UniteCREATEINDEX idx_flooding_cleaned_porto ON flooding_cleaned_porto USING gist(geom);EXPLAINANALYZECREATETABLE flooding_cleaned_porto_union ASSELECT ST_Union(geom)::geometry(multipolygon, 4326) geomFROM flooding_cleaned_porto;--- time: 348.667 ms# Reduce geometries againCCREATE INDEX idx_flooding_cleaned_porto_union ON flooding_cleaned_porto_union USING gist(geom);EXPLAINANALYZECREATETABLE flooding_cleaned_porto_union_simple ASSELECT (ST_Dump(geom)).geom::geometry(polygon, 4326) geomFROM flooding_cleaned_porto_union;---time: 16.893 ms/*This allowed to calculate the area of each polygon findingslivers that were removed using the following code.*/SELECTCOUNT(*)FROM flooding_cleaned_porto_union_simple ; --- count= 99DELETEFROMflooding_cleaned_porto_union_simpleWHEREST_Area(geom) <0.0001; ---count= 4.
1.3 Visualize flood extent
The following map shows the difference between the original flood extent subdivided and delimited by the AoI and the flood extent cleaned after removing the slivers polygons. The purpose is to check the results from a qualitative point of view.
Create map to visualize the flood extent in the AoI and its simplified version
# Load the flood extent and remove Z dimensionflooding_cleaned_porto_union_simple <-st_zm(sf::st_read(connection, DBI::Id(schema="agile_gis_2025_rs",table="flooding_cleaned_porto_union_simple")))flooding_all <-st_zm(sf::st_read(connection, DBI::Id(schema="agile_gis_2025_rs",table="flooding_raw")))# Visualize the raw flood extent and its simplified versionlibrary(mapview)library(leafsync)m1_flood <-mapview(flooding_subdivided_porto, color="#f1a340",col.region="#f1a340",layer.name ="Raw flooding extent")m2_flood <-mapview(flooding_cleaned_porto_union_simple,color ="#998ec3",col.region="#998ec3",layer.name="Simplified flooding extent")m1_flood | m2_flood
Meanwhile, the following bar chart shows the difference on the size of the flood extent. Originally the size of the flood extent was nearly 20 MB covering an area larger than the AoI. After being reduced to the AoI, the flood extent reduced its size to nearly 664 kB Lastly, simplifying the geometry of the flood extent in the AoI reduced its size to 464 kB.
# Detect floodThe downloaded flood extent covered a larger area in Rio Grande do Sul.However, the area of interest containing the urban dense cities such as Porto Alegre was smaller.Therefore, a series of operations reduced the large area focusing on Porto Alegre, while improving the query performance. A positive aspect of these set of operations in the methodology is to allow demanding analysis without expensive hardware (See computational environment), which indirectly means a lower consume of energy and environment impact. If a still larger area of interest is required, we carried out tests with duckdb and its extension spatial ^[[DuckDB spatial: https://duckdb.org/docs/stable/extensions/spatial/overview.html](https://duckdb.org/docs/stable/extensions/spatial/overview.html)] with sucess.```{r}#| eval: true#| echo: false#| include: false## This is added to fix errors that arise when the first code chunk is not in R.1+1``````{=html}<iframe width="780" height="500" src="data/media/detect_flood.html" title="Flood extent of the AoI"></iframe>``````{r}#| eval: false#| echo: true#| code-summary: Visualization of flood extentlibrary(sf)library(DBI)# Load the flood extent and remove Z dimensionflooding_subdivided_porto <-st_zm(sf::st_read(connection, DBI::Id(schema="agile_gis_2025_rs",table="flooding_subdivided_porto")))core_metropolitan_area <-st_zm(sf::st_read(connection, DBI::Id(schema="agile_gis_2025_rs",table="core_metropolitan_area")))# Create leaflet productlibrary(leaflet.extras)library(leaflet)leaflet() |>addTiles() |>addProviderTiles("OpenStreetMap", group ="OpenStreetMap") |>addProviderTiles("Esri.WorldImagery", group ="Esri.WorldImagery") |>addPolygons(data=flooding_subdivided_porto,fillColor ="#dec8b7ff",fillOpacity =0.6,weight =1,color ="#4b2609",dashArray ="1",group="Flood extent") |>addPolygons(data=core_metropolitan_area,fillColor ="yellow",fillOpacity =0.05,weight =2,color ="red",dashArray ="1",group="Core Metropolitan Area") |>addLayersControl(baseGroups =c("OpenStreetMap", "Esri.WorldImagery"),overlayGroups =c("Flood extent","Core Metropolitan Area"),options =layersControlOptions(collapsed =FALSE) )```These set of operations are grouped into the section subdivide the flood extent and simplify geometry.## Subdivide the flood extentThe code chunk "[AoI-6] Dect flood extent" includes one query with several CTE to create the table _flooding_subdivided_porto_.Firstly, we subdivided the extent of the flooding following the post published by the co-founder of PostGIS ^[[https://blog.cleverelephant.ca/2019/11/subdivide.html](https://blog.cleverelephant.ca/2019/11/subdivide.html)], Paul Ramsay.```{sql}#| eval: false#| echo: true#| code-summary: "[AoI-6] Detect flood extent"CREATETABLE flooding_subdivided_porto ASWITH porto_alegre_ghs AS(SELECT ghs.*FROM urban_center_4326 AS ghsJOIN nutsON st_intersects(nuts.wkb_geometry, ghs.geom)WHERE nuts.shapename ='Porto Alegre'),--- Select core metropolitan areas in the municipalities that intersects with ghscore_metropolitan_area_ghs AS (SELECT st_union(wkb_geometry) AS core_geomFROM nutsJOIN porto_alegre_ghsON st_intersects(nuts.wkb_geometry, porto_alegre_ghs.geom)),---Subdivide the flood extent to increase spatial indexes performanceflooding_sul_subdivided AS (SELECT st_subdivide(geom) as the_geomFROM flooding_raw)/* Select the flooding subunits that intersects withthe previous bounding box */SELECT flooding_sul_subdivided.*FROM flooding_sul_subdivided, core_metropolitan_area_ghsWHERE st_intersects(flooding_sul_subdivided.the_geom, core_metropolitan_area_ghs.core_geom);--- time: 39099.137 ms```## Simplify geometryThe reason for simplifying the geometry by removing the slivers polygons was that spatial operations such as st_difference, which are required to obtain the post-disaster network, are computationally expensive. Based on the Carto’s post, we removed the slivers polygons using the calculated area ^[[https://icarto.es/en/improve-performance-making-the-difference-between-two-layers-with-postgis/](https://icarto.es/en/improve-performance-making-the-difference-between-two-layers-with-postgis/)].Another technique in PostGIS to removethese slivers polygons is via dilation and erosion ^[[https://trac.osgeo.org/postgis/wiki/UsersWikiBufferCoast](https://trac.osgeo.org/postgis/wiki/UsersWikiBufferCoast)] ^[[https://cdn2.hubspot.net/hubfs/2283855/PostGIS%20Day%202019%20-%20Overview.pdf](https://cdn2.hubspot.net/hubfs/2283855/PostGIS%20Day%202019%20-%20Overview.pdf)], which could be used in future studies. The table _flooding_cleaned_porto_union_simple_ is a lighter version of the flood extent```{sql}#| eval: false#| echo: true#| code-summary: "[AoI-7] Simplifying geometry"# Reduce geometryCREATEINDEX idx_flooding_subdivided_porto ON flooding_subdivided_porto USING gist(the_geom);EXPLAINANALYZECREATETABLE flooding_cleaned_porto ASSELECT ST_Force2D((ST_Dump(the_geom)).geom)::geometry(Polygon, 4326) AS geomFROM flooding_subdivided_porto;--- time: 25.746 ms# UniteCREATEINDEX idx_flooding_cleaned_porto ON flooding_cleaned_porto USING gist(geom);EXPLAINANALYZECREATETABLE flooding_cleaned_porto_union ASSELECT ST_Union(geom)::geometry(multipolygon, 4326) geomFROM flooding_cleaned_porto;--- time: 348.667 ms# Reduce geometries againCCREATE INDEX idx_flooding_cleaned_porto_union ON flooding_cleaned_porto_union USING gist(geom);EXPLAINANALYZECREATETABLE flooding_cleaned_porto_union_simple ASSELECT (ST_Dump(geom)).geom::geometry(polygon, 4326) geomFROM flooding_cleaned_porto_union;---time: 16.893 ms/*This allowed to calculate the area of each polygon findingslivers that were removed using the following code.*/SELECTCOUNT(*)FROM flooding_cleaned_porto_union_simple ; --- count= 99DELETEFROMflooding_cleaned_porto_union_simpleWHEREST_Area(geom) <0.0001; ---count= 4.```## Visualize flood extentThe following map shows the difference between the original flood extent subdivided and delimited by the AoI and the flood extent cleaned after removing the slivers polygons. The purpose is to check the results from a qualitative point of view.```{=html}<iframe width="780" height="500" src="/data/media/detect_flood_compare_raw_aoi_raw.html" title"Raw flood extent and its simplication"></iframe>``````{r}#| eval: false#| echo: true#| code-summary: "Create map to visualize the flood extent in the AoI and its simplified version"# Load the flood extent and remove Z dimensionflooding_cleaned_porto_union_simple <-st_zm(sf::st_read(connection, DBI::Id(schema="agile_gis_2025_rs",table="flooding_cleaned_porto_union_simple")))flooding_all <-st_zm(sf::st_read(connection, DBI::Id(schema="agile_gis_2025_rs",table="flooding_raw")))# Visualize the raw flood extent and its simplified versionlibrary(mapview)library(leafsync)m1_flood <-mapview(flooding_subdivided_porto, color="#f1a340",col.region="#f1a340",layer.name ="Raw flooding extent")m2_flood <-mapview(flooding_cleaned_porto_union_simple,color ="#998ec3",col.region="#998ec3",layer.name="Simplified flooding extent")m1_flood | m2_flood ```Meanwhile, the following bar chart shows the difference on the size of the flood extent. Originally the size of the flood extent was nearly 20 MB covering an area larger than the AoI. After being reduced to the AoI, the flood extent reduced its size to nearly 664 kB Lastly, simplifying the geometry of the flood extent in the AoI reduced its size to 464 kB.```{r}#| eval: false#| echo: true#| code-summary: "Create barplot to compare flood extent's versions"# Compare size of both versions## Create dataframelibrary(tidyverse)df_flood <-data_frame(version =c("1.Original","2.AoI","3.AoI simplified"),size_bytes =c( object.size(flooding_all)/1000,object.size(flooding_subdivided_porto)/1000,object.size(flooding_cleaned_porto_union_simple)/1000)) ## Custom Y-axis labels labels <-function(x) {paste(x, "kB")}## Create column plotlibrary(ggplot2)library(ggbreak)flood_plot <-ggplot(data=df_flood) + ggplot2::geom_col(aes(x=version, y=size_bytes, fill=version)) +labs(x="Flood extent",y="Size in kBytes") +theme_minimal() +theme(legend.position ="bottom") +scale_fill_manual(values=c("#e66101","#f1a340","#998ec3")) +scale_y_break(c(664,20000), ticklabels =c(df_flood$size_bytes[3], df_flood$size_bytes[2],round(df_flood$size_bytes[1])))flood_plot +scale_y_continuous(label = labels)```