Skip to content

Commit

Permalink
split hazard module (CLIMADA-project#871)
Browse files Browse the repository at this point in the history
* Add geodataframe to centroids: first commit

* Remove raster methods, obsolete methods, and fix some.

* Remove test for removed methods

* Fix from geodataframe method

* Remove set meta to lat lon

* Update all I/O methods

* Remove area pixel

* Add method to compute the area per pixel (not attribute)

* Make get_area_pixel

* Remove not often used dist_coast and elevation

* Remove unused meta

* Change Centroids call to new signature

* Remove vector - raster hazard methods

* Restore legacy excell reader

* Remove clear method

* Remove clear test

* Update read/write centroids in hazard

* Support empty on land or region id

* Check for empty region id

* Set region id correctly

* Remove scheduler

* Add docstring

* Add legacy read hdf5

* Replace incorrect dot product

* Remove meta in assign centroids

* Remove matlab test file

* Update set lat/lon

* Update test with init

* Add note on method

* Add from_meta class method

* Update centroids init

* Update read raster intensity / fraction data

* undo changelog duplications

* Update test for points outside of raster within threshold

* Docstring and cosmetics

* Add to default crs method

* Fix legacy from hdf5 for empty extra values

* Update legacy from_hdf5 to exclude 'latitude'/'longitude' from extra

* Update centroids.select to work properly with mask and indices

* Fix forecast translate bug

* Remove _set_centroids

* Remove reproject raster

* Remove test hazard raster

* Update naming for plot fraction centroids

* Replace HAZ_DEMO_MAT with HAZ_TEST_TC

* Replace mat file with hdf5 test file

* Rewrite write raster method for hazard

* Add a default for sel_cen in mask

* Correct indent typos

* Change duplicate test function name

* Replace mat file with hdf5

* Remove unused mat file import

* Add legacy code to read old centroid hdf5 files.

* Update access to centroids dist coast in gdf

* test_trop_cyclone: get test centroids from data api

* Add method to estimate meta raster from centroids

* Remove unecessary conversion to ne_geom

* Add good lat/lon values for unit test

* centroids.centr.write_hdf5: eliminate side effect

* centroids.centr.write_hdf5: elimination of side effect futile for pandas>=2.1

* centroids.test.TestCentroidsFuns.test_select_pass: adapt to downsized LAT/LON arrays

* test_vec_ras: fix TestCentroids.test_centroids_check_pass

* test_vec_ras: remove TestReader.test_write_read_points_h5 as it is redundant
to and covered by test_centr.TestCentroidsWriter.test_read_write_hdf5

* fix typo in from_exposures method

* centroids.centr.from_csv: write class method

* centroids.centr.from_csv: correct order of xy

* centroids.centr.from_excel: update method for gdf

* centroids.centr. update csv and excel methods

* Update tests in test_vec_ras

* Update more tests and remove some with meta

* centroids.centr.from_excel fix columnn indexing

* Add possibility to NOT recenter crs for countires.

This lead to a bug in the assign region id method because longitude of the countries geometry was shifted, but not the entry lat/lon points.

* Add to_csre convenience method

* Prepare the stage for setting other than admin0 region id

* Remove uneeded import

* cenctroids.centr: fix the obvious

* gencenctroids.centr: change crs of the geodataframe instead of the geometry

* white space cosmetics

* PEP8

* Add solid region id and on land tests

* Auto stash before merge of "feature/centroids_as_gdf" and "origin/feature/centroids_as_gdf"

* Update distance and area tests

* Update set crs in load vector shape file

* centr.Centroids.from_excel: deal with region_id column

* Fix write hazard raster with centroids.get_meta

* Area pixel now correctly uses CEA

* Add actual NE CRS test.

* Make on_land and region_id always part of centroids

* Update docstring

* Update import path

* Add overwrite argument to set region id and on land

* Remove from base grid method

* Fix some linting

* Add some docstrings

* Avoid costly computation in from geodataframe

* Add comment

* Fix typo

* Fix set crs in from geodataframe

* Make properties return arrays and not series

* Remove not needed matlab vars

* Revert return numpy arrays.

* update from excel

* update from_excel and from_csv

* centroids.centr: from_excel fix column renaming and adapt to "new argument names"

* Centroids.from_excel fix: switch key,val in test_storm_europe rather than in centr!

* Return numpy arrays instead of series

* Add some basic tests

* Add tests for meta

* Add tentative changelog.

* Make keywords argument only : name lat/lon : remove set_*

* Auto stash before revert of "Make keywords argument only : name lat/lon : remove set_*"

* remove file

* Fix exposures method test

* Fix setting region id and on land at init.

* Fix typo

* Fix all close values for big numbers

* Improve cosmetics.

* update docstrings, doc cosmetics

* update from_csv method, add test

* remove DEF_VAR_CSV

* Update from_excel method and test

* remove unnecessary import

* fix storm_europe tests

* improve csv and excel tests

* Apply suggestions from code review

Co-authored-by: Lukas Riedel <[email protected]>

* add excel, csv write methods and tests

* Replace gdf with _gdf

* Revert "Replace gdf with _gdf"

This reverts commit c7e6ffd.

* Update to_crs methods

* Raise error if wrong exposures

* Make from geodataframe more restrictive.

* Use consistently to_crs method

* Improve read centroids

* Add literal to import

* Update to crs with inplace argument

* improve excel, csv method, test based on review

* Update typing

* Fix equal centroids for different gdf columns ordering

* Make consistent gdf column ordering

* Simplify from excel/csv

* Add support for legacy hazard excell

* Avoid single column loading error for df

* fix path in write_excel, write_csv

* fixing docstrings in centr.py

* Update code cosmetics

* Fix typo

* Allo for kwargs in init and update from exposures

* code linting

* Centroids: test from_meta

* Centroids: refined tests

* Centroids: fix from_exposures

* Centroids: implement get_pixel_shapes

* Centroids: code linting and docstrings

* hazard.test.test_base: remove undefined classes from main

* hazard.base: remove unused imports

* hazard.base: clean up imports

* cosmetics

* hazard.base.write_htdf5: add inline comment.

* Centroids: fix _gdf_from_legacy_hdf signature

* Centroids._gdf_from_legacy_hdf5: static, not classmethod

* hazard.centroids.centr: pylint

* centroids.centr : fix pydoc typos and pylint

* climada.hazard.base: pylint

* hazard.centroids.centr: pylint

* climada.test.test_calicbration: use hzard test file with hdf5 format

* Update climada/hazard/centroids/test/test_vec_ras.py

Co-authored-by: Emanuel Schmid <[email protected]>

* Hazard: fix write_raster, and some docstrings

* test.test_calibration: fix test file name

* test_api_client: added test for basic centroids plotting
(just because it fails in PR CLIMADA-project#787)

* centr.Centroids.plot: all changes reverted
(with the exception of the obsolete `self.set_meta_to_lat_lon()`)

* Hazard tutorial: use centroids.get_meta() instead of the attribute meta

* Hazard tutorial: use Centroids() instead of from_lat_lon()

* hazard tutorial: update Hazard from raster section

* hazard tutorial: fix dist_coast access

* climada.hazard.Hazard: remove broken vector file support

* split hazard.base module into base, base_io and base_plot

* test_base_xarray: logs format changed due to "sub-modules"

* climada.hazard.HazardIO: remove broken vector file support

* hazard.base_io: remove from_mat, move reproject_vector back to base

* rename test_base_xarray

* hazard.test: move TestReadExcel and TestReadHDF5 to test_base_io

* climada.hazard.test.base/_io: fix imports

* climada.hazard.centr: add deprecated methods section

* fix from_tracks method, remove side effect

* climada.hazard.trop_cyclone.TropCytlone.from_tracks: option for predefined dist_coast in centroids

* TropicalCyclone.from_tracks pydoc

* white space cosmetics

* CHANGELOG: list deprecated and removed methods

* Refactor and add to centroids testing

* trop_cyclone.TropCyclone.from_tracks: fix dist_coast type

* climada.hazard.Centroids: raise exception in deprecated methods that really _are_ failing now

* redo lost commits on hazard.base

* changelog: list added Centroids methods

* hazard.centr: depreecated methods: pydoc string instaed of pass

* fixing geodataframe issues

* Revert "fixing geodataframe issues"

This reverts commit d6d2859.

* Changelog and module renaming

* Changelog and module renaming

* module renaming

---------

Co-authored-by: Chahan Kropf <[email protected]>
Co-authored-by: Sarah Hülsen <[email protected]>
Co-authored-by: Igor Detring <[email protected]>
Co-authored-by: Chahan M. Kropf <[email protected]>
Co-authored-by: Lukas Riedel <[email protected]>
Co-authored-by: Thomas Vogt <[email protected]>
Co-authored-by: Thomas Vogt <[email protected]>
  • Loading branch information
8 people authored and gdeskos committed Jun 30, 2024
1 parent 5c041fe commit b2b0872
Show file tree
Hide file tree
Showing 7 changed files with 1,641 additions and 1,565 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Code freeze date: YYYY-MM-DD
CLIMADA tutorials. [#872](https://github.com/CLIMADA-project/climada_python/pull/872)
- Centroids complete overhaul. Most function should be backward compatible. Internal data is stored in a geodataframe attribute. Raster are now stored as points, and the meta attribute is removed. Several methds were deprecated or removed. [#787](https://github.com/CLIMADA-project/climada_python/pull/787)
- Improved error messages produced by `ImpactCalc.impact()` in case impact function in the exposures is not found in impf_set [#863](https://github.com/CLIMADA-project/climada_python/pull/863)
- Changed module structure: `climada.hazard.Hazard` has been split into the modules `base`, `io` and `plot` [#871](https://github.com/CLIMADA-project/climada_python/pull/871)

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion climada/entity/exposures/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ def from_raster(cls, file_name, band=1, src_crs=None, window=None,
reproject to given crs
transform : rasterio.Affine
affine transformation to apply
wdith : float
width : float
number of lons for transform
height : float
number of lats for transform
Expand Down
1,468 changes: 6 additions & 1,462 deletions climada/hazard/base.py

Large diffs are not rendered by default.

1,250 changes: 1,250 additions & 0 deletions climada/hazard/io.py

Large diffs are not rendered by default.

283 changes: 283 additions & 0 deletions climada/hazard/plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
"""
This file is part of CLIMADA.
Copyright (C) 2017 ETH Zurich, CLIMADA contributors listed in AUTHORS.
CLIMADA is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free
Software Foundation, version 3.
CLIMADA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with CLIMADA. If not, see <https://www.gnu.org/licenses/>.
---
Define Hazard Plotting Methods.
"""

import numpy as np
import matplotlib.pyplot as plt

import climada.util.plot as u_plot


# pylint: disable=no-member

class HazardPlot():
"""
Contains all plotting methods of the Hazard class
"""

def plot_rp_intensity(self, return_periods=(25, 50, 100, 250),
smooth=True, axis=None, figsize=(9, 13), adapt_fontsize=True,
**kwargs):
"""Compute and plot hazard exceedance intensity maps for different
return periods. Calls local_exceedance_inten.
Parameters
----------
return_periods: tuple(int), optional
return periods to consider
smooth: bool, optional
smooth plot to plot.RESOLUTIONxplot.RESOLUTION
axis: matplotlib.axes._subplots.AxesSubplot, optional
axis to use
figsize: tuple, optional
figure size for plt.subplots
kwargs: optional
arguments for pcolormesh matplotlib function used in event plots
Returns
-------
axis, inten_stats: matplotlib.axes._subplots.AxesSubplot, np.ndarray
intenstats is return_periods.size x num_centroids
"""
inten_stats = self.local_exceedance_inten(np.array(return_periods))
colbar_name = 'Intensity (' + self.units + ')'
title = list()
for ret in return_periods:
title.append('Return period: ' + str(ret) + ' years')
axis = u_plot.geo_im_from_array(inten_stats, self.centroids.coord,
colbar_name, title, smooth=smooth, axes=axis,
figsize=figsize, adapt_fontsize=adapt_fontsize, **kwargs)
return axis, inten_stats

def plot_intensity(self, event=None, centr=None, smooth=True, axis=None, adapt_fontsize=True,
**kwargs):
"""Plot intensity values for a selected event or centroid.
Parameters
----------
event: int or str, optional
If event > 0, plot intensities of
event with id = event. If event = 0, plot maximum intensity in
each centroid. If event < 0, plot abs(event)-largest event. If
event is string, plot events with that name.
centr: int or tuple, optional
If centr > 0, plot intensity
of all events at centroid with id = centr. If centr = 0,
plot maximum intensity of each event. If centr < 0,
plot abs(centr)-largest centroid where higher intensities
are reached. If tuple with (lat, lon) plot intensity of nearest
centroid.
smooth: bool, optional
Rescale data to RESOLUTIONxRESOLUTION pixels (see constant
in module `climada.util.plot`)
axis: matplotlib.axes._subplots.AxesSubplot, optional
axis to use
kwargs: optional
arguments for pcolormesh matplotlib function
used in event plots or for plot function used in centroids plots
Returns
-------
matplotlib.axes._subplots.AxesSubplot
Raises
------
ValueError
"""
col_label = f'Intensity ({self.units})'
crs_epsg, _ = u_plot.get_transformation(self.centroids.geometry.crs)
if event is not None:
if isinstance(event, str):
event = self.get_event_id(event)
return self._event_plot(event, self.intensity, col_label,
smooth, crs_epsg, axis, adapt_fontsize=adapt_fontsize, **kwargs)
if centr is not None:
if isinstance(centr, tuple):
_, _, centr = self.centroids.get_closest_point(centr[0], centr[1])
return self._centr_plot(centr, self.intensity, col_label, axis, **kwargs)

raise ValueError("Provide one event id or one centroid id.")

def plot_fraction(self, event=None, centr=None, smooth=True, axis=None,
**kwargs):
"""Plot fraction values for a selected event or centroid.
Parameters
----------
event: int or str, optional
If event > 0, plot fraction of event
with id = event. If event = 0, plot maximum fraction in each
centroid. If event < 0, plot abs(event)-largest event. If event
is string, plot events with that name.
centr: int or tuple, optional
If centr > 0, plot fraction
of all events at centroid with id = centr. If centr = 0,
plot maximum fraction of each event. If centr < 0,
plot abs(centr)-largest centroid where highest fractions
are reached. If tuple with (lat, lon) plot fraction of nearest
centroid.
smooth: bool, optional
Rescale data to RESOLUTIONxRESOLUTION pixels (see constant
in module `climada.util.plot`)
axis: matplotlib.axes._subplots.AxesSubplot, optional
axis to use
kwargs: optional
arguments for pcolormesh matplotlib function
used in event plots or for plot function used in centroids plots
Returns
-------
matplotlib.axes._subplots.AxesSubplot
Raises
------
ValueError
"""
col_label = 'Fraction'
if event is not None:
if isinstance(event, str):
event = self.get_event_id(event)
return self._event_plot(event, self.fraction, col_label, smooth, axis,
**kwargs)
if centr is not None:
if isinstance(centr, tuple):
_, _, centr = self.centroids.get_closest_point(centr[0], centr[1])
return self._centr_plot(centr, self.fraction, col_label, axis, **kwargs)

raise ValueError("Provide one event id or one centroid id.")

def _event_plot(self, event_id, mat_var, col_name, smooth, crs_espg, axis=None,
figsize=(9, 13), adapt_fontsize=True, **kwargs):
"""Plot an event of the input matrix.
Parameters
----------
event_id: int or np.array(int)
If event_id > 0, plot mat_var of
event with id = event_id. If event_id = 0, plot maximum
mat_var in each centroid. If event_id < 0, plot
abs(event_id)-largest event.
mat_var: sparse matrix
Sparse matrix where each row is an event
col_name: sparse matrix
Colorbar label
smooth: bool, optional
smooth plot to plot.RESOLUTIONxplot.RESOLUTION
axis: matplotlib.axes._subplots.AxesSubplot, optional
axis to use
figsize: tuple, optional
figure size for plt.subplots
kwargs: optional
arguments for pcolormesh matplotlib function
Returns
-------
matplotlib.figure.Figure, matplotlib.axes._subplots.AxesSubplot
"""
if not isinstance(event_id, np.ndarray):
event_id = np.array([event_id])
array_val = list()
l_title = list()
for ev_id in event_id:
if ev_id > 0:
try:
event_pos = np.where(self.event_id == ev_id)[0][0]
except IndexError as err:
raise ValueError(f'Wrong event id: {ev_id}.') from err
im_val = mat_var[event_pos, :].toarray().transpose()
title = f'Event ID {self.event_id[event_pos]}: {self.event_name[event_pos]}'
elif ev_id < 0:
max_inten = np.asarray(np.sum(mat_var, axis=1)).reshape(-1)
event_pos = np.argpartition(max_inten, ev_id)[ev_id:]
event_pos = event_pos[np.argsort(max_inten[event_pos])][0]
im_val = mat_var[event_pos, :].toarray().transpose()
title = (f'{np.abs(ev_id)}-largest Event. ID {self.event_id[event_pos]}:'
f' {self.event_name[event_pos]}')
else:
im_val = np.max(mat_var, axis=0).toarray().transpose()
title = f'{self.haz_type} max intensity at each point'

array_val.append(im_val)
l_title.append(title)

return u_plot.geo_im_from_array(array_val, self.centroids.coord, col_name,
l_title, smooth=smooth, axes=axis, figsize=figsize,
proj=crs_espg, adapt_fontsize=adapt_fontsize, **kwargs)

def _centr_plot(self, centr_idx, mat_var, col_name, axis=None, **kwargs):
"""Plot a centroid of the input matrix.
Parameters
----------
centr_id: int
If centr_id > 0, plot mat_var
of all events at centroid with id = centr_id. If centr_id = 0,
plot maximum mat_var of each event. If centr_id < 0,
plot abs(centr_id)-largest centroid where highest mat_var
are reached.
mat_var: sparse matrix
Sparse matrix where each column represents
a centroid
col_name: sparse matrix
Colorbar label
axis: matplotlib.axes._subplots.AxesSubplot, optional
axis to use
kwargs: optional
arguments for plot matplotlib function
Returns
-------
matplotlib.figure.Figure, matplotlib.axes._subplots.AxesSubplot
"""
coord = self.centroids.coord
if centr_idx > 0:
try:
centr_pos = centr_idx
except IndexError as err:
raise ValueError(f'Wrong centroid id: {centr_idx}.') from err
array_val = mat_var[:, centr_pos].toarray()
title = (
f'Centroid {centr_idx}:'
f' ({np.around(coord[centr_pos, 0], 3)}, {np.around(coord[centr_pos, 1],3)})'
)
elif centr_idx < 0:
max_inten = np.asarray(np.sum(mat_var, axis=0)).reshape(-1)
centr_pos = np.argpartition(max_inten, centr_idx)[centr_idx:]
centr_pos = centr_pos[np.argsort(max_inten[centr_pos])][0]
array_val = mat_var[:, centr_pos].toarray()

title = (
f'{np.abs(centr_idx)}-largest Centroid. {centr_pos}:'
f' ({np.around(coord[centr_pos, 0], 3)}, {np.around(coord[centr_pos, 1], 3)})'
)
else:
array_val = np.max(mat_var, axis=1).toarray()
title = f'{self.haz_type} max intensity at each event'

if not axis:
_, axis = plt.subplots(1)
if 'color' not in kwargs:
kwargs['color'] = 'b'
axis.set_title(title)
axis.set_xlabel('Event number')
axis.set_ylabel(str(col_name))
axis.plot(range(len(array_val)), array_val, **kwargs)
axis.set_xlim([0, len(array_val)])
return axis
Loading

0 comments on commit b2b0872

Please sign in to comment.