ShakeMapIndexerModule.java

  1. package gov.usgs.earthquake.shakemap;

  2. import java.awt.image.BufferedImage;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.math.BigDecimal;
  6. import java.util.Map;
  7. import java.util.logging.Level;
  8. import java.util.logging.Logger;

  9. import javax.imageio.ImageIO;

  10. import gov.usgs.earthquake.indexer.DefaultIndexerModule;
  11. import gov.usgs.earthquake.indexer.IndexerModule;
  12. import gov.usgs.earthquake.indexer.ProductSummary;
  13. import gov.usgs.earthquake.product.Content;
  14. import gov.usgs.earthquake.product.Product;
  15. import gov.usgs.util.StreamUtils;

  16. /**
  17.  * ShakeMap Indexer Module.
  18.  *
  19.  * Provides a higher and more specific level of support for ShakeMap products,
  20.  * including reading additional product information out of the ShakeMap content
  21.  * files provided with the Product and placing it into the ProductSummary for
  22.  * the Product itself.
  23.  */
  24. public class ShakeMapIndexerModule extends DefaultIndexerModule {

  25.     private static final Logger LOGGER = Logger
  26.             .getLogger(ShakeMapIndexerModule.class.getName());

  27.     /** Path to overlay img */
  28.     public static final String OVERLAY_IMAGE_PATH = "download/ii_overlay.png";
  29.     /** Property for overlay width */
  30.     public static final String OVERLAY_WIDTH_PROPERTY = "overlayWidth";
  31.     /** Property for overlay height */
  32.     public static final String OVERLAY_HEIGHT_PROPERTY = "overlayHeight";

  33.     /** CONTAINS_EPICENTER_WEIGHT */
  34.     public static final int CONTAINS_EPICENTER_WEIGHT = 50;
  35.     /** CENTERED_ON_EPICENTER_WEIGHT */
  36.     public static final int CENTERED_ON_EPICENTER_WEIGHT = 25;
  37.     /** Number of degrees at which no additional weight will be
  38.      * assigned based on the proximity of the map center to the
  39.      * epicenter.
  40.      */
  41.     public static final double MAX_DELTA_DEGREES = 2.0;

  42.     /** ShakeMap atlas is the most preferred ShakeMap contributor */
  43.     public static final String SHAKEMAP_ATLAS_SOURCE = "atlas";
  44.     /** Atlas weight */
  45.     public static final int SHAKEMAP_ATLAS_WEIGHT = 200;

  46.     @Override
  47.     public int getSupportLevel(Product product) {
  48.         int supportLevel = IndexerModule.LEVEL_UNSUPPORTED;
  49.         String type = getBaseProductType(product.getId().getType());
  50.         // Support only ShakeMap products that contain grid.xml
  51.         if (type.equals("shakemap") && product.getContents().containsKey(
  52.                 ShakeMap.GRID_XML_ATTACHMENT))
  53.             supportLevel = IndexerModule.LEVEL_SUPPORTED;
  54.         return supportLevel;
  55.     }

  56.     @Override
  57.     public ProductSummary getProductSummary(Product product) throws Exception {
  58.         // Load additional properties into the ProductSummary by loading these
  59.         // properties specifically through a ShakeMap product
  60.         ProductSummary summary = super.getProductSummary(new ShakeMap(product));

  61.         Content overlayImage = product.getContents().get(OVERLAY_IMAGE_PATH);
  62.         if (overlayImage != null) {
  63.             InputStream overlayInputStream = null;
  64.             try {
  65.                 overlayInputStream = overlayImage.getInputStream();
  66.                 BufferedImage info = ImageIO.read(overlayInputStream);
  67.                 summary.getProperties().put(OVERLAY_WIDTH_PROPERTY,
  68.                         Integer.toString(info.getWidth()));
  69.                 summary.getProperties().put(OVERLAY_HEIGHT_PROPERTY,
  70.                         Integer.toString(info.getHeight()));
  71.                 LOGGER.finest("overlay width=" + info.getWidth() +
  72.                         ", overlay height=" + info.getHeight());
  73.             } catch (IOException e) {
  74.                 LOGGER.log(Level.WARNING, "exception reading "
  75.                         + OVERLAY_IMAGE_PATH + " width/height", e);
  76.             } finally {
  77.                 StreamUtils.closeStream(overlayInputStream);
  78.             }
  79.         }

  80.         return summary;
  81.     }

  82.     @Override
  83.     protected long getPreferredWeight(ProductSummary summary)
  84.             throws Exception {
  85.         // Get the default preferred weight value from the parent class
  86.         long weight = super.getPreferredWeight(summary);

  87.         if (SHAKEMAP_ATLAS_SOURCE.equals(summary.getSource())) {
  88.             weight += SHAKEMAP_ATLAS_WEIGHT;
  89.         }

  90.         // check that shakemap has event properties and map extents
  91.         Map<String, String> properties = summary.getProperties();
  92.         if (summary.getEventLatitude() == null ||
  93.                 summary.getEventLongitude() == null ||
  94.                 properties.get(ShakeMap.MINIMUM_LATITUDE_PROPERTY) == null ||
  95.                 properties.get(ShakeMap.MAXIMUM_LATITUDE_PROPERTY) == null ||
  96.                 properties.get(ShakeMap.MINIMUM_LONGITUDE_PROPERTY) == null ||
  97.                 properties.get(ShakeMap.MAXIMUM_LONGITUDE_PROPERTY) == null) {
  98.             return weight;
  99.         }

  100.         // Get properties for comparison to alter authoritative weight
  101.         BigDecimal eventLat = summary.getEventLatitude();
  102.         BigDecimal eventLon = summary.getEventLongitude();
  103.         BigDecimal minLat = new BigDecimal(properties.get(
  104.                 ShakeMap.MINIMUM_LATITUDE_PROPERTY));
  105.         BigDecimal maxLat = new BigDecimal(properties.get(
  106.                 ShakeMap.MAXIMUM_LATITUDE_PROPERTY));
  107.         BigDecimal minLon = new BigDecimal(properties.get(
  108.                 ShakeMap.MINIMUM_LONGITUDE_PROPERTY));
  109.         BigDecimal maxLon = new BigDecimal(properties.get(
  110.                 ShakeMap.MAXIMUM_LONGITUDE_PROPERTY));
  111.         BigDecimal centerLat = minLat.add(maxLat).divide(new BigDecimal(2));
  112.         BigDecimal centerLon = minLon.add(maxLon).divide(new BigDecimal(2));

  113.         // Calculate delta in degrees between map center and event epicenter
  114.         double latDelta = Math.abs(centerLat.doubleValue() - eventLat.doubleValue());
  115.         double lonDelta = Math.abs(centerLon.doubleValue() - eventLon.doubleValue());
  116.         double locationDelta = (double) Math.sqrt(Math.pow(latDelta, 2)
  117.                 + Math.pow(lonDelta, 2));

  118.         // Increase weight dynamically if the map center is within
  119.         // MAX_DELTA_DEGREES of the event epicenter
  120.         if (locationDelta <= MAX_DELTA_DEGREES) {
  121.             // Add more weight based on the map center being closer to
  122.             // the event epicenter
  123.             weight += Math.round((1 - (locationDelta / MAX_DELTA_DEGREES))
  124.                     * CENTERED_ON_EPICENTER_WEIGHT);
  125.         }

  126.         // Increase weight further if the map contains the epicenter within
  127.         // its boundaries.
  128.         if (eventLat.longValue() < maxLat.longValue()
  129.                 && eventLat.longValue() > minLat.longValue()
  130.                 && eventLon.longValue() < maxLon.longValue()
  131.                 && eventLon.longValue() > minLon.longValue()) {
  132.             weight += CONTAINS_EPICENTER_WEIGHT;
  133.         }

  134.         return weight;
  135.     }

  136. }