'}
rupdic = get_rupture_dict(arist.rupture_dict, arist.ignore_shakemap)
+ if 'shakemap_array' in rupdic:
+ del rupdic['shakemap_array']
if arist.station_data_file is None:
# NOTE: giving precedence to the station_data_file uploaded via form
try:
diff --git a/openquake/hazardlib/shakemap/parsers.py b/openquake/hazardlib/shakemap/parsers.py
index 6cf145dcceb7..c685ff3dbe63 100644
--- a/openquake/hazardlib/shakemap/parsers.py
+++ b/openquake/hazardlib/shakemap/parsers.py
@@ -32,6 +32,7 @@
import json
import zipfile
import pytz
+import base64
import pandas as pd
from datetime import datetime
from shapely.geometry import Polygon
@@ -533,10 +534,72 @@ def load_rupdic_from_finite_fault(usgs_id, mag, products):
rupdic = {'lon': lon, 'lat': lat, 'dep': float(p['depth']),
'mag': mag, 'rake': 0.,
'local_timestamp': str(local_time), 'time_event': time_event,
- 'is_point_rup': True, 'usgs_id': usgs_id, 'rupture_file': None}
+ 'is_point_rup': True,
+ 'pga_map_png': None, 'mmi_map_png': None,
+ 'usgs_id': usgs_id, 'rupture_file': None}
return rupdic
+def get_shakemap_version(usgs_id):
+ # USGS event page to get ShakeMap details
+ product_url = US_GOV + f"/earthquakes/feed/v1.0/detail/{usgs_id}.geojson"
+ # Get the JSON data for the earthquake event
+ try:
+ with urlopen(product_url) as response:
+ event_data = json.loads(response.read().decode())
+ except Exception as e:
+ print(f"Error: Unable to fetch data for event {usgs_id} - {e}")
+ return None
+ if ("properties" in event_data and "products" in event_data["properties"] and
+ "shakemap" in event_data["properties"]["products"]):
+ shakemap_data = event_data["properties"]["products"]["shakemap"][0]
+ # e.g.: 'https://earthquake.usgs.gov/product/shakemap/'
+ # 'us7000n7n8/us/1726699735514/download/intensity.jpg'
+ version_id = shakemap_data["contents"]["download/intensity.jpg"]["url"].split(
+ '/')[-3]
+ return version_id
+ else:
+ print(f"No ShakeMap found for event {usgs_id}")
+ return None
+
+
+def download_jpg(usgs_id, what):
+ """
+ It can be used to download a jpg file from the USGS service, returning it in a
+ base64 format that can be easily passed to a Django template
+ """
+ version_id = get_shakemap_version(usgs_id)
+ if version_id:
+ intensity_url = (f'{US_GOV}/product/shakemap/{usgs_id}/us/'
+ f'{version_id}/download/{what}.jpg')
+ try:
+ with urlopen(intensity_url) as img_response:
+ img_data = img_response.read()
+ img_base64 = base64.b64encode(img_data).decode('utf-8')
+ return img_base64
+ except Exception as e:
+ print(f"Error: Unable to download the {what} image - {e}")
+ return None
+ else:
+ print("Error: Could not retrieve the ShakeMap version ID.")
+ return None
+
+
+def download_grid(shakemap_contents):
+ if 'download/grid.xml' in shakemap_contents:
+ url = shakemap_contents.get('download/grid.xml')['url']
+ logging.info('Downloading grid.xml')
+ grid_fname = gettemp(urlopen(url).read(), suffix='.xml')
+ return grid_fname
+
+
+def download_rupture_data(shakemap_contents):
+ url = shakemap_contents.get('download/rupture.json')['url']
+ logging.info('Downloading rupture.json')
+ rup_data = json.loads(urlopen(url).read())
+ return rup_data
+
+
def download_rupture_dict(usgs_id, ignore_shakemap=False):
"""
Download a rupture from the USGS site given a ShakeMap ID.
@@ -561,6 +624,7 @@ def download_rupture_dict(usgs_id, ignore_shakemap=False):
try:
products['finite-fault']
except KeyError:
+ # NOTE: we might also try reading information from phase-data or origin
raise MissingLink(
'There is no shakemap nor finite-fault info for %s' % usgs_id)
return load_rupdic_from_finite_fault(usgs_id, mag, products)
@@ -568,9 +632,11 @@ def download_rupture_dict(usgs_id, ignore_shakemap=False):
contents = shakemap['contents']
if 'download/rupture.json' not in contents:
return load_rupdic_from_finite_fault(usgs_id, mag, products)
- url = contents.get('download/rupture.json')['url']
- logging.info('Downloading rupture.json')
- rup_data = json.loads(urlopen(url).read())
+ shakemap_array = None
+ grid_fname = download_grid(contents)
+ if grid_fname is not None:
+ shakemap_array = get_shakemap_array(grid_fname)
+ rup_data = download_rupture_data(contents)
feats = rup_data['features']
is_point_rup = len(feats) == 1 and feats[0]['geometry']['type'] == 'Point'
md = rup_data['metadata']
@@ -584,6 +650,7 @@ def download_rupture_dict(usgs_id, ignore_shakemap=False):
'mag': md['mag'], 'rake': md['rake'],
'local_timestamp': str(local_time), 'time_event': time_event,
'is_point_rup': is_point_rup,
+ 'shakemap_array': shakemap_array,
'usgs_id': usgs_id, 'rupture_file': None}
try:
oq_rup = convert_to_oq_rupture(rup_data)
@@ -597,6 +664,7 @@ def download_rupture_dict(usgs_id, ignore_shakemap=False):
'mag': md['mag'], 'rake': md['rake'],
'local_timestamp': str(local_time), 'time_event': time_event,
'is_point_rup': True,
+ 'shakemap_array': shakemap_array,
'usgs_id': usgs_id, 'rupture_file': None, 'error': error_msg}
comment_str = (
f"
+
+
diff --git a/openquake/server/tests/test_aristotle_mode.py b/openquake/server/tests/test_aristotle_mode.py
index a6ea9583925f..7ccc9021a22e 100644
--- a/openquake/server/tests/test_aristotle_mode.py
+++ b/openquake/server/tests/test_aristotle_mode.py
@@ -253,6 +253,7 @@ def test_get_rupture_data_from_shakemap_conversion_error(self):
expected_keys = [
'is_point_rup', 'local_timestamp', 'time_event', 'lon', 'lat',
'dep', 'mag', 'rake', 'usgs_id',
+ 'mmi_map_png', 'pga_map_png',
'rupture_file', 'rupture_file_from_usgs', 'error',
'station_data_file_from_usgs', 'mosaic_models', 'trts']
self.assertEqual(sorted(ret_dict.keys()), sorted(expected_keys))
@@ -293,6 +294,7 @@ def test_get_rupture_data_from_shakemap_correctly_converted(self):
'is_point_rup', 'local_timestamp', 'time_event', 'lon', 'lat',
'dep', 'mag', 'rake', 'usgs_id',
'rupture_file', 'rupture_file_from_usgs',
+ 'mmi_map_png', 'pga_map_png',
'station_data_error',
'station_data_file_from_usgs', 'trts', 'mosaic_models', 'trt']
self.assertEqual(sorted(ret_dict.keys()), sorted(expected_keys))
@@ -330,6 +332,7 @@ def test_get_point_rupture_data_from_shakemap(self):
expected_keys = [
'is_point_rup', 'local_timestamp', 'time_event', 'lon', 'lat',
'dep', 'mag', 'rake', 'usgs_id',
+ 'mmi_map_png', 'pga_map_png',
'rupture_file', 'rupture_file_from_usgs',
'station_data_file_from_usgs', 'trts',
'mosaic_models']
@@ -350,11 +353,14 @@ def test_get_rupture_data_from_finite_fault(self):
expected_keys = [
'is_point_rup', 'local_timestamp', 'time_event', 'lon', 'lat',
'dep', 'mag', 'rake', 'usgs_id',
+ 'mmi_map_png', 'pga_map_png',
'rupture_file', 'rupture_file_from_usgs',
'station_data_file_from_usgs', 'trts',
'mosaic_models']
self.assertEqual(sorted(ret_dict.keys()), sorted(expected_keys))
self.assertEqual(ret_dict['rupture_file'], None)
+ self.assertEqual(ret_dict['mmi_map_png'], None)
+ self.assertEqual(ret_dict['pga_map_png'], None)
self.assertEqual(ret_dict['usgs_id'], 'us6000jllz')
self.assertEqual(ret_dict['mosaic_models'], ['ARB', 'MIE'])
self.assertEqual(ret_dict['trts'], {
diff --git a/openquake/server/views.py b/openquake/server/views.py
index 304842fea8c8..c60276bbe3ae 100644
--- a/openquake/server/views.py
+++ b/openquake/server/views.py
@@ -54,6 +54,7 @@
from openquake.calculators.getters import NotFound
from openquake.calculators.export import export
from openquake.calculators.extract import extract as _extract
+from openquake.calculators.postproc.plots import plot_shakemap # , plot_rupture
from openquake.engine import __version__ as oqversion
from openquake.engine.export import core
from openquake.engine import engine, aelo, aristotle
@@ -758,6 +759,26 @@ def aristotle_get_rupture_data(request):
rupdic['mosaic_models'] = mosaic_models
rupdic['rupture_file_from_usgs'] = rupdic['rupture_file']
rupdic['station_data_file_from_usgs'] = station_data_file
+ oq_rup = None
+ if 'oq_rup' in rupdic:
+ oq_rup = rupdic['oq_rup']
+ # FIXME: check if we want to display the rupture png as a separate plot, instead
+ # of inserting the hypocenter and the rupture boundaries in the gmf plots
+ # # Agg is a non-interactive backend
+ # rupdic['rupture_png'] = plot_rupture(
+ # rupdic['oq_rup'], backend='Agg', figsize=(6, 6),
+ # with_populated_places=True, return_base64=True)
+ del rupdic['oq_rup']
+ if 'shakemap_array' in rupdic:
+ shakemap_array = rupdic['shakemap_array']
+ figsize = (14, 7) # fitting in a single row in the template without resizing
+ rupdic['pga_map_png'] = plot_shakemap(
+ shakemap_array, 'PGA', backend='Agg', figsize=figsize,
+ with_populated_places=False, return_base64=True, rupture=oq_rup)
+ rupdic['mmi_map_png'] = plot_shakemap(
+ shakemap_array, 'MMI', backend='Agg', figsize=figsize,
+ with_populated_places=False, return_base64=True, rupture=oq_rup)
+ del rupdic['shakemap_array']
response_data = rupdic
return HttpResponse(content=json.dumps(response_data), content_type=JSON,
status=200)