/* * Copyright (c) 2014 Oculus Info Inc. http://www.oculusinfo.com/ * * Released under the MIT License. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.oculusinfo.tile.rendering; import com.oculusinfo.binning.TileIndex; import com.oculusinfo.binning.TilePyramid; import com.oculusinfo.binning.io.PyramidIO; import com.oculusinfo.binning.io.serialization.TileSerializer; import com.oculusinfo.binning.properties.TileIndexProperty; import com.oculusinfo.factory.ConfigurableFactory; import com.oculusinfo.factory.ConfigurationException; import com.oculusinfo.factory.ConfigurationProperty; import com.oculusinfo.factory.properties.*; import com.oculusinfo.factory.providers.FactoryProvider; import com.oculusinfo.factory.util.Pair; import com.oculusinfo.tile.rendering.transformations.combine.TileCombinerFactory; import com.oculusinfo.tile.rendering.transformations.tile.TileTransformer; import com.oculusinfo.tile.rendering.transformations.value.ValueTransformerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** * The root node of the ConfigurableFactory tree that represents a layer's configured * state. Each layer can be represented as a JSON object. * * Nodes that contain values are represented by ConfigurableProperty objects. * Nodes that contain class enumerations and are represented by ConfigurableFactory objects. * * @author nkronenfeld, kbirk */ public class LayerConfiguration extends ConfigurableFactory<LayerConfiguration> { private static final Logger LOGGER = LoggerFactory.getLogger(LayerConfiguration.class); /** * public configuration paths, properties under these paths are accessible to the client. */ public static final List<String> TILE_COMBINE_PATH = Collections.unmodifiableList(Arrays.asList("public", "tileCombiner")); public static final List<String> TILE_TRANSFORM_PATH = Collections.unmodifiableList(Arrays.asList("public", "tileTransform")); public static final List<String> ALPHA_VALUE_TRANSFORM_PATH = Collections.unmodifiableList( Arrays.asList( "public","alphaValueTransform" ) ); public static final List<String> VALUE_TRANSFORM_PATH = Collections.unmodifiableList( Arrays.asList( "public","valueTransform" ) ); public static final List<String> FILTER_PATH = Collections.unmodifiableList( Arrays.asList( "public", "filter" ) ); public static final List<String> TILE_PYRAMID_PATH = Collections.unmodifiableList( Arrays.asList( "public", "pyramid" ) ); public static final List<String> RENDERER_PATH = Collections.unmodifiableList( Arrays.asList( "public", "renderer" ) ); /** * private configuration paths, properties under public nodes are not accessible to the client */ public static final List<String> DATA_PATH = Collections.unmodifiableList( Arrays.asList( "private", "data" ) ); public static final List<String> PYRAMID_IO_PATH = Collections.unmodifiableList( Arrays.asList( "private", "data","pyramidio" ) ); public static final List<String> SERIALIZER_PATH = Collections.unmodifiableList( Arrays.asList( "private", "data","serializer" ) ); public static final List<String> REST_ENDPOINT_PATH = Collections.unmodifiableList( Arrays.asList( "private" ) ); public static final String DEFAULT_VERSION = "v1.0"; public static final JSONProperty FILTER_PROPS = new JSONProperty("filterProps", "Filter properties that will be used in elasticsearch queries", null); public static final StringProperty LAYER_ID = new StringProperty("id", "The ID of the layer", null); public static final StringProperty DATA_ID = new StringProperty("id", "The ID of the data source of the layer; exact format depends on how the layer is stored.", null); public static final StringProperty NORMALIZATION_DATA_ID = new StringProperty("id", "Stuff", null); public static final StringProperty REST_ENDPOINT = new StringProperty("restEndpoint", "The REST endpoint used for the layer, defaults to 'tile'", "tile"); public static final IntegerProperty COARSENESS = new IntegerProperty("coarseness", "Used by the standard heatmap renderer to allow the client to specify getting coarser tiles than needed, for efficiency (if needed)", 1); public static final StringProperty PIXEL_SHAPE = new StringProperty("pixelShape", "Used by the standard heatmap renderer to allow either circular or square 'pixels' if coarseness > 2x2. Valid settings are 'circle' (default) or 'square'.", "circle"); public static final IntegerProperty OUTPUT_WIDTH = new IntegerProperty("outputWidth", "The output image width, defaults to the standard 256", 256); public static final IntegerProperty OUTPUT_HEIGHT = new IntegerProperty("outputHeight", "The output image height, defaults to the standard 256", 256); public static final StringProperty RANGE_MODE = new StringProperty("rangeMode", "The mode for handling range extrema", "clamp"); public static final IntegerProperty RANGE_MIN = new IntegerProperty("rangeMin", "The minimum value set to the lower bound of the color ramp spectrum", 0); public static final IntegerProperty RANGE_MAX = new IntegerProperty("rangeMax", "The maximum value set to the upper bound of the color ramp spectrum", 100); public static final TileIndexProperty TILE_COORDINATE = new TileIndexProperty("tileCoordinate", "For server use only, on a tile-by-tile basis", null); public static final StringProperty LEVEL_MINIMUMS = new StringProperty("levelMinimums", "For server use only, on a tile-by-tile basis", null); public static final StringProperty LEVEL_MAXIMUMS = new StringProperty("levelMaximums", "For server use only, on a tile-by-tile basis", null); public static final BooleanProperty ALPHA_RAMP = new BooleanProperty("alphaRamp", "In addition to using a colour ramp, an alpha ramp is applies based on bin density", false); private static Set<ConfigurationProperty<?>> LOCAL_PROPERTIES = Collections.unmodifiableSet(new HashSet<ConfigurationProperty<?>>(Arrays.asList( TILE_COORDINATE, LEVEL_MAXIMUMS, LEVEL_MINIMUMS ))); private ValueTransformerFactory _transformFactory; private ValueTransformerFactory _alphaTransformFactory; private TileIndex _tileCoordinate; private String _levelMinimum; private String _levelMaximum; public LayerConfiguration( FactoryProvider<PyramidIO> pyramidIOFactoryProvider, FactoryProvider<TilePyramid> tilePyramidFactoryProvider, FactoryProvider<TileSerializer<?>> serializationFactoryProvider, FactoryProvider<TileDataImageRenderer<?>> rendererFactoryProvider, FactoryProvider<TileTransformer<?>> tileTransformerFactoryProvider, ConfigurableFactory<?> parent, List<String> path) { this( pyramidIOFactoryProvider, tilePyramidFactoryProvider, serializationFactoryProvider, rendererFactoryProvider, tileTransformerFactoryProvider, null, parent, path); } public LayerConfiguration( FactoryProvider<PyramidIO> pyramidIOFactoryProvider, FactoryProvider<TilePyramid> tilePyramidFactoryProvider, FactoryProvider<TileSerializer<?>> serializationFactoryProvider, FactoryProvider<TileDataImageRenderer<?>> rendererFactoryProvider, FactoryProvider<TileTransformer<?>> tileTransformerFactoryProvider, String name, ConfigurableFactory<?> parent, List<String> path) { super( name, LayerConfiguration.class, parent, path ); addProperty(FILTER_PROPS, FILTER_PATH); addProperty(LAYER_ID); addProperty(REST_ENDPOINT, REST_ENDPOINT_PATH); addProperty(OUTPUT_WIDTH); addProperty(OUTPUT_HEIGHT); addProperty(DATA_ID, DATA_PATH); addProperty(COARSENESS, RENDERER_PATH); addProperty(PIXEL_SHAPE, RENDERER_PATH); addProperty(RANGE_MIN, RENDERER_PATH); addProperty(RANGE_MAX, RENDERER_PATH); addProperty(RANGE_MODE, RENDERER_PATH); addProperty(ALPHA_RAMP, RENDERER_PATH); addProperty(TILE_COORDINATE); addProperty(LEVEL_MINIMUMS); addProperty(LEVEL_MAXIMUMS); _transformFactory = new ValueTransformerFactory( "valueTransformer", this, VALUE_TRANSFORM_PATH ); addChildFactory(_transformFactory); _alphaTransformFactory = new ValueTransformerFactory( "alphaValueTransformer", this, ALPHA_VALUE_TRANSFORM_PATH ); addChildFactory( _alphaTransformFactory ); addChildFactory( rendererFactoryProvider.createFactory(this, RENDERER_PATH) ); addChildFactory( pyramidIOFactoryProvider.createFactory(this, PYRAMID_IO_PATH) ); addChildFactory( serializationFactoryProvider.createFactory(this, SERIALIZER_PATH) ); addChildFactory( tileTransformerFactoryProvider.createFactory(this, TILE_TRANSFORM_PATH) ); addChildFactory( tilePyramidFactoryProvider.createFactory(this, TILE_PYRAMID_PATH) ); addChildFactory( new TileCombinerFactory(pyramidIOFactoryProvider, serializationFactoryProvider, this, TILE_COMBINE_PATH)); } @Override protected LayerConfiguration create () { return this; } @Override public <PT> PT getPropertyValue (ConfigurationProperty<PT> property) throws ConfigurationException { if (LOCAL_PROPERTIES.contains(property)) { if (TILE_COORDINATE.equals(property)) { return property.getType().cast(_tileCoordinate); } else if (LEVEL_MAXIMUMS.equals(property)) { return property.getType().cast(_levelMaximum); } else if (LEVEL_MINIMUMS.equals(property)) { return property.getType().cast(_levelMinimum); } } return super.getPropertyValue(property); } /** * Set the tile index, and level minimum and maximum for the impending read * @param tileIndex The index of the tile to be rendererd. * @param levelMinimum The level minimum. * @param levelMaximum The level maximum. */ public void setLevelProperties (TileIndex tileIndex, String levelMinimum, String levelMaximum) { _tileCoordinate = tileIndex; _levelMaximum = levelMaximum; _levelMinimum = levelMinimum; try { TileTransformer<?> tileTransformer = produce(TileTransformer.class); if (null != tileTransformer) { Pair<Double, Double> extrema = tileTransformer.getTransformedExtrema(this); _transformFactory.setExtrema(extrema.getFirst(), extrema.getSecond()); Pair<Double, Double> rawExtrema = tileTransformer.getRawExtrema(this); _alphaTransformFactory.setExtrema(rawExtrema.getFirst(), rawExtrema.getSecond()/2); } } catch (ConfigurationException e1) { String layer; try { layer = getPropertyValue(LAYER_ID); } catch (ConfigurationException e2) { layer = "unknown layer"; } LOGGER.warn("Error determining layer-specific extrema for "+layer); } } /** * This is a placeholder for the caching configuration to override; it does * nothing in this version. * * Theoretically, it allows for a hook point for extending classes to make * last-minute preparations before actually rendering a tile, whether to * JSON or an image. * * @param layer The layer to be rendered. * @param tile The tile to be rendered * @param tileSet Any other tiles that will need to be rendered along with * this one. */ public void prepareForRendering (String layer, TileIndex tile, Iterable<TileIndex> tileSet) { // NOOP } }