1
1
# coding=UTF-8
2
2
"""InVEST Habitat Quality model."""
3
3
import collections
4
+ import csv
4
5
import logging
5
6
import os
6
7
213
214
"output" : {
214
215
"type" : "directory" ,
215
216
"contents" : {
216
- "deg_sum_out_c .tif" : {
217
+ "deg_sum_c .tif" : {
217
218
"about" : (
218
219
"Relative level of habitat degradation on the current "
219
220
"landscape." ),
220
221
"bands" : {1 : {"type" : "ratio" }}
221
222
},
222
- "deg_sum_out_f .tif" : {
223
+ "deg_sum_f .tif" : {
223
224
"about" : (
224
225
"Relative level of habitat degradation on the future "
225
226
"landscape." ),
226
227
"bands" : {1 : {"type" : "ratio" }},
227
228
"created_if" : "lulc_fut_path"
228
229
},
229
- "quality_out_c .tif" : {
230
+ "quality_c .tif" : {
230
231
"about" : (
231
232
"Relative level of habitat quality on the current "
232
233
"landscape." ),
233
234
"bands" : {1 : {"type" : "ratio" }}
234
235
},
235
- "quality_out_f .tif" : {
236
+ "quality_f .tif" : {
236
237
"about" : (
237
238
"Relative level of habitat quality on the future "
238
239
"landscape." ),
239
240
"bands" : {1 : {"type" : "ratio" }},
240
241
"created_if" : "lulc_fut_path"
241
242
},
242
- "rarity_out_c .tif" : {
243
+ "rarity_c .tif" : {
243
244
"about" : (
244
245
"Relative habitat rarity on the current landscape "
245
- "vis-a-vis the baseline map. The grid cell’ s values "
246
+ "vis-a-vis the baseline map. The grid cell' s values "
246
247
"are defined between a range of 0 and 1 where 0.5 "
247
248
"indicates no abundance change between the baseline "
248
249
"and current or projected map. Values between 0 and 0.5 "
258
259
"created_if" : "lulc_bas_path" ,
259
260
"bands" : {1 : {"type" : "ratio" }}
260
261
},
261
- "rarity_out_f .tif" : {
262
+ "rarity_f .tif" : {
262
263
"about" : (
263
264
"Relative habitat rarity on the future landscape "
264
- "vis-a-vis the baseline map. The grid cell’ s values "
265
+ "vis-a-vis the baseline map. The grid cell' s values "
265
266
"are defined between a range of 0 and 1 where 0.5 "
266
267
"indicates no abundance change between the baseline "
267
268
"and current or projected map. Values between 0 and "
278
279
"created_if" : "lulc_bas_path and lulc_fut_path" ,
279
280
"bands" : {1 : {"type" : "ratio" }}
280
281
},
282
+ "rarity_c.csv" : {
283
+ "about" : ("Table of rarity values by LULC code for the "
284
+ "current landscape." ),
285
+ "index_col" : "lulc_code" ,
286
+ "columns" : {
287
+ "lulc_code" : {
288
+ "type" : "number" ,
289
+ "units" : u .none ,
290
+ "about" : "LULC class" ,
291
+ },
292
+ "rarity_value" : {
293
+ "type" : "number" ,
294
+ "units" : u .none ,
295
+ "about" : (
296
+ "Relative habitat rarity on the current landscape "
297
+ "vis-a-vis the baseline map. The rarity values "
298
+ "are defined between a range of 0 and 1 where 0.5 "
299
+ "indicates no abundance change between the baseline "
300
+ "and current or projected map. Values between 0 and 0.5 "
301
+ "indicate a habitat is more abundant and the closer "
302
+ "the value is to 0 the lesser the likelihood that the "
303
+ "preservation of that habitat type on the current or "
304
+ "future landscape is important to biodiversity conservation. "
305
+ "Values between 0.5 and 1 indicate a habitat is less "
306
+ "abundant and the closer the value is to 1 the greater "
307
+ "the likelihood that the preservation of that habitat "
308
+ "type on the current or future landscape is important "
309
+ "to biodiversity conservation." ),
310
+ },
311
+ },
312
+ "created_if" : "lulc_bas_path" ,
313
+ },
314
+ "rarity_f.csv" : {
315
+ "about" : ("Table of rarity values by LULC code for the "
316
+ "future landscape." ),
317
+ "index_col" : "lulc_code" ,
318
+ "columns" : {
319
+ "lulc_code" : {
320
+ "type" : "number" ,
321
+ "units" : u .none ,
322
+ "about" : "LULC class" ,
323
+ },
324
+ "rarity_value" : {
325
+ "type" : "number" ,
326
+ "units" : u .none ,
327
+ "about" : (
328
+ "Relative habitat rarity on the future landscape "
329
+ "vis-a-vis the baseline map. The rarity values "
330
+ "are defined between a range of 0 and 1 where 0.5 "
331
+ "indicates no abundance change between the baseline "
332
+ "and current or projected map. Values between 0 and 0.5 "
333
+ "indicate a habitat is more abundant and the closer "
334
+ "the value is to 0 the lesser the likelihood that the "
335
+ "preservation of that habitat type on the current or "
336
+ "future landscape is important to biodiversity conservation. "
337
+ "Values between 0.5 and 1 indicate a habitat is less "
338
+ "abundant and the closer the value is to 1 the greater "
339
+ "the likelihood that the preservation of that habitat "
340
+ "type on the current or future landscape is important "
341
+ "to biodiversity conservation." ),
342
+ },
343
+ },
344
+ "created_if" : "lulc_bas_path and lulc_fut_path" ,
345
+ },
281
346
}
282
347
},
283
348
"intermediate" : {
@@ -743,13 +808,16 @@ def execute(args):
743
808
intermediate_output_dir ,
744
809
f'new_cover{ lulc_key } { file_suffix } .tif' )
745
810
746
- rarity_path = os .path .join (
811
+ rarity_raster_path = os .path .join (
747
812
output_dir , f'rarity{ lulc_key } { file_suffix } .tif' )
748
813
814
+ rarity_csv_path = os .path .join (
815
+ output_dir , f'rarity{ lulc_key } { file_suffix } .csv' )
816
+
749
817
_ = task_graph .add_task (
750
818
func = _compute_rarity_operation ,
751
819
args = ((lulc_base_path , 1 ), (lulc_path , 1 ), (new_cover_path , 1 ),
752
- rarity_path ),
820
+ rarity_raster_path , rarity_csv_path ),
753
821
dependent_task_list = [align_task ],
754
822
task_name = f'rarity{ lulc_time } ' )
755
823
@@ -773,7 +841,7 @@ def _calculate_habitat_quality(deg_hab_raster_list, quality_out_path, ksq):
773
841
pygeoprocessing .raster_map (
774
842
op = lambda degradation , habitat : (
775
843
habitat * (1 - (degradation ** _SCALING_PARAM ) /
776
- (degradation ** _SCALING_PARAM + ksq ))),
844
+ (degradation ** _SCALING_PARAM + ksq ))),
777
845
rasters = deg_hab_raster_list ,
778
846
target_path = quality_out_path )
779
847
@@ -829,8 +897,9 @@ def total_degradation(*arrays):
829
897
830
898
831
899
def _compute_rarity_operation (
832
- base_lulc_path_band , lulc_path_band , new_cover_path , rarity_path ):
833
- """Calculate habitat rarity.
900
+ base_lulc_path_band , lulc_path_band , new_cover_path ,
901
+ rarity_raster_path , rarity_csv_path ):
902
+ """Calculate habitat rarity and generate raster and CSV output.
834
903
835
904
Output rarity values will be an index from 0 - 1 where:
836
905
pixel > 0.5 - more rare
@@ -846,7 +915,8 @@ def _compute_rarity_operation(
846
915
new_cover_path (tuple): a 2 tuple for the path to intermediate
847
916
raster file for trimming ``lulc_path_band`` to
848
917
``base_lulc_path_band`` of the form (path, band index).
849
- rarity_path (string): path to output rarity raster.
918
+ rarity_raster_path (string): path to output rarity raster.
919
+ rarity_csv_path (string): path to output rarity CSV.
850
920
851
921
Returns:
852
922
None
@@ -857,15 +927,13 @@ def _compute_rarity_operation(
857
927
base_lulc_path_band [0 ])
858
928
base_pixel_size = base_raster_info ['pixel_size' ]
859
929
base_area = float (abs (base_pixel_size [0 ]) * abs (base_pixel_size [1 ]))
860
- base_nodata = base_raster_info ['nodata' ][0 ]
861
930
862
931
lulc_code_count_b = _raster_pixel_count (base_lulc_path_band )
863
932
864
933
# get the area of a cur/fut pixel
865
934
lulc_raster_info = pygeoprocessing .get_raster_info (lulc_path_band [0 ])
866
935
lulc_pixel_size = lulc_raster_info ['pixel_size' ]
867
936
lulc_area = float (abs (lulc_pixel_size [0 ]) * abs (lulc_pixel_size [1 ]))
868
- lulc_nodata = lulc_raster_info ['nodata' ][0 ]
869
937
870
938
# Trim cover_x to the mask of base.
871
939
pygeoprocessing .raster_map (
@@ -895,13 +963,34 @@ def _compute_rarity_operation(
895
963
code_index [code ] = 0.0
896
964
897
965
pygeoprocessing .reclassify_raster (
898
- new_cover_path , code_index , rarity_path , gdal .GDT_Float32 ,
966
+ new_cover_path , code_index , rarity_raster_path , gdal .GDT_Float32 ,
899
967
_OUT_NODATA )
900
968
969
+ _generate_rarity_csv (code_index , rarity_csv_path )
970
+
901
971
LOGGER .info ('Finished rarity computation on'
902
972
f' { os .path .basename (lulc_path_band [0 ])} land cover.' )
903
973
904
974
975
+ def _generate_rarity_csv (rarity_dict , target_csv_path ):
976
+ """Generate CSV containing rarity values by LULC code.
977
+
978
+ Args:
979
+ rarity_dict (dict): dictionary containing LULC codes (as keys)
980
+ and their associated rarity values (as values).
981
+ target_csv_path (string): path to output CSV.
982
+
983
+ Returns:
984
+ None
985
+ """
986
+ lulc_codes = sorted (rarity_dict )
987
+ with open (target_csv_path , 'w' , newline = '' ) as csvfile :
988
+ writer = csv .writer (csvfile , delimiter = ',' )
989
+ writer .writerow (['lulc_code' , 'rarity_value' ])
990
+ for lulc_code in lulc_codes :
991
+ writer .writerow ([lulc_code , rarity_dict [lulc_code ]])
992
+
993
+
905
994
def _raster_pixel_count (raster_path_band ):
906
995
"""Count unique pixel values in single band raster.
907
996
0 commit comments