ShakeMap.java

  1. package gov.usgs.earthquake.shakemap;

  2. import gov.usgs.earthquake.product.Content;
  3. import gov.usgs.earthquake.product.Product;
  4. import gov.usgs.util.StreamUtils;
  5. import gov.usgs.util.XmlUtils;

  6. import java.io.InputStream;
  7. import java.math.BigDecimal;
  8. import java.util.Date;
  9. import java.util.HashMap;
  10. import java.util.logging.Level;
  11. import java.util.logging.Logger;

  12. /**
  13.  * ShakeMap object to add additional Product properties based on contents.
  14.  *
  15.  * This subclass of Product provides access to additional ShakeMap-specific
  16.  * attributes and loads these attributes, as well as additional Product
  17.  * attributes from ShakeMap source XML files.
  18.  */
  19. public class ShakeMap extends Product {

  20.     /** Property for event description */
  21.     public static final String EVENT_DESCRIPTION_PROPERTY = "event-description";
  22.     /** Property for event type */
  23.     public static final String EVENT_TYPE_PROPERTY = "event-type";
  24.     /** Property for map status */
  25.     public static final String MAP_STATUS_PROPERTY = "map-status";
  26.     /** Property for max latitude */
  27.     public static final String MAXIMUM_LATITUDE_PROPERTY = "maximum-latitude";
  28.     /** Property for max longitude */
  29.     public static final String MAXIMUM_LONGITUDE_PROPERTY = "maximum-longitude";
  30.     /** Property for min latitude */
  31.     public static final String MINIMUM_LATITUDE_PROPERTY = "minimum-latitude";
  32.     /** Property for min longitude */
  33.     public static final String MINIMUM_LONGITUDE_PROPERTY = "minimum-longitude";
  34.     /** Property for process timestamp */
  35.     public static final String PROCESS_TIMESTAMP_PROPERTY = "process-timestamp";

  36.     private static final Logger LOGGER = Logger.getLogger(ShakeMap.class
  37.             .getName());

  38.     /** References to file content in the Product */
  39.     public static final String GRID_XML_ATTACHMENT = "download/grid.xml";
  40.     /** References to file content in the Product */
  41.     public static final String INFO_XML_ATTACHMENT = "download/info.xml";

  42.     // The files below have been decided to be unsupported at this time to
  43.     // encourage
  44.     // adoption of grid.xml by all networks.
  45.     // public static final String GRID_XYZ_ATTACHMENT = "download/grid.xyz.zip";
  46.     // public static final String STATIONLIST_XML_ATTACHMENT =
  47.     // "download/stationlist.xml";
  48.     /** Invisible attachment */
  49.     public static final String INVISIBLE_ATTACHMENT = ".invisible";

  50.     /** A suffix added to all event codes for scenarios */
  51.     public static final String SCENARIO_ID_SUFFIX = "_se";

  52.     // Map types
  53.     /** Map type - actual */
  54.     public static final String ACTUAL = "ACTUAL";
  55.     /** Map type - scenario */
  56.     public static final String SCENARIO = "SCENARIO";
  57.     /** Map type - test */
  58.     public static final String TEST = "TEST";

  59.     /** key in info.xml for maximum mmi */
  60.     public static final String MAXIMUM_MMI_INFO_KEY = "mi_max";
  61.     /** Property for max MMI */
  62.     public static final String MAXIMUM_MMI_PROPERTY = "maxmmi";

  63.     /**
  64.      * @param product
  65.      *            the base product to be converted to a ShakeMap product
  66.      */
  67.     public ShakeMap(final Product product) {
  68.         super(product);

  69.         // prefer grid attachment
  70.         Content gridxml = product.getContents().get(GRID_XML_ATTACHMENT);
  71.         if (gridxml != null) {
  72.             InputStream gridXmlIn = null;
  73.             try {
  74.                 // parse grid.xml
  75.                 GridXMLHandler gridxmlHandler = new GridXMLHandler();
  76.                 gridXmlIn = gridxml.getInputStream();
  77.                 HashMap<String, String> grid = gridxmlHandler.parse(gridXmlIn);
  78.                 // parse through hash maps to set shakemap properties
  79.                 this.setGridXMLProperties(grid);
  80.             } catch (Exception e) {
  81.                 // error parsing grid
  82.                 LOGGER.log(Level.WARNING, "error parsing grid.xml", e);
  83.             } finally {
  84.                 StreamUtils.closeStream(gridXmlIn);
  85.             }
  86.         }

  87.         Content infoxml = product.getContents().get(INFO_XML_ATTACHMENT);
  88.         if (infoxml != null) {
  89.             InputStream infoXmlIn = null;
  90.             try {
  91.                 // parse info.xml
  92.                 InfoXMLHandler infoxmlHandler = new InfoXMLHandler();
  93.                 infoXmlIn = infoxml.getInputStream();
  94.                 HashMap<String, String> info = infoxmlHandler.parse(infoXmlIn);
  95.                 // parse through hash maps to set shakemap properties
  96.                 this.setInfoXMLProperties(info);
  97.             } catch (Exception e) {
  98.                 LOGGER.log(Level.WARNING, "error parsing info.xml", e);
  99.             } finally {
  100.                 StreamUtils.closeStream(infoXmlIn);
  101.             }
  102.         }


  103.         /*
  104.          * else { // At this time we are disabling all non-grid.xml
  105.          * functionality // as all shakemaps sent in should have a grid.xml
  106.          * file.
  107.          *
  108.          * //otherwise try gridXYZ (has most) + stationlist (has depth) source =
  109.          * product.getContents().get(GRID_XYZ_ATTACHMENT); if (source != null) {
  110.          * GridXYZHandler handler = new GridXYZHandler(this); try {
  111.          * handler.parse(source.getInputStream()); } catch (Exception e) {
  112.          * //error parsing gridxyz throw new IllegalArgumentException(e); } }
  113.          *
  114.          * source = product.getContents().get(STATIONLIST_XML_ATTACHMENT); if
  115.          * (source != null) { StationlistXMLHandler handler = new
  116.          * StationlistXMLHandler(this); try {
  117.          * handler.parse(source.getInputStream()); } catch (Exception e) {
  118.          * //error parsing stationlist throw new IllegalArgumentException(e); }
  119.          * } }
  120.          */
  121.     }

  122.     /**
  123.      * @param gridXML
  124.      *            shakemap properties hash keyed by grid.xml attribute name
  125.      */
  126.     public void setGridXMLProperties (HashMap<String, String> gridXML) {
  127.         String depth;
  128.         String eventDescription;
  129.         String eventId;
  130.         String eventSource;
  131.         String eventSourceCode;
  132.         String eventTime;
  133.         String eventType;
  134.         String latitude;
  135.         String longitude;
  136.         String magnitude;
  137.         String mapStatus;
  138.         String maximumLatitude;
  139.         String maximumLongitude;
  140.         String minimumLatitude;
  141.         String minimumLongitude;
  142.         String processTimestamp;
  143.         String version;


  144.         // eventId
  145.         eventSource = gridXML.get(GridXMLHandler.EVENT_NETWORK_XML);
  146.         eventSourceCode = gridXML.get(GridXMLHandler.EVENT_ID_XML);
  147.         eventId = eventSource + eventSourceCode;

  148.         if (valueIsEmpty(getEventId(), eventId))  {
  149.             setEventId(eventSource, eventSourceCode);
  150.         }

  151.         // less preferred eventId (if not already set)
  152.         eventSource = gridXML.get(GridXMLHandler.SHAKEMAPGRID_ORIGINATOR_XML);
  153.         eventSourceCode = gridXML.get(GridXMLHandler.SHAKEMAPGRID_ID_XML);
  154.         eventId = eventSource + eventSourceCode;

  155.         if (valueIsEmpty(getEventId(), eventId))  {
  156.             setEventId(eventSource, eventSourceCode);
  157.         }


  158.         // ShakeMap Metadata
  159.         processTimestamp = gridXML.get(GridXMLHandler.SHAKEMAPGRID_TIMESTAMP_XML);
  160.         if (valueIsEmpty(XmlUtils.formatDate(getProcessTimestamp()), processTimestamp)) {
  161.             setProcessTimestamp(XmlUtils.getDate(processTimestamp));
  162.         }

  163.         version = gridXML.get(GridXMLHandler.SHAKEMAPGRID_VERSION_XML);
  164.         if (valueIsEmpty(getVersion(), version)) {
  165.             setVersion(version);
  166.         }

  167.         eventType = gridXML.get(GridXMLHandler.SHAKEMAPGRID_EVENT_TYPE_XML);
  168.         if (valueIsEmpty(getEventType(), eventType)) {
  169.             setEventType(eventType);
  170.         }

  171.         mapStatus = gridXML.get(GridXMLHandler.SHAKEMAPGRID_EVENT_STATUS_XML);
  172.         if (valueIsEmpty(getMapStatus(), mapStatus)) {
  173.             setMapStatus(mapStatus);
  174.         }


  175.         // ShakeMap Grid
  176.         minimumLongitude = gridXML.get(GridXMLHandler.GRIDSPEC_LONMIN_XML);
  177.         if (valueIsEmpty(getString(getMinimumLongitude()), minimumLongitude)) {
  178.             setMinimumLongitude(getBigDecimal(minimumLongitude));
  179.         }

  180.         maximumLongitude = gridXML.get(GridXMLHandler.GRIDSPEC_LONMAX_XML);
  181.         if (valueIsEmpty(getString(getMaximumLongitude()), maximumLongitude)) {
  182.             setMaximumLongitude(getBigDecimal(maximumLongitude));
  183.         }

  184.         minimumLatitude = gridXML.get(GridXMLHandler.GRIDSPEC_LATMIN_XML);
  185.         if (valueIsEmpty(getString(getMinimumLatitude()), minimumLatitude)) {
  186.             setMinimumLatitude(getBigDecimal(minimumLatitude));
  187.         }

  188.         maximumLatitude = gridXML.get(GridXMLHandler.GRIDSPEC_LATMAX_XML);
  189.         if (valueIsEmpty(getString(getMaximumLatitude()), maximumLatitude)) {
  190.             setMaximumLatitude(getBigDecimal(maximumLatitude));
  191.         }


  192.         // Event
  193.         latitude = gridXML.get(GridXMLHandler.EVENT_LATITUDE_XML);
  194.         if (valueIsEmpty(getString(getLatitude()), latitude)) {
  195.             setLatitude(getBigDecimal(latitude));
  196.         }

  197.         longitude = gridXML.get(GridXMLHandler.EVENT_LONGITUDE_XML);
  198.         if (valueIsEmpty(getString(getLongitude()), longitude)) {
  199.             setLongitude(getBigDecimal(longitude));
  200.         }

  201.         magnitude = gridXML.get(GridXMLHandler.EVENT_MAGNITUDE_XML);
  202.         if (valueIsEmpty(getString(getMagnitude()), magnitude)) {
  203.             setMagnitude(getBigDecimal(magnitude));
  204.         }

  205.         depth = gridXML.get(GridXMLHandler.EVENT_DEPTH_XML);
  206.         if (valueIsEmpty(getString(getDepth()), depth)) {
  207.             setDepth(getBigDecimal(depth));
  208.         }

  209.         eventTime = gridXML.get(GridXMLHandler.EVENT_TIMESTAMP_XML)
  210.                 .replace("GMT", "Z")
  211.                 .replace("UTC","Z");
  212.         if (valueIsEmpty(XmlUtils.formatDate(getEventTime()), eventTime)) {
  213.             setEventTime(XmlUtils.getDate(eventTime));
  214.         }

  215.         eventDescription = gridXML.get(GridXMLHandler.EVENT_DESCRIPTION_XML);
  216.         if (valueIsEmpty(getEventDescription(), eventDescription)) {
  217.             setEventDescription(eventDescription);
  218.         }

  219.     };

  220.     /**
  221.      * @param infoXML
  222.      *            shakemap properties hash keyed by info.xml attribute name
  223.      */
  224.     public void setInfoXMLProperties (HashMap<String, String> infoXML) {
  225.         // read maxmmi from info.xml
  226.         if (infoXML.containsKey(MAXIMUM_MMI_INFO_KEY)) {
  227.             this.getProperties().put(MAXIMUM_MMI_PROPERTY,
  228.                     infoXML.get(MAXIMUM_MMI_INFO_KEY));
  229.         }
  230.     };

  231.     /**
  232.      * @param productValue
  233.      *            the value from the PDL object
  234.      * @param xmlValue
  235.      *            the value from the XML document
  236.      * @return if the shakemap property is already set
  237.      */
  238.     public boolean valueIsEmpty (String productValue, String xmlValue) {
  239.         // nothing to be set
  240.         if (xmlValue == null) {
  241.             return false;
  242.         }
  243.         // no value has been set
  244.         if (productValue == null) {
  245.             return true;
  246.         }
  247.         // value is set and values are different, log warning
  248.         if (!productValue.equals(xmlValue)) {
  249.             LOGGER.log(Level.FINE,
  250.                     "The ShakeMap property value: \"" + xmlValue + "\"" +
  251.                     " does not match the product value: \"" + productValue + "\".");
  252.         }
  253.         return false;
  254.     }

  255.     /**
  256.      * @param mapStatus
  257.      *            the map status to set
  258.      */
  259.     public void setMapStatus(String mapStatus) {
  260.         getProperties().put(MAP_STATUS_PROPERTY, mapStatus);
  261.     }

  262.     /**
  263.      * @return the status of this map
  264.      */
  265.     public String getMapStatus() {
  266.         return getProperties().get(MAP_STATUS_PROPERTY);
  267.     }

  268.     /**
  269.      * @param eventType
  270.      *            the event type to set
  271.      */
  272.     public void setEventType(String eventType) {
  273.         getProperties().put(EVENT_TYPE_PROPERTY, eventType);
  274.     }

  275.     /**
  276.      * @return the event type of this product as defined in ShakeMap
  277.      */
  278.     public String getEventType() {
  279.         return getProperties().get(EVENT_TYPE_PROPERTY);
  280.     }

  281.     /**
  282.      * @param processTimestamp
  283.      *            the process timestamp to set
  284.      */
  285.     public void setProcessTimestamp(Date processTimestamp) {
  286.         getProperties().put(PROCESS_TIMESTAMP_PROPERTY,
  287.                 XmlUtils.formatDate(processTimestamp));
  288.     }

  289.     /**
  290.      * @return the process timestamp of this ShakeMap
  291.      */
  292.     public Date getProcessTimestamp() {
  293.         return XmlUtils
  294.                 .getDate(getProperties().get(PROCESS_TIMESTAMP_PROPERTY));
  295.     }

  296.     /**
  297.      * @return the event description text for this ShakeMap
  298.      */
  299.     public String getEventDescription() {
  300.         return getProperties().get(EVENT_DESCRIPTION_PROPERTY);
  301.     }

  302.     /**
  303.      * @param eventDescription
  304.      *            the event description to set
  305.      */
  306.     public void setEventDescription(String eventDescription) {
  307.         getProperties().put(EVENT_DESCRIPTION_PROPERTY, eventDescription);
  308.     }

  309.     /**
  310.      * @return the minimum longitude boundary of this ShakeMap
  311.      */
  312.     public BigDecimal getMinimumLongitude() {
  313.         return getBigDecimal(getProperties().get(MINIMUM_LONGITUDE_PROPERTY));
  314.     }

  315.     /**
  316.      * @param minimumLongitude
  317.      *            the minimum longitude to set
  318.      */
  319.     public void setMinimumLongitude(BigDecimal minimumLongitude) {
  320.         getProperties().put(MINIMUM_LONGITUDE_PROPERTY,
  321.                 minimumLongitude.toPlainString());
  322.     }

  323.     /**
  324.      * @return the maximum longitude boundary of this ShakeMap
  325.      */
  326.     public BigDecimal getMaximumLongitude() {
  327.         return getBigDecimal(getProperties().get(MAXIMUM_LONGITUDE_PROPERTY));
  328.     }

  329.     /**
  330.      * @param maximumLongitude
  331.      *            the maximum longitude to set
  332.      */
  333.     public void setMaximumLongitude(BigDecimal maximumLongitude) {
  334.         getProperties().put(MAXIMUM_LONGITUDE_PROPERTY,
  335.                 maximumLongitude.toPlainString());
  336.     }

  337.     /**
  338.      * @return the minimum latitude boundary of this ShakeMap
  339.      */
  340.     public BigDecimal getMinimumLatitude() {
  341.         return getBigDecimal(getProperties().get(MINIMUM_LATITUDE_PROPERTY));
  342.     }

  343.     /**
  344.      * @param minimumLatitude
  345.      *            the minimum latitude to set
  346.      */
  347.     public void setMinimumLatitude(BigDecimal minimumLatitude) {
  348.         getProperties().put(MINIMUM_LATITUDE_PROPERTY,
  349.                 minimumLatitude.toPlainString());
  350.     }

  351.     /**
  352.      * @return the maximum latitude boundary of this ShakeMap
  353.      */
  354.     public BigDecimal getMaximumLatitude() {
  355.         return getBigDecimal(getProperties().get(MAXIMUM_LATITUDE_PROPERTY));
  356.     }

  357.     /**
  358.      * @param maximumLatitude
  359.      *            the maximum latitude to set
  360.      */
  361.     public void setMaximumLatitude(BigDecimal maximumLatitude) {
  362.         getProperties().put(MAXIMUM_LATITUDE_PROPERTY,
  363.                 maximumLatitude.toPlainString());
  364.     }

  365.     /**
  366.      * Returns String value as BigDecimal
  367.      * @param value to return as BigDecimal
  368.      * @return a BigDecimal
  369.      */
  370.     protected BigDecimal getBigDecimal (String value) {
  371.         if (value == null) {
  372.             return null;
  373.         }
  374.         return new BigDecimal(value);
  375.     }

  376.     /**
  377.      * Returns BigDecimal value as String
  378.      * @param value a BigDecimal
  379.      * @return a string
  380.      */
  381.     protected String getString (BigDecimal value) {
  382.         if (value == null) {
  383.             return null;
  384.         }
  385.         return value.toString();
  386.     }

  387. }