From 600e470c265c84d88f4b2aa593d1d7cb157cb1c9 Mon Sep 17 00:00:00 2001 From: William Savran Date: Mon, 26 Jul 2021 14:06:09 -0700 Subject: [PATCH] refactor polygon to models module --- csep/core/catalogs.py | 1 - csep/core/forecasts.py | 3 +- csep/core/regions.py | 72 ++----------------------------------- csep/models.py | 81 +++++++++++++++++++++++++++++++++++++++++- tests/test_catalog.py | 4 ++- tests/test_spatial.py | 3 +- 6 files changed, 89 insertions(+), 75 deletions(-) diff --git a/csep/core/catalogs.py b/csep/core/catalogs.py index f21d82f0..7617f04b 100644 --- a/csep/core/catalogs.py +++ b/csep/core/catalogs.py @@ -10,7 +10,6 @@ import pandas # CSEP Imports -from csep.core import regions from csep.utils.time_utils import epoch_time_to_utc_datetime, datetime_to_utc_epoch, strptime_to_utc_datetime, \ millis_to_days, parse_string_format, days_to_millis, strptime_to_utc_epoch, utc_now_datetime, create_utc_datetime from csep.utils.stats import min_or_none, max_or_none diff --git a/csep/core/forecasts.py b/csep/core/forecasts.py index 263e0997..abc41f88 100644 --- a/csep/core/forecasts.py +++ b/csep/core/forecasts.py @@ -7,7 +7,8 @@ import numpy from csep.utils.log import LoggingMixin -from csep.core.regions import CartesianGrid2D, Polygon, create_space_magnitude_region +from csep.core.regions import CartesianGrid2D, create_space_magnitude_region +from csep.models import Polygon from csep.utils.calc import bin1d_vec from csep.utils.time_utils import decimal_year, datetime_to_utc_epoch from csep.core.catalogs import AbstractBaseCatalog diff --git a/csep/core/regions.py b/csep/core/regions.py index 934f148c..0510f9d8 100644 --- a/csep/core/regions.py +++ b/csep/core/regions.py @@ -5,14 +5,14 @@ from xml.etree import ElementTree as ET # Third-party imports -import matplotlib.path import numpy import numpy as np -import pyproj # PyCSEP imports from csep.utils.calc import bin1d_vec, cleaner_range, first_nonnan, last_nonnan from csep.utils.scaling_relationships import WellsAndCoppersmith +from csep.models import Polygon + def california_relm_collection_region(dh_scale=1, magnitudes=None, name="relm-california-collection"): """ Return collection region for California RELM testing region @@ -463,74 +463,6 @@ def _bin_catalog_probability(lons, lats, n_poly, mask, idx_map, binx, biny): event_counts[hash_idx] = 1 return event_counts -class Polygon: - """ - Represents polygons defined through a collection of vertices. - - This polygon is assumed to be 2d, but could contain an arbitrary number of vertices. The path is treated as not being - closed. - """ - def __init__(self, points): - # instance members - self.points = points - self.origin = self.points[0] - - # https://matplotlib.org/3.1.1/api/path_api.html - self.path = matplotlib.path.Path(self.points) - - def __str__(self): - return str(self.origin) - - def contains(self, points): - """ Returns a bool array which is True if the path contains the corresponding point. - - Args: - points: 2d numpy array - - """ - nd_points = np.array(points) - if nd_points.ndim == 1: - nd_points = nd_points.reshape(1,-1) - return self.path.contains_points(nd_points) - - def centroid(self): - """ return the centroid of the polygon.""" - c0, c1 = 0, 0 - k = len(self.points) - for p in self.points: - c0 = c0 + p[0] - c1 = c1 + p[1] - return c0 / k, c1 / k - - def get_xcoords(self): - return np.array(self.points)[:,0] - - def get_ycoords(self): - return np.array(self.points)[:,1] - - @classmethod - def from_great_circle_radius(cls, centroid, radius, num_points=10): - """ - Generates a polygon object from a given radius and centroid location. - - Args: - centroid: (lon, lat) - radius: should be in (meters) - num_points: more points is higher resolution polygon - - Returns: - polygon - """ - geod = pyproj.Geod(ellps='WGS84') - azim = np.linspace(0, 360, num_points) - # create vectors with same length as azim for computations - center_lons = np.ones(num_points) * centroid[0] - center_lats = np.ones(num_points) * centroid[1] - radius = np.ones(num_points) * radius - # get new lons and lats - endlon, endlat, backaz = geod.fwd(center_lons, center_lats, azim, radius) - # class method - return cls(np.column_stack([endlon, endlat])) class CartesianGrid2D: """Represents a 2D cartesian gridded region. diff --git a/csep/models.py b/csep/models.py index 0cecfc6e..5224a83d 100644 --- a/csep/models.py +++ b/csep/models.py @@ -1,6 +1,10 @@ +import matplotlib.path import numpy # CSEP Imports +import numpy as np +import pyproj + from csep.utils.time_utils import datetime_to_utc_epoch, epoch_time_to_utc_datetime from csep.utils import plots @@ -142,6 +146,7 @@ def plot(self, show=False, plot_args=None): ax = plots.plot_number_test(self, show=show, plot_args=plot_args) return ax + class CatalogPseudolikelihoodTestResult(EvaluationResult): def __init__(self, **kwargs): @@ -158,6 +163,7 @@ def plot(self, show=False, plot_args=None): ax = plots.plot_likelihood_test(self, show=show, plot_args=plot_args) return ax + class CatalogMagnitudeTestResult(EvaluationResult): def __init__(self, **kwargs): @@ -172,6 +178,7 @@ def plot(self, show=False, plot_args=None): ax = plots.plot_magnitude_test(self, show=show, plot_args=plot_args) return ax + class CatalogSpatialTestResult(EvaluationResult): def __init__(self, **kwargs): @@ -190,6 +197,7 @@ def plot(self, show=False, plot_args=None): ax = plots.plot_spatial_test(self, show=show, plot_args=plot_args) return ax + class CalibrationTestResult(EvaluationResult): def __init__(self, **kwargs): @@ -206,6 +214,7 @@ def plot(self, show=False, axes=None, plot_args=None): ax = plots.plot_calibration_test(self, show=show, axes=axes, plot_args=plot_args) return ax + class EvaluationConfiguration: """ Store information about the evaluation which will be used to store metadata about the evaluation. @@ -282,4 +291,74 @@ def update_version(self, name, version, fnames): e['fnames'] = fnames found = True if not found: - self.evaluations.append({'name': name, 'version': version, 'fnames': fnames}) \ No newline at end of file + self.evaluations.append({'name': name, 'version': version, 'fnames': fnames}) + + +class Polygon: + """ + Represents polygons defined through a collection of vertices. + + This polygon is assumed to be 2d, but could contain an arbitrary number of vertices. The path is treated as not being + closed. + """ + def __init__(self, points): + # instance members + self.points = points + self.origin = self.points[0] + + # https://matplotlib.org/3.1.1/api/path_api.html + self.path = matplotlib.path.Path(self.points) + + def __str__(self): + return str(self.origin) + + def contains(self, points): + """ Returns a bool array which is True if the path contains the corresponding point. + + Args: + points: 2d numpy array + + """ + nd_points = np.array(points) + if nd_points.ndim == 1: + nd_points = nd_points.reshape(1,-1) + return self.path.contains_points(nd_points) + + def centroid(self): + """ return the centroid of the polygon.""" + c0, c1 = 0, 0 + k = len(self.points) + for p in self.points: + c0 = c0 + p[0] + c1 = c1 + p[1] + return c0 / k, c1 / k + + def get_xcoords(self): + return np.array(self.points)[:,0] + + def get_ycoords(self): + return np.array(self.points)[:,1] + + @classmethod + def from_great_circle_radius(cls, centroid, radius, num_points=10): + """ + Generates a polygon object from a given radius and centroid location. + + Args: + centroid: (lon, lat) + radius: should be in (meters) + num_points: more points is higher resolution polygon + + Returns: + polygon + """ + geod = pyproj.Geod(ellps='WGS84') + azim = np.linspace(0, 360, num_points) + # create vectors with same length as azim for computations + center_lons = np.ones(num_points) * centroid[0] + center_lats = np.ones(num_points) * centroid[1] + radius = np.ones(num_points) * radius + # get new lons and lats + endlon, endlat, backaz = geod.fwd(center_lons, center_lats, azim, radius) + # class method + return cls(np.column_stack([endlon, endlat])) \ No newline at end of file diff --git a/tests/test_catalog.py b/tests/test_catalog.py index 969c52c8..d908dc64 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -9,7 +9,9 @@ from csep.core import regions, forecasts from csep.utils.time_utils import strptime_to_utc_epoch, strptime_to_utc_datetime from csep.core.catalogs import CSEPCatalog, AbstractBaseCatalog -from csep.core.regions import CartesianGrid2D, Polygon, compute_vertices +from csep.core.regions import CartesianGrid2D, compute_vertices +from csep.models import Polygon + def comcat_path(): root_dir = os.path.dirname(os.path.abspath(__file__)) diff --git a/tests/test_spatial.py b/tests/test_spatial.py index aea862f6..229b8eac 100644 --- a/tests/test_spatial.py +++ b/tests/test_spatial.py @@ -5,7 +5,8 @@ import numpy from csep.core.regions import CartesianGrid2D, compute_vertex, compute_vertices, _bin_catalog_spatio_magnitude_counts, \ - _bin_catalog_spatial_counts, _bin_catalog_probability, Polygon, global_region + _bin_catalog_spatial_counts, _bin_catalog_probability, global_region +from csep.models import Polygon class TestPolygon(unittest.TestCase):