Renaming files, variables, everything to 'urban_nature'*.
RE:#1179
This commit is contained in:
parent
1e2959378f
commit
8787e918e6
|
@ -131,7 +131,7 @@ ARGS_SPEC = {
|
|||
"summarized."
|
||||
),
|
||||
},
|
||||
'greenspace_demand': {
|
||||
'urban_nature_demand': {
|
||||
'type': 'number',
|
||||
'name': 'urban nature demand per capita',
|
||||
'units': u.m**2, # defined as m² per capita
|
||||
|
@ -293,9 +293,9 @@ ARGS_SPEC = {
|
|||
|
||||
|
||||
_OUTPUT_BASE_FILES = {
|
||||
'greenspace_supply': 'greenspace_supply.tif',
|
||||
'urban_nature_supply': 'urban_nature_supply.tif',
|
||||
'admin_boundaries': 'admin_boundaries.gpkg',
|
||||
'greenspace_balance': 'greenspace_balance.tif',
|
||||
'urban_nature_balance': 'urban_nature_balance.tif',
|
||||
}
|
||||
|
||||
_INTERMEDIATE_BASE_FILES = {
|
||||
|
@ -304,10 +304,10 @@ _INTERMEDIATE_BASE_FILES = {
|
|||
'aligned_lulc': 'aligned_lulc.tif',
|
||||
'masked_lulc': 'masked_lulc.tif',
|
||||
'aligned_mask': 'aligned_valid_pixels_mask.tif',
|
||||
'greenspace_area': 'greenspace_area.tif',
|
||||
'greenspace_population_ratio': 'greenspace_population_ratio.tif',
|
||||
'urban_nature_area': 'urban_nature_area.tif',
|
||||
'urban_nature_population_ratio': 'urban_nature_population_ratio.tif',
|
||||
'convolved_population': 'convolved_population.tif',
|
||||
'greenspace_supply_demand_budget': 'greenspace_supply_demand_budget.tif',
|
||||
'urban_nature_supply_demand_budget': 'urban_nature_supply_demand_budget.tif',
|
||||
'undersupplied_population': 'undersupplied_population.tif',
|
||||
'oversupplied_population': 'oversupplied_population.tif',
|
||||
'reprojected_admin_boundaries': 'reprojected_admin_boundaries.gpkg',
|
||||
|
@ -352,8 +352,8 @@ def execute(args):
|
|||
unit belonging to the given population group. The name of the
|
||||
population group (everything other than a leading ``"pop_"``) must
|
||||
uniquely identify the group.
|
||||
args['greenspace_demand'] (number): (required) A positive, nonzero
|
||||
number indicating the required greenspace, in m² per capita.
|
||||
args['urban_nature_demand'] (number): (required) A positive, nonzero
|
||||
number indicating the required urban_nature, in m² per capita.
|
||||
args['decay_function'] (string): (required) The selected kernel type.
|
||||
Must be one of the keys in ``KERNEL_TYPES``.
|
||||
args['search_radius_mode'] (string): (required). The selected search
|
||||
|
@ -601,16 +601,16 @@ def execute(args):
|
|||
if args['search_radius_mode'] == RADIUS_OPT_UNIFORM:
|
||||
search_radii = set([float(args['search_radius'])])
|
||||
elif args['search_radius_mode'] == RADIUS_OPT_URBAN_NATURE:
|
||||
greenspace_attrs = attr_table[attr_table['urban_nature'] == 1]
|
||||
urban_nature_attrs = attr_table[attr_table['urban_nature'] == 1]
|
||||
try:
|
||||
search_radii = set(greenspace_attrs['search_radius_m'].unique())
|
||||
search_radii = set(urban_nature_attrs['search_radius_m'].unique())
|
||||
except KeyError as missing_key:
|
||||
raise ValueError(
|
||||
f"The column {str(missing_key)} is missing from the LULC "
|
||||
f"attribute table {args['lulc_attribute_table']}")
|
||||
# Build an iterable of plain tuples: (lucode, search_radius_m)
|
||||
lucode_to_search_radii = list(
|
||||
greenspace_attrs[['lucode', 'search_radius_m']].itertuples(
|
||||
urban_nature_attrs[['lucode', 'search_radius_m']].itertuples(
|
||||
index=False, name=None))
|
||||
elif args['search_radius_mode'] == RADIUS_OPT_POP_GROUP:
|
||||
pop_group_table = utils.read_csv_to_dataframe(
|
||||
|
@ -667,50 +667,50 @@ def execute(args):
|
|||
dependent_task_list=[
|
||||
kernel_tasks[search_radius_m], population_mask_task])
|
||||
|
||||
greenspace_pixels_path = os.path.join(
|
||||
intermediate_dir, f'greenspace_area{suffix}.tif')
|
||||
greenspace_reclassification_task = graph.add_task(
|
||||
_reclassify_greenspace_area,
|
||||
urban_nature_pixels_path = os.path.join(
|
||||
intermediate_dir, f'urban_nature_area{suffix}.tif')
|
||||
urban_nature_reclassification_task = graph.add_task(
|
||||
_reclassify_urban_nature_area,
|
||||
kwargs={
|
||||
'lulc_raster_path': file_registry['masked_lulc'],
|
||||
'lulc_attribute_table': args['lulc_attribute_table'],
|
||||
'target_raster_path': greenspace_pixels_path,
|
||||
'target_raster_path': urban_nature_pixels_path,
|
||||
},
|
||||
target_path_list=[greenspace_pixels_path],
|
||||
target_path_list=[urban_nature_pixels_path],
|
||||
task_name='Identify urban nature areas',
|
||||
dependent_task_list=[lulc_mask_task]
|
||||
)
|
||||
|
||||
greenspace_population_ratio_path = os.path.join(
|
||||
urban_nature_population_ratio_path = os.path.join(
|
||||
intermediate_dir,
|
||||
f'greenspace_population_ratio{suffix}.tif')
|
||||
greenspace_population_ratio_task = graph.add_task(
|
||||
_calculate_greenspace_population_ratio,
|
||||
args=(greenspace_pixels_path,
|
||||
f'urban_nature_population_ratio{suffix}.tif')
|
||||
urban_nature_population_ratio_task = graph.add_task(
|
||||
_calculate_urban_nature_population_ratio,
|
||||
args=(urban_nature_pixels_path,
|
||||
decayed_population_path,
|
||||
greenspace_population_ratio_path),
|
||||
urban_nature_population_ratio_path),
|
||||
task_name=(
|
||||
'2SFCA: Calculate R_j urban nature/population ratio - '
|
||||
f'{search_radius_m}'),
|
||||
target_path_list=[greenspace_population_ratio_path],
|
||||
target_path_list=[urban_nature_population_ratio_path],
|
||||
dependent_task_list=[
|
||||
greenspace_reclassification_task, decayed_population_task,
|
||||
urban_nature_reclassification_task, decayed_population_task,
|
||||
])
|
||||
|
||||
greenspace_supply_task = graph.add_task(
|
||||
urban_nature_supply_task = graph.add_task(
|
||||
_convolve_and_set_lower_bound,
|
||||
kwargs={
|
||||
'signal_path_band': (
|
||||
greenspace_population_ratio_path, 1),
|
||||
urban_nature_population_ratio_path, 1),
|
||||
'kernel_path_band': (kernel_path, 1),
|
||||
'target_path': file_registry['greenspace_supply'],
|
||||
'target_path': file_registry['urban_nature_supply'],
|
||||
'working_dir': intermediate_dir,
|
||||
},
|
||||
task_name='2SFCA - urban nature supply',
|
||||
target_path_list=[file_registry['greenspace_supply']],
|
||||
target_path_list=[file_registry['urban_nature_supply']],
|
||||
dependent_task_list=[
|
||||
kernel_tasks[search_radius_m],
|
||||
greenspace_population_ratio_task])
|
||||
urban_nature_population_ratio_task])
|
||||
|
||||
# Search radius mode 2: Search radii are defined per greenspace lulc class.
|
||||
elif args['search_radius_mode'] == RADIUS_OPT_URBAN_NATURE:
|
||||
|
@ -736,87 +736,87 @@ def execute(args):
|
|||
dependent_task_list=[
|
||||
kernel_tasks[search_radius_m], population_mask_task])
|
||||
|
||||
partial_greenspace_supply_paths = []
|
||||
partial_greenspace_supply_tasks = []
|
||||
partial_urban_nature_supply_paths = []
|
||||
partial_urban_nature_supply_tasks = []
|
||||
for lucode, search_radius_m in lucode_to_search_radii:
|
||||
greenspace_pixels_path = os.path.join(
|
||||
urban_nature_pixels_path = os.path.join(
|
||||
intermediate_dir,
|
||||
f'greenspace_area_lucode_{lucode}{suffix}.tif')
|
||||
greenspace_reclassification_task = graph.add_task(
|
||||
_reclassify_greenspace_area,
|
||||
f'urban_nature_area_lucode_{lucode}{suffix}.tif')
|
||||
urban_nature_reclassification_task = graph.add_task(
|
||||
_reclassify_urban_nature_area,
|
||||
kwargs={
|
||||
'lulc_raster_path': file_registry['masked_lulc'],
|
||||
'lulc_attribute_table': args['lulc_attribute_table'],
|
||||
'target_raster_path': greenspace_pixels_path,
|
||||
'only_these_greenspace_codes': set([lucode]),
|
||||
'target_raster_path': urban_nature_pixels_path,
|
||||
'only_these_urban_nature_codes': set([lucode]),
|
||||
},
|
||||
target_path_list=[greenspace_pixels_path],
|
||||
target_path_list=[urban_nature_pixels_path],
|
||||
task_name=f'Identify urban nature areas with lucode {lucode}',
|
||||
dependent_task_list=[lulc_mask_task]
|
||||
)
|
||||
|
||||
greenspace_population_ratio_path = os.path.join(
|
||||
urban_nature_population_ratio_path = os.path.join(
|
||||
intermediate_dir,
|
||||
f'greenspace_population_ratio_lucode_{lucode}{suffix}.tif')
|
||||
greenspace_population_ratio_task = graph.add_task(
|
||||
_calculate_greenspace_population_ratio,
|
||||
args=(greenspace_pixels_path,
|
||||
f'urban_nature_population_ratio_lucode_{lucode}{suffix}.tif')
|
||||
urban_nature_population_ratio_task = graph.add_task(
|
||||
_calculate_urban_nature_population_ratio,
|
||||
args=(urban_nature_pixels_path,
|
||||
decayed_population_paths[search_radius_m],
|
||||
greenspace_population_ratio_path),
|
||||
urban_nature_population_ratio_path),
|
||||
task_name=(
|
||||
'2SFCA: Calculate R_j urban nature/population ratio - '
|
||||
f'{search_radius_m}'),
|
||||
target_path_list=[greenspace_population_ratio_path],
|
||||
target_path_list=[urban_nature_population_ratio_path],
|
||||
dependent_task_list=[
|
||||
greenspace_reclassification_task,
|
||||
urban_nature_reclassification_task,
|
||||
decayed_population_tasks[search_radius_m],
|
||||
])
|
||||
|
||||
greenspace_supply_path = os.path.join(
|
||||
urban_nature_supply_path = os.path.join(
|
||||
intermediate_dir,
|
||||
f'greenspace_supply_lucode_{lucode}{suffix}.tif')
|
||||
partial_greenspace_supply_paths.append(greenspace_supply_path)
|
||||
partial_greenspace_supply_tasks.append(graph.add_task(
|
||||
f'urban_nature_supply_lucode_{lucode}{suffix}.tif')
|
||||
partial_urban_nature_supply_paths.append(urban_nature_supply_path)
|
||||
partial_urban_nature_supply_tasks.append(graph.add_task(
|
||||
pygeoprocessing.convolve_2d,
|
||||
kwargs={
|
||||
'signal_path_band': (
|
||||
greenspace_population_ratio_path, 1),
|
||||
urban_nature_population_ratio_path, 1),
|
||||
'kernel_path_band': (kernel_paths[search_radius_m], 1),
|
||||
'target_path': greenspace_supply_path,
|
||||
'target_path': urban_nature_supply_path,
|
||||
'working_dir': intermediate_dir,
|
||||
},
|
||||
task_name=f'2SFCA - greenspace supply for lucode {lucode}',
|
||||
target_path_list=[greenspace_supply_path],
|
||||
task_name=f'2SFCA - urban_nature supply for lucode {lucode}',
|
||||
target_path_list=[urban_nature_supply_path],
|
||||
dependent_task_list=[
|
||||
kernel_tasks[search_radius_m],
|
||||
greenspace_population_ratio_task]))
|
||||
urban_nature_population_ratio_task]))
|
||||
|
||||
greenspace_supply_task = graph.add_task(
|
||||
urban_nature_supply_task = graph.add_task(
|
||||
ndr._sum_rasters,
|
||||
kwargs={
|
||||
'raster_path_list': partial_greenspace_supply_paths,
|
||||
'raster_path_list': partial_urban_nature_supply_paths,
|
||||
'target_nodata': FLOAT32_NODATA,
|
||||
'target_result_path': file_registry['greenspace_supply'],
|
||||
'target_result_path': file_registry['urban_nature_supply'],
|
||||
},
|
||||
task_name='2SFCA - urban nature supply total',
|
||||
target_path_list=[file_registry['greenspace_supply']],
|
||||
dependent_task_list=partial_greenspace_supply_tasks
|
||||
target_path_list=[file_registry['urban_nature_supply']],
|
||||
dependent_task_list=partial_urban_nature_supply_tasks
|
||||
)
|
||||
|
||||
# Search radius mode 3: search radii are defined per population group.
|
||||
elif args['search_radius_mode'] == RADIUS_OPT_POP_GROUP:
|
||||
LOGGER.info("Running model with search radius mode "
|
||||
f"{RADIUS_OPT_POP_GROUP}")
|
||||
greenspace_pixels_path = os.path.join(
|
||||
intermediate_dir, f'greenspace_area{suffix}.tif')
|
||||
greenspace_reclassification_task = graph.add_task(
|
||||
_reclassify_greenspace_area,
|
||||
urban_nature_pixels_path = os.path.join(
|
||||
intermediate_dir, f'urban_nature_area{suffix}.tif')
|
||||
urban_nature_reclassification_task = graph.add_task(
|
||||
_reclassify_urban_nature_area,
|
||||
kwargs={
|
||||
'lulc_raster_path': file_registry['masked_lulc'],
|
||||
'lulc_attribute_table': args['lulc_attribute_table'],
|
||||
'target_raster_path': greenspace_pixels_path,
|
||||
'target_raster_path': urban_nature_pixels_path,
|
||||
},
|
||||
target_path_list=[greenspace_pixels_path],
|
||||
target_path_list=[urban_nature_pixels_path],
|
||||
task_name='Identify urban nature areas',
|
||||
dependent_task_list=[lulc_mask_task]
|
||||
)
|
||||
|
@ -862,18 +862,18 @@ def execute(args):
|
|||
dependent_task_list=decayed_population_in_group_tasks
|
||||
)
|
||||
|
||||
greenspace_population_ratio_task = graph.add_task(
|
||||
_calculate_greenspace_population_ratio,
|
||||
args=(greenspace_pixels_path,
|
||||
urban_nature_population_ratio_task = graph.add_task(
|
||||
_calculate_urban_nature_population_ratio,
|
||||
args=(urban_nature_pixels_path,
|
||||
sum_of_decayed_population_path,
|
||||
file_registry['greenspace_population_ratio']),
|
||||
file_registry['urban_nature_population_ratio']),
|
||||
task_name=(
|
||||
'2SFCA: Calculate R_j urban nature/population ratio - '
|
||||
f'{search_radius_m}'),
|
||||
target_path_list=[
|
||||
file_registry['greenspace_population_ratio']],
|
||||
file_registry['urban_nature_population_ratio']],
|
||||
dependent_task_list=[
|
||||
greenspace_reclassification_task,
|
||||
urban_nature_reclassification_task,
|
||||
sum_of_decayed_population_task,
|
||||
])
|
||||
|
||||
|
@ -883,85 +883,85 @@ def execute(args):
|
|||
search_radii = dict(
|
||||
group_radii_table[['pop_group', 'search_radius_m']].itertuples(
|
||||
index=False, name=None))
|
||||
greenspace_supply_by_group_paths = {}
|
||||
greenspace_supply_by_group_tasks = []
|
||||
greenspace_supply_demand_by_group_paths = {}
|
||||
greenspace_supply_demand_by_group_tasks = []
|
||||
urban_nature_supply_by_group_paths = {}
|
||||
urban_nature_supply_by_group_tasks = []
|
||||
urban_nature_supply_demand_by_group_paths = {}
|
||||
urban_nature_supply_demand_by_group_tasks = []
|
||||
supply_population_paths = {'over': {}, 'under': {}}
|
||||
supply_population_tasks = {'over': {}, 'under': {}}
|
||||
for pop_group, proportional_pop_path in (
|
||||
proportional_population_paths.items()):
|
||||
search_radius_m = search_radii[pop_group]
|
||||
greenspace_supply_to_group_path = os.path.join(
|
||||
urban_nature_supply_to_group_path = os.path.join(
|
||||
intermediate_dir,
|
||||
f'greenspace_supply_to_{pop_group}{suffix}.tif')
|
||||
greenspace_supply_by_group_paths[
|
||||
pop_group] = greenspace_supply_to_group_path
|
||||
greenspace_supply_by_group_task = graph.add_task(
|
||||
f'urban_nature_supply_to_{pop_group}{suffix}.tif')
|
||||
urban_nature_supply_by_group_paths[
|
||||
pop_group] = urban_nature_supply_to_group_path
|
||||
urban_nature_supply_by_group_task = graph.add_task(
|
||||
pygeoprocessing.convolve_2d,
|
||||
kwargs={
|
||||
'signal_path_band': (
|
||||
file_registry['greenspace_population_ratio'], 1),
|
||||
file_registry['urban_nature_population_ratio'], 1),
|
||||
'kernel_path_band': (kernel_paths[search_radius_m], 1),
|
||||
'target_path': greenspace_supply_to_group_path,
|
||||
'target_path': urban_nature_supply_to_group_path,
|
||||
'working_dir': intermediate_dir,
|
||||
},
|
||||
task_name=f'2SFCA - urban nature supply for {pop_group}',
|
||||
target_path_list=[greenspace_supply_to_group_path],
|
||||
target_path_list=[urban_nature_supply_to_group_path],
|
||||
dependent_task_list=[
|
||||
kernel_tasks[search_radius_m],
|
||||
greenspace_population_ratio_task])
|
||||
greenspace_supply_by_group_tasks.append(
|
||||
greenspace_supply_by_group_task)
|
||||
urban_nature_population_ratio_task])
|
||||
urban_nature_supply_by_group_tasks.append(
|
||||
urban_nature_supply_by_group_task)
|
||||
|
||||
# Calculate SUP_DEMi_cap for each population group.
|
||||
per_cap_greenspace_balance_pop_group_path = os.path.join(
|
||||
per_cap_urban_nature_balance_pop_group_path = os.path.join(
|
||||
output_dir,
|
||||
f'greenspace_balance_{pop_group}{suffix}.tif')
|
||||
per_cap_greenspace_balance_pop_group_task = graph.add_task(
|
||||
f'urban_nature_balance_{pop_group}{suffix}.tif')
|
||||
per_cap_urban_nature_balance_pop_group_task = graph.add_task(
|
||||
pygeoprocessing.raster_calculator,
|
||||
kwargs={
|
||||
'base_raster_path_band_const_list': [
|
||||
(greenspace_supply_to_group_path, 1),
|
||||
(float(args['greenspace_demand']), 'raw')
|
||||
(urban_nature_supply_to_group_path, 1),
|
||||
(float(args['urban_nature_demand']), 'raw')
|
||||
],
|
||||
'local_op': _greenspace_balance_op,
|
||||
'local_op': _urban_nature_balance_op,
|
||||
'target_raster_path':
|
||||
per_cap_greenspace_balance_pop_group_path,
|
||||
per_cap_urban_nature_balance_pop_group_path,
|
||||
'datatype_target': gdal.GDT_Float32,
|
||||
'nodata_target': FLOAT32_NODATA
|
||||
},
|
||||
task_name=(
|
||||
f'Calculate per-capita urban nature balance - {pop_group}'),
|
||||
target_path_list=[
|
||||
per_cap_greenspace_balance_pop_group_path],
|
||||
per_cap_urban_nature_balance_pop_group_path],
|
||||
dependent_task_list=[
|
||||
greenspace_supply_by_group_task,
|
||||
urban_nature_supply_by_group_task,
|
||||
])
|
||||
|
||||
greenspace_supply_demand_by_group_path = os.path.join(
|
||||
urban_nature_supply_demand_by_group_path = os.path.join(
|
||||
intermediate_dir,
|
||||
f'greenspace_supply_demand_budget_{pop_group}{suffix}.tif')
|
||||
greenspace_supply_demand_by_group_paths[
|
||||
pop_group] = greenspace_supply_demand_by_group_path
|
||||
greenspace_supply_demand_by_group_tasks.append(graph.add_task(
|
||||
f'urban_nature_supply_demand_budget_{pop_group}{suffix}.tif')
|
||||
urban_nature_supply_demand_by_group_paths[
|
||||
pop_group] = urban_nature_supply_demand_by_group_path
|
||||
urban_nature_supply_demand_by_group_tasks.append(graph.add_task(
|
||||
pygeoprocessing.raster_calculator,
|
||||
kwargs={
|
||||
'base_raster_path_band_const_list': [
|
||||
(per_cap_greenspace_balance_pop_group_path, 1),
|
||||
(per_cap_urban_nature_balance_pop_group_path, 1),
|
||||
(proportional_pop_path, 1)
|
||||
],
|
||||
'local_op': _greenspace_supply_demand_op,
|
||||
'local_op': _urban_nature_supply_demand_op,
|
||||
'target_raster_path': (
|
||||
greenspace_supply_demand_by_group_path),
|
||||
urban_nature_supply_demand_by_group_path),
|
||||
'datatype_target': gdal.GDT_Float32,
|
||||
'nodata_target': FLOAT32_NODATA
|
||||
},
|
||||
task_name='Calculate per-capita urban nature supply-demand',
|
||||
target_path_list=[
|
||||
greenspace_supply_demand_by_group_path],
|
||||
urban_nature_supply_demand_by_group_path],
|
||||
dependent_task_list=[
|
||||
per_cap_greenspace_balance_pop_group_task,
|
||||
per_cap_urban_nature_balance_pop_group_task,
|
||||
proportional_population_tasks[pop_group],
|
||||
]))
|
||||
|
||||
|
@ -978,7 +978,7 @@ def execute(args):
|
|||
kwargs={
|
||||
'base_raster_path_band_const_list': [
|
||||
(proportional_pop_path, 1),
|
||||
(per_cap_greenspace_balance_pop_group_path, 1),
|
||||
(per_cap_urban_nature_balance_pop_group_path, 1),
|
||||
(op, 'raw'), # numpy element-wise comparator
|
||||
],
|
||||
'local_op': _filter_population,
|
||||
|
@ -991,41 +991,41 @@ def execute(args):
|
|||
f'{pop_group}'),
|
||||
target_path_list=[supply_population_path],
|
||||
dependent_task_list=[
|
||||
per_cap_greenspace_balance_pop_group_task,
|
||||
per_cap_urban_nature_balance_pop_group_task,
|
||||
proportional_population_tasks[pop_group],
|
||||
])
|
||||
|
||||
greenspace_supply_task = graph.add_task(
|
||||
urban_nature_supply_task = graph.add_task(
|
||||
_weighted_sum,
|
||||
kwargs={
|
||||
'raster_path_list':
|
||||
[greenspace_supply_by_group_paths[group] for group in
|
||||
[urban_nature_supply_by_group_paths[group] for group in
|
||||
sorted(split_population_fields)],
|
||||
'weight_raster_list':
|
||||
[pop_group_proportion_paths[group] for group in
|
||||
sorted(split_population_fields)],
|
||||
'target_path': file_registry['greenspace_supply'],
|
||||
'target_path': file_registry['urban_nature_supply'],
|
||||
},
|
||||
task_name='2SFCA - urban nature supply total',
|
||||
target_path_list=[file_registry['greenspace_supply']],
|
||||
target_path_list=[file_registry['urban_nature_supply']],
|
||||
dependent_task_list=[
|
||||
*greenspace_supply_by_group_tasks,
|
||||
*urban_nature_supply_by_group_tasks,
|
||||
*pop_group_proportion_tasks.values(),
|
||||
])
|
||||
|
||||
greenspace_supply_demand_budget_task = graph.add_task(
|
||||
urban_nature_supply_demand_budget_task = graph.add_task(
|
||||
ndr._sum_rasters,
|
||||
kwargs={
|
||||
'raster_path_list':
|
||||
list(greenspace_supply_demand_by_group_paths.values()),
|
||||
list(urban_nature_supply_demand_by_group_paths.values()),
|
||||
'target_nodata': FLOAT32_NODATA,
|
||||
'target_result_path':
|
||||
file_registry['greenspace_supply_demand_budget'],
|
||||
file_registry['urban_nature_supply_demand_budget'],
|
||||
},
|
||||
task_name='2SFCA - greenspace supply-demand budget',
|
||||
task_name='2SFCA - urban_nature supply-demand budget',
|
||||
target_path_list=[
|
||||
file_registry['greenspace_supply_demand_budget']],
|
||||
dependent_task_list=greenspace_supply_demand_by_group_tasks
|
||||
file_registry['urban_nature_supply_demand_budget']],
|
||||
dependent_task_list=urban_nature_supply_demand_by_group_tasks
|
||||
)
|
||||
|
||||
# Summary stats for RADIUS_OPT_POP_GROUP
|
||||
|
@ -1034,8 +1034,8 @@ def execute(args):
|
|||
kwargs={
|
||||
'source_aoi_vector_path': file_registry['reprojected_admin_boundaries'],
|
||||
'target_aoi_vector_path': file_registry['admin_boundaries'],
|
||||
'greenspace_sup_dem_paths_by_pop_group':
|
||||
greenspace_supply_demand_by_group_paths,
|
||||
'urban_nature_sup_dem_paths_by_pop_group':
|
||||
urban_nature_supply_demand_by_group_paths,
|
||||
'proportional_pop_paths_by_pop_group':
|
||||
proportional_population_paths,
|
||||
'undersupply_by_pop_group': supply_population_paths['under'],
|
||||
|
@ -1046,54 +1046,54 @@ def execute(args):
|
|||
target_path_list=[file_registry['admin_boundaries']],
|
||||
dependent_task_list=[
|
||||
aoi_reprojection_task,
|
||||
*greenspace_supply_demand_by_group_tasks,
|
||||
*urban_nature_supply_demand_by_group_tasks,
|
||||
*proportional_population_tasks.values(),
|
||||
*supply_population_tasks['under'].values(),
|
||||
*supply_population_tasks['over'].values(),
|
||||
])
|
||||
|
||||
# Greenspace budget, supply/demand and over/undersupply rasters are the
|
||||
# same for uniform radius and for split greenspace modes.
|
||||
# same for uniform radius and for split urban_nature modes.
|
||||
if args['search_radius_mode'] in (RADIUS_OPT_UNIFORM,
|
||||
RADIUS_OPT_URBAN_NATURE):
|
||||
# This is "SUP_DEMi_cap" from the user's guide
|
||||
per_capita_greenspace_balance_task = graph.add_task(
|
||||
per_capita_urban_nature_balance_task = graph.add_task(
|
||||
pygeoprocessing.raster_calculator,
|
||||
kwargs={
|
||||
'base_raster_path_band_const_list': [
|
||||
(file_registry['greenspace_supply'], 1),
|
||||
(float(args['greenspace_demand']), 'raw')
|
||||
(file_registry['urban_nature_supply'], 1),
|
||||
(float(args['urban_nature_demand']), 'raw')
|
||||
],
|
||||
'local_op': _greenspace_balance_op,
|
||||
'target_raster_path': file_registry['greenspace_balance'],
|
||||
'local_op': _urban_nature_balance_op,
|
||||
'target_raster_path': file_registry['urban_nature_balance'],
|
||||
'datatype_target': gdal.GDT_Float32,
|
||||
'nodata_target': FLOAT32_NODATA
|
||||
},
|
||||
task_name='Calculate per-capita urban nature balance',
|
||||
target_path_list=[file_registry['greenspace_balance']],
|
||||
target_path_list=[file_registry['urban_nature_balance']],
|
||||
dependent_task_list=[
|
||||
greenspace_supply_task,
|
||||
urban_nature_supply_task,
|
||||
])
|
||||
|
||||
# This is "SUP_DEMi" from the user's guide
|
||||
greenspace_supply_demand_task = graph.add_task(
|
||||
urban_nature_supply_demand_task = graph.add_task(
|
||||
pygeoprocessing.raster_calculator,
|
||||
kwargs={
|
||||
'base_raster_path_band_const_list': [
|
||||
(file_registry['greenspace_balance'], 1),
|
||||
(file_registry['urban_nature_balance'], 1),
|
||||
(file_registry['masked_population'], 1)
|
||||
],
|
||||
'local_op': _greenspace_supply_demand_op,
|
||||
'local_op': _urban_nature_supply_demand_op,
|
||||
'target_raster_path': (
|
||||
file_registry['greenspace_supply_demand_budget']),
|
||||
file_registry['urban_nature_supply_demand_budget']),
|
||||
'datatype_target': gdal.GDT_Float32,
|
||||
'nodata_target': FLOAT32_NODATA
|
||||
},
|
||||
task_name='Calculate per-capita urban nature supply-demand',
|
||||
target_path_list=[
|
||||
file_registry['greenspace_supply_demand_budget']],
|
||||
file_registry['urban_nature_supply_demand_budget']],
|
||||
dependent_task_list=[
|
||||
per_capita_greenspace_balance_task,
|
||||
per_capita_urban_nature_balance_task,
|
||||
population_mask_task,
|
||||
])
|
||||
|
||||
|
@ -1121,7 +1121,7 @@ def execute(args):
|
|||
kwargs={
|
||||
'base_raster_path_band_const_list': [
|
||||
(proportional_pop_path, 1),
|
||||
(file_registry['greenspace_balance'], 1),
|
||||
(file_registry['urban_nature_balance'], 1),
|
||||
(op, 'raw'), # numpy element-wise comparator
|
||||
],
|
||||
'local_op': _filter_population,
|
||||
|
@ -1132,7 +1132,7 @@ def execute(args):
|
|||
task_name=f'Determine {supply_type}supplied populations',
|
||||
target_path_list=[supply_population_path],
|
||||
dependent_task_list=[
|
||||
greenspace_supply_demand_task,
|
||||
urban_nature_supply_demand_task,
|
||||
population_mask_task,
|
||||
*list(proportional_population_tasks.values()),
|
||||
]))
|
||||
|
@ -1142,8 +1142,8 @@ def execute(args):
|
|||
kwargs={
|
||||
'source_aoi_vector_path': file_registry['reprojected_admin_boundaries'],
|
||||
'target_aoi_vector_path': file_registry['admin_boundaries'],
|
||||
'greenspace_budget_path': file_registry[
|
||||
'greenspace_supply_demand_budget'], # TODO: is this the correct raster?
|
||||
'urban_nature_budget_path': file_registry[
|
||||
'urban_nature_supply_demand_budget'], # TODO: is this the correct raster?
|
||||
'population_path': file_registry['masked_population'],
|
||||
'undersupplied_populations_path': file_registry[
|
||||
'undersupplied_population'],
|
||||
|
@ -1157,7 +1157,7 @@ def execute(args):
|
|||
dependent_task_list=[
|
||||
population_mask_task,
|
||||
aoi_reprojection_task,
|
||||
greenspace_supply_demand_task,
|
||||
urban_nature_supply_demand_task,
|
||||
*supply_population_tasks
|
||||
])
|
||||
|
||||
|
@ -1389,9 +1389,9 @@ def _rasterize_aois(base_raster_path, aois_vector_path,
|
|||
option_list=[f"ATTRIBUTE={id_fieldname}"])
|
||||
|
||||
|
||||
def _reclassify_greenspace_area(
|
||||
def _reclassify_urban_nature_area(
|
||||
lulc_raster_path, lulc_attribute_table, target_raster_path,
|
||||
only_these_greenspace_codes=None):
|
||||
only_these_urban_nature_codes=None):
|
||||
"""Reclassify LULC pixels into the urban nature area they represent.
|
||||
|
||||
After execution, urban nature pixels will have values representing the
|
||||
|
@ -1404,7 +1404,7 @@ def _reclassify_greenspace_area(
|
|||
LULC attributes. Must have "lucode" and "urban_nature" columns.
|
||||
target_raster_path (string): Where the reclassified urban nature raster
|
||||
should be written.
|
||||
only_these_greenspace_codes=None (iterable or None): If ``None``, all
|
||||
only_these_urban_nature_codes=None (iterable or None): If ``None``, all
|
||||
lucodes with a ``urban_nature`` value of 1 will be reclassified to
|
||||
1. If an iterable, must be an iterable of landuse codes matching
|
||||
codes in the lulc attribute table. Only these landcover codes will
|
||||
|
@ -1419,26 +1419,26 @@ def _reclassify_greenspace_area(
|
|||
squared_pixel_area = abs(
|
||||
numpy.multiply(*_square_off_pixels(lulc_raster_path)))
|
||||
|
||||
if only_these_greenspace_codes:
|
||||
valid_greenspace_codes = set(only_these_greenspace_codes)
|
||||
if only_these_urban_nature_codes:
|
||||
valid_urban_nature_codes = set(only_these_urban_nature_codes)
|
||||
else:
|
||||
valid_greenspace_codes = set(
|
||||
valid_urban_nature_codes = set(
|
||||
lucode for lucode, attributes in attribute_table_dict.items()
|
||||
if (attributes['urban_nature']) == 1)
|
||||
|
||||
greenspace_area_map = {}
|
||||
urban_nature_area_map = {}
|
||||
for lucode, attributes in attribute_table_dict.items():
|
||||
greenspace_area = 0
|
||||
if lucode in valid_greenspace_codes:
|
||||
greenspace_area = squared_pixel_area
|
||||
greenspace_area_map[lucode] = greenspace_area
|
||||
urban_nature_area = 0
|
||||
if lucode in valid_urban_nature_codes:
|
||||
urban_nature_area = squared_pixel_area
|
||||
urban_nature_area_map[lucode] = urban_nature_area
|
||||
|
||||
lulc_raster_info = pygeoprocessing.get_raster_info(lulc_raster_path)
|
||||
greenspace_area_map[lulc_raster_info['nodata'][0]] = FLOAT32_NODATA
|
||||
urban_nature_area_map[lulc_raster_info['nodata'][0]] = FLOAT32_NODATA
|
||||
|
||||
utils.reclassify_raster(
|
||||
raster_path_band=(lulc_raster_path, 1),
|
||||
value_map=greenspace_area_map,
|
||||
value_map=urban_nature_area_map,
|
||||
target_raster_path=target_raster_path,
|
||||
target_datatype=gdal.GDT_Float32,
|
||||
target_nodata=FLOAT32_NODATA,
|
||||
|
@ -1450,16 +1450,16 @@ def _reclassify_greenspace_area(
|
|||
)
|
||||
|
||||
|
||||
def _filter_population(population, greenspace_budget, numpy_filter_op):
|
||||
def _filter_population(population, urban_nature_budget, numpy_filter_op):
|
||||
"""Filter the population by a defined op and the urban nature budget.
|
||||
|
||||
Note:
|
||||
The ``population`` and ``greenspace_budget`` inputs must have the same
|
||||
The ``population`` and ``urban_nature_budget`` inputs must have the same
|
||||
shape and must both use ``FLOAT32_NODATA`` as their nodata value.
|
||||
|
||||
Args:
|
||||
population (numpy.array): A numpy array with population counts.
|
||||
greenspace_budget (numpy.array): A numpy array with the urban nature
|
||||
urban_nature_budget (numpy.array): A numpy array with the urban nature
|
||||
budget values.
|
||||
numpy_filter_op (callable): A function that takes a numpy array as
|
||||
parameter 1 and a scalar value as parameter 2. This function must
|
||||
|
@ -1467,16 +1467,16 @@ def _filter_population(population, greenspace_budget, numpy_filter_op):
|
|||
|
||||
Returns:
|
||||
A ``numpy.array`` with the population values where the
|
||||
``greenspace_budget`` pixels match the ``numpy_filter_op``.
|
||||
``urban_nature_budget`` pixels match the ``numpy_filter_op``.
|
||||
"""
|
||||
population_matching_filter = numpy.full(
|
||||
population.shape, FLOAT32_NODATA, dtype=numpy.float32)
|
||||
valid_pixels = (
|
||||
~numpy.isclose(greenspace_budget, FLOAT32_NODATA) &
|
||||
~numpy.isclose(urban_nature_budget, FLOAT32_NODATA) &
|
||||
~numpy.isclose(population, FLOAT32_NODATA))
|
||||
|
||||
population_matching_filter[valid_pixels] = numpy.where(
|
||||
numpy_filter_op(greenspace_budget[valid_pixels], 0),
|
||||
numpy_filter_op(urban_nature_budget[valid_pixels], 0),
|
||||
population[valid_pixels], # If condition is true, use population
|
||||
0 # If condition is false, use 0
|
||||
)
|
||||
|
@ -1486,7 +1486,7 @@ def _filter_population(population, greenspace_budget, numpy_filter_op):
|
|||
def _supply_demand_vector_for_pop_groups(
|
||||
source_aoi_vector_path,
|
||||
target_aoi_vector_path,
|
||||
greenspace_sup_dem_paths_by_pop_group,
|
||||
urban_nature_sup_dem_paths_by_pop_group,
|
||||
proportional_pop_paths_by_pop_group,
|
||||
undersupply_by_pop_group,
|
||||
oversupply_by_pop_group):
|
||||
|
@ -1495,7 +1495,7 @@ def _supply_demand_vector_for_pop_groups(
|
|||
Args:
|
||||
source_aoi_vector_path (str): The source AOI vector path.
|
||||
target_aoi_vector_path (str): The target AOI vector path.
|
||||
greenspace_sup_dem_paths_by_pop_group (dict): A dict mapping population
|
||||
urban_nature_sup_dem_paths_by_pop_group (dict): A dict mapping population
|
||||
group names to rasters of urban nature supply/demand for the given
|
||||
group.
|
||||
proportional_pop_paths_by_pop_group (dict): A dict mapping population
|
||||
|
@ -1538,8 +1538,8 @@ def _supply_demand_vector_for_pop_groups(
|
|||
# trim the leading 'pop_'
|
||||
groupname = re.sub(POP_FIELD_REGEX, '', pop_group_field)
|
||||
|
||||
greenspace_sup_dem_stats = _get_zonal_stats(
|
||||
greenspace_sup_dem_paths_by_pop_group[pop_group_field])
|
||||
urban_nature_sup_dem_stats = _get_zonal_stats(
|
||||
urban_nature_sup_dem_paths_by_pop_group[pop_group_field])
|
||||
proportional_pop_stats = _get_zonal_stats(
|
||||
proportional_pop_paths_by_pop_group[pop_group_field])
|
||||
undersupply_stats = _get_zonal_stats(
|
||||
|
@ -1550,7 +1550,7 @@ def _supply_demand_vector_for_pop_groups(
|
|||
for feature_id in feature_ids:
|
||||
group_population_in_region = proportional_pop_stats[
|
||||
feature_id]['sum']
|
||||
group_sup_dem_in_region = greenspace_sup_dem_stats[
|
||||
group_sup_dem_in_region = urban_nature_sup_dem_stats[
|
||||
feature_id]['sum']
|
||||
stats_by_feature[feature_id][f'SUP_DEMadm_cap_{groupname}'] = (
|
||||
group_sup_dem_in_region / group_population_in_region)
|
||||
|
@ -1576,7 +1576,7 @@ def _supply_demand_vector_for_pop_groups(
|
|||
def _supply_demand_vector_for_single_raster_modes(
|
||||
source_aoi_vector_path,
|
||||
target_aoi_vector_path,
|
||||
greenspace_budget_path,
|
||||
urban_nature_budget_path,
|
||||
population_path,
|
||||
undersupplied_populations_path,
|
||||
oversupplied_populations_path,
|
||||
|
@ -1587,7 +1587,7 @@ def _supply_demand_vector_for_single_raster_modes(
|
|||
source_aoi_vector_path (str): Path to the source aois vector.
|
||||
target_aoi_vector_path (str): Path to where the target aois vector
|
||||
should be written.
|
||||
greenspace_budget_path (str): Path to a raster of urban nature
|
||||
urban_nature_budget_path (str): Path to a raster of urban nature
|
||||
supply/demand budget.
|
||||
population_path (str): Path to a population raster.
|
||||
undersupplied_populations_path (str): Path to a raster of oversupplied
|
||||
|
@ -1604,7 +1604,7 @@ def _supply_demand_vector_for_single_raster_modes(
|
|||
return pygeoprocessing.zonal_statistics(
|
||||
(raster_path, 1), source_aoi_vector_path)
|
||||
|
||||
greenspace_budget_stats = _get_zonal_stats(greenspace_budget_path)
|
||||
urban_nature_budget_stats = _get_zonal_stats(urban_nature_budget_path)
|
||||
population_stats = _get_zonal_stats(population_path)
|
||||
undersupplied_stats = _get_zonal_stats(undersupplied_populations_path)
|
||||
oversupplied_stats = _get_zonal_stats(oversupplied_populations_path)
|
||||
|
@ -1625,10 +1625,10 @@ def _supply_demand_vector_for_single_raster_modes(
|
|||
pop_proportions_by_fid[id_field][group] = value
|
||||
|
||||
stats_by_feature = {}
|
||||
for fid in greenspace_budget_stats.keys():
|
||||
for fid in urban_nature_budget_stats.keys():
|
||||
stats = {
|
||||
'SUP_DEMadm_cap': (
|
||||
greenspace_budget_stats[fid]['sum'] /
|
||||
urban_nature_budget_stats[fid]['sum'] /
|
||||
population_stats[fid]['sum']),
|
||||
'Pund_adm': undersupplied_stats[fid]['sum'],
|
||||
'Povr_adm': oversupplied_stats[fid]['sum'],
|
||||
|
@ -1688,42 +1688,42 @@ def _write_supply_demand_vector(source_aoi_vector_path, feature_attrs,
|
|||
target_vector = None
|
||||
|
||||
|
||||
def _greenspace_balance_op(greenspace_supply, greenspace_demand):
|
||||
def _urban_nature_balance_op(urban_nature_supply, urban_nature_demand):
|
||||
"""Calculate the per-capita urban nature balance.
|
||||
|
||||
This is the amount of urban nature that each pixel has above (positive
|
||||
values) or below (negative values) the user-defined ``greenspace_demand``
|
||||
values) or below (negative values) the user-defined ``urban_nature_demand``
|
||||
value.
|
||||
|
||||
Args:
|
||||
greenspace_supply (numpy.array): The supply of urban nature available to
|
||||
urban_nature_supply (numpy.array): The supply of urban nature available to
|
||||
each person in the population. This is ``Ai`` in the User's Guide.
|
||||
This matrix must have ``FLOAT32_NODATA`` as its nodata value.
|
||||
greenspace_demand (float): The policy-defined urban nature requirement,
|
||||
urban_nature_demand (float): The policy-defined urban nature requirement,
|
||||
in square meters per person.
|
||||
|
||||
Returns:
|
||||
A ``numpy.array`` of the calculated urban nature budget.
|
||||
"""
|
||||
balance = numpy.full(
|
||||
greenspace_supply.shape, FLOAT32_NODATA, dtype=numpy.float32)
|
||||
valid_pixels = ~numpy.isclose(greenspace_supply, FLOAT32_NODATA)
|
||||
balance[valid_pixels] = greenspace_supply[valid_pixels] - greenspace_demand
|
||||
urban_nature_supply.shape, FLOAT32_NODATA, dtype=numpy.float32)
|
||||
valid_pixels = ~numpy.isclose(urban_nature_supply, FLOAT32_NODATA)
|
||||
balance[valid_pixels] = urban_nature_supply[valid_pixels] - urban_nature_demand
|
||||
return balance
|
||||
|
||||
|
||||
def _greenspace_supply_demand_op(greenspace_balance, population):
|
||||
def _urban_nature_supply_demand_op(urban_nature_balance, population):
|
||||
"""Calculate the supply/demand of urban nature per person.
|
||||
|
||||
Args:
|
||||
greenspace_balance (numpy.array): The area of urban nature budgeted to
|
||||
urban_nature_balance (numpy.array): The area of urban nature budgeted to
|
||||
each person, relative to a minimum required per-person area of
|
||||
urban nature. This matrix must have ``FLOAT32_NODATA`` as its nodata
|
||||
value. This matrix must be the same size and shape as
|
||||
``population``.
|
||||
population (numpy.array): Pixel values represent the population count
|
||||
of the pixel. This matrix must be the same size and shape as
|
||||
``greenspace_budget``, and must have ``FLOAT32_NODATA`` as its
|
||||
``urban_nature_budget``, and must have ``FLOAT32_NODATA`` as its
|
||||
nodata value.
|
||||
|
||||
Returns:
|
||||
|
@ -1731,22 +1731,22 @@ def _greenspace_supply_demand_op(greenspace_balance, population):
|
|||
supplied to each individual in each pixel.
|
||||
"""
|
||||
supply_demand = numpy.full(
|
||||
greenspace_balance.shape, FLOAT32_NODATA, dtype=numpy.float32)
|
||||
urban_nature_balance.shape, FLOAT32_NODATA, dtype=numpy.float32)
|
||||
valid_pixels = (
|
||||
~numpy.isclose(greenspace_balance, FLOAT32_NODATA) &
|
||||
~numpy.isclose(urban_nature_balance, FLOAT32_NODATA) &
|
||||
~numpy.isclose(population, FLOAT32_NODATA))
|
||||
supply_demand[valid_pixels] = (
|
||||
greenspace_balance[valid_pixels] * population[valid_pixels])
|
||||
urban_nature_balance[valid_pixels] * population[valid_pixels])
|
||||
return supply_demand
|
||||
|
||||
|
||||
def _calculate_greenspace_population_ratio(
|
||||
greenspace_area_raster_path, convolved_population_raster_path,
|
||||
def _calculate_urban_nature_population_ratio(
|
||||
urban_nature_area_raster_path, convolved_population_raster_path,
|
||||
target_ratio_raster_path):
|
||||
"""Calculate the urban nature-population ratio R_j.
|
||||
|
||||
Args:
|
||||
greenspace_area_raster_path (string): The path to a raster representing
|
||||
urban_nature_area_raster_path (string): The path to a raster representing
|
||||
the area of the pixel that represents urban nature. Pixel values
|
||||
will be ``0`` if there is no urban nature.
|
||||
convolved_population_raster_path (string): The path to a raster
|
||||
|
@ -1758,19 +1758,19 @@ def _calculate_greenspace_population_ratio(
|
|||
Returns:
|
||||
``None``.
|
||||
"""
|
||||
greenspace_nodata = pygeoprocessing.get_raster_info(
|
||||
greenspace_area_raster_path)['nodata'][0]
|
||||
urban_nature_nodata = pygeoprocessing.get_raster_info(
|
||||
urban_nature_area_raster_path)['nodata'][0]
|
||||
population_nodata = pygeoprocessing.get_raster_info(
|
||||
convolved_population_raster_path)['nodata'][0]
|
||||
|
||||
def _greenspace_population_ratio(greenspace_area, convolved_population):
|
||||
def _urban_nature_population_ratio(urban_nature_area, convolved_population):
|
||||
"""Calculate the urban nature-population ratio R_j.
|
||||
|
||||
Args:
|
||||
greenspace_area (numpy.array): A numpy array representing the area
|
||||
urban_nature_area (numpy.array): A numpy array representing the area
|
||||
of urban nature in the pixel. Pixel values will be ``0`` if
|
||||
there is no urban nature. Pixel values may also match
|
||||
``greenspace_nodata``.
|
||||
``urban_nature_nodata``.
|
||||
convolved_population (numpy.array): A numpy array where each pixel
|
||||
represents the total number of people within a search radius of
|
||||
each pixel, perhaps weighted by a search kernel.
|
||||
|
@ -1788,7 +1788,7 @@ def _calculate_greenspace_population_ratio(
|
|||
# ASSUMPTION: population nodata value is not close to 0.
|
||||
# Shouldn't be if we're coming from convolution.
|
||||
out_array = numpy.full(
|
||||
greenspace_area.shape, FLOAT32_NODATA, dtype=numpy.float32)
|
||||
urban_nature_area.shape, FLOAT32_NODATA, dtype=numpy.float32)
|
||||
|
||||
# Small negative values should already have been filtered out in
|
||||
# another function after the convolution.
|
||||
|
@ -1796,15 +1796,15 @@ def _calculate_greenspace_population_ratio(
|
|||
valid_pixels = (convolved_population > 0)
|
||||
|
||||
# R_j is a ratio only calculated for the urban nature pixels.
|
||||
greenspace_pixels = ~numpy.isclose(greenspace_area, 0)
|
||||
valid_pixels &= greenspace_pixels
|
||||
urban_nature_pixels = ~numpy.isclose(urban_nature_area, 0)
|
||||
valid_pixels &= urban_nature_pixels
|
||||
if population_nodata is not None:
|
||||
valid_pixels &= ~utils.array_equals_nodata(
|
||||
convolved_population, population_nodata)
|
||||
|
||||
if greenspace_nodata is not None:
|
||||
if urban_nature_nodata is not None:
|
||||
valid_pixels &= ~utils.array_equals_nodata(
|
||||
greenspace_area, greenspace_nodata)
|
||||
urban_nature_area, urban_nature_nodata)
|
||||
|
||||
# The user's guide specifies that if the population in the search
|
||||
# radius is numerically 0, the urban nature/population ratio should be
|
||||
|
@ -1817,13 +1817,13 @@ def _calculate_greenspace_population_ratio(
|
|||
# nature on that pixel.
|
||||
population_close_to_zero = (convolved_population <= 1.0)
|
||||
out_array[population_close_to_zero] = (
|
||||
greenspace_area[population_close_to_zero])
|
||||
out_array[~greenspace_pixels] = 0
|
||||
urban_nature_area[population_close_to_zero])
|
||||
out_array[~urban_nature_pixels] = 0
|
||||
|
||||
valid_pixels_with_population = (
|
||||
valid_pixels & (~population_close_to_zero))
|
||||
out_array[valid_pixels_with_population] = (
|
||||
greenspace_area[valid_pixels_with_population] /
|
||||
urban_nature_area[valid_pixels_with_population] /
|
||||
convolved_population[valid_pixels_with_population])
|
||||
|
||||
# eliminate pixel values < 0
|
||||
|
@ -1832,9 +1832,9 @@ def _calculate_greenspace_population_ratio(
|
|||
return out_array
|
||||
|
||||
pygeoprocessing.raster_calculator(
|
||||
[(greenspace_area_raster_path, 1),
|
||||
[(urban_nature_area_raster_path, 1),
|
||||
(convolved_population_raster_path, 1)],
|
||||
_greenspace_population_ratio, target_ratio_raster_path,
|
||||
_urban_nature_population_ratio, target_ratio_raster_path,
|
||||
gdal.GDT_Float32, FLOAT32_NODATA)
|
||||
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ def _build_model_args(workspace):
|
|||
'lulc_attribute_table': os.path.join(
|
||||
workspace, 'lulc_attributes.csv'),
|
||||
'decay_function': 'gaussian',
|
||||
'greenspace_demand': 100, # square meters
|
||||
'urban_nature_demand': 100, # square meters
|
||||
'admin_boundaries_vector_path': os.path.join(
|
||||
workspace, 'aois.geojson'),
|
||||
}
|
||||
|
@ -337,30 +337,30 @@ class UNATests(unittest.TestCase):
|
|||
numpy.testing.assert_allclose(
|
||||
expected_array, kernel)
|
||||
|
||||
def test_greenspace_balance(self):
|
||||
"""UNA: Test the per-capita greenspace balance functions."""
|
||||
def test_urban_nature_balance(self):
|
||||
"""UNA: Test the per-capita urban_nature balance functions."""
|
||||
from natcap.invest import urban_nature_access
|
||||
|
||||
nodata = urban_nature_access.FLOAT32_NODATA
|
||||
greenspace_supply = numpy.array([
|
||||
urban_nature_supply = numpy.array([
|
||||
[nodata, 100.5],
|
||||
[75, 100]], dtype=numpy.float32)
|
||||
greenspace_demand = 50
|
||||
urban_nature_demand = 50
|
||||
|
||||
population = numpy.array([
|
||||
[50, 100],
|
||||
[40.75, nodata]], dtype=numpy.float32)
|
||||
|
||||
greenspace_budget = urban_nature_access._greenspace_balance_op(
|
||||
greenspace_supply, greenspace_demand)
|
||||
expected_greenspace_budget = numpy.array([
|
||||
urban_nature_budget = urban_nature_access._urban_nature_balance_op(
|
||||
urban_nature_supply, urban_nature_demand)
|
||||
expected_urban_nature_budget = numpy.array([
|
||||
[nodata, 50.5],
|
||||
[25, 50]], dtype=numpy.float32)
|
||||
numpy.testing.assert_allclose(
|
||||
greenspace_budget, expected_greenspace_budget)
|
||||
urban_nature_budget, expected_urban_nature_budget)
|
||||
|
||||
supply_demand = urban_nature_access._greenspace_supply_demand_op(
|
||||
greenspace_budget, population)
|
||||
supply_demand = urban_nature_access._urban_nature_supply_demand_op(
|
||||
urban_nature_budget, population)
|
||||
expected_supply_demand = numpy.array([
|
||||
[nodata, 100 * 50.5],
|
||||
[25 * 40.75, nodata]], dtype=numpy.float32)
|
||||
|
@ -479,13 +479,13 @@ class UNATests(unittest.TestCase):
|
|||
admin_vector = None
|
||||
admin_layer = None
|
||||
|
||||
def test_split_greenspace(self):
|
||||
def test_split_urban_nature(self):
|
||||
from natcap.invest import urban_nature_access
|
||||
|
||||
args = _build_model_args(self.workspace_dir)
|
||||
args['search_radius_mode'] = urban_nature_access.RADIUS_OPT_URBAN_NATURE
|
||||
|
||||
# The split greenspace feature requires an extra column in the
|
||||
# The split urban_nature feature requires an extra column in the
|
||||
# attribute table.
|
||||
attribute_table = pandas.read_csv(args['lulc_attribute_table'])
|
||||
new_search_radius_values = {
|
||||
|
@ -667,7 +667,7 @@ class UNATests(unittest.TestCase):
|
|||
"""UNA: all modes have same results when consistent radii.
|
||||
|
||||
Although the different modes have different ways of defining their
|
||||
search radii, the greenspace_supply raster should be numerically
|
||||
search radii, the urban_nature_supply raster should be numerically
|
||||
equivalent if they all use the same search radii.
|
||||
|
||||
This is a good gut-check of basic model behavior across modes.
|
||||
|
@ -685,20 +685,20 @@ class UNATests(unittest.TestCase):
|
|||
urban_nature_access.RADIUS_OPT_UNIFORM)
|
||||
uniform_args['search_radius'] = search_radius
|
||||
|
||||
# build args for split greenspace mode
|
||||
split_greenspace_args = _build_model_args(
|
||||
os.path.join(self.workspace_dir, 'radius_greenspace'))
|
||||
split_greenspace_args['results_suffix'] = 'greenspace'
|
||||
split_greenspace_args['search_radius_mode'] = (
|
||||
# build args for split urban_nature mode
|
||||
split_urban_nature_args = _build_model_args(
|
||||
os.path.join(self.workspace_dir, 'radius_urban_nature'))
|
||||
split_urban_nature_args['results_suffix'] = 'urban_nature'
|
||||
split_urban_nature_args['search_radius_mode'] = (
|
||||
urban_nature_access.RADIUS_OPT_URBAN_NATURE)
|
||||
attribute_table = pandas.read_csv(
|
||||
split_greenspace_args['lulc_attribute_table'])
|
||||
split_urban_nature_args['lulc_attribute_table'])
|
||||
new_search_radius_values = dict(
|
||||
(lucode, search_radius) for lucode in attribute_table['lucode'])
|
||||
attribute_table['search_radius_m'] = attribute_table['lucode'].map(
|
||||
new_search_radius_values)
|
||||
attribute_table.to_csv(
|
||||
split_greenspace_args['lulc_attribute_table'], index=False)
|
||||
split_urban_nature_args['lulc_attribute_table'], index=False)
|
||||
|
||||
# build args for split population group mode
|
||||
pop_group_args = _build_model_args(
|
||||
|
@ -728,21 +728,21 @@ class UNATests(unittest.TestCase):
|
|||
pop_group_args['population_raster_path'])['projection_wkt'],
|
||||
'GeoJSON', fields, attributes)
|
||||
|
||||
for args in (uniform_args, split_greenspace_args, pop_group_args):
|
||||
for args in (uniform_args, split_urban_nature_args, pop_group_args):
|
||||
urban_nature_access.execute(args)
|
||||
|
||||
uniform_radius_supply = pygeoprocessing.raster_to_numpy_array(
|
||||
os.path.join(uniform_args['workspace_dir'], 'output',
|
||||
'greenspace_supply_uniform.tif'))
|
||||
split_greenspace_supply = pygeoprocessing.raster_to_numpy_array(
|
||||
os.path.join(split_greenspace_args['workspace_dir'], 'output',
|
||||
'greenspace_supply_greenspace.tif'))
|
||||
'urban_nature_supply_uniform.tif'))
|
||||
split_urban_nature_supply = pygeoprocessing.raster_to_numpy_array(
|
||||
os.path.join(split_urban_nature_args['workspace_dir'], 'output',
|
||||
'urban_nature_supply_urban_nature.tif'))
|
||||
split_pop_groups_supply = pygeoprocessing.raster_to_numpy_array(
|
||||
os.path.join(pop_group_args['workspace_dir'], 'output',
|
||||
'greenspace_supply_popgroup.tif'))
|
||||
'urban_nature_supply_popgroup.tif'))
|
||||
|
||||
numpy.testing.assert_allclose(
|
||||
uniform_radius_supply, split_greenspace_supply, rtol=1e-6)
|
||||
uniform_radius_supply, split_urban_nature_supply, rtol=1e-6)
|
||||
numpy.testing.assert_allclose(
|
||||
uniform_radius_supply, split_pop_groups_supply, rtol=1e-6)
|
||||
|
||||
|
|
Loading…
Reference in New Issue