|
110 | 110 | "- [geopandas built-in dataset from the Natural Earth](https://www.naturalearthdata.com/downloads/50m-cultural-vectors/50m-admin-0-countries-2/), but the dataset with the boundaries of countries is not curently available there." |
111 | 111 | ] |
112 | 112 | }, |
| 113 | + { |
| 114 | + "cell_type": "markdown", |
| 115 | + "metadata": {}, |
| 116 | + "source": [ |
| 117 | + "The class below is preprocessing the input data for follow-up ingestion of protected areas:\n", |
| 118 | + "- extracting all file names of input land-use/land-cover datasets and years they represent\n", |
| 119 | + "- sending a request to ohsome API [endpoint](https://api.ohsome.org/v1/elements/geometry)\n", |
| 120 | + "- fetching the country code(s) (ISO 3166-1 alpha-3 standard) from a bounding box of input raster\n", |
| 121 | + "\n" |
| 122 | + ] |
| 123 | + }, |
113 | 124 | { |
114 | 125 | "cell_type": "code", |
115 | 126 | "execution_count": null, |
|
203 | 214 | " # update lulc_series with files that exist\n", |
204 | 215 | " return existing_lulc_series\n", |
205 | 216 | " \n", |
206 | | - " #NOTE Ohsome API is using openstreetmap data, which may not be the best source to fetch country codes from bounding box with. The GAUL dataset provided by FAO (UN) is a better source for this but it is not available through API.\n", |
207 | 217 | " def get_country_code_from_bbox(self, bbox:str, output_path:str) -> set:\n", |
208 | 218 | " \"\"\"\n", |
209 | 219 | " This function sends a request to the ohsome API to get the country code from a given bounding box\n", |
|
242 | 252 | " return unique_country_names\n", |
243 | 253 | " else:\n", |
244 | 254 | " raise Exception(f\"Error: {response.status_code}\")\n", |
245 | | - "\n", |
246 | | - " \n", |
247 | | - " \n", |
| 255 | + " \n", |
248 | 256 | " def fetch_lulc_country_codes(self, output_path:str) -> dict[set]:\n", |
249 | 257 | " \"\"\"\n", |
250 | 258 | " Fetch the country codes for the LULC rasters\n", |
|
264 | 272 | " return lulc_country_codes" |
265 | 273 | ] |
266 | 274 | }, |
| 275 | + { |
| 276 | + "cell_type": "markdown", |
| 277 | + "metadata": {}, |
| 278 | + "source": [ |
| 279 | + "Now, we should run this class:" |
| 280 | + ] |
| 281 | + }, |
267 | 282 | { |
268 | 283 | "cell_type": "code", |
269 | 284 | "execution_count": null, |
|
283 | 298 | "source": [ |
284 | 299 | "#### Looping over countries from the bounding box\n", |
285 | 300 | "\n", |
286 | | - "Now, we can loop over the countries of the bounding box of input raster dataset, fetch json response and convert them into GeoJSON format." |
| 301 | + "To get the data from the World Database on Protected Areas, we are defining functions to convert JSON responses from the Protected Planet API to a merged GeoJSON per country of interest." |
287 | 302 | ] |
288 | 303 | }, |
289 | 304 | { |
|
413 | 428 | " return geojson_filepath" |
414 | 429 | ] |
415 | 430 | }, |
| 431 | + { |
| 432 | + "cell_type": "markdown", |
| 433 | + "metadata": {}, |
| 434 | + "source": [ |
| 435 | + "The following class is used to fetch data from the World Database on Protected Areas:\n", |
| 436 | + "- defining API token, configuration parameter on marine areas (by default, marine areas are not considered) and accessing the list of country codes from previous function\n", |
| 437 | + "- combining all protected areas for each country, concatenating by pages, and processing them as a single GeoJSON\n", |
| 438 | + "- saving GeoJSONs to output directory and exporting to GeoPackages" |
| 439 | + ] |
| 440 | + }, |
416 | 441 | { |
417 | 442 | "cell_type": "code", |
418 | 443 | "execution_count": null, |
|
542 | 567 | "# define country codes from the previous block\n", |
543 | 568 | "countries = unique_country_names\n", |
544 | 569 | "\n", |
545 | | - "# TODO remove API response in final versions\n", |
546 | 570 | "# directory to save GeoJSON files\n", |
547 | 571 | "protected_areas_data_output_dir = os.path.join(pa_output_dir, \"pa_data\")\n", |
548 | 572 | "os.makedirs(protected_areas_data_output_dir, exist_ok=True)\n", |
|
574 | 598 | "4. Compress protected areas." |
575 | 599 | ] |
576 | 600 | }, |
| 601 | + { |
| 602 | + "cell_type": "markdown", |
| 603 | + "metadata": {}, |
| 604 | + "source": [ |
| 605 | + "The code below transforms fetched data on protected areas:\n", |
| 606 | + "- reprojects to the same CRS as the LULC raster dataset, and filters them based on the year of establishment into separate GeoPackage files\n", |
| 607 | + "- rasterises vector data on protected areas, using the spatial extent, spatial resolution and CRS of the input LULC raster dataset" |
| 608 | + ] |
| 609 | + }, |
577 | 610 | { |
578 | 611 | "cell_type": "code", |
579 | 612 | "execution_count": null, |
|
702 | 735 | " except subprocess.CalledProcessError as e:\n", |
703 | 736 | " print(f\"Error rasterizing protected areas: {e}\")\n", |
704 | 737 | " \n", |
705 | | - " \n", |
706 | | - "\n", |
707 | 738 | " def rasterize_pa_geopackage(self, lulc_metadata:RasterMetadata, pa_to_yearly_rasters:bool=True , keep_intermediate_gpkg:bool=False) -> None:\n", |
708 | 739 | " \"\"\"\n", |
709 | 740 | " Rasterizes the protected areas to the same extent and resolution as the LULC raster dataset.\n", |
|
744 | 775 | "metadata": {}, |
745 | 776 | "source": [ |
746 | 777 | "It is important to extract year stamps from the filenames. \\\n", |
747 | | - "**WARNING:** The name of your input dataset must always end up with four-digit year before the file extension!" |
748 | | - ] |
749 | | - }, |
750 | | - { |
751 | | - "cell_type": "markdown", |
752 | | - "metadata": {}, |
753 | | - "source": [ |
| 778 | + "**WARNING:** The name of your input dataset must always end up with four-digit year before the file extension!\n", |
| 779 | + "Protected areas should be filtered by year stamp according to the PA's establishment year.\n", |
| 780 | + "\n", |
754 | 781 | "Then, extent of LULC files (minimum and maximum coordinates) is extracted." |
755 | 782 | ] |
756 | 783 | }, |
757 | | - { |
758 | | - "cell_type": "markdown", |
759 | | - "metadata": {}, |
760 | | - "source": [ |
761 | | - "Protected areas should be filtered by year stamp according to the PA's establishment year." |
762 | | - ] |
763 | | - }, |
764 | | - { |
765 | | - "cell_type": "markdown", |
766 | | - "metadata": {}, |
767 | | - "source": [ |
768 | | - "Rasterization function based on yearstamps of protected areas is launched." |
769 | | - ] |
770 | | - }, |
771 | 784 | { |
772 | 785 | "cell_type": "code", |
773 | 786 | "execution_count": null, |
|
781 | 794 | "os.makedirs(raster_output_dir, exist_ok=True)" |
782 | 795 | ] |
783 | 796 | }, |
| 797 | + { |
| 798 | + "cell_type": "markdown", |
| 799 | + "metadata": {}, |
| 800 | + "source": [ |
| 801 | + "Rastesization function based on yearstamps of protected areas is launched." |
| 802 | + ] |
| 803 | + }, |
784 | 804 | { |
785 | 805 | "cell_type": "code", |
786 | 806 | "execution_count": null, |
|
806 | 826 | "4. Compression and assignment of null values." |
807 | 827 | ] |
808 | 828 | }, |
| 829 | + { |
| 830 | + "cell_type": "markdown", |
| 831 | + "metadata": {}, |
| 832 | + "source": [ |
| 833 | + "Code below is responsible for:\n", |
| 834 | + "- converting a shell script to Unix format in case of Windows-inherited symbols\n", |
| 835 | + "- runs raster calculation Shell command using `subprocess`" |
| 836 | + ] |
| 837 | + }, |
809 | 838 | { |
810 | 839 | "cell_type": "code", |
811 | 840 | "execution_count": null, |
|
878 | 907 | "metadata": {}, |
879 | 908 | "source": [ |
880 | 909 | "#### 4. Updating landscape impedance\n", |
881 | | - "Impedance is reclassified by [CSV table](data/input/impedance/lulc_descr_esa.csv) and compressed (through LZW compression, not Cloud Optimised Geotiff standard to avoid any further issues in processing). Landscape impedance is required by Miramon ICT and Graphab tools both.\n", |
| 910 | + "Impedance is reclassified by [CSV table](data/input/impedance/lulc_descr_esa.csv) and compressed (through LZW compression, not Cloud Optimised Geotiff standard to avoid any further issues in processing). Landscape impedance, or landscape resistance, or movement cost is required by many software used in biodiversity studies, including Graphab and MiraMon ICT plugin.\n", |
882 | 911 | "\n", |
883 | 912 | "Let's import another set of libraries needed and define the class to update the impedance." |
884 | 913 | ] |
885 | 914 | }, |
| 915 | + { |
| 916 | + "cell_type": "markdown", |
| 917 | + "metadata": {}, |
| 918 | + "source": [ |
| 919 | + "Now, we will update the landscape impedance dataset with the fetched data on protected areas:\n", |
| 920 | + "- updating the impedance dataset based on the reclassification table or the multiplier effect of protected areas. To change the method of updating impedance, modify `lulc_reclass_table: false` in [the configuration file](config/config.yaml). By default, it uses the multiplicating effect of protected areas. For example, the same LULC type with protected status will be less difficult for species to pass through. So, if the impedance of non-protected LULC type is 30 and multiplicator is 0.3, protected LULC type will be defined by impedance = 9.\n", |
| 921 | + "- multiplying a raster based on the effect of protected areas (if chosen)\n", |
| 922 | + "- generating a reclassification dictionary from a reclassification table for impedance, depending on the data type (if chosen)\n", |
| 923 | + "\n" |
| 924 | + ] |
| 925 | + }, |
886 | 926 | { |
887 | 927 | "cell_type": "code", |
888 | 928 | "execution_count": null, |
|
942 | 982 | " self.tiff_files = [f for f in os.listdir(self.input_folder) if f.endswith('_pa.tif')] # ADDED SUFFIX (UPDATED LULC)\n", |
943 | 983 | " self.impedance_files = [f for f in os.listdir(self.output_folder) if f.endswith('.tif')] # IMPEDANCE DATASET\n", |
944 | 984 | "\n", |
945 | | - "\n", |
946 | 985 | " def update_impedance(self, impedance_dir:str) -> None:\n", |
947 | 986 | " \"\"\"\n", |
948 | 987 | " Updates the impedance dataset based on the reclassification table or the multiplier effect of protected areas.\n", |
|
1012 | 1051 | " \n", |
1013 | 1052 | " print(\"Multiplication complete for:\", impedance_in_path + \"\\n------------------------------------\")\n", |
1014 | 1053 | " \n", |
1015 | | - " \n", |
1016 | 1054 | " def apply_multiplier(self, impedance_in_path:str, impedance_out_path:str, lulc_path:str, reclass_table:str, pa_effect:float) -> str:\n", |
1017 | 1055 | " \"\"\"\n", |
1018 | 1056 | " Multiplies a raster based on the effect of protected areas.\n", |
|
1110 | 1148 | " \n", |
1111 | 1149 | " return reclass_dict , has_decimal , data_type\n", |
1112 | 1150 | "\n", |
1113 | | - "\n", |
1114 | 1151 | " def reclassify_raster(self, input_raster:str, output_raster:str, reclass_table:str) -> str:\n", |
1115 | 1152 | " \"\"\"\n", |
1116 | 1153 | " Reclassifies a raster based on a reclassification table.\n", |
|
1207 | 1244 | "metadata": {}, |
1208 | 1245 | "source": [ |
1209 | 1246 | "#### 5. Updating landscape affinity \n", |
1210 | | - "Landscape affinity is computed and compressed based on the math expression processing landscape impedance. By now, landscape affinity is computed as a reversed value of landscape impedance but it is planned to develop it as a more flexible input to compute connectivity further. This output is required by Miramon ICT software, not Graphab." |
| 1247 | + "Landscape affinity is computed and compressed based on the math expression processing landscape impedance. By now, landscape affinity is computed as a reversed value of landscape impedance but it is planned to develop it as a more flexible input to compute connectivity further. This output is required by Miramon ICT software, but not Graphab." |
1211 | 1248 | ] |
1212 | 1249 | }, |
1213 | 1250 | { |
|
0 commit comments