From c4f1faa3518d6bd34dd5261437246839c1a4d5e7 Mon Sep 17 00:00:00 2001 From: elbeejay Date: Fri, 8 Mar 2024 18:42:04 -0500 Subject: [PATCH 1/2] doc-strings for urbanraster.py --- src/GOSTurban/UrbanRaster.py | 87 +++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/src/GOSTurban/UrbanRaster.py b/src/GOSTurban/UrbanRaster.py index 9a05029..4ff5940 100644 --- a/src/GOSTurban/UrbanRaster.py +++ b/src/GOSTurban/UrbanRaster.py @@ -33,7 +33,16 @@ def tPrint(s): def geocode_cities(urban_extents): """Generate names for polygon urban extents - :param urban_extents: geopandas dataframe of polygons to be named. Need to be in epsg:4326 + Parameters + ---------- + urban_extents : gpd.GeoDataFrame + geopandas dataframe of polygons to be named. Need to be in epsg:4326 + + Returns + ------- + gpd.GeoDataFrame + input geopandas dataframe with added columns for city, state, and country + """ geolocator = Nominatim(user_agent="new_app") all_res = [] @@ -71,7 +80,15 @@ def __init__(self, inRaster): """ Create urban definitions using gridded population data. - :param inRaster: string or rasterio object representing gridded population data + Parameters + ---------- + inRaster : string or rasterio object + string or object representing gridded population data + + Returns + ------- + None. + """ if type(inRaster) == str: self.inR = rasterio.open(inRaster) @@ -106,10 +123,30 @@ def calculateDegurba( (12) Rural, dispersed, low density - dens: >50, (11) Rural, dispersed, low density - the rest that are populated - :param urbDens: integer of the minimum density value to be counted as urban - :param hdDens: integer of the minimum density value to be counted as high density - :param urbThresh: integer minimum total settlement population to be considered urban - :param hdThresh: integer minimum total settlement population to be considered high density + Parameters + ---------- + urbDens : integer + minimum density value to be counted as urban + hdDens : integer + minimum density value to be counted as high density + urbThresh : integer + minimum total settlement population to be considered urban + hdThresh : integer + minimum total settlement population to be considered high density + minPopThresh : integer + minimum population to be considered a settlement + out_raster : string, optional + path to save the output raster. The default is "". + print_message : string, optional + message to print with each step. The default is "". + verbose : boolean, optional + print messages. The default is False. + + Returns + ------- + dictionary + dictionary containing the final raster, the high density raster, the urban raster, and the shapes of the urban areas + """ popRaster = self.inR @@ -291,17 +328,33 @@ def calculateUrban( Generate urban extents from gridded population data through the application of a minimum density threshold and a minimum total population threshold - :param densVal: integer of the minimum density value to be counted as urban - :param totalPopThresh: integer minimum total settlement population to ne considered urban - :param smooth: boolean to run a single modal smoothing function (this should be run when running - on WorldPop as the increased resolution often leads to small holes and funny shapes - :param verbose: boolean on what messages to receive - :param queen: boolean to determine whether to dissolve final shape to connect queen's contiguity - :param raster: string path to create a boolean raster of urban and not. - Empty string is the default and will create no raster - :param raster_pop: string path to create a raster of the population layer only in the urban areas - Empty string is the default and will create no raster - :returns: GeoPandasDataFrame of the urban extents + Parameters + ---------- + densVal : integer, optional + minimum density value to be counted as urban + totalPopThresh : integer, optional + minimum total settlement population to ne considered urban + smooth : boolean, optional + toggle to run a single modal smoothing function (this should be run when running + on WorldPop as the increased resolution often leads to small holes and funny shapes + verbose : boolean + what messages to receive + queen : boolean + determine whether to dissolve final shape to connect queen's contiguity + raster : str + string path to create a boolean raster of urban and not. + Empty string is the default and will create no raster + raster_pop : str + string path to create a raster of the population layer only in the urban areas + Empty string is the default and will create no raster + print_message : str + message to print with each step. The default is "". + + Returns + ------- + gpd.GeoDataFrame + geopandas dataframe of urban extents + """ popRaster = self.inR From 737570b643dccb33283384a3553fa9bfd5901369 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 8 Mar 2024 23:43:01 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/_toc.yml | 1 - docs/conf.py | 2 +- .../Replications/URB_NovelUrbanization.py | 30 ++++---- notebooks/Tutorials/LEI_Example.ipynb | 72 ++++++++++++------- .../Tutorials/UrbanAreas_tutorials.ipynb | 8 +-- requirements.txt | 2 - src/GOSTurban/LEI.py | 2 +- 7 files changed, 71 insertions(+), 46 deletions(-) diff --git a/docs/_toc.yml b/docs/_toc.yml index 8000614..209f5d8 100755 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -15,4 +15,3 @@ parts: chapters: - file: notebooks/Tutorials/UrbanAreas_tutorials.ipynb - file: notebooks/Tutorials/LEI_Example.ipynb - diff --git a/docs/conf.py b/docs/conf.py index 08d061d..5867762 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,7 +24,7 @@ "sphinx_jupyterbook_latex", "sphinx.ext.napoleon", "sphinxcontrib.apidoc", - #"nbsphinx" + # "nbsphinx" ] external_toc_exclude_missing = True external_toc_path = "_toc.yml" diff --git a/notebooks/Replications/URB_NovelUrbanization.py b/notebooks/Replications/URB_NovelUrbanization.py index a31150b..4897bf6 100644 --- a/notebooks/Replications/URB_NovelUrbanization.py +++ b/notebooks/Replications/URB_NovelUrbanization.py @@ -1,39 +1,43 @@ -import sys, os, shutil, requests +import sys +import os +import shutil +import requests import rasterio -import pandas as pd -import geopandas as gpd -import numpy as np import GOSTurban.UrbanRaster as urban + def download_pop_file(url, filename): # Open the url r = requests.get(url) # Set decode_content value to True, otherwise the downloaded image file's size will be zero. r.raw.decode_content = True # Open a local file with wb ( write binary ) permission. - with open(filename,'wb') as f: + with open(filename, "wb") as f: shutil.copyfileobj(r.raw, f) + def main(iso3, out_folder): # download the population data - wp_url = f'https://data.worldpop.org/GIS/Population/Global_2000_2020_1km/2020/{iso3.upper()}/{iso3.lower()}_ppp_2020_1km_Aggregated.tif' - print (wp_url) + wp_url = f"https://data.worldpop.org/GIS/Population/Global_2000_2020_1km/2020/{iso3.upper()}/{iso3.lower()}_ppp_2020_1km_Aggregated.tif" + print(wp_url) if not os.path.exists(out_folder): os.makedirs(out_folder) - out_file = os.path.join(out_folder, f'{iso3}_ppp_2020_1km_Aggregated.tif') + out_file = os.path.join(out_folder, f"{iso3}_ppp_2020_1km_Aggregated.tif") out_urban = os.path.join(out_folder, "urban_extents.geojson") out_hd_urban = os.path.join(out_folder, "hd_urban_extents.geojson") - + try: if not os.path.exists(out_file): download_pop_file(wp_url, out_file) except: print(f"Could not download national population data for {iso3} from {wp_url}") - print("If you can manually download to the defined out_folder, the script will run") + print( + "If you can manually download to the defined out_folder, the script will run" + ) if os.path.exists(out_file): - inR = rasterio.open(out_file) + inR = rasterio.open(out_file) urban_calculator = urban.urbanGriddedPop(inR) urban_extents = urban_calculator.calculateUrban( densVal=300, totalPopThresh=5000, smooth=False, queen=False @@ -43,14 +47,14 @@ def main(iso3, out_folder): densVal=1500, totalPopThresh=50000, smooth=True, - queen=True, # high density extents use queen's case contiguity, and are smoothed + queen=True, # high density extents use queen's case contiguity, and are smoothed ) urban_extents.to_file(out_urban, driver="GeoJSON") hd_urban_extents.to_file(out_hd_urban, driver="GeoJSON") + if __name__ == "__main__": iso3 = sys.argv[1] out_folder = sys.argv[2] main(iso3, out_folder) - diff --git a/notebooks/Tutorials/LEI_Example.ipynb b/notebooks/Tutorials/LEI_Example.ipynb index 2a78b22..b9f0bea 100644 --- a/notebooks/Tutorials/LEI_Example.ipynb +++ b/notebooks/Tutorials/LEI_Example.ipynb @@ -24,20 +24,16 @@ "outputs": [], "source": [ "import os\n", - "import sys\n", - "import importlib\n", "import rasterio\n", "import rasterio.features\n", "\n", "import geopandas as gpd\n", "import pandas as pd\n", - "import numpy as np\n", "\n", "import GOSTrocks.rasterMisc as rMisc\n", "import GOSTrocks.ghslMisc as ghslMisc\n", "import GOSTurban.LEI as lei\n", "import GOSTrocks.mapMisc as mapMisc\n", - "import GOSTrocks.rasterMisc as rMisc\n", "\n", "%load_ext autoreload\n", "%autoreload 2" @@ -71,18 +67,22 @@ " temp_folder = \"C:/Temp\"\n", " # clip from global GHSL file\n", " ghsl_folder = \"J:/Data/GLOBAL/GHSL/built\"\n", - " ghsl_files = [os.path.join(ghsl_folder, x) for x in os.listdir(ghsl_folder) if x.endswith(\".tif\")] \n", + " ghsl_files = [\n", + " os.path.join(ghsl_folder, x)\n", + " for x in os.listdir(ghsl_folder)\n", + " if x.endswith(\".tif\")\n", + " ]\n", " inA = gpd.read_file(aoi_file)\n", - " \n", + "\n", " temp_ghsl_files = []\n", " for ghsl_file in ghsl_files:\n", " temp_file = os.path.join(temp_folder, os.path.basename(ghsl_file))\n", " temp_ghsl_files.append(temp_file)\n", " if not os.path.exists(temp_file):\n", " rMisc.clipRaster(rasterio.open(ghsl_file), inA, temp_file)\n", - " \n", + "\n", " ghsl_res, ghsl_profile = ghslMisc.combine_ghsl_annual(temp_ghsl_files)\n", - " with rasterio.open(input_ghsl, 'w', **ghsl_profile) as outR:\n", + " with rasterio.open(input_ghsl, \"w\", **ghsl_profile) as outR:\n", " outR.write_band(1, ghsl_res)" ] }, @@ -116,7 +116,7 @@ "source": [ "ghsl_r = rasterio.open(input_ghsl)\n", "ghsl_d = ghsl_r.read()\n", - "ghsl_d[ghsl_d == ghsl_r.meta['nodata']] = 0\n", + "ghsl_d[ghsl_d == ghsl_r.meta[\"nodata\"]] = 0\n", "\n", "thresh = list(range(1975, 2031, 5))\n", "with rMisc.create_rasterio_inmemory(ghsl_r.profile, ghsl_d) as temp_ghsl:\n", @@ -218,8 +218,14 @@ ], "source": [ "# This calculates the change from 1990 and 2000\n", - "lei_raw = lei.calculate_LEI(input_ghsl, old_list=list(range(1975,1991,5)), new_list=list(range(1995,2001,5)))\n", - "lei_90_00 = gpd.GeoDataFrame(pd.DataFrame(lei_raw, columns=[\"geometry\", \"old\", \"total\"]), geometry='geometry', crs=ghsl_r.crs)\n", + "lei_raw = lei.calculate_LEI(\n", + " input_ghsl, old_list=list(range(1975, 1991, 5)), new_list=list(range(1995, 2001, 5))\n", + ")\n", + "lei_90_00 = gpd.GeoDataFrame(\n", + " pd.DataFrame(lei_raw, columns=[\"geometry\", \"old\", \"total\"]),\n", + " geometry=\"geometry\",\n", + " crs=ghsl_r.crs,\n", + ")\n", "lei_90_00[\"LEI\"] = lei_90_00[\"old\"] / lei_90_00[\"total\"]\n", "\n", "lei_90_00.head()" @@ -260,10 +266,12 @@ } ], "source": [ - "#Map LEI results\n", - "leap_val=0.30\n", - "exp_val=0.70\n", - "lei_90_00['area'] = lei_90_00['geometry'].apply(lambda x: x.area)\n", + "# Map LEI results\n", + "leap_val = 0.30\n", + "exp_val = 0.70\n", + "lei_90_00[\"area\"] = lei_90_00[\"geometry\"].apply(lambda x: x.area)\n", + "\n", + "\n", "def calculate_LEI(val, leap_val, exp_val):\n", " if val <= leap_val:\n", " return 3\n", @@ -271,8 +279,14 @@ " return 2\n", " else:\n", " return 1\n", - "lei_90_00[\"class\"] = lei_90_00[\"LEI\"].apply(lambda x: calculate_LEI(x, leap_val, exp_val))\n", - "mapMisc.static_map_vector(lei_90_00, \"class\", edgecolor='match', colormap=\"Dark2\")#, basemap=ctx.providers.CartoDB.Voyager)" + "\n", + "\n", + "lei_90_00[\"class\"] = lei_90_00[\"LEI\"].apply(\n", + " lambda x: calculate_LEI(x, leap_val, exp_val)\n", + ")\n", + "mapMisc.static_map_vector(\n", + " lei_90_00, \"class\", edgecolor=\"match\", colormap=\"Dark2\"\n", + ") # , basemap=ctx.providers.CartoDB.Voyager)" ] }, { @@ -395,7 +409,9 @@ ], "source": [ "# This calculates the change from 2000 and 2014\n", - "lei_raw = lei.calculate_LEI(input_ghsl, old_list=list(range(1975,2011,5)), new_list=list(range(2015,2030,5)))\n", + "lei_raw = lei.calculate_LEI(\n", + " input_ghsl, old_list=list(range(1975, 2011, 5)), new_list=list(range(2015, 2030, 5))\n", + ")\n", "lei_00_14 = pd.DataFrame(lei_raw, columns=[\"geometry\", \"old\", \"total\"])\n", "lei_00_14[\"LEI\"] = lei_00_14[\"old\"] / lei_00_14[\"total\"]\n", "lei_00_14.head()" @@ -436,10 +452,12 @@ } ], "source": [ - "#Map LEI results\n", - "leap_val=0.30\n", - "exp_val=0.70\n", - "lei_90_00['area'] = lei_90_00['geometry'].apply(lambda x: x.area)\n", + "# Map LEI results\n", + "leap_val = 0.30\n", + "exp_val = 0.70\n", + "lei_90_00[\"area\"] = lei_90_00[\"geometry\"].apply(lambda x: x.area)\n", + "\n", + "\n", "def calculate_LEI(val, leap_val, exp_val):\n", " if val <= leap_val:\n", " return 3\n", @@ -447,8 +465,14 @@ " return 2\n", " else:\n", " return 1\n", - "lei_90_00[\"class\"] = lei_90_00[\"LEI\"].apply(lambda x: calculate_LEI(x, leap_val, exp_val))\n", - "mapMisc.static_map_vector(lei_90_00, \"class\", edgecolor='match', colormap=\"Dark2\")#, basemap=ctx.providers.CartoDB.Voyager)" + "\n", + "\n", + "lei_90_00[\"class\"] = lei_90_00[\"LEI\"].apply(\n", + " lambda x: calculate_LEI(x, leap_val, exp_val)\n", + ")\n", + "mapMisc.static_map_vector(\n", + " lei_90_00, \"class\", edgecolor=\"match\", colormap=\"Dark2\"\n", + ") # , basemap=ctx.providers.CartoDB.Voyager)" ] }, { diff --git a/notebooks/Tutorials/UrbanAreas_tutorials.ipynb b/notebooks/Tutorials/UrbanAreas_tutorials.ipynb index 1d20c25..8ba47e3 100644 --- a/notebooks/Tutorials/UrbanAreas_tutorials.ipynb +++ b/notebooks/Tutorials/UrbanAreas_tutorials.ipynb @@ -100,7 +100,7 @@ } ], "source": [ - "mapMisc.static_map_raster(inR, thresh=[1,5,50,100,300,1000,3000])" + "mapMisc.static_map_raster(inR, thresh=[1, 5, 50, 100, 300, 1000, 3000])" ] }, { @@ -223,7 +223,7 @@ "urban_extents = urban_calculator.calculateUrban(\n", " densVal=300, totalPopThresh=5000, smooth=False, queen=False, verbose=True\n", ")\n", - "urban_extents['Type'] = 1\n", + "urban_extents[\"Type\"] = 1\n", "urban_extents.head()" ] }, @@ -406,7 +406,7 @@ " queen=True, # high density extents use queen's case contiguity, and\n", " verbose=True,\n", ") # High density extents have hole smoothing applied.\n", - "hd_urban_extents['Type'] = 2\n", + "hd_urban_extents[\"Type\"] = 2\n", "hd_urban_extents.head()" ] }, @@ -491,7 +491,7 @@ ], "source": [ "combo_extents = pd.concat([urban_extents, hd_urban_extents])\n", - "mapMisc.static_map_vector(combo_extents, \"Type\", colormap='magma')" + "mapMisc.static_map_vector(combo_extents, \"Type\", colormap=\"magma\")" ] }, { diff --git a/requirements.txt b/requirements.txt index abbc506..f1a28c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,5 +10,3 @@ elevation geojson hatch git - - diff --git a/src/GOSTurban/LEI.py b/src/GOSTurban/LEI.py index 5ee1cf0..5293a34 100755 --- a/src/GOSTurban/LEI.py +++ b/src/GOSTurban/LEI.py @@ -195,4 +195,4 @@ def calculate_LEI(val, leap_val, exp_val): res["class"] = res["LEI"].apply(lambda x: calculate_LEI(x, leap_val, exp_val)) xx = res.groupby("class") - return xx['area'].sum() + return xx["area"].sum()