Implement Urban Mental Health Option 1: Tree Canopy Cover + NDVI Inputs#2314
Implement Urban Mental Health Option 1: Tree Canopy Cover + NDVI Inputs#2314claire-simpson wants to merge 13 commits intonatcap:feature/urban-health-modelfrom
Conversation
…lation; better document fit and apply functions natcap#2141
| numpy.testing.assert_allclose( | ||
| actual_mean_ndvi[key], expected_mean_ndvi[key], atol=1e-6) | ||
|
|
||
| # def test_option1_tcc_input(self): |
There was a problem hiding this comment.
Template for test for whole model, but I commented it out because there is still uncertainty around population weighting and whether to convolve TCC before fitting GAM - see #2316
|
|
||
| curve_smooth = gam.predict(centers.reshape(-1, 1)) | ||
|
|
||
| fig, ax = plt.subplots() |
There was a problem hiding this comment.
I believe matplotlib is a new dependency as of the reports PR so I assume this would be the first time a graph is created within a model. This was an intermediate output of the demo model and seems useful for interpreting the relationship between TCC and NDVI, so I think it'd be great to ultimately include in the report. However maybe saving this as a standalone isn't needed?
There was a problem hiding this comment.
If it feels like a useful standalone output, I think it's fine to save on its own along with adding to the report if it'd be useful there too.
It might be worth thinking about whether we should be saving the numpy arrays for centers, curve, and others as intermediate outputs.
dcdenu4
left a comment
There was a problem hiding this comment.
Thanks @claire-simpson ! I don't have too many comments but think it'd be best to walk through the curve fitting part on a call, after we talk with Yingjie, or with Yingjie too!
| import matplotlib.pyplot as plt | ||
| import numpy | ||
| import pandas | ||
| from pygam import LinearGAM, s # Are we ok to add pygam as new invest dependency? Alternatively, could us scipy.UnivariateSpline |
There was a problem hiding this comment.
I'd be interesting in talking about how scipy.UnivariateSpline could be an alternative.
| mental disorder cases at the pixel level, based on the selected urban | ||
| greening scenario. | ||
|
|
||
| Args: |
There was a problem hiding this comment.
Has this docstring been keeping pace with the MODEL_SPEC updates? In terms of descriptive text and required / optional flags.
| if args['scenario'] == 'tcc_ndvi': | ||
| LOGGER.info("Using Tree Canopy Cover and NDVI inputs") | ||
| mean_buffered_tcc_task = task_graph.add_task( | ||
| func=pygeoprocessing.convolve_2d, |
There was a problem hiding this comment.
Yep, I think using a dichotomous kernel and convolving with normalize_kernel=True gets you a mean value within the given kernel radius.
| file_registry['tree_cover_buffer_mean'], | ||
| args['tree_cover_target'], | ||
| file_registry['ndvi_alt_buffer_mean'], | ||
| file_registry['result_fig_tc_ndvi_plot']), |
There was a problem hiding this comment.
Should the plot output also be in the target_path_list below?
| Writes alt NDVI raster where each pixel's NDVI is increased based on | ||
| the difference between the target NDVI (based on tc_target) and the | ||
| NDVI predicted by the TC-->NDVI curve at that pixel's tree cover value. |
There was a problem hiding this comment.
Should we mention how population is used at all for weighting?
| population_path (str): path to population raster | ||
| tree_cover_path (str): path to tree cover raster with pixels in | ||
| range [0, 100] | ||
| tc_target (float): target tree canopy cover value (in range [0,100]) |
There was a problem hiding this comment.
| tc_target (float): target tree canopy cover value (in range [0,100]) | |
| tc_target (float): target tree canopy cover value as a percentage (in range [0,100]) |
| None | ||
| """ | ||
|
|
||
| centers, curve = _fit_tc_to_ndvi_curve( |
There was a problem hiding this comment.
I'm wondering if this should be its own taskgraph step in the workflow instead of called in here? Benefits could be avoided re-computation, more modular step by step breakout in execute, and maybe more targeted testing?
| Args: | ||
| base_ndvi_path (str): path to baseline NDVI raster | ||
| tree_cover_path (str): path to tree cover raster | ||
| population_path (str): path to population raster |
There was a problem hiding this comment.
Density or count? For future us mostly.
|
|
||
| curve_smooth = gam.predict(centers.reshape(-1, 1)) | ||
|
|
||
| fig, ax = plt.subplots() |
There was a problem hiding this comment.
If it feels like a useful standalone output, I think it's fine to save on its own along with adding to the report if it'd be useful there too.
It might be worth thinking about whether we should be saving the numpy arrays for centers, curve, and others as intermediate outputs.
| file_registry['population_aligned'], | ||
| file_registry['tree_cover_buffer_mean'], | ||
| args['tree_cover_target'], | ||
| file_registry['ndvi_alt_buffer_mean'], |
There was a problem hiding this comment.
Maybe its worth updating ndvi_alt_buffer_mean, since this function isn't really returning a buffered mean? Right?
Description
Implements Option 1 (tree canopy cover–based scenarios) for the Urban Mental Health model by translating a user-defined tree cover target into an NDVI-based nature exposure scenario (to create 'alternate NDVI').
This PR adds a population-weighted, non-linear translation between tree canopy cover (%) and NDVI exposure, following the framework described in the UMH design document.
The steps are as follows:
search_radiussearch_radiusNDVI_alt = NDVI_base + (NDVI_target - f(TCC_pixel))NE_delta = NDVI_alt - NDVI_baseNotes:
pygamas an InVEST dependency (see below)Open Questions
pygamas a dependency? There are definitely alternative options like usingscipy.interpolate.UnivariateSpline. If so, I'd need to add pygam torequirements.txt.iterblocks)Fixes #2141
Checklist