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.getName());

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

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

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

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

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

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

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

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

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

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

  138.     // eventId
  139.     eventSource = gridXML.get(GridXMLHandler.EVENT_NETWORK_XML);
  140.     eventSourceCode = gridXML.get(GridXMLHandler.EVENT_ID_XML);
  141.     eventId = eventSource + eventSourceCode;

  142.     if (valueIsEmpty(getEventId(), eventId)) {
  143.       setEventId(eventSource, eventSourceCode);
  144.     }

  145.     // less preferred eventId (if not already set)
  146.     eventSource = gridXML.get(GridXMLHandler.SHAKEMAPGRID_ORIGINATOR_XML);
  147.     eventSourceCode = gridXML.get(GridXMLHandler.SHAKEMAPGRID_ID_XML);
  148.     eventId = eventSource + eventSourceCode;

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

  152.     // ShakeMap Metadata
  153.     processTimestamp = gridXML.get(GridXMLHandler.SHAKEMAPGRID_TIMESTAMP_XML);
  154.     if (valueIsEmpty(XmlUtils.formatDate(getProcessTimestamp()), processTimestamp)) {
  155.       setProcessTimestamp(XmlUtils.getDate(processTimestamp));
  156.     }

  157.     version = gridXML.get(GridXMLHandler.SHAKEMAPGRID_VERSION_XML);
  158.     if (valueIsEmpty(getVersion(), version)) {
  159.       setVersion(version);
  160.     }

  161.     eventType = gridXML.get(GridXMLHandler.SHAKEMAPGRID_EVENT_TYPE_XML);
  162.     if (valueIsEmpty(getEventType(), eventType)) {
  163.       setEventType(eventType);
  164.     }

  165.     mapStatus = gridXML.get(GridXMLHandler.SHAKEMAPGRID_EVENT_STATUS_XML);
  166.     if (valueIsEmpty(getMapStatus(), mapStatus)) {
  167.       setMapStatus(mapStatus);
  168.     }

  169.     // ShakeMap Grid
  170.     minimumLongitude = gridXML.get(GridXMLHandler.GRIDSPEC_LONMIN_XML);
  171.     if (valueIsEmpty(getString(getMinimumLongitude()), minimumLongitude)) {
  172.       setMinimumLongitude(getBigDecimal(minimumLongitude));
  173.     }

  174.     maximumLongitude = gridXML.get(GridXMLHandler.GRIDSPEC_LONMAX_XML);
  175.     if (valueIsEmpty(getString(getMaximumLongitude()), maximumLongitude)) {
  176.       setMaximumLongitude(getBigDecimal(maximumLongitude));
  177.     }

  178.     minimumLatitude = gridXML.get(GridXMLHandler.GRIDSPEC_LATMIN_XML);
  179.     if (valueIsEmpty(getString(getMinimumLatitude()), minimumLatitude)) {
  180.       setMinimumLatitude(getBigDecimal(minimumLatitude));
  181.     }

  182.     maximumLatitude = gridXML.get(GridXMLHandler.GRIDSPEC_LATMAX_XML);
  183.     if (valueIsEmpty(getString(getMaximumLatitude()), maximumLatitude)) {
  184.       setMaximumLatitude(getBigDecimal(maximumLatitude));
  185.     }

  186.     // Event
  187.     latitude = gridXML.get(GridXMLHandler.EVENT_LATITUDE_XML);
  188.     if (valueIsEmpty(getString(getLatitude()), latitude)) {
  189.       setLatitude(getBigDecimal(latitude));
  190.     }

  191.     longitude = gridXML.get(GridXMLHandler.EVENT_LONGITUDE_XML);
  192.     if (valueIsEmpty(getString(getLongitude()), longitude)) {
  193.       setLongitude(getBigDecimal(longitude));
  194.     }

  195.     magnitude = gridXML.get(GridXMLHandler.EVENT_MAGNITUDE_XML);
  196.     if (valueIsEmpty(getString(getMagnitude()), magnitude)) {
  197.       setMagnitude(getBigDecimal(magnitude));
  198.     }

  199.     depth = gridXML.get(GridXMLHandler.EVENT_DEPTH_XML);
  200.     if (valueIsEmpty(getString(getDepth()), depth)) {
  201.       setDepth(getBigDecimal(depth));
  202.     }

  203.     eventTime = gridXML.get(GridXMLHandler.EVENT_TIMESTAMP_XML).replace("GMT", "Z").replace("UTC", "Z");
  204.     if (valueIsEmpty(XmlUtils.formatDate(getEventTime()), eventTime)) {
  205.       setEventTime(XmlUtils.getDate(eventTime));
  206.     }

  207.     eventDescription = gridXML.get(GridXMLHandler.EVENT_DESCRIPTION_XML);
  208.     if (valueIsEmpty(getEventDescription(), eventDescription)) {
  209.       setEventDescription(eventDescription);
  210.     }

  211.   };

  212.   /**
  213.    * @param infoXML shakemap properties hash keyed by info.xml attribute name
  214.    */
  215.   public void setInfoXMLProperties(HashMap<String, String> infoXML) {
  216.     // read maxmmi from info.xml
  217.     if (infoXML.containsKey(MAXIMUM_MMI_INFO_KEY)) {
  218.       this.getProperties().put(MAXIMUM_MMI_PROPERTY, infoXML.get(MAXIMUM_MMI_INFO_KEY));
  219.     }
  220.   };

  221.   /**
  222.    * @param productValue the value from the PDL object
  223.    * @param xmlValue     the value from the XML document
  224.    * @return if the shakemap property is already set
  225.    */
  226.   public boolean valueIsEmpty(String productValue, String xmlValue) {
  227.     // nothing to be set
  228.     if (xmlValue == null) {
  229.       return false;
  230.     }
  231.     // no value has been set
  232.     if (productValue == null) {
  233.       return true;
  234.     }
  235.     // value is set and values are different, log warning
  236.     if (!productValue.equals(xmlValue)) {
  237.       LOGGER.log(Level.FINE, "The ShakeMap property value: \"" + xmlValue + "\""
  238.           + " does not match the product value: \"" + productValue + "\".");
  239.     }
  240.     return false;
  241.   }

  242.   /**
  243.    * @param mapStatus the map status to set
  244.    */
  245.   public void setMapStatus(String mapStatus) {
  246.     getProperties().put(MAP_STATUS_PROPERTY, mapStatus);
  247.   }

  248.   /**
  249.    * @return the status of this map
  250.    */
  251.   public String getMapStatus() {
  252.     return getProperties().get(MAP_STATUS_PROPERTY);
  253.   }

  254.   /**
  255.    * @param eventType the event type to set
  256.    */
  257.   public void setEventType(String eventType) {
  258.     getProperties().put(EVENT_TYPE_PROPERTY, eventType);
  259.   }

  260.   /**
  261.    * @return the event type of this product as defined in ShakeMap
  262.    */
  263.   public String getEventType() {
  264.     return getProperties().get(EVENT_TYPE_PROPERTY);
  265.   }

  266.   /**
  267.    * @param processTimestamp the process timestamp to set
  268.    */
  269.   public void setProcessTimestamp(Date processTimestamp) {
  270.     getProperties().put(PROCESS_TIMESTAMP_PROPERTY, XmlUtils.formatDate(processTimestamp));
  271.   }

  272.   /**
  273.    * @return the process timestamp of this ShakeMap
  274.    */
  275.   public Date getProcessTimestamp() {
  276.     return XmlUtils.getDate(getProperties().get(PROCESS_TIMESTAMP_PROPERTY));
  277.   }

  278.   /**
  279.    * @return the event description text for this ShakeMap
  280.    */
  281.   public String getEventDescription() {
  282.     return getProperties().get(EVENT_DESCRIPTION_PROPERTY);
  283.   }

  284.   /**
  285.    * @param eventDescription the event description to set
  286.    */
  287.   public void setEventDescription(String eventDescription) {
  288.     getProperties().put(EVENT_DESCRIPTION_PROPERTY, eventDescription);
  289.   }

  290.   /**
  291.    * @return the minimum longitude boundary of this ShakeMap
  292.    */
  293.   public BigDecimal getMinimumLongitude() {
  294.     return getBigDecimal(getProperties().get(MINIMUM_LONGITUDE_PROPERTY));
  295.   }

  296.   /**
  297.    * @param minimumLongitude the minimum longitude to set
  298.    */
  299.   public void setMinimumLongitude(BigDecimal minimumLongitude) {
  300.     getProperties().put(MINIMUM_LONGITUDE_PROPERTY, minimumLongitude.toPlainString());
  301.   }

  302.   /**
  303.    * @return the maximum longitude boundary of this ShakeMap
  304.    */
  305.   public BigDecimal getMaximumLongitude() {
  306.     return getBigDecimal(getProperties().get(MAXIMUM_LONGITUDE_PROPERTY));
  307.   }

  308.   /**
  309.    * @param maximumLongitude the maximum longitude to set
  310.    */
  311.   public void setMaximumLongitude(BigDecimal maximumLongitude) {
  312.     getProperties().put(MAXIMUM_LONGITUDE_PROPERTY, maximumLongitude.toPlainString());
  313.   }

  314.   /**
  315.    * @return the minimum latitude boundary of this ShakeMap
  316.    */
  317.   public BigDecimal getMinimumLatitude() {
  318.     return getBigDecimal(getProperties().get(MINIMUM_LATITUDE_PROPERTY));
  319.   }

  320.   /**
  321.    * @param minimumLatitude the minimum latitude to set
  322.    */
  323.   public void setMinimumLatitude(BigDecimal minimumLatitude) {
  324.     getProperties().put(MINIMUM_LATITUDE_PROPERTY, minimumLatitude.toPlainString());
  325.   }

  326.   /**
  327.    * @return the maximum latitude boundary of this ShakeMap
  328.    */
  329.   public BigDecimal getMaximumLatitude() {
  330.     return getBigDecimal(getProperties().get(MAXIMUM_LATITUDE_PROPERTY));
  331.   }

  332.   /**
  333.    * @param maximumLatitude the maximum latitude to set
  334.    */
  335.   public void setMaximumLatitude(BigDecimal maximumLatitude) {
  336.     getProperties().put(MAXIMUM_LATITUDE_PROPERTY, maximumLatitude.toPlainString());
  337.   }

  338.   /**
  339.    * Returns String value as BigDecimal
  340.    *
  341.    * @param value to return as BigDecimal
  342.    * @return a BigDecimal
  343.    */
  344.   protected BigDecimal getBigDecimal(String value) {
  345.     if (value == null) {
  346.       return null;
  347.     }
  348.     return new BigDecimal(value);
  349.   }

  350.   /**
  351.    * Returns BigDecimal value as String
  352.    *
  353.    * @param value a BigDecimal
  354.    * @return a string
  355.    */
  356.   protected String getString(BigDecimal value) {
  357.     if (value == null) {
  358.       return null;
  359.     }
  360.     return value.toString();
  361.   }

  362. }