ngageoint / elasticgeo

ElasticGeo provides a GeoTools data store that allows geospatial features from an Elasticsearch index to be published via OGC services using GeoServer.
GNU General Public License v3.0
169 stars 85 forks source link

MetricGeoHashGrid #27

Closed nreese closed 7 years ago

nreese commented 7 years ago

New GeoHashGrid implementation that allows for pulling metric values from the geohashgrid bucket.

Should there be better error handling? What if the bucket does not contain the key metric? How should this method respond? I was thinking that maybe computeCellValue method should throw an exception in these situations. How does that sound? What should the Exception type be?

Is there any way to pass parameters to GeoHasGrid from the SLD? Maybe the key should not be hard coded but passed via a parameter. Thoughts?

Usage example with the max metric

<html>
  <head>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@0.7.7/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@0.7.7/dist/leaflet.js"></script>
  </head>
  <body>
    <div id="mapid" style="height: 500px; width: 1000px;"></div>
    <script type="text/javascript">
      var mymap = L.map('mapid').setView([0, -160], 2);
      L.tileLayer(
        'http://a.tile.openstreetmap.org/{z}/{x}/{y}.png', 
        {
          attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors'
        }).addTo(mymap);

      var wmsOptions = {
        format: 'image/png',
        layers: 'elastic:event',
        transparent: true,
        version: '1.1.1',
        styles: 'earthquake',
        viewparams: 'a:{"aggs": {"geohash_grid": {"field": "location"\\,"precision": 2}\\,"aggs": {"metric": {"max": {"field": "magnitude"}}}}}'
      };
      L.tileLayer.wms(
        'http://localhost:8080/geoserver/elastic/wms', 
        wmsOptions).addTo(mymap);
    </script>
  </body>
</html>
<StyledLayerDescriptor version="1.0.0"
    xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd"
    xmlns="http://www.opengis.net/sld"
    xmlns:ogc="http://www.opengis.net/ogc"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <NamedLayer>
    <Name>GeoHashGrid</Name>
    <UserStyle>
      <Title>GeoHashGrid</Title>
      <Abstract>GeoHashGrid aggregation</Abstract>
      <FeatureTypeStyle>
        <Transformation>
          <ogc:Function name="vec:GeoHashGrid">
            <ogc:Function name="parameter">
              <ogc:Literal>data</ogc:Literal>
            </ogc:Function>
            <ogc:Function name="parameter">
              <ogc:Literal>gridStrategy</ogc:Literal>
              <ogc:Literal>Metric</ogc:Literal>
            </ogc:Function>
            <ogc:Function name="parameter">
              <ogc:Literal>pixelsPerCell</ogc:Literal>
              <ogc:Literal>1</ogc:Literal>
            </ogc:Function>
            <ogc:Function name="parameter">
              <ogc:Literal>outputBBOX</ogc:Literal>
              <ogc:Function name="env">
                <ogc:Literal>wms_bbox</ogc:Literal>
              </ogc:Function>
            </ogc:Function>
            <ogc:Function name="parameter">
              <ogc:Literal>outputWidth</ogc:Literal>
              <ogc:Function name="env">
                <ogc:Literal>wms_width</ogc:Literal>
              </ogc:Function>
            </ogc:Function>
            <ogc:Function name="parameter">
              <ogc:Literal>outputHeight</ogc:Literal>
              <ogc:Function name="env">
                <ogc:Literal>wms_height</ogc:Literal>
              </ogc:Function>
            </ogc:Function>
          </ogc:Function>
        </Transformation>
        <Rule>
         <RasterSymbolizer>
           <Geometry>
             <!-- Actual geometry property name in feature source -->
             <ogc:PropertyName>location</ogc:PropertyName></Geometry>
           <Opacity>0.6</Opacity>
           <ColorMap type="ramp" >
             <ColorMapEntry color="#FFFFFF" quantity="0" label="nodata" opacity="0"/>
             <ColorMapEntry color="#ffffb2" quantity="1" label="label"/>
             <ColorMapEntry color="#fed976" quantity="2" label="label"/>
             <ColorMapEntry color="#feb24c" quantity="3" label="label"/>
             <ColorMapEntry color="#fd8d3c" quantity="4" label="label"/>
             <ColorMapEntry color="#fd8d3c" quantity="4" label="label"/>
             <ColorMapEntry color="#fc4e2a" quantity="5" label="label"/>
             <ColorMapEntry color="#e31a1c" quantity="6" label="label"/>
             <ColorMapEntry color="#b10026" quantity="7" label="label"/>
             <ColorMapEntry color="#800026" quantity="11" label="label"/>
           </ColorMap>
         </RasterSymbolizer>
        </Rule>
      </FeatureTypeStyle>
    </UserStyle>
  </NamedLayer>
 </StyledLayerDescriptor>
sjudeng commented 7 years ago

Thanks for the contribution. Can you update documentation showing basic usage? Maybe just a table showing the supported grid strategies (basic, metric) with description.

Supporting throwing exceptions in computeCellValue makes sense to me as well. I don't have any preference on type of exception (new custom exception, IllegalArgumentException, etc.) but whatever it is it should probably be caught and wrapped in a ProcessException in GeoHashGridProcess. If you get in there feel free to remove the iae.printStackTrace(); ... just noticed that and not sure how it snuck in.

I also think supporting an argument for the key makes sense. To generalize the capability I'd recommend adding support for a new parameter, computeArgs, in GeoHashGridProcess. Hopefully the type could be String[] or List<String>. The user would provide these args through another sld function parameter and they'd then be passed on to the computeCellValue implementation. Docs could be updated describing what (if any) computeArgs are required for the given strategy. What do you think?

nreese commented 7 years ago

I am thinking about a new custom exception. That way, we can log a very friendly, easy to understand message about why the GeoHashGridProcess.execute method returned null. I am thinking this will be a very common mistake since the values are defined in two places (SLD and WMS parameter) and it will be super easy to have a mismatch. What about calling it a KeyNotFound exception?

Can SLD parameters support maps? It would be nice to have key value pairs passed to the GeoHashGrid.

sjudeng commented 7 years ago

Sounds good to me. Regarding parameters, I've only ever done parameters with basic types or feature collections. The docs say lists/arrays are supported but I'm not sure about maps. If maps aren't supported I think an array wouldn't be too bad ... docs would just have to describe what the first, second, etc. value represents for the relevant strategy.

nreese commented 7 years ago

I decided to just throw an IllegalArgumentException from inside computeCellValue when the bucket does not contain the required keys. I tried the custom exception route but lambda functions do not play nice with checked exceptions and things were getting messy.

sjudeng commented 7 years ago

This looks good to me. Are you done for now on this or are you still looking at adding support for the additional array/map computeArgs parameter? I think there's value here even without the parameter but I'll wait on merge if you're working on that.

nreese commented 7 years ago

Lets go ahead and merge. I am working on another GeoHashGrid implementation for dealing with nested buckets. I will figure out the parameter stuff on that one.