/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* Copyright (C) 2007-2008-2009 GeoSolutions S.A.S.
* http://www.geo-solutions.it
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.sldservice.rest.resource;
import java.awt.Color;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.SLDHandler;
import org.geoserver.catalog.StyleHandler;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.Styles;
import org.geoserver.catalog.rest.AbstractCatalogResource;
import org.geoserver.catalog.rest.StyleFormat;
import org.geoserver.rest.RestletException;
import org.geoserver.rest.format.DataFormat;
import org.geoserver.rest.format.MediaTypes;
import org.geoserver.sldservice.utils.classifier.ColorRamp;
import org.geoserver.sldservice.utils.classifier.impl.BlueColorRamp;
import org.geoserver.sldservice.utils.classifier.impl.CustomColorRamp;
import org.geoserver.sldservice.utils.classifier.impl.GrayColorRamp;
import org.geoserver.sldservice.utils.classifier.impl.JetColorRamp;
import org.geoserver.sldservice.utils.classifier.impl.RandomColorRamp;
import org.geoserver.sldservice.utils.classifier.impl.RedColorRamp;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.styling.ColorMap;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.Rule;
import org.geotools.styling.Style;
import org.geotools.styling.StyleBuilder;
import org.geotools.styling.Symbolizer;
import org.geotools.util.Version;
import org.opengis.filter.FilterFactory2;
import org.restlet.Context;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;
/**
* @author Alessio Fabiani, GeoSolutions SAS
*/
public class RasterizerResource extends AbstractCatalogResource {
private final static Logger LOGGER = Logger.getLogger(RasterizerResource.class.toString());
/**
* media type for SLD
*/
public static final MediaType MEDIATYPE_SLD = new MediaType( "application/vnd.ogc.sld+xml" );
static {
MediaTypes.registerExtension( "sld", MEDIATYPE_SLD );
}
public enum COLORRAMP_TYPE {RED, BLUE, GRAY, JET, RANDOM, CUSTOM};
public enum COLORMAP_TYPE {RAMP, INTERVALS, VALUES};
private static final double DEFAULT_MIN = 0.0;
private static final double DEFAULT_MAX = 100.0;
private static final double DEFAULT_MIN_DECREMENT = 0.000000001;
private static final int DEFAULT_CLASSES = 100;
private static final int DEFAULT_DIGITS = 5;
private static final int DEFAULT_TYPE = ColorMap.TYPE_RAMP;
public RasterizerResource(Context context, Request request, Response response, Catalog catalog) {
super(context, request, response, StyleInfo.class, catalog);
}
@Override
protected List<DataFormat> createSupportedFormats(Request request,Response response) {
List<DataFormat> formats = super.createSupportedFormats(request,response);
StyleHandler sh = Styles.handler(SLDHandler.MIMETYPE_10);
Version ver = sh.versionForMimeType(SLDHandler.MIMETYPE_10);
formats.add(new StyleFormat(sh.mimeType(ver), ver, false, sh, request, catalog.getResourcePool().getEntityResolver()));
return formats;
}
@Override
protected Object handleObjectGet() throws Exception {
Request req = getRequest();
Form parameters = req.getResourceRef().getQueryAsForm();
String layer = getAttribute("layer");
if (layer == null) {
return new ArrayList();
}
double min = parameters.getFirstValue("min") != null ? Double.parseDouble(parameters.getFirstValue("min")) : DEFAULT_MIN;
double max = parameters.getFirstValue("max") != null ? Double.parseDouble(parameters.getFirstValue("max")) : DEFAULT_MAX;
int classes = parameters.getFirstValue("classes") != null ? Integer.parseInt(parameters.getFirstValue("classes")) : DEFAULT_CLASSES;
int digits = parameters.getFirstValue("digits") != null ? Integer.parseInt(parameters.getFirstValue("digits")) : DEFAULT_DIGITS;
final String type = parameters.getFirstValue("type");
final String startColor = parameters.getFirstValue("startColor");
final String endColor = parameters.getFirstValue("endColor");
final String midColor = parameters.getFirstValue("midColor");
int colormapType = DEFAULT_TYPE;
if (type != null){
if (type.equalsIgnoreCase(COLORMAP_TYPE.INTERVALS.toString())){
colormapType = ColorMap.TYPE_INTERVALS;
} else if (type.equalsIgnoreCase(COLORMAP_TYPE.VALUES.toString())){
colormapType = ColorMap.TYPE_VALUES;
}
}
COLORRAMP_TYPE ramp = parameters.getFirstValue("ramp") != null ? COLORRAMP_TYPE.valueOf(parameters.getFirstValue("ramp").toUpperCase()) : COLORRAMP_TYPE.RED;
if (min == max)
min = min - Double.MIN_VALUE;
LayerInfo layerInfo = catalog.getLayerByName(layer);
if (layerInfo != null) {
ResourceInfo obj = layerInfo.getResource();
/* Check if it's feature type or coverage */
if (obj instanceof CoverageInfo) {
CoverageInfo cvInfo;
cvInfo = (CoverageInfo) obj;
StyleInfo defaultStyle = layerInfo.getDefaultStyle();
RasterSymbolizer rasterSymbolizer = getRasterSymbolizer(defaultStyle);
if (rasterSymbolizer == null) {
throw new RestletException( "RasterSymbolizer SLD expected!", Status.CLIENT_ERROR_EXPECTATION_FAILED);
}
Style rasterized = remapStyle(defaultStyle, rasterSymbolizer, min, max, classes, ramp, layer, digits, colormapType, startColor, endColor, midColor);
//check the format, if specified as sld, return the sld itself
DataFormat format = getFormatGet();
if ( format instanceof StyleFormat ) {
return rasterized;
}
return defaultStyle;
}
}
return new ArrayList();
}
/**
*
* @param defaultStyle
* @param rasterSymbolizer
* @param layerName
* @param midColor
* @param endColor
* @param startColor
*
* @throws Exception
*/
private Style remapStyle(StyleInfo defaultStyle, RasterSymbolizer rasterSymbolizer, double min, double max,
int classes, COLORRAMP_TYPE ramp, String layerName, final int digits, final int colorMapType, String startColor, String endColor, String midColor) throws Exception {
StyleBuilder sb = new StyleBuilder();
ColorMap originalColorMap = rasterSymbolizer.getColorMap();
ColorMap resampledColorMap = null;
int numClasses = originalColorMap.getColorMapEntries().length;
// if (numClasses > 0) {
// resampledColorMap = originalColorMap;
// double res = (max - min) / (numClasses - 1);
// int c = 0;
// for (ColorMapEntry cmEntry : resampledColorMap.getColorMapEntries()) {
// cmEntry.setQuantity(sb.literalExpression(min + res * c));
// c++;
// }
// } else
if (classes > 0) {
final String[] labels = new String[classes+1];
final double[] quantities = new double[classes+1];
ColorRamp colorRamp = null;
quantities[0] = min - DEFAULT_MIN_DECREMENT;
if (colorMapType == ColorMap.TYPE_INTERVALS){
max = max + DEFAULT_MIN_DECREMENT;
min = min + DEFAULT_MIN_DECREMENT;
}
double res = (max - min) / (classes - 1);
labels[0] = "transparent";
final String format ="%." + digits + "f";
for (int c = 1; c <= classes; c++) {
quantities[c] = min + res * (c-1);
labels[c] = String.format(Locale.US, format, quantities[c]);
}
switch (ramp) {
case RED:
colorRamp = new RedColorRamp();
break;
case BLUE:
colorRamp = new BlueColorRamp();
break;
case GRAY:
colorRamp = new GrayColorRamp();
break;
case JET:
colorRamp = new JetColorRamp();
break;
case RANDOM:
colorRamp = new RandomColorRamp();
break;
case CUSTOM:
colorRamp = new CustomColorRamp();
CustomColorRamp customRamp = (CustomColorRamp)colorRamp;
if(startColor != null) {
customRamp.setStartColor(Color.decode(startColor));
}
if(endColor != null) {
customRamp.setEndColor(Color.decode(endColor));
}
if(midColor != null) {
customRamp.setMid(Color.decode(midColor));
}
break;
}
colorRamp.setNumClasses(classes);
List<Color> realColorRamp = new ArrayList<Color>();
realColorRamp.add(Color.BLACK);
realColorRamp.addAll(colorRamp.getRamp());
resampledColorMap = sb.createColorMap(
labels,
quantities,
realColorRamp.toArray(new Color[1]),
colorMapType
);
FilterFactory2 filterFactory = CommonFactoryFinder.getFilterFactory2(null);
resampledColorMap.getColorMapEntry(0).setOpacity(filterFactory.literal(0));
} else {
return defaultStyle.getStyle();
}
// StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory(null);
// RasterSymbolizer symbolizer = styleFactory.createRasterSymbolizer();
// symbolizer.setColorMap(resampledColorMap);
rasterSymbolizer.setColorMap(resampledColorMap);
Style style = sb.createStyle(layerName, rasterSymbolizer);
return style;
}
/**
*
* @param defaultStyle
*
*/
private RasterSymbolizer getRasterSymbolizer(StyleInfo sInfo) {
RasterSymbolizer rasterSymbolizer = null;
try {
for (FeatureTypeStyle ftStyle : sInfo.getStyle().getFeatureTypeStyles()) {
for (Rule rule : ftStyle.getRules()) {
for (Symbolizer sym : rule.getSymbolizers()) {
if (sym instanceof RasterSymbolizer) {
rasterSymbolizer = (RasterSymbolizer) sym;
break;
}
}
if (rasterSymbolizer != null) break;
}
if (rasterSymbolizer != null) break;
}
} catch (IOException e) {
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, "The following exception has occurred "
+ e.getLocalizedMessage(), e);
return null;
}
return rasterSymbolizer;
}
@Override
public boolean allowPost() {
return false;
}
@Override
protected String handleObjectPost(Object object) {
return null;
}
@Override
protected void handleObjectPut(Object object) {
// do nothing, we do not allow post
}
}