diff --git a/README.md b/README.md index c6840c7..7fbc1d5 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,17 @@ The Cities Indicator Framework (CIF) is a set of Python tools to make it easier ## Quick start If all you want to do is use the CIF, the quickest way to get started is to use our [WRI Cities Indicator Framework Colab Notebook](https://colab.research.google.com/drive/1PV1H-godxJ6h42p74Ij9sdFh3T0RN-7j#scrollTo=eM14UgpmpZL-) +## PR Review +0. Prerequisites + 1. Git + * On Windows I recommend WSL https://learn.microsoft.com/en-us/windows/wsl/tutorials/wsl-git + 3. https://cli.github.com/ + * On MacOS I recommend the Homebrew option + * If you don't have an ssh key, it will install one for you + 4. Conda (or Mamba) to install dependencies + * If you have Homebrew `brew install --cask miniconda` + + ## Dependencies ### Conda `conda env create -f environment.yml` diff --git a/docs/developer.md b/docs/developer.md index ec98610..e273e9f 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -79,6 +79,16 @@ Hopefully we already have the layers you need in `city_metrix/layers/` and you c 4. Import the new layer class to [city_metrix/layers/\_\_init\_\_.py](../city_metrix/layers/__init__.py) +5. Add a test to [tests/layers.py](../tests/layers.py) to ensure the new layer is working as expected. + +6. Add a section to the get layers.ipynb notebook to demonstrate how to use the new layer. + +7. Create a PR to merge the new layer into the main branch with these in the PR description: + - Link to Jira ticket (if any) + - A brief description of the new layer + - A link to the Airtable record for the new layer + - Explain how to test the new layer + ## Adding an Indicator Once you have all the data layers you need as inputs, here is the process to create an indicator using them. diff --git a/environment.yml b/environment.yml index b00d08f..5848dbc 100644 --- a/environment.yml +++ b/environment.yml @@ -17,6 +17,7 @@ dependencies: - dask[complete]=2023.11.0 - matplotlib=3.8.2 - jupyterlab=4.0.10 + - geemap=0.32.0 - pip=23.3.1 - pip: - cartoframes==1.2.5 diff --git a/notebooks/tutorial/get layers.ipynb b/notebooks/tutorial/get layers.ipynb index 8382493..b426bdb 100644 --- a/notebooks/tutorial/get layers.ipynb +++ b/notebooks/tutorial/get layers.ipynb @@ -23,12 +23,16 @@ "source": [ "Every layer is defined as separate python class:\n", "\n", - "| Layer name | class name | Parameters | Layer metadata |\n", + "| Layer name | class name | Parameter defaults | Layer metadata |\n", "| ---- | ---- | ---- | ---- |\n", - "| Tropical Tree Cover | `TreeCover()` | `min_tree_cover`: a threshold to use to filter the minimum percent of tree cover| |\n", - "| EsaWorldCover | `EsaWorldCover()` | `EsaWorldCoverClass`: a specific class of land cover| |\n", - "| Land Surface Temeprature | `LandSurfaceTemperature()` | | |\n", - "| High Land Surface Temeprature | `HighLandSurfaceTemperature()` | | |" + "| Tropical Tree Cover | `TreeCover()` | `min_tree_cover=None`: a threshold to use to filter the minimum percent of tree cover| |\n", + "| EsaWorldCover | `EsaWorldCover()` | `land_cover_class=None`; `EsaWorldCoverClass`: a specific class of land cover| |\n", + "| Land Surface Temeprature | `LandSurfaceTemperature()` | `start_date=\"2013-01-01\", end_date=\"2023-01-01\"` | |\n", + "| Albedo | `Albedo()` | `start_date=\"2021-01-01\", end_date=\"2022-01-01\"` | |\n", + "| Natural Areas | `NaturalAreas()` | `none` | |\n", + "| Open Street Map | `OpenStreetMap()` | `osm_class=None`; `OpenStreetMapClass`: Groupings of OSM Tags for various land uses | |\n", + "| Building Hight | `AverageNetBuildingHeight()` | `none` | |\n", + "| Building Footprints | `OpenBuildings()` | `country='USA'` | |" ] }, { @@ -43,10 +47,45 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 66, "id": "7ed2c665-e6d8-4e98-95ac-41aab749493f", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import os\n", "import geopandas as gpd\n", @@ -57,17 +96,51 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 67, "id": "602a6217-fd80-4cec-b40b-20de68b8f62b", "metadata": {}, "outputs": [ { "data": { + "text/html": [ + "\n", + " \n", + " " + ], "text/plain": [ - "'C:\\\\Users\\\\Saif.Shabou\\\\OneDrive - World Resources Institute\\\\Documents\\\\cities-indicators-framework\\\\citymetrix\\\\cities-cif'" + "" ] }, - "execution_count": 3, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'/Users/Chris.Rowe'" + ] + }, + "execution_count": 67, "metadata": {}, "output_type": "execute_result" } @@ -79,104 +152,93 @@ ] }, { - "cell_type": "code", - "execution_count": 4, - "id": "d4f01f2f-5164-4a05-9997-f70b2abe6b37", - "metadata": {}, - "outputs": [], - "source": [ - "os.environ['GCS_BUCKET']='gee-exports'\n", - "os.environ['GOOGLE_APPLICATION_USER']='developers@citiesindicators.iam.gserviceaccount.com'\n", - "os.environ['GOOGLE_APPLICATION_CREDENTIALS']='C:\\\\Users\\Saif.Shabou\\OneDrive - World Resources Institute\\Documents\\cities-indicators-framework\\citymetrix\\credentials-citiesindicators.json'" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "014804d3-5473-48c1-919a-8275a7cba083", + "cell_type": "markdown", + "id": "6280fc2f", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Authenticating to GEE with configured credentials file.\n" - ] - } - ], "source": [ - "from city_metrix.layers import Albedo, EsaWorldCoverClass, EsaWorldCover, HighLandSurfaceTemperature, TreeCover, OpenStreetMap, OpenStreetMapClass" + "In most cases you shouldn't need to set these. GEE will open your browser to authenticate. " ] }, { "cell_type": "code", - "execution_count": 6, - "id": "53554a74-6fa9-4030-8ee7-dd1df79f0d75", + "execution_count": 68, + "id": "d4f01f2f-5164-4a05-9997-f70b2abe6b37", "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
geo_idgeo_levelgeo_namegeo_parent_namecreation_dategeometry
0BRA-Salvador_ADM4-union_1ADM4-unionBRA-SalvadorBRA-Salvador2022-08-03MULTIPOLYGON (((-38.50135 -13.01134, -38.50140...
\n", - "
" + " \n", + " " ], "text/plain": [ - " geo_id geo_level geo_name geo_parent_name \\\n", - "0 BRA-Salvador_ADM4-union_1 ADM4-union BRA-Salvador BRA-Salvador \n", - "\n", - " creation_date geometry \n", - "0 2022-08-03 MULTIPOLYGON (((-38.50135 -13.01134, -38.50140... " + "" ] }, - "execution_count": 6, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" } ], "source": [ - "# load boundary\n", + "#os.environ['GCS_BUCKET']='gee-exports'\n", + "#os.environ['GOOGLE_APPLICATION_USER']='developers@citiesindicators.iam.gserviceaccount.com'\n", + "#os.environ['GOOGLE_APPLICATION_CREDENTIALS']='C:\\\\Users\\Saif.Shabou\\OneDrive - World Resources Institute\\Documents\\cities-indicators-framework\\citymetrix\\credentials-citiesindicators.json'" + ] + }, + { + "cell_type": "markdown", + "id": "0f4dfdd1", + "metadata": {}, + "source": [ + "# Load boundary\n", + "Pick one to test with or add your own" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53554a74-6fa9-4030-8ee7-dd1df79f0d75", + "metadata": {}, + "outputs": [], + "source": [ + "# load boundary from s3\n", "boundary_path = 'https://cities-indicators.s3.eu-west-3.amazonaws.com/data/boundaries/boundary-BRA-Salvador-ADM4union.geojson'\n", "city_gdf = gpd.read_file(boundary_path, driver='GeoJSON')\n", "city_gdf.head()" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "18ef8d1f", + "metadata": {}, + "outputs": [], + "source": [ + "# Get area in sqare miles\n", + "city_gdf.to_crs(epsg=3857).area / 10**6" + ] + }, { "cell_type": "markdown", "id": "e4cb233c-5248-4528-823c-364b33a45db2", @@ -4402,12 +4464,189 @@ "city_AverageNetBuidingHeight.plot()" ] }, + { + "cell_type": "markdown", + "id": "465f357c", + "metadata": {}, + "source": [ + "# Building Footprints" + ] + }, { "cell_type": "code", "execution_count": null, "id": "10787dc0-65f8-4906-8d5a-76e625243e9c", "metadata": {}, "outputs": [], + "source": [ + "from city_metrix.layers import OpenBuildings" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "0a9ee177", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Get smaller area\n", + "city_centroid = city_gdf.centroid\n", + "city_centroid_buffer = city_centroid.buffer(0.01)" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "83145215", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Load Building Footprint layer\n", + "# Takes about 2 minutes\n", + "city_OpenBuildings = OpenBuildings(country='BRA').get_data(city_centroid_buffer.total_bounds)" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "95d831e1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e100c2c80dcd4037a829d30b46ecf268", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map(center=[0, 0], controls=(WidgetControl(options=['position', 'transparent_bg'], widget=SearchDataGUI(childr…" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# create a map showing city_OpenBuildings using geemap\n", + "import geemap\n", + "Map = geemap.Map()\n", + "Map.add_gdf(city_OpenBuildings, 'OpenBuildings')\n", + "#Map.add_gdf(city_centroid_buffer, 'Buffer')\n", + "Map\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95c9221e", + "metadata": {}, + "outputs": [], "source": [] } ], @@ -4427,7 +4666,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.13" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/tests/layers.py b/tests/layers.py index a8bd285..f8e4687 100644 --- a/tests/layers.py +++ b/tests/layers.py @@ -1,6 +1,6 @@ import ee -from city_metrix.layers import LandsatCollection2, Albedo, LandSurfaceTemperature, EsaWorldCover, EsaWorldCoverClass, TreeCover, AverageNetBuildingHeight, OpenStreetMap, OpenStreetMapClass, UrbanLandUse +from city_metrix.layers import LandsatCollection2, Albedo, LandSurfaceTemperature, EsaWorldCover, EsaWorldCoverClass, TreeCover, AverageNetBuildingHeight, OpenStreetMap, OpenStreetMapClass, UrbanLandUse, OpenBuildings from city_metrix.layers.layer import get_image_collection from .conftest import MockLayer, MockMaskLayer, ZONES, LARGE_ZONES, MockLargeLayer, MockGroupByLayer, \ MockLargeGroupByLayer @@ -97,3 +97,6 @@ def test_open_street_map(): def test_urban_land_use(): assert UrbanLandUse().get_data(SAMPLE_BBOX).count() +def test_openbuildings(): + count = OpenBuildings().get_data(SAMPLE_BBOX).count().sum() + assert count