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

ARISTOTLE: Plot shakemaps, ruptures and assets on top of basemaps obtained from external tiles services #10160

Draft
wants to merge 48 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
de205da
Use contextily and pyproj to render openstreetmap as basemap in the p…
ptormene Nov 8, 2024
4c3b9ec
Merge branch 'plotshakemap' into geopandas
ptormene Nov 14, 2024
ff1abba
Refresh from import-templates branch and replace a deprecated method …
ptormene Nov 18, 2024
c868ff0
Replace deprecated method to project to webmercator also in plot_avg_gmf
ptormene Nov 18, 2024
0b5651c
Use CartoDB.Positron and add attribution
ptormene Nov 20, 2024
eefd9ae
Implement add_rupture_webmercator and add_surface_webmercator
ptormene Nov 20, 2024
1904f4c
Add the possibility to plot the rupture with a webmercator basemap
ptormene Nov 20, 2024
d991750
Plot assets and rupture in webmercator with Positron basemap
ptormene Nov 21, 2024
5b2ea47
Extract add_basemap function adding tiles and attribution
ptormene Nov 22, 2024
0a4d4c8
Small reduction of figure size when creating shakemap plots
ptormene Nov 22, 2024
51900b2
Add contextily to linux requirements (FIXME: add url to our wheelhous…
ptormene Nov 22, 2024
9142724
Comment out some variables that might probably be discarded
ptormene Nov 22, 2024
d034d6c
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Nov 25, 2024
b739b5f
Merge remote-tracking branch 'origin/import-templates' into webmercator
ptormene Nov 26, 2024
864e2b1
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Nov 28, 2024
1a1fb64
Merge branch 'import-templates' into webmercator
ptormene Nov 28, 2024
82e8220
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Dec 2, 2024
e97d11b
Do 'import contextily' instead of 'import contextily as ctx'
ptormene Dec 2, 2024
7735ba3
Merge branch 'import-templates' into webmercator
ptormene Dec 2, 2024
1f7a6e7
Merge branch 'import-templates' into webmercator
ptormene Dec 3, 2024
bffdc38
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Dec 3, 2024
2e992a6
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Dec 4, 2024
b9c88c9
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Dec 6, 2024
f2503cb
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Dec 6, 2024
b3b2737
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Dec 10, 2024
7ad93ce
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Dec 12, 2024
25055c1
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Dec 13, 2024
f779292
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Dec 19, 2024
9127dc4
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 7, 2025
11debcb
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 9, 2025
9978a26
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 13, 2025
8e448e5
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 15, 2025
40622a2
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 15, 2025
01a07e2
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 20, 2025
96ec1bd
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 22, 2025
16dac2c
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 22, 2025
2121fb2
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 23, 2025
038230c
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 27, 2025
cdc68bb
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 28, 2025
d51f255
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Jan 31, 2025
ccdf683
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Feb 5, 2025
d8916e5
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Feb 5, 2025
f6ebe53
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Feb 6, 2025
b334400
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Feb 7, 2025
4d9f4d3
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Feb 10, 2025
d542b2d
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Feb 10, 2025
9923c07
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Feb 17, 2025
b0f361b
Merge remote-tracking branch 'origin/master' into webmercator
ptormene Feb 18, 2025
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
2 changes: 1 addition & 1 deletion openquake/calculators/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
import configparser
import collections

from openquake.commands.plot_assets import main as plot_assets
from openquake.commands.plot_assets_webmercator import main as plot_assets
from openquake.baselib import general, hdf5, config
from openquake.baselib import parallel
from openquake.baselib.performance import Monitor, idx_start_stop
Expand Down
150 changes: 122 additions & 28 deletions openquake/calculators/postproc/plots.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import os
import base64
import numpy
import contextily
from pyproj import Transformer
from shapely.geometry import MultiPolygon
from openquake.commonlib import readinput, datastore
from openquake.hmtk.plotting.patch import PolygonPatch
Expand All @@ -33,6 +35,23 @@ def import_plt():
return plt


def add_basemap(ax, min_x, min_y, max_x, max_y,
source=contextily.providers.CartoDB.Positron):
# NOTE: another interesting option:
# source = contextily.providers.TopPlusOpen.Grey
img, extent = contextily.bounds2img(min_x, min_y, max_x, max_y, source=source)
ax.imshow(img, extent=extent, interpolation='bilinear', alpha=1)
ax.text(
0.01, 0.01, # Position: Bottom-left corner (normalized coordinates)
source['attribution'],
transform=ax.transAxes, # Place text relative to axes
fontsize=8, color="black", alpha=0.5,
ha="left", # Horizontal alignment
va="bottom", # Vertical alignment
)
return ax


def adjust_limits(x_min, x_max, y_min, y_max, padding=0.5):
# Make the plot display all items with some margin, looking square
x_min, x_max = x_min - padding, x_max + padding
Expand All @@ -46,6 +65,7 @@ def adjust_limits(x_min, x_max, y_min, y_max, padding=0.5):
ylim = y_center - max_range / 2, y_center + max_range / 2
return xlim, ylim


def add_borders(ax, read_df=readinput.read_countries_df, buffer=0, alpha=0.1):
plt = import_plt()
polys = read_df(buffer)['geom']
Expand Down Expand Up @@ -113,30 +133,35 @@ def plot_shakemap(shakemap_array, imt, backend=None, figsize=(10, 10),
matplotlib.use(backend)
_fig, ax = plt.subplots(figsize=figsize)
ax.set_aspect('equal')
ax.grid(True)
# ax.grid(True)
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
title = 'Avg GMF for %s' % imt
ax.set_title(title)
gmf = shakemap_array['val'][imt]
markersize = 5
coll = ax.scatter(shakemap_array['lon'], shakemap_array['lat'], c=gmf,
cmap='jet', s=markersize)
plt.colorbar(coll)
ax = add_borders(ax, alpha=0.2)
min_x = shakemap_array['lon'].min()
max_x = shakemap_array['lon'].max()
min_y = shakemap_array['lat'].min()
max_y = shakemap_array['lat'].max()
markersize = 0.005
transformer = Transformer.from_crs('EPSG:4326', 'EPSG:3857', always_xy=True)
x_webmercator, y_webmercator = transformer.transform(
shakemap_array['lon'], shakemap_array['lat'])
min_x, min_y, max_x, max_y = (min(x_webmercator),
min(y_webmercator),
max(x_webmercator),
max(y_webmercator))
if rupture is not None:
ax, rup_min_x, rup_min_y, rup_max_x, rup_max_y = add_rupture(
ax, rupture, hypo_alpha=0.8, hypo_markersize=8, surf_alpha=0.9,
ax, rup_min_x, rup_min_y, rup_max_x, rup_max_y = add_rupture_webmercator(
ax, rupture, hypo_alpha=0.8, hypo_markersize=8, surf_alpha=1,
surf_facecolor='none', surf_linestyle='--')
min_x = min(min_x, rup_min_x)
max_x = max(max_x, rup_max_x)
min_y = min(min_y, rup_min_y)
max_y = max(max_y, rup_max_y)
xlim, ylim = adjust_limits(min_x, max_x, min_y, max_y)
xlim, ylim = adjust_limits(min_x, max_x, min_y, max_y, padding=1E5)
min_x, max_x = xlim
min_y, max_y = ylim
add_basemap(ax, min_x, min_y, max_x, max_y)
coll = ax.scatter(x_webmercator, y_webmercator, c=gmf, cmap='jet', s=markersize,
alpha=0.4)
plt.colorbar(coll, ax=ax)
ax.set_xlim(*xlim)
ax.set_ylim(*ylim)
if with_cities:
Expand All @@ -151,33 +176,32 @@ def plot_avg_gmf(ex, imt):
plt = import_plt()
_fig, ax = plt.subplots(figsize=(10, 10))
ax.set_aspect('equal')
ax.grid(True)
# ax.grid(True)
ax.set_xlabel('Lon')
ax.set_ylabel('Lat')

title = 'Avg GMF for %s' % imt
assetcol = get_assetcol(ex.calc_id)
if assetcol is not None:
country_iso_codes = get_country_iso_codes(ex.calc_id, assetcol)
if country_iso_codes is not None:
title += ' (Countries: %s)' % country_iso_codes
ax.set_title(title)

avg_gmf = ex.get('avg_gmf?imt=%s' % imt)
gmf = avg_gmf[imt]
markersize = 5
coll = ax.scatter(avg_gmf['lons'], avg_gmf['lats'], c=gmf, cmap='jet',
s=markersize)
plt.colorbar(coll)

ax = add_borders(ax)

minx = avg_gmf['lons'].min()
maxx = avg_gmf['lons'].max()
miny = avg_gmf['lats'].min()
maxy = avg_gmf['lats'].max()

xlim, ylim = adjust_limits(minx, maxx, miny, maxy)
transformer = Transformer.from_crs('EPSG:4326', 'EPSG:3857', always_xy=True)
x_webmercator, y_webmercator = transformer.transform(
avg_gmf['lons'], avg_gmf['lats'])
min_x, min_y, max_x, max_y = (min(x_webmercator),
min(y_webmercator),
max(x_webmercator),
max(y_webmercator))
xlim, ylim = adjust_limits(min_x, max_x, min_y, max_y, padding=1E5)
min_x, max_x = xlim
min_y, max_y = ylim
add_basemap(ax, min_x, min_y, max_x, max_y)
coll = ax.scatter(x_webmercator, y_webmercator, c=gmf, cmap='jet', s=markersize)
plt.colorbar(coll, ax=ax)
ax.set_xlim(*xlim)
ax.set_ylim(*ylim)
return plt
Expand All @@ -195,6 +219,25 @@ def add_surface(ax, surface, label, alpha=0.5, facecolor=None, linestyle='-'):
return surface.get_bounding_box()


def add_surface_webmercator(
ax, surface, label, alpha=0.5, facecolor=None, linestyle='-'):
transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True)
lon, lat = surface.get_surface_boundaries()
x, y = transformer.transform(lon, lat) # Transform lat/lon to Web Mercator
fill_params = {
'alpha': alpha,
'edgecolor': 'grey',
'label': label
}
if facecolor is not None:
fill_params['facecolor'] = facecolor
ax.fill(x, y, **fill_params)
lon_min, lon_max, lat_max, lat_min = surface.get_bounding_box()
x_min, y_max = transformer.transform(lon_min, lat_max) # Top-left corner
x_max, y_min = transformer.transform(lon_max, lat_min) # Bottom-right corner
return x_min, x_max, y_min, y_max


def add_rupture(ax, rup, hypo_alpha=0.5, hypo_markersize=8, surf_alpha=0.5,
surf_facecolor=None, surf_linestyle='-'):
if hasattr(rup.surface, 'surfaces'):
Expand All @@ -220,6 +263,30 @@ def add_rupture(ax, rup, hypo_alpha=0.5, hypo_markersize=8, surf_alpha=0.5,
return ax, min_x, min_y, max_x, max_y


def add_rupture_webmercator(
ax, rup, hypo_alpha=0.5, hypo_markersize=8, surf_alpha=0.5,
surf_facecolor=None, surf_linestyle='-'):
transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True)
min_x, max_x = float('inf'), float('-inf')
min_y, max_y = float('inf'), float('-inf')
if hasattr(rup.surface, 'surfaces'):
for surf_idx, surface in enumerate(rup.surface.surfaces):
min_x_, max_x_, min_y_, max_y_ = add_surface_webmercator(
ax, surface, 'Surface %d' % surf_idx, alpha=surf_alpha,
facecolor=surf_facecolor, linestyle=surf_linestyle)
min_x, max_x = min(min_x, min_x_), max(max_x, max_x_)
min_y, max_y = min(min_y, min_y_), max(max_y, max_y_)
else:
min_x, max_x, min_y, max_y = add_surface_webmercator(
ax, rup.surface, 'Surface', alpha=surf_alpha,
facecolor=surf_facecolor, linestyle=surf_linestyle)
hypo_x, hypo_y = transformer.transform(rup.hypocenter.longitude,
rup.hypocenter.latitude)
ax.plot(hypo_x, hypo_y, marker='*', color='orange', label='Hypocenter',
alpha=hypo_alpha, linestyle='', markersize=hypo_markersize)
return ax, min_x, min_y, max_x, max_y


def plot_rupture(rup, backend=None, figsize=(10, 10),
with_cities=False, return_base64=False):
# NB: matplotlib is imported inside since it is a costly import
Expand All @@ -245,6 +312,33 @@ def plot_rupture(rup, backend=None, figsize=(10, 10),
return plt


def plot_rupture_webmercator(rup, backend=None, figsize=(10, 10), return_base64=False):
# NB: matplotlib is imported inside since it is a costly import
plt = import_plt()
if backend is not None:
# we may need to use a non-interactive backend
import matplotlib
matplotlib.use(backend)
_fig, ax = plt.subplots(figsize=figsize)
ax.set_aspect('equal')
# ax.grid(True)
ax, min_x, min_y, max_x, max_y = add_rupture_webmercator(
ax, rup, hypo_alpha=0.8, hypo_markersize=8, surf_alpha=0.3,
surf_linestyle='--')
xlim, ylim = adjust_limits(min_x, max_x, min_y, max_y, padding=1E5)
min_x, max_x = xlim
min_y, max_y = ylim
add_basemap(ax, min_x, min_y, max_x, max_y,
source=contextily.providers.TopPlusOpen.Color)
ax.set_xlim(*xlim)
ax.set_ylim(*ylim)
ax.legend()
if return_base64:
return plt_to_base64(plt)
else:
return plt


def add_surface_3d(ax, surface, label):
lon, lat, depth = surface.get_surface_boundaries_3d()
lon_grid = numpy.array([[lon[0], lon[1]], [lon[3], lon[2]]])
Expand Down
15 changes: 13 additions & 2 deletions openquake/commands/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@
from openquake.hazardlib.geo.utils import PolygonPlotter
from openquake.hazardlib.contexts import Effect, get_effect_by_mag
from openquake.hazardlib.calc.filters import getdefault, IntegrationDistance
from openquake.calculators.getters import get_ebrupture
from openquake.calculators.getters import get_ebrupture, get_rupture_from_dstore
from openquake.calculators.extract import (
Extractor, WebExtractor, clusterize)
from openquake.calculators.postproc.plots import (
plot_avg_gmf, import_plt, add_borders, plot_rupture, plot_rupture_3d)
plot_avg_gmf, import_plt, add_borders, plot_rupture, plot_rupture_webmercator,
plot_rupture_3d)
from openquake.calculators.postproc.aelo_plots import (
plot_mean_hcurves_rtgm, plot_disagg_by_src, plot_governing_mce)

Expand Down Expand Up @@ -1064,6 +1065,16 @@ def make_figure_rupture(extractors, what):
return plot_rupture(ebr.rupture)


def make_figure_rupture_webmercator(extractors, what):
"""
$ oq plot "rupture_webmercator?"
"""
[ex] = extractors
dstore = ex.dstore
rup = get_rupture_from_dstore(dstore, rup_id=0)
return plot_rupture_webmercator(rup)


def make_figure_rupture_3d(extractors, what):
"""
$ oq plot "rupture_3d?"
Expand Down
Loading
Loading