/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2017, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/
package org.geotools.mbstyle.function;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.awt.Color;
import org.geotools.data.DataUtilities;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.text.ecql.ECQL;
import org.junit.Test;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Function;
/**
* Test the {@link ExponentialFunction}, {@link ZoomLevelFunction} and {@link CSSFunction}.
*/
public class MBFunctionFactoryTest {
public static FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
@Test
public void colorFunction() throws Exception {
Function expr = (Function) ECQL.toExpression("css('#ff0000')");
assertEquals("hex", Color.red, expr.evaluate(null, Color.class));
assertEquals("hex", "#FF0000", expr.evaluate(null, String.class));
expr = (Function) ECQL.toExpression("css('red')");
assertEquals("css", Color.red, expr.evaluate(null, Color.class));
assertEquals("css", "#FF0000", expr.evaluate(null, String.class));
expr = (Function) ECQL.toExpression("css('rgb(255,0,0)')");
assertEquals("rgb", Color.red, expr.evaluate(null, Color.class));
assertEquals("rgb", "#FF0000", expr.evaluate(null, String.class));
}
@Test
public void expontentialFunctionNumeric() throws Exception {
//
// base 1.0 works as a simple interpolate
//
Function expr = (Function) ECQL.toExpression("Exponential( 0, 1.0, 0,0, 10,100)");
assertEquals( 0, (int) expr.evaluate(null,Integer.class));
expr = (Function) ECQL.toExpression("Exponential( 5, 1.0, 0,0, 10,100)");
assertEquals( 50, (int) expr.evaluate(null,Integer.class));
expr = (Function) ECQL.toExpression("Exponential(10, 1.0, 0,0, 10,100)");
assertEquals(100, (int) expr.evaluate(null,Integer.class));
//
// base 0.7 increases curve
//
expr = (Function) ECQL.toExpression("Exponential( 0, 0.7, 0,0, 10,100)");
assertEquals( 0, (int) expr.evaluate(null,Integer.class));
expr = (Function) ECQL.toExpression("Exponential( 5, 0.7, 0,0, 10,100)");
assertTrue( 50 < (int) expr.evaluate(null,Integer.class));
expr = (Function) ECQL.toExpression("Exponential(10, 0.7, 0,0, 10,100)");
assertEquals(100, (int) expr.evaluate(null,Integer.class));
//
// base 1.4 decreases curve
//
expr = (Function) ECQL.toExpression("Exponential( 0, 1.4, 0,0, 10,100)");
assertEquals( 0, (int) expr.evaluate(null,Integer.class));
expr = (Function) ECQL.toExpression("Exponential( 5, 1.4, 0,0, 10,100)");
assertTrue( 50 > (int) expr.evaluate(null,Integer.class));
expr = (Function) ECQL.toExpression("Exponential(10, 1.5, 0,0, 10,100)");
assertEquals(100, (int) expr.evaluate(null,Integer.class));
}
@Test
public void expontentialFunctionColor() throws Exception {
//
// base 1.0 works as a simple interpolate
//
Function expr = (Function) ECQL.toExpression("Exponential( 0, 1.0, 0,'#000000', 10,'#ffffff')");
assertEquals( Color.BLACK, expr.evaluate(null,Color.class));
expr = (Function) ECQL.toExpression("Exponential( 5, 1.0, 0,'#000000', 10,'#ffffff')");
assertEquals( Color.GRAY, expr.evaluate(null,Color.class));
expr = (Function) ECQL.toExpression("Exponential(10, 1.0, 0,'#000000', 10,'#ffffff')");
assertEquals( Color.WHITE, expr.evaluate(null,Color.class));
//
// base 0.7 increases curve
//
expr = (Function) ECQL.toExpression("Exponential( 0, 0.7, 0,'#000000', 10,'#ffffff')");
assertEquals( Color.BLACK, expr.evaluate(null,Color.class));
expr = (Function) ECQL.toExpression("Exponential( 5, 0.7, 0,'#000000', 10,'#ffffff')");
assertTrue( Color.GRAY.getRed() < expr.evaluate(null,Color.class).getRed() );
expr = (Function) ECQL.toExpression("Exponential(10, 0.7, 0,'#000000', 10,'#ffffff')");
assertEquals( Color.WHITE, expr.evaluate(null,Color.class));
//
// base 1.4 decreases curve
//
expr = (Function) ECQL.toExpression("Exponential( 0, 1.4, 0,'#000000', 10,'#ffffff')");
assertEquals( Color.BLACK, expr.evaluate(null,Color.class));
expr = (Function) ECQL.toExpression("Exponential( 5, 1.4, 0,'#000000', 10,'#ffffff')");
assertTrue( Color.GRAY.getRed() > expr.evaluate(null,Color.class).getRed() );
expr = (Function) ECQL.toExpression("Exponential(10, 1.4, 0,'#000000', 10,'#ffffff')");
assertEquals( Color.WHITE, expr.evaluate(null,Color.class));
}
/**
* Tests for the {@link DefaultIfNullFunction} to verify that it falls back to the provided value
* when the input value is null in various contexts.
*/
@Test
public void defaultIfNullFunctionTest() throws Exception {
// Test that is passes through non-null values
Function f = (Function) ECQL.toExpression("DefaultIfNull('#FF0000', '#000000')");
assertEquals(Color.RED, f.evaluate(null, Color.class));
// Test fallback to default when input arg is null
f = ff.function("DefaultIfNull", ff.literal(null), ff.literal("#000000"));
assertEquals(Color.BLACK, f.evaluate(null, Color.class));
// Test fallback to default when the input conversion fails and returns null
SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE",
"id:\"\",temperature:0.0,location=4326");
SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|NOT_A_VALID_NUMBER|POINT(0,0)");
f = ff.function("DefaultIfNull", ff.property("temperature"), ff.literal(789));
assertEquals(789, f.evaluate(feature, Number.class).intValue());
// Test fallback to default when exponential fails and returns null)
Function exp = ff.function("Exponential", ff.property("temperature"), ff.literal(1), ff.literal(0), ff.literal("#000000"), ff.literal(10),ff.literal("#ffffff"));
f = ff.function("DefaultIfNull", exp, ff.literal(789));
assertEquals(789, f.evaluate(feature, Number.class));
// Test fallback to default when recode returns null
Function recode = ff.function("Recode", ff.property("temperature"), ff.literal(1), ff.literal("Output1"), ff.literal(2), ff.literal("Output2"));
f = ff.function("DefaultIfNull", recode, ff.literal("DefautValue"));
assertEquals("DefautValue", f.evaluate(feature, String.class));
}
/**
* Tests for {@link ZoomLevelFunction}, converting scale 3857 denominators to zoom levels.
*/
@Test
public void zoomFunctionTest() throws Exception {
double tol = .00000001;
Function f = (Function) ECQL.toExpression("zoomLevel(" + ZoomLevelFunction.EPSG_3857_O_SCALE +", 'EPSG:3857')");
assertEquals(0.0, f.evaluate(null, Number.class).doubleValue(), tol);
Function f2 = (Function) ECQL.toExpression("zoomLevel(" + (ZoomLevelFunction.EPSG_3857_O_SCALE / 2.0) +", 'EPSG:3857')");
assertEquals(1.0, f2.evaluate(null, Number.class).doubleValue(), tol);
Function fhalf = (Function) ECQL.toExpression("zoomLevel(" + (ZoomLevelFunction.EPSG_3857_O_SCALE / (Math.pow(2.0, 0.5))) +", 'EPSG:3857')");
assertEquals(0.5, fhalf.evaluate(null, Number.class).doubleValue(), tol);
double scaleDenomForZoom22 = 133.295598972;
Function f22 = (Function) ECQL.toExpression("zoomLevel(" + scaleDenomForZoom22 +", 'EPSG:3857')");
assertEquals(22.0, f22.evaluate(null, Number.class).doubleValue(), tol);
}
}