Skip to content

Commit b8ecd8d

Browse files
committed
add function to select regions.
1 parent 8acf0bf commit b8ecd8d

File tree

4 files changed

+187
-35
lines changed

4 files changed

+187
-35
lines changed

climada/entity/exposures/base.py

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from climada.entity.tag import Tag
1717
from climada.util.coordinates import GridPoints
1818
from climada.util.config import CONFIG
19-
import climada.util.plot as plot
19+
import climada.util.plot as u_plot
2020

2121
LOGGER = logging.getLogger(__name__)
2222

@@ -26,7 +26,7 @@
2626
}
2727
""" Supported files format to read from """
2828

29-
class Exposures(object):
29+
class Exposures():
3030
"""Defines exposures attributes and basic methods. Loads from
3131
files with format defined in FILE_EXT.
3232
@@ -149,10 +149,10 @@ def plot(self, ignore_zero=True, pop_name=True, buffer_deg=1.0,
149149
kwargs['reduce_C_function'] = np.sum
150150
if ignore_zero:
151151
pos_vals = self.value > 0
152-
return plot.geo_bin_from_array(self.value[pos_vals], \
152+
return u_plot.geo_bin_from_array(self.value[pos_vals], \
153153
self.coord[pos_vals, :], cbar_label, title, pop_name, \
154154
buffer_deg, extend, **kwargs)
155-
return plot.geo_bin_from_array(self.value, self.coord, cbar_label, \
155+
return u_plot.geo_bin_from_array(self.value, self.coord, cbar_label, \
156156
title, pop_name, buffer_deg, extend, **kwargs)
157157

158158
def read(self, files, descriptions='', var_names=None):
@@ -261,6 +261,48 @@ def append(self, exposures):
261261
self.id[dup_id] = new_id
262262
new_id += 1
263263

264+
def select_region(self, reg_id):
265+
"""Return reference exposure of given region.
266+
267+
Parameters:
268+
reg_id (int): region id to select
269+
270+
Returns:
271+
Exposures
272+
"""
273+
sel_idx = np.argwhere(self.region_id == reg_id)
274+
if sel_idx.size:
275+
sel_idx = sel_idx.reshape((sel_idx.size,))
276+
else:
277+
LOGGER.info('No exposure with region id %s.', reg_id)
278+
return None
279+
280+
sel_exp = Exposures()
281+
sel_exp.tag = self.tag
282+
sel_exp.description = 'Region ' + str(reg_id)
283+
sel_exp.ref_year = self.ref_year
284+
sel_exp.value_unit = self.value_unit
285+
# Obligatory variables
286+
sel_exp.coord = self.coord[sel_idx, :]
287+
sel_exp.value = self.value[sel_idx]
288+
sel_exp.impact_id = self.impact_id[sel_idx]
289+
sel_exp.id = self.id[sel_idx]
290+
# Optional variables.
291+
if self.deductible.size:
292+
sel_exp.deductible = self.deductible[sel_idx]
293+
if self.cover.size:
294+
sel_exp.cover = self.cover[sel_idx]
295+
if self.category_id.size:
296+
sel_exp.category_id = self.category_id[sel_idx]
297+
if self.region_id.size:
298+
sel_exp.region_id = self.region_id[sel_idx]
299+
300+
sel_exp.assigned = dict()
301+
for key, value in self.assigned.items():
302+
sel_exp.assigned[key] = value[sel_idx]
303+
304+
return sel_exp
305+
264306
@staticmethod
265307
def get_sup_file_format():
266308
""" Get supported file extensions that can be read.

climada/entity/exposures/black_marble.py

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import requests
1616
import shapely.vectorized
1717
from cartopy.io import shapereader
18+
from iso3166 import countries as iso_cntry
1819

1920
from climada.entity.exposures.base import Exposures
2021
from climada.util.files_handler import download_file
@@ -59,15 +60,19 @@
5960

6061
class BlackMarble(Exposures):
6162
"""Defines exposures from night light intensity, GDP and income group.
63+
Attribute region_id is defined as:
64+
- United Nations Statistics Division (UNSD) 3-digit equivalent numeric code
65+
- 0 if country not found in UNSD.
66+
- -1 for water
6267
"""
6368

6469
def __init__(self):
6570
""" Empty initializer. """
6671
Exposures.__init__(self)
6772

68-
def set_countries(self, countries, \
69-
ref_year=CONFIG['entity']['present_ref_year'], res_km=1, \
70-
sea_res=(0, 1), from_hr=None, **kwargs):
73+
def set_countries(self, countries,
74+
ref_year=CONFIG['entity']['present_ref_year'],
75+
res_km=1, sea_res=(0, 1), from_hr=None, **kwargs):
7176
""" Model countries using values at reference year. If GDP or income
7277
group not available for that year, consider the value of the closest
7378
available year.
@@ -80,10 +85,11 @@ def set_countries(self, countries, \
8085
res_km (float, optional): approx resolution in km. Default: 1km.
8186
sea_res (tuple, optional): (sea_coast_km, sea_res_km), where first
8287
parameter is distance from coast to fill with water and second
83-
parameter is resolution between sea points
84-
from_hr (bool, optional)
88+
parameter is resolution between sea points.
89+
from_hr (bool, optional): force to use higher resolution image,
90+
independently of its year of acquisition.
8591
kwargs (optional): 'gdp' and 'inc_grp' dictionaries with keys the
86-
country ISO_alpha3 code. If provided, these are used
92+
country ISO_alpha3 code. If provided, these are used.
8793
"""
8894
self.clear()
8995
shp_file = shapereader.natural_earth(resolution='10m',
@@ -97,23 +103,38 @@ def set_countries(self, countries, \
97103
nightlight, coord_nl, fn_nl, res_fact = get_nightlight(ref_year, \
98104
cntry_info, res_km, from_hr)
99105

100-
# TODO parallize per thread?
101106
for cntry_iso, cntry_val in cntry_info.items():
102107
LOGGER.info('Processing country %s.', cntry_val[1])
103108
self.append(self._set_one_country(cntry_val, nightlight, \
104109
coord_nl, fn_nl, res_fact, res_km, cntry_admin1[cntry_iso]))
105110

106111
add_sea(self, sea_res)
107112

108-
def set_region(self, centroids, ref_year=2013, resolution=1):
113+
def set_region(self, centroids,
114+
ref_year=CONFIG['entity']['present_ref_year'], res_km=1):
109115
""" Model a specific region given by centroids."""
110116
# TODO: accept input centroids as well
111117
raise NotImplementedError
112118

113-
def fill_centroids(self):
114-
""" From coordinates information, generate Centroids instance."""
115-
# add sea in lower resolution
116-
raise NotImplementedError
119+
def select_region(self, reg_id):
120+
""" Select exposures with input region.
121+
122+
Parameters:
123+
reg_id (int, str): integer iso equivalent country numeric code or
124+
string iso alpha-3 or alpha-2 code or country name.
125+
126+
Returns:
127+
Exposures
128+
"""
129+
if isinstance(reg_id, int):
130+
return Exposures.select_region(self, reg_id)
131+
132+
try:
133+
return Exposures.select_region(self, \
134+
int(iso_cntry.get(reg_id).numeric))
135+
except KeyError:
136+
LOGGER.info('No country %s found.', reg_id)
137+
return None
117138

118139
@staticmethod
119140
def _set_one_country(cntry_info, nightlight, coord_nl, fn_nl, res_fact,
@@ -155,8 +176,9 @@ def _set_one_country(cntry_info, nightlight, coord_nl, fn_nl, res_fact,
155176
return exp_bkmrb
156177

157178
def country_iso_geom(countries, shp_file):
158-
""" Get country ISO alpha_3, country id (defined as country appearance
159-
order in natural earth shape file) and country's geometry.
179+
""" Get country ISO alpha_3, country id (defined as the United Nations
180+
Statistics Division (UNSD) 3-digit equivalent numeric codes and 0 if
181+
country not found) and country's geometry shape.
160182
161183
Parameters:
162184
countries (list or dict): list of country names (admin0) or dict
@@ -172,8 +194,6 @@ def country_iso_geom(countries, shp_file):
172194
for info_idx, info in enumerate(list_records):
173195
countries_shp[info.attributes['ADMIN'].title()] = info_idx
174196

175-
num_codes = [iso3 for iso3 in wb.country_codes if len(iso3) == 3]
176-
177197
cntry_info = dict()
178198
cntry_admin1 = dict()
179199
if isinstance(countries, list):
@@ -198,10 +218,10 @@ def country_iso_geom(countries, shp_file):
198218
raise ValueError
199219
iso3 = list_records[country_idx].attributes['ADM0_A3']
200220
try:
201-
iso_num = num_codes.index(iso3)
202-
except ValueError:
203-
iso_num = len(num_codes)
204-
cntry_info[iso3] = [iso_num, country_name.title(),
221+
cntry_id = int(iso_cntry.get(iso3).numeric)
222+
except KeyError:
223+
cntry_id = 0
224+
cntry_info[iso3] = [cntry_id, country_name.title(),
205225
list_records[country_idx].geometry]
206226
cntry_admin1[iso3] = _fill_admin1_geom(iso3, admin1_rec, prov_list)
207227

@@ -261,7 +281,7 @@ def get_nightlight(ref_year, cntry_info, res_km, from_hr=None):
261281
else:
262282
nl_year = 2012
263283
LOGGER.info("Nightlights from NASA's earth observatory for " + \
264-
"year %s.", nl_year)
284+
"year %s.", str(nl_year))
265285
res_fact = DEF_RES_NASA_KM/res_km
266286
geom = [info[2] for info in cntry_info.values()]
267287
geom = shapely.ops.cascaded_union(geom)
@@ -281,7 +301,7 @@ def get_nightlight(ref_year, cntry_info, res_km, from_hr=None):
281301
elif ref_year > 2013:
282302
nl_year = 2013
283303
LOGGER.info("Nightlights from NOAA's earth observation group "+ \
284-
"for year %s.", nl_year)
304+
"for year %s.", str(nl_year))
285305
res_fact = DEF_RES_NOAA_KM/res_km
286306
# nightlight intensity with 30 arcsec resolution
287307
nightlight, coord_nl, fn_nl = nl_utils.load_nightlight_noaa(nl_year)
@@ -301,7 +321,7 @@ def add_sea(exp, sea_res):
301321
return
302322

303323
LOGGER.info("Adding sea at %s km resolution and %s km distance from " + \
304-
"coast.", sea_res[1], sea_res[0])
324+
"coast.", str(sea_res[1]), str(sea_res[0]))
305325

306326
sea_res = (sea_res[0]/ONE_LAT_KM, sea_res[1]/ONE_LAT_KM)
307327

climada/entity/exposures/test/test_base.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,11 +347,44 @@ def test_check_wrongId_fail(self):
347347
self.assertIn('There are exposures with the same identifier.',
348348
cm.output[0])
349349

350+
class TestSelectRegion(unittest.TestCase):
351+
"""Test select_region from the Exposures class"""
352+
def test_sel_reg_pass(self):
353+
"""Select region"""
354+
expo = good_exposures()
355+
sel_expo = expo.select_region(1)
356+
357+
self.assertEqual(sel_expo.value.size, 1)
358+
self.assertEqual(sel_expo.value[0], 1)
359+
self.assertEqual(sel_expo.id.size, 1)
360+
self.assertEqual(sel_expo.id[0], 1)
361+
self.assertEqual(sel_expo.impact_id.size, 1)
362+
self.assertEqual(sel_expo.impact_id[0], 1)
363+
self.assertEqual(sel_expo.deductible.size, 1)
364+
self.assertEqual(sel_expo.deductible[0], 1)
365+
self.assertEqual(sel_expo.category_id.size, 1)
366+
self.assertEqual(sel_expo.category_id[0], 1)
367+
self.assertEqual(sel_expo.region_id.size, 1)
368+
self.assertEqual(sel_expo.region_id[0], 1)
369+
self.assertEqual(sel_expo.cover.size, 0)
370+
self.assertEqual(sel_expo.assigned['TC'].size, 1)
371+
self.assertEqual(sel_expo.assigned['TC'][0], 1)
372+
self.assertEqual(sel_expo.coord.shape[0], 1)
373+
self.assertEqual(sel_expo.coord[0, 0], 1)
374+
self.assertEqual(sel_expo.coord[0, 1], 2)
375+
376+
def test_sel_wrong_pass(self):
377+
"""Select non-existent region"""
378+
expo = good_exposures()
379+
sel_expo = expo.select_region(5)
380+
self.assertEqual(sel_expo, None)
381+
350382
# Execute Tests
351383
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestChecker)
352384
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestAssign))
353385
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestAppend))
354386
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestReadParallel))
355387
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestConstructor))
356388
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestRemove))
389+
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestSelectRegion))
357390
unittest.TextTestRunner(verbosity=2).run(TESTS)

climada/entity/exposures/test/test_black_marble.py

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ def test_che_kos_pass(self):
3535
self.assertEqual(len(iso_name), len(country_name))
3636
self.assertTrue('CHE'in iso_name)
3737
self.assertTrue('KOS'in iso_name)
38-
self.assertEqual(iso_name['CHE'][0], 41)
38+
self.assertEqual(iso_name['CHE'][0], 756)
3939
self.assertEqual(iso_name['CHE'][1], 'Switzerland')
4040
self.assertIsInstance(iso_name['CHE'][2], shapely.geometry.multipolygon.MultiPolygon)
41-
self.assertEqual(iso_name['KOS'][0], 252)
41+
self.assertEqual(iso_name['KOS'][0], 0)
4242
self.assertEqual(iso_name['KOS'][1], 'Kosovo')
4343
self.assertIsInstance(iso_name['KOS'][2], shapely.geometry.multipolygon.MultiPolygon)
4444

@@ -48,7 +48,7 @@ def test_haiti_pass(self):
4848
iso_name, _ = country_iso_geom(country_name, SHP_FILE)
4949

5050
self.assertEqual(len(iso_name), len(country_name))
51-
self.assertEqual(iso_name['HTI'][0], 100)
51+
self.assertEqual(iso_name['HTI'][0], 332)
5252
self.assertEqual(iso_name['HTI'][1], 'Haiti')
5353
self.assertIsInstance(iso_name['HTI'][2], shapely.geometry.multipolygon.MultiPolygon)
5454

@@ -64,7 +64,7 @@ def test_bolivia_pass(self):
6464
iso_name, _ = country_iso_geom(country_name, SHP_FILE)
6565

6666
self.assertEqual(len(iso_name), len(country_name))
67-
self.assertEqual(iso_name['BOL'][0], 31)
67+
self.assertEqual(iso_name['BOL'][0], 68)
6868
self.assertEqual(iso_name['BOL'][1], 'Bolivia')
6969
self.assertIsInstance(iso_name['BOL'][2], shapely.geometry.multipolygon.MultiPolygon)
7070

@@ -73,6 +73,62 @@ def test_korea_pass(self):
7373
country_name = ['Korea']
7474
with self.assertRaises(ValueError):
7575
country_iso_geom(country_name, SHP_FILE)
76+
77+
def test_select_reg_pass(self):
78+
"""Select counrty """
79+
exp = BlackMarble()
80+
exp.value = np.arange(0, 20, 0.1)
81+
exp.coord = np.ones((200, 2))
82+
exp.impact_id = np.ones((200,))
83+
exp.id = np.arange(200)
84+
85+
exp.region_id = np.ones((200,))
86+
exp.region_id[:101] = exp.region_id[:101]*780
87+
exp.region_id[101:] = exp.region_id[101:]*894
88+
89+
sel_exp = exp.select_region('TTO')
90+
self.assertEqual(sel_exp.value.size, 101)
91+
sel_exp = exp.select_region('Trinidad and Tobago')
92+
self.assertEqual(sel_exp.value.size, 101)
93+
sel_exp = exp.select_region('TT')
94+
self.assertEqual(sel_exp.value.size, 101)
95+
96+
sel_exp = exp.select_region('ZMB')
97+
self.assertEqual(sel_exp.value.size, 99)
98+
sel_exp = exp.select_region('Zambia')
99+
self.assertEqual(sel_exp.value.size, 99)
100+
sel_exp = exp.select_region('ZM')
101+
self.assertEqual(sel_exp.value.size, 99)
102+
103+
def test_select_not_included_pass(self):
104+
"""Select country that is not contained."""
105+
exp = BlackMarble()
106+
exp.value = np.arange(0, 20, 0.1)
107+
exp.coord = np.ones((200, 2))
108+
exp.impact_id = np.ones((200,))
109+
exp.id = np.arange(200)
110+
111+
exp.region_id = np.ones((200,))
112+
exp.region_id[:101] = exp.region_id[:101]*780
113+
exp.region_id[101:] = exp.region_id[101:]*894
114+
115+
sel_exp = exp.select_region('UGA')
116+
self.assertEqual(sel_exp, None)
117+
118+
def test_select_wrong_fail(self):
119+
"""Select country that is not contained."""
120+
exp = BlackMarble()
121+
exp.value = np.arange(0, 20, 0.1)
122+
exp.coord = np.ones((200, 2))
123+
exp.impact_id = np.ones((200,))
124+
exp.id = np.arange(200)
125+
126+
exp.region_id = np.ones((200,))
127+
exp.region_id[:101] = exp.region_id[:101]*780
128+
exp.region_id[101:] = exp.region_id[101:]*894
129+
130+
sel_exp = exp.select_region('TTU')
131+
self.assertEqual(sel_exp, None)
76132

77133
class TestProvinces(unittest.TestCase):
78134
"""Tst black marble with admin1."""
@@ -371,8 +427,9 @@ def test_set_econ_indicators_pass(self):
371427
self.assertAlmostEqual(exp.value.sum(), gdp*(inc_grp+1), 5)
372428

373429
# Execute Tests
374-
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestEconIndices)
375-
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCountryIso))
376-
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestNightLight))
377-
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestProvinces))
430+
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestCountryIso)
431+
#TESTS = unittest.TestLoader().loadTestsFromTestCase(TestEconIndices)
432+
#TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCountryIso))
433+
#TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestNightLight))
434+
#TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestProvinces))
378435
unittest.TextTestRunner(verbosity=2).run(TESTS)

0 commit comments

Comments
 (0)