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

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

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

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

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

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

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

  74.     return summary;
  75.   }

  76.   @Override
  77.   protected long getPreferredWeight(ProductSummary summary) throws Exception {
  78.     // Get the default preferred weight value from the parent class
  79.     long weight = super.getPreferredWeight(summary);

  80.     if (SHAKEMAP_ATLAS_SOURCE.equals(summary.getSource())) {
  81.       weight += SHAKEMAP_ATLAS_WEIGHT;
  82.     }

  83.     // check that shakemap has event properties and map extents
  84.     Map<String, String> properties = summary.getProperties();
  85.     if (summary.getEventLatitude() == null || summary.getEventLongitude() == null
  86.         || properties.get(ShakeMap.MINIMUM_LATITUDE_PROPERTY) == null
  87.         || properties.get(ShakeMap.MAXIMUM_LATITUDE_PROPERTY) == null
  88.         || properties.get(ShakeMap.MINIMUM_LONGITUDE_PROPERTY) == null
  89.         || properties.get(ShakeMap.MAXIMUM_LONGITUDE_PROPERTY) == null) {
  90.       return weight;
  91.     }

  92.     // Get properties for comparison to alter authoritative weight
  93.     BigDecimal eventLat = summary.getEventLatitude();
  94.     BigDecimal eventLon = summary.getEventLongitude();
  95.     BigDecimal minLat = new BigDecimal(properties.get(ShakeMap.MINIMUM_LATITUDE_PROPERTY));
  96.     BigDecimal maxLat = new BigDecimal(properties.get(ShakeMap.MAXIMUM_LATITUDE_PROPERTY));
  97.     BigDecimal minLon = new BigDecimal(properties.get(ShakeMap.MINIMUM_LONGITUDE_PROPERTY));
  98.     BigDecimal maxLon = new BigDecimal(properties.get(ShakeMap.MAXIMUM_LONGITUDE_PROPERTY));
  99.     BigDecimal centerLat = minLat.add(maxLat).divide(new BigDecimal(2));
  100.     BigDecimal centerLon = minLon.add(maxLon).divide(new BigDecimal(2));

  101.     // Calculate delta in degrees between map center and event epicenter
  102.     double latDelta = Math.abs(centerLat.doubleValue() - eventLat.doubleValue());
  103.     double lonDelta = Math.abs(centerLon.doubleValue() - eventLon.doubleValue());
  104.     double locationDelta = (double) Math.sqrt(Math.pow(latDelta, 2) + Math.pow(lonDelta, 2));

  105.     // Increase weight dynamically if the map center is within
  106.     // MAX_DELTA_DEGREES of the event epicenter
  107.     if (locationDelta <= MAX_DELTA_DEGREES) {
  108.       // Add more weight based on the map center being closer to
  109.       // the event epicenter
  110.       weight += Math.round((1 - (locationDelta / MAX_DELTA_DEGREES)) * CENTERED_ON_EPICENTER_WEIGHT);
  111.     }

  112.     // Increase weight further if the map contains the epicenter within
  113.     // its boundaries.
  114.     if (eventLat.longValue() < maxLat.longValue() && eventLat.longValue() > minLat.longValue()
  115.         && eventLon.longValue() < maxLon.longValue() && eventLon.longValue() > minLon.longValue()) {
  116.       weight += CONTAINS_EPICENTER_WEIGHT;
  117.     }

  118.     return weight;
  119.   }

  120. }