Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor polygon to models module #135

Merged
merged 1 commit into from
Aug 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion csep/core/catalogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion csep/core/forecasts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
72 changes: 2 additions & 70 deletions csep/core/regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
81 changes: 80 additions & 1 deletion csep/models.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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.
Expand Down Expand Up @@ -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})
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]))
4 changes: 3 additions & 1 deletion tests/test_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__))
Expand Down
3 changes: 2 additions & 1 deletion tests/test_spatial.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down