Skip to content

Commit ce30fa4

Browse files
distribute parts of _define_target_grid to respective helper modules
1 parent 00f6afe commit ce30fa4

File tree

4 files changed

+79
-48
lines changed

4 files changed

+79
-48
lines changed

climada/entity/exposures/litpop/gpw_population.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import numpy as np
2525
import rasterio
26+
from affine import Affine
2627

2728
from climada import CONFIG
2829
from climada.util.constants import SYSTEM_DIR
@@ -171,3 +172,53 @@ def get_gpw_file_path(gpw_version, reference_year, data_dir=None, verbose=True):
171172
f" different folder. The data can be downloaded from {sedac_browse_url}, e.g.,"
172173
f" {sedac_file_url} (Free NASA Earthdata login required)."
173174
)
175+
176+
177+
def grid_aligned_with_gpw(reference_year, gpw_version, res_arcsec, data_dir=SYSTEM_DIR):
178+
"""
179+
Defines a grid based on population metadata.
180+
181+
Parameters
182+
----------
183+
reference_year : int
184+
The reference year for population and nightlight data.
185+
gpw_version : int
186+
Version number of GPW population data.
187+
res_arcsec : int or None
188+
Desired resolution in arcseconds. If None, aligns to population grid.
189+
data_dir : str
190+
Path to input data directory.
191+
192+
Returns
193+
-------
194+
grid : dict
195+
A dictionary containing grid metadata, following the raster grid
196+
specification.
197+
"""
198+
res_deg = res_arcsec / 3600
199+
200+
file_path = get_gpw_file_path(
201+
gpw_version, reference_year, data_dir=data_dir, verbose=False
202+
)
203+
with rasterio.open(file_path, "r") as src:
204+
global_crs = src.crs
205+
gpw_transform = src.transform
206+
# Align grid resolution with GPW dataset
207+
aligned_lon_min = -180 + (round((gpw_transform[2] - (-180)) / res_deg) * res_deg)
208+
aligned_lat_max = 90 - (round((90 - gpw_transform[5]) / res_deg) * res_deg)
209+
210+
global_transform = Affine(res_deg, 0, aligned_lon_min, 0, -res_deg, aligned_lat_max)
211+
212+
global_width = round(360 / res_deg)
213+
global_height = round(180 / res_deg)
214+
215+
# Define the target grid using the computed values
216+
return {
217+
"driver": "GTiff",
218+
"dtype": "float32",
219+
"nodata": None,
220+
"crs": global_crs,
221+
"width": global_width,
222+
"height": global_height,
223+
"transform": global_transform,
224+
}

climada/entity/exposures/litpop/litpop.py

Lines changed: 12 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@
3333
import climada.util.finance as u_fin
3434
from climada import CONFIG
3535
from climada.entity.exposures.base import DEF_REF_YEAR, INDICATOR_IMPF, Exposures
36-
from climada.entity.exposures.litpop import gpw_population as pop_util
37-
from climada.entity.exposures.litpop import nightlight as nl_util
3836
from climada.util.constants import SYSTEM_DIR
3937

38+
from .gpw_population import grid_aligned_with_gpw, load_gpw_pop_shape
39+
from .nightlight import BM_NIGHTLIGHT_GRID, load_nasa_nl_shape
40+
4041
LOGGER = logging.getLogger(__name__)
4142

4243
GPW_VERSION = CONFIG.exposures.litpop.gpw_population.gpw_version.int()
@@ -868,46 +869,14 @@ def _define_target_grid(reference_year, gpw_version, data_dir, res_arcsec):
868869
A dictionary containing grid metadata, following the raster grid
869870
specification.
870871
"""
871-
res_deg = res_arcsec / 3600
872872
if res_arcsec == 15:
873-
# Black Marble Nightlights Grid
874-
# pylint: disable=c-extension-no-member
875-
global_crs = rasterio.crs.CRS.from_epsg(4326) # WGS84 projection
876-
global_transform = Affine(res_deg, 0, -180, 0, -res_deg, 90)
877-
global_width = round(360 / res_deg)
878-
global_height = round(180 / res_deg)
879-
else:
880-
# GPW Population Grid
881-
file_path = pop_util.get_gpw_file_path(
882-
gpw_version, reference_year, data_dir=data_dir, verbose=False
883-
)
884-
with rasterio.open(file_path, "r") as src:
885-
global_crs = src.crs
886-
gpw_transform = src.transform
887-
# Align grid resolution with GPW dataset
888-
aligned_lon_min = -180 + (
889-
round((gpw_transform[2] - (-180)) / res_deg) * res_deg
890-
)
891-
aligned_lat_max = 90 - (round((90 - gpw_transform[5]) / res_deg) * res_deg)
892-
893-
global_transform = Affine(
894-
res_deg, 0, aligned_lon_min, 0, -res_deg, aligned_lat_max
895-
)
896-
897-
global_width = round(360 / res_deg)
898-
global_height = round(180 / res_deg)
899-
900-
# Define the target grid using the computed values
901-
target_grid = {
902-
"driver": "GTiff",
903-
"dtype": "float32",
904-
"nodata": None,
905-
"crs": global_crs,
906-
"width": global_width,
907-
"height": global_height,
908-
"transform": global_transform,
909-
}
910-
return target_grid
873+
return BM_NIGHTLIGHT_GRID
874+
return grid_aligned_with_gpw(
875+
reference_year=reference_year,
876+
gpw_version=gpw_version,
877+
res_arcsec=res_arcsec,
878+
data_dir=data_dir,
879+
)
911880

912881
@staticmethod
913882
def _from_country(
@@ -1126,7 +1095,7 @@ def _get_litpop_single_polygon(
11261095
# set nightlight offset (delta) to 1 in case n>0, c.f. delta in Eq. 1 of paper:
11271096
offsets = (0, 0) if exponents[1] == 0 else (1, 0)
11281097
# import population data (2d array), meta data, and global grid info,
1129-
pop, meta_pop, _ = pop_util.load_gpw_pop_shape(
1098+
pop, meta_pop, _ = load_gpw_pop_shape(
11301099
geometry=polygon,
11311100
reference_year=reference_year,
11321101
gpw_version=gpw_version,
@@ -1135,7 +1104,7 @@ def _get_litpop_single_polygon(
11351104
)
11361105
total_population = pop.sum()
11371106
# import nightlight data (2d array) and associated meta data:
1138-
nlight, meta_nl = nl_util.load_nasa_nl_shape(
1107+
nlight, meta_nl = load_nasa_nl_shape(
11391108
polygon, reference_year, data_dir=data_dir, dtype=float
11401109
)
11411110

climada/entity/exposures/litpop/nightlight.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import numpy as np
3232
import rasterio
3333
import scipy.sparse as sparse
34+
from affine import Affine
3435
from osgeo import gdal
3536
from PIL import Image
3637
from shapefile import Shape
@@ -69,6 +70,17 @@
6970
]
7071
"""Nightlight NASA files which generate the whole earth when put together."""
7172

73+
BM_NIGHTLIGHT_GRID = {
74+
"driver": "GTiff",
75+
"dtype": "float32",
76+
"nodata": None,
77+
"crs": rasterio.crs.CRS.from_epsg(4326), # pylint: disable=c-extension-no-member
78+
"width": round(360 / NASA_RESOLUTION_DEG),
79+
"height": round(180 / NASA_RESOLUTION_DEG),
80+
"transform": Affine(NASA_RESOLUTION_DEG, 0, -180, 0, -NASA_RESOLUTION_DEG, 90),
81+
}
82+
"""Grid aligned with Nightlight NASA files"""
83+
7284

7385
def load_nasa_nl_shape(geometry, year, data_dir=SYSTEM_DIR, dtype="float32"):
7486
"""Read nightlight data from NASA BlackMarble tiles
@@ -146,6 +158,7 @@ def load_nasa_nl_shape(geometry, year, data_dir=SYSTEM_DIR, dtype="float32"):
146158
if idx == 0:
147159
meta = meta_tmp
148160
# set correct CRS from local tile's CRS to global WGS 84:
161+
# pylint: disable=c-extension-no-member
149162
meta.update({"crs": rasterio.crs.CRS.from_epsg(4326), "dtype": dtype})
150163
if len(req_files) == 1: # only one tile required:
151164
return np.array(out_image, dtype=dtype), meta
@@ -264,9 +277,7 @@ def check_nl_local_file_exists(required_files=None, check_path=SYSTEM_DIR, year=
264277
required_files = np.ones(
265278
np.count_nonzero(BM_FILENAMES),
266279
)
267-
LOGGER.warning(
268-
"The parameter 'required_files' was too short and " "is ignored."
269-
)
280+
LOGGER.warning("The parameter 'required_files' was too short and is ignored.")
270281
if isinstance(check_path, str):
271282
check_path = Path(check_path)
272283
if not check_path.is_dir():

climada/test/test_litpop_integr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
)
4545

4646

47-
class TestLitPopExposure(unittest.TestCase):
47+
class TestLitPopExposures(unittest.TestCase):
4848
"""Test LitPop exposure data model:"""
4949

5050
def test_netherlands150_pass(self):
@@ -592,7 +592,7 @@ def test_litpop_grid_consistency(self):
592592

593593

594594
if __name__ == "__main__":
595-
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestLitPopExposure)
595+
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestLitPopExposures)
596596
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestAdmin1))
597597
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestGPWPopulation))
598598
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestLitPopGridAlignment))

0 commit comments

Comments
 (0)