Skip to content

Commit 5a1485a

Browse files
authored
Merge pull request #138 from wsavran/whs_load_region_from_json
add serialization to regions for json formatted catalog
2 parents dc48a52 + e03514f commit 5a1485a

File tree

4 files changed

+98
-23
lines changed

4 files changed

+98
-23
lines changed

csep/core/catalogs.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
millis_to_days, parse_string_format, days_to_millis, strptime_to_utc_epoch, utc_now_datetime, create_utc_datetime
1515
from csep.utils.stats import min_or_none, max_or_none
1616
from csep.utils.calc import discretize
17+
from csep.core.regions import CartesianGrid2D
1718
from csep.utils.comcat import SummaryEvent
1819
from csep.core.exceptions import CSEPSchedulerException, CSEPCatalogException, CSEPIOException
1920
from csep.utils.calc import bin1d_vec
@@ -23,6 +24,7 @@
2324
from csep.utils.file import get_file_extension
2425
from csep.utils.plots import plot_catalog
2526

27+
2628
class AbstractBaseCatalog(LoggingMixin):
2729
"""
2830
Abstract catalog base class for PyCSEP catalogs. This class should not and cannot be used on its own. This just
@@ -147,13 +149,17 @@ def from_dict(cls, adict, **kwargs):
147149
This needs to handle reading in region information at some point.
148150
"""
149151

152+
region_loader = {
153+
'CartesianGrid2D': CartesianGrid2D
154+
}
155+
150156
# could these be class values? can be changed later.
151-
exclude = ['_catalog']
157+
exclude = ['_catalog', 'region']
152158
time_members = ['date_accessed', 'start_time', 'end_time']
153159
catalog = adict.get('catalog', None)
154-
155160
out = cls(data=catalog, **kwargs)
156161

162+
# here we are looping over the items in the class and finding the associated value in the dict
157163
for k, v in out.__dict__.items():
158164
if k not in exclude:
159165
if k not in time_members:
@@ -163,6 +169,16 @@ def from_dict(cls, adict, **kwargs):
163169
pass
164170
else:
165171
setattr(out, k, _none_or_datetime(adict[k]))
172+
else:
173+
if k == 'region':
174+
# tries to read class id from catalog, should fail silently if catalog doesn't exist
175+
try:
176+
class_id = adict[k].get('class_id', None)
177+
if class_id is None:
178+
class_id = 'CartesianGrid2D'
179+
setattr(out, k, region_loader[class_id].from_dict(adict[k]))
180+
except AttributeError:
181+
pass
166182
return out
167183

168184
@classmethod

csep/core/forecasts.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -686,10 +686,10 @@ def get_expected_rates(self, verbose=False):
686686
magnitudes=self.magnitudes, name=self.name)
687687
return self.expected_rates
688688

689-
def plot(self, plot_args = None, **kwargs):
689+
def plot(self, plot_args = None, verbose=True, **kwargs):
690690
plot_args = plot_args or {}
691691
if self.expected_rates is None:
692-
self.get_expected_rates()
692+
self.get_expected_rates(verbose=verbose)
693693
args_dict = {'title': self.name,
694694
'grid_labels': True,
695695
'grid': True,

csep/core/regions.py

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,21 @@
1414
from csep.models import Polygon
1515

1616

17-
def california_relm_collection_region(dh_scale=1, magnitudes=None, name="relm-california-collection"):
17+
def california_relm_collection_region(dh_scale=1, magnitudes=None, name="relm-california-collection", use_midpoint=True):
1818
""" Return collection region for California RELM testing region
1919
20-
Args:
21-
dh_scale (int): factor of two multiple to change the grid size
22-
mangitudes (array-like): array representing the lower bin edges of the magnitude bins
23-
name (str): human readable identifer
20+
Args:
21+
dh_scale (int): factor of two multiple to change the grid size
22+
mangitudes (array-like): array representing the lower bin edges of the magnitude bins
23+
name (str): human readable identifer
24+
use_midpoints (bool): if true, treat values in file as midpoints. default = true.
25+
26+
Returns:
27+
:class:`csep.core.spatial.CartesianGrid2D`
28+
29+
Raises:
30+
ValueError: dh_scale must be a factor of two
31+
2432
"""
2533
if dh_scale % 2 != 0 and dh_scale != 1:
2634
raise ValueError("dh_scale must be a factor of two or dh_scale must equal unity.")
@@ -29,8 +37,11 @@ def california_relm_collection_region(dh_scale=1, magnitudes=None, name="relm-ca
2937
dh = 0.1
3038
root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
3139
filepath = os.path.join(root_dir, 'artifacts', 'Regions', 'RELMCollectionArea.dat')
32-
midpoints = numpy.loadtxt(filepath)
33-
origins = midpoints - dh / 2
40+
points = numpy.loadtxt(filepath)
41+
if use_midpoint:
42+
origins = numpy.array(points) - dh / 2
43+
else:
44+
origins = numpy.array(points)
3445

3546
if dh_scale > 1:
3647
origins = increase_grid_resolution(origins, dh, dh_scale)
@@ -45,7 +56,7 @@ def california_relm_collection_region(dh_scale=1, magnitudes=None, name="relm-ca
4556

4657
return relm_region
4758

48-
def california_relm_region(dh_scale=1, magnitudes=None, name="relm-california"):
59+
def california_relm_region(dh_scale=1, magnitudes=None, name="relm-california", use_midpoint=True):
4960
"""
5061
Returns class representing California testing region.
5162
@@ -71,8 +82,11 @@ def california_relm_region(dh_scale=1, magnitudes=None, name="relm-california"):
7182
root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
7283
filepath = os.path.join(root_dir, 'artifacts', 'Regions', 'csep-forecast-template-M5.xml')
7384
csep_template = os.path.expanduser(filepath)
74-
midpoints, dh = parse_csep_template(csep_template)
75-
origins = numpy.array(midpoints) - dh / 2
85+
points, dh = parse_csep_template(csep_template)
86+
if use_midpoint:
87+
origins = numpy.array(points) - dh / 2
88+
else:
89+
origins = numpy.array(points)
7690

7791
if dh_scale > 1:
7892
origins = increase_grid_resolution(origins, dh, dh_scale)
@@ -87,7 +101,7 @@ def california_relm_region(dh_scale=1, magnitudes=None, name="relm-california"):
87101

88102
return relm_region
89103

90-
def italy_csep_region(dh_scale=1, magnitudes=None, name="csep-italy"):
104+
def italy_csep_region(dh_scale=1, magnitudes=None, name="csep-italy", use_midpoint=True):
91105
"""
92106
Returns class representing Italian testing region.
93107
@@ -98,6 +112,8 @@ def italy_csep_region(dh_scale=1, magnitudes=None, name="csep-italy"):
98112
dh_scale: can resample this grid by factors of 2
99113
magnitudes (array-like): bin edges for magnitudes. if provided, will be bound to the output region class.
100114
this argument provides a short-cut for creating space-magnitude regions.
115+
name (str): human readable identify given to the region
116+
use_midpoint (bool): if true, treat values in file as midpoints. default = true.
101117
102118
Returns:
103119
:class:`csep.core.spatial.CartesianGrid2D`
@@ -113,8 +129,12 @@ def italy_csep_region(dh_scale=1, magnitudes=None, name="csep-italy"):
113129
root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
114130
filepath = os.path.join(root_dir, 'artifacts', 'Regions', 'forecast.italy.M5.xml')
115131
csep_template = os.path.expanduser(filepath)
116-
midpoints, dh = parse_csep_template(csep_template)
117-
origins = numpy.array(midpoints) - dh / 2
132+
points, dh = parse_csep_template(csep_template)
133+
if use_midpoint:
134+
origins = numpy.array(points) - dh / 2
135+
else:
136+
origins = numpy.array(points)
137+
118138

119139
if dh_scale > 1:
120140
origins = increase_grid_resolution(origins, dh, dh_scale)
@@ -129,13 +149,20 @@ def italy_csep_region(dh_scale=1, magnitudes=None, name="csep-italy"):
129149

130150
return italy_region
131151

132-
def italy_csep_collection_region(dh_scale=1, magnitudes=None, name="csep-italy-collection"):
152+
def italy_csep_collection_region(dh_scale=1, magnitudes=None, name="csep-italy-collection", use_midpoint=True):
133153
""" Return collection region for Italy CSEP collection region
134154
135155
Args:
136156
dh_scale (int): factor of two multiple to change the grid size
137157
mangitudes (array-like): array representing the lower bin edges of the magnitude bins
138158
name (str): human readable identifer
159+
use_midpoint (bool): if true, treat values in file as midpoints. default = true.
160+
161+
Returns:
162+
:class:`csep.core.spatial.CartesianGrid2D`
163+
164+
Raises:
165+
ValueError: dh_scale must be a factor of two
139166
"""
140167
if dh_scale % 2 != 0 and dh_scale != 1:
141168
raise ValueError("dh_scale must be a factor of two or dh_scale must equal unity.")
@@ -144,8 +171,12 @@ def italy_csep_collection_region(dh_scale=1, magnitudes=None, name="csep-italy-c
144171
dh = 0.1
145172
root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
146173
filepath = os.path.join(root_dir, 'artifacts', 'Regions', 'italy.collection.nodes.dat')
147-
midpoints = numpy.loadtxt(filepath)
148-
origins = midpoints - dh / 2
174+
points = numpy.loadtxt(filepath)
175+
if use_midpoint:
176+
origins = numpy.array(points) - dh / 2
177+
else:
178+
origins = numpy.array(points)
179+
149180

150181
if dh_scale > 1:
151182
origins = increase_grid_resolution(origins, dh, dh_scale)
@@ -487,6 +518,9 @@ def __init__(self, polygons, dh, name='cartesian2d', mask=None):
487518
self.xs = xs
488519
self.ys = ys
489520

521+
def __eq__(self, other):
522+
return self.to_dict() == other.to_dict()
523+
490524
@property
491525
def num_nodes(self):
492526
""" Number of polygons in region """
@@ -582,13 +616,36 @@ def to_dict(self):
582616
adict = {
583617
'name': str(self.name),
584618
'dh': float(self.dh),
585-
'polygons': [{'lat': float(poly.origin[1]), 'lon': float(poly.origin[0])} for poly in self.polygons]
619+
'polygons': [{'lat': float(poly.origin[1]), 'lon': float(poly.origin[0])} for poly in self.polygons],
620+
'class_id': self.__class__.__name__
586621
}
587622
return adict
588623

589624
@classmethod
590625
def from_dict(cls, adict):
591-
raise NotImplementedError("Todo!")
626+
""" Creates a region object from a dictionary """
627+
origins = adict.get('polygons', None)
628+
dh = adict.get('dh', None)
629+
magnitudes = adict.get('magnitudes', None)
630+
name = adict.get('name', 'CartesianGrid2D')
631+
632+
if origins is None:
633+
raise AttributeError("cannot create region object without origins")
634+
if dh is None:
635+
raise AttributeError("cannot create region without dh")
636+
if origins is not None:
637+
try:
638+
origins = numpy.array([[adict['lon'], adict['lat']] for adict in origins])
639+
except:
640+
raise TypeError('origins must be numpy array like.')
641+
if magnitudes is not None:
642+
try:
643+
magnitudes = numpy.array(magnitudes)
644+
except:
645+
raise TypeError('magnitudes must be numpy array like.')
646+
647+
out = cls.from_origins(origins, dh=dh, magnitudes=magnitudes, name=name)
648+
return out
592649

593650
@classmethod
594651
def from_origins(cls, origins, dh=None, magnitudes=None, name=None):

tests/test_spatial.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ def test_get_index_of_good(self):
121121
test_idx = self.cart_grid.get_index_of([test[0]], [test[1]])
122122
numpy.testing.assert_allclose(test_idx, 0)
123123

124+
def test_to_from_dict(self):
125+
self.assertEqual(self.cart_grid, CartesianGrid2D.from_dict(self.cart_grid.to_dict()))
126+
124127
class TestCatalogBinning(unittest.TestCase):
125128

126129
def setUp(self):
@@ -227,7 +230,6 @@ def test_bin_spatial_magnitudes(self):
227230
self.assertEqual(test_result[0, 1], 1)
228231
self.assertEqual(test_result[9, 0], 1)
229232

230-
231233
def test_global_region_binning(self):
232234

233235
gr = global_region()

0 commit comments

Comments
 (0)