eqevents

Static event website generator

Description

EQEvents is a static website generator for SeisComP XML event parameters. It can run in a spooler mode or process single files. It uses a template engine to create the HTML files. It features an index page and event detail page. More information can be added though Plugins.

EQEvents is meant to run as output service for GDS (gempa dissemination server).

Plugins

The different EQEvents features are implemented by various plugins which may be activated by adding a plugin to the list of processingPlugins, e.g., browser,gds,gis,leaflet,rss,search,shakemap.

The following plugins exist:

Name

Description

browser

Adds a calendar based event browser.

bulletins

Adds bulletins containing additional information generated by an external source to an event detail page. This plugin is rarely used and requires customization.

gds

Updates gempa dissemination server (GDS) database with event processing information. It is recommended to activate this plugin if EQEvents is triggered by GDS.

gis

Generates a detailed event map using gempa image server (GIS).

leaflet

Generates a dynamic map showing the events of the current event table on the index page, in the event browser and the event search.

notification

Notifies connected clients about event and bulletin updates through a socket io interface. This plugin is rarely used and requires customization.

plots

Adds distance, azimuth, and polar plot to event detail page.

rss

Creates a RSS feet users may subscribe to.

search

Adds an event search. Note: The search function is implemented as a WSGI script and needs additional installation steps, described in Dynamic Content - The Search and Feedback Plugins.

feedback

Adds an email feedback form. Note: The feedback function is implemented as a WSGI script and needs additional installation steps, described in Dynamic Content - The Search and Feedback Plugins.

shakemap

Generates a shake map using gempa’s autosigma module.

System dependencies which may be required by the plugins:

  • python-matplotlib

  • python-jinja2

Processing

Offline

To process a single XML file run EQEvents and pass it an XML file:

eqevents -i event.xml --debug

Spooler

In contrast to a typical GDS service EQEvents is not monitored by GDS but runs as a SeisComP module. The $SEISCOMP_ROOT/etc/gds.cfg only defines a spool directory but no spooler nor filter command. The same directory needs to be configured in $SEISCOMP_ROOT/etc/eqevents.cfg. If an event matches the GDS dissemination criteria or an operator manually disseminated an event the GDS will place a *.content file containing the SeisComP XML and a *.address file into the spool directory where they are found by EQEvents.

Processing details:

  • The file name of the address and content file must match.

  • Files with the same name but different extension are processed as auxiliary files.

  • Each file found is deleted either after processing or due to wrong the file name format.

  • The processing starts when the content file is found.

  • The content file contains a match information according to the GDS dissemination rules, see ignoreMatch.

  • If multiple files for the same event are found then only the newest one is processed and the others are deleted.

  • The file name format is <SECONDS.MILLIS>-<GDSID>-<EVENTID>-<SERVICE>[-FILTER].<EXT>

    Name

    Description

    SECONDS.MILLIS

    Time of dissemination

    GDSID

    ID of the dissemination procedure used to report back the dissemination status

    EVENTID

    Event ID used to skip older files of same event

    SERVICE

    GDS service name, unused

    FILTER

    GDS custom filter name, unused

    EXT

    File name extension

Reprocessing

If a template was changed or new plugins have been added all static pages need to be reprocessed. As a precondition for this the event XML data must be available, see backupXML.

To initiate reprocessing stop EQEvents and run it using the reprocess command-line option.

eqevents --reprocess

Templates

EQEvents uses templates in a well defined structure. The path to the template directory can be configured with templateDirectory. The structure is as follows:

base.html

Base template inherited by almost all templates

feed.rss

RSS web feed

index.html

Index page

leaflet-map.html

Leaflet map, used in: index, browser, results

results.html

Search result page

search.html

Search parameter page

table.html

Event list table, used in index, browser, results

browser/day.html

Browser day page showing all events of a particular day

browser/index.html

Browser index page showing all years

browser/month.html

Browser month page showing all days

browser/year.html

Browser year page showing all months

event/arrivals.html

Event details arrival list

event/base.html

Event details base template

event/overview.html

Event details overview page

event/quality.html

Event details quality plots

Templates are processed by Python Jinja and follow its documented syntax. Each template receives a context with several variables defined.

ep

The EventParameters structure parsed from XML converted to a Python object. Attributes are accessible following the SeisComP ML schema names. They are mapped directly from the XML tag/attribute names. Below a simple example how to access all picks in the EventParameters object in template syntax:

{% for p in ep.pick %}
{{ p.waveformID.networkCode }}.{{ p.waveformID.stationCode }} {{ p.time.value|qmldate|date }}
{% endfor %}

The special filter qmldate is a special filter added by EQEvents to convert a date string from SeisComP ML to an Python datetime object. That object can then be further processed with the Jinja date filter.

event

The event object currently processed.

origin

The preferred origin of the currently processed event.

focalMechanism

The preferred focal mechanism of the currently processed event.

magnitude

The preferred magnitude of the currently processed event.

POOL

The POOL that maps a publicID to an object.

{% for arr in origin.arrival %}
{% p = POOL[arr.pickID] %}
{% if p %}
<div>{{ p.time.value|qmldate|data }}</div>
{% else %}
<div class="error">Referenced pick {{ arr.pickID }} not available</div>
{% endif %}
{% endfor %}

This makes it easier to access referenced objects. Otherwise the template code must iterate over all pick objects and compare the publicID in combination with a linear search which is quite slow for larger events.

ID

The current event ID after cleaning invalid characters. That ID is used as event folder name and all event ID depending filenames.

ROOT_URL

The relative path to the site index file. This can be different according to the templates rendered. If paths are required this variable should be prepended.

<a href="{{ ROOT_URL }}index.html">Home</a>
STATIC_URL

The path to the static files according to staticURL.

<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/page.css">
filename

The path and filename of the current XML file being processed.

Webpages

The following images show the different generated pages with the default templates.

_images/browser-map.png

Index page with event map

_images/index.png

Index page

_images/event-overview.png

Event overview

_images/event-arrivals.png

Event arrivals

_images/event-quality.png

Event quality

_images/browser.png

Browser index

_images/browser-year.png

Browser page for one year

_images/browser-month.png

Browser page for one month

_images/browser-day.png

Browser page for one day

_images/search.png

Search page

Web Server Integration

EQEvents has been deployed using NGINX or Apache. If unsure which web server to use then select NGINX because this is the most lightweight setup. Since EQEvents by default only consists of static pages the web server integration is straight forward. You may either decide to

  1. generate the pages directly into the web server html directory by setting outputDirectory to e.g., /usr/share/nginx/html/eqevents (NGINX) or /var/www/html/eqevents (Apache) or

  2. you use a softlink to point from inside the html directory to the default EQEvents output directory:

ln -s /home/sysop/seiscomp/var/lib/eqevents/pages /usr/share/nginx/html/eqevents

In case of (1.) make sure that the EQEvents system user, probably sysop, is allowed to write to the html directory. In case of (2.) the x access right must be set for the web server group and for all directories on the path. Also security modules like SELinux might prevent access of the web server process to the user directory. If this is the case use following command to grant those access rights:

chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/etc/global.cfg
chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/etc/eqevents.cfg
chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/etc/defaults/global.cfg
chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/etc/defaults/eqevents.cfg
chcon -Rv --type=httpd_sys_content_t /home/sysop/seiscomp/var/lib/eqevents/pages
chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/var/lib/eqevents/eqevents.db
chcon -Rv --type=httpd_sys_content_t /home/sysop/seiscomp/share/eqevents/templates
chcon -v --type=httpd_sys_content_t /home/sysop/seiscomp/share/eqevents/search-wsgi.py
chcon -Rv --type=texrel_shlib_ti /home/sysop/seiscomp/lib
#setsebool -P httpd_read_user_content 1

Dynamic Content - The Search and Feedback Plugins

The search and the feedback plugin require a WSGI module to generate dynamic content. The installation depends on the web server you selected and is covered different section

NGINX (Gunicorn)

  1. Add the search or feedback plugin to processingPlugins.

  2. Install gunicorn

    python3 -m pip install --user gunicorn
    
  3. Copy the relevant parts of the server and upstream directives from $SEISCOMP_ROOT/share/eqevents/examples/nginx-gunicorn.conf to your

    • /etc/nginx/nginx.conf or

    • /etc/nginx/sites-available/YOUR-VHOST.conf in case you are using a virtual host setup

  4. Copy the Gunicorn configuration file to the EQEvents base directory:

    cd $SEISCOMP_ROOT/share/eqevents/
    cp examples/search-gunicorn.py .
    #cp examples/feedback-gunicorn.py .
    

    Adjust the configuration, e.g., logging level, path and format if necessary.

  5. Copy the Gunicorn socket and service systemd configuration:

    sudo cp $SEISCOMP_ROOT/share/gds/web/examples/gunicorn/gunicorn-eqevents-search.* /etc/systemd/system
    #sudo cp $SEISCOMP_ROOT/share/gds/web/examples/gunicorn/gunicorn-eqevents-feedback.* /etc/systemd/system
    

    Adjust the user, group, or WorkingDirectory variables if necessary. Change type depending on your Gunicorn version. Then enable and start the service:

    sudo systemctl enable gunicorn-eqevents-search
    sudo systemctl start gunicorn-eqevents-search
    #sudo systemctl enable gunicorn-eqevents-feedback
    #sudo systemctl start gunicorn-eqevents-feedback
    
  6. Test the NGINX configuration:

    sudo nginx -t
    
  7. Enable and start NGINX:

    sudo systemctl enable nginx
    sudo systemctl start nginx
    

Trouble shooting

  • Check if the socket file /var/run/gunicorn-eqevents-{search,feedback}.sock was created and ensure that the NGINX user (nginx or www-data) has write access to this file

  • Make a request directly at the socket:

    sudo -u nginx curl --unix-socket /var/run/gunicorn-eqevents-search.sock http
    
  • By default Gunicorn logs to $HOME/.seiscomp/log/eqevents-web/{search,feedback}-{access,error}.log

  • Reload gunicorn-eqvents-{search,feedback} service after modifications to your $SEISCOMP_ROOT/share/gds/{search,feedback}-gunicorn.py

  • Reload systemd configuration after modifications to systemd service and socket files: sudo systemctl daemon-reload

  • Reload the NGINX server configuration: sudo nginx -t && sudo systemctl reload nginx

Apache (WSGI)

  1. Add the search or feedback plugin to processingPlugins.

  2. Install and enable mod_wsgi

    a2enmod wsgi
    
  3. Configure WSGI handler, e.g., in /etc/apache2/sites-available/eqevents.conf

    WSGIScriptAlias /eqevents/search /home/sysop/seiscomp/share/eqevents/search-wsgi.py
    #WSGIScriptAlias /eqevents/search /home/sysop/seiscomp/share/eqevents/feedback-wsgi.py
    WSGIPythonPath /home/sysop/seiscomp/lib/python
    WSGIDaemonProcess eqevents processes=2 threads=1 python-path=/home/sysop/seiscomp/lib/python
    WSGIProcessGroup eqevents
    
  4. Configure access to static content (optional, same file as above)

    <Location "/eqevents">
      Options -Indexes
      SetHandler None
      Require all granted
    </Location>
    
  5. Set environment variables

    Ubuntu: /etc/apache2/envvars

    export LD_LIBRARY_PATH=/home/sysop/seiscomp/lib:$LD_LIBRARY_PATH
    export SEISCOMP_ROOT=/home/sysop/seiscomp
    

    RHEL: /etc/sysconfig/httpd

    LD_LIBRARY_PATH=/home/sysop/seiscomp/lib:$LD_LIBRARY_PATH
    SEISCOMP_ROOT=/home/sysop/seiscomp
    
  6. Enable site

    a2ensite eqevents
    
  7. Restart Apache

Module Configuration

etc/defaults/global.cfg
etc/defaults/eqevents.cfg
etc/global.cfg
etc/eqevents.cfg
~/.seiscomp/global.cfg
~/.seiscomp/eqevents.cfg

eqevents inherits global options.

Note

Modules/plugins may require a license file. The default path to license files is @DATADIR@/licenses/ which can be overridden by global configuration of the parameter gempa.licensePath. Example:

gempa.licensePath = @CONFIGDIR@/licenses
database

Default: @ROOTDIR@/var/lib/eqevents/eqevents.db

Type: path

Path to the sqlite3 database file.

templateDirectory

Default: @DATADIR@/eqevents/templates/default

Type: path

Directory to read templates from.

outputDirectory

Default: @ROOTDIR@/var/lib/eqevents/pages

Type: path

Target directory for generated web pages.

spoolerDirectory

Default: @ROOTDIR@/var/lib/eqevents/spool

Type: path

Spooler directory to read content files from.

spoolerInterval

Default: 10

Unit: s

Type: int

Polling interval in seconds the spooler will scan the service directory for new files.

backupXML

Default: false

Type: boolean

Defines whether to backup the input XML in the output HTML directory per event as [id].xml. The XML backup allows easy reprocessing of the Web page in case of template changes, see ‘--reprocess’ command line option.

staticURL

Default: static/

Type: string

URL prefix for static files used in the templates. Can be used as {{ STATIC_URL }} in templates.

enableArrivals

Default: true

Type: boolean

Enables arrival output as part of the event detail page.

numberOfIndexEvents

Default: 50

Type: int

Number of events for the index page.

numberOfIndexDays

Type: int

Events of this number of past days for the index page. If this parameter is not configured then numberOfIndexEvents is in effect. If this parameter is configured then it takes precedence over numberOfIndexEvents.

maxDays

Default: 0

Type: int

Maximum number of days to keep events for. Older events are removed. A value of 0 or below means unlimited.

ignoreType

Default: false

Type: boolean

An event might be marked as fake event using the event type ‘not existing’ or ‘other’. Set this parameter to ‘true’ to process fake events respectively to prevent them from being deleted.

ignoreMatch

Default: false

Type: boolean

The GDS uses the ‘match’ flag to signal whether an event solution fulfilled the dissemination criteria. E.g. an updated event revision might correct the epicenter to a point outside the area of interest. Set this parameter to ‘true’ to process not matching events respectively to prevent them from being deleted.

processingPlugins

Default: browser, plots

Type: list:string

List of eqevents processing plugins.

showAuthor

Default: false

Type: boolean

Include origin author information in generated web pages.

Note

filter.* Parameters which filter the event data processed by the various plugins.

filter.minArrivalWeight

Type: double

Hides arrivals having a weight smaller than the configured threshold. To remove unused phases set a value of >0.

Note

gds.* Configuration parameters of the GDS plugin to generate gds reports. To use that plugin, add ‘gds’ to the list of processing plugins.

gds.db

Type: string

GDS database URL used to report back the processing state, e.g. ‘mysql://user:pass@localhost/gds’.

Note

gis.* Configuration parameters of the GIS plugin to generate map files. To use that plugin, add ‘gis’ to the list of processing plugins.

gis.url

Default: http://localhost:20001

Type: string

URL of the GIS RESTful API.

gis.timeout

Default: 10

Type: int

Defines the timeout for GIS requests. If the timeout expires and GIS did not return an empty image is generated.

gis.post

Default: false

Type: boolean

By default the image is requested from the GIS using the HTTP-GET mothod along with key event parameters. If set to ‘true’ the HTTP-POST method is used instead to transfer the whole event XML. This allows the GIS to render an image with even more information, like used stations or moment tensor solutions.

Note

rss.* Configuration parameters of the RSS plugin. To use that plugin, add ‘rss’ to the list of processing plugins.

rss.days

Default: 1

Type: int

Number of (last) days to generate the RSS feed for.

rss.title

Default: EQ Events RSS Channel - last ${days}

Type: string

Title of the RSS feed. The variable ‘${days}’ is replaced by the string containing the number of days configured in the ‘rss.days’ parameter, e.g. ‘2 days’.

rss.desc

Default: Real-time earthquake locations

Type: string

Description of RSS feed that is copied to the description tag of the RSS XML.

Default: https://localhost

Type: string

URL prefix for each event detail page. EQEvents just deals with relative paths but to transform an RSS item link into an absolute URL this prefix is required.

Note

notification.* Configuration parameters of the NOTIFICATION plugin to send notifications to a socket.io-server. To use that plugin, add ‘notification’ to the list of processing plugins.

notification.server

Default: localhost

Type: string

Hostname of the Socket.IO server.

notification.port

Default: 8000

Type: int

Port of the Socket.IO server.

notification.namespace

Default: /event

Type: string

Socket.IO namespace.

Note

leaflet.* Configuration parameters for the Leaflet map plugin that renders an interactive map on the index page.

Note

leaflet.init.* By default the map view is set to the bounds of the current event list. This configuration group allows to define a fix coordinate and zoom level for the initial map view.

leaflet.init.lat

Unit: degrees

Type: double

Initial latitude to center the map view.

leaflet.init.lon

Unit: degrees

Type: double

Initial longitude to center the map view.

leaflet.init.zoom

Default: 3

Type: int

Initial zoom level for the map view.

Note

leaflet.tile.* Parmeters controlling the map tile layer.

leaflet.tile.url

Default: http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png

Type: string

Tile URL. See https://leafletjs.com/reference-1.7.1.html#tilelayer for a list of supported placeholders.

leaflet.tile.attribution

Default: "Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors"

Type: string

Tile attribution string displayed on the bottom right corner.

leaflet.tile.maxZoom

Default: 18

Type: int

Maximum available zoom level.

Note

leaflet.control.* Defines available map controls typically displayed at bottom left map corner.

leaflet.control.scaleMetric

Default: true

Type: boolean

Adds a scale showing the map resolution in meter or kilometer at the map center.

leaflet.control.scaleImperial

Default: false

Type: boolean

Adds a scale showing the map resolution in feet or miles at the map center.

leaflet.control.mousePosition

Default: true

Type: boolean

Adds a control showing the geo location under the mouse cursor.

Note

search.* The search plugin provides a page to configure a search filter.

search.resultLimit

Default: 100

Type: int

The maximum number of search results.

search.distanceUnit

Default: km

Type: string

The unit used for the radius of the circular region filter. This can either be ‘km’, ‘mi’ or ‘deg’. Conversion constants used: 1 mile = 1.609344 kilometers, 1 degree = 111.195079734632 kilometers.

search.enableMap

Default: true

Type: boolean

Enables interactive map region selection.

Note

feedback.* The feedback plugin implemets a email feedback form.

feedback.from

Type: string

The email’s ‘From’ address (mandatory).

feedback.to

Type: string

The email’s ‘To’ address (mandatory).

feedback.subject

Default: EQEvents feedback

Type: string

The email’s ‘Subject’.

feedback.host

Default: localhost

Type: string

SMTP host.

feedback.port

Default: 25

Type: int

SMTP port.

feedback.user

Type: string

User in case the SMTP server requires authorization.

feedback.password

Type: string

Password in case the SMTP server requires authorization.

feedback.tls

Default: false

Type: boolean

Use StartTLS.

feedback.ssl

Default: false

Type: boolean

Use Secure Socket Layer (SSL).

Note

shakemap.* The shakemap plugin adds a shake map image to the event overview page. The image is generated by an external application, e.g. autosigma.

shakemap.cmd

Default: autosigma --ep

Type: string

Command generating the result file, invoked with the location of the SC3 XML file as last paramter.

shakemap.minMag

Default: 5.0

Type: float

Minimum magnitude to trigger shake map generation.

shakemap.resultFile

Default: @ROOTDIR@/var/sigma/incidents/grid-mmi.png

Type: string

Expected location of the result file.

Command-Line Options

Generic

Verbosity

EQEvents

-i, --input string

Input file to be processed immediately. EQEvents will not change into spooler mode after processing this file.

--reprocess

Reprocess event detail pages and index pages for events found in database. This mode requires backupXML being enabled.

--reprocess-index

Reprocess index pages for events found in database.