/* * 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.parse; import static org.geotools.mbstyle.parse.MBStyleTestUtils.categories; import static org.geotools.mbstyle.parse.MBStyleTestUtils.equalInt; import static org.geotools.mbstyle.parse.MBStyleTestUtils.evaluatesTo; import static org.hamcrest.Matchers.any; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; import java.awt.Color; import java.util.EnumSet; import java.util.Enumeration; import java.util.List; import org.geotools.data.DataUtilities; import org.geotools.factory.CommonFactoryFinder; import org.geotools.filter.function.EnvFunction; import org.geotools.filter.text.ecql.ECQL; import org.geotools.mbstyle.layer.LineMBLayer.LineJoin; import org.geotools.mbstyle.MapboxTestUtils; import org.geotools.mbstyle.parse.MBFunction.FunctionType; import org.json.simple.JSONObject; 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.Expression; import org.opengis.filter.expression.Function; import org.opengis.filter.expression.Literal; import org.opengis.filter.expression.PropertyName; /** * Try out property and zoom function. * * See example https://www.mapbox.com/help/gl-dds-ref/ online. */ public class MBFunctionTest { public static FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(); @Test public void colorIdentityTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326,color:java.awt.Color,text:String:"); SimpleFeature feature1 = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)|#FF0000|red"); SimpleFeature feature2 = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)|#0000FF|rgb(0,0,255)"); // color test MBFunction function = new MBFunction(MapboxTestUtils.object("{'property':'color','type':'identity'}")); assertTrue("property", function.category().contains(MBFunction.FunctionCategory.PROPERTY)); assertTrue("identity", function.getType() == FunctionType.IDENTITY ); Expression color = function.color(); assertEquals("color",Color.RED, color.evaluate(feature1, Color.class)); assertEquals("color",Color.BLUE, color.evaluate(feature2, Color.class)); function = new MBFunction(MapboxTestUtils.object("{'property':'text','type':'identity'}")); Expression text= function.color(); assertEquals("text",Color.RED, text.evaluate(feature1, Color.class)); assertEquals("text",Color.BLUE, text.evaluate(feature2, Color.class)); } /** * Assert that the color identity function correctly returns the default value when the property is not a valid color. */ @Test public void colorIdentityDefaultValueTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326,color:java.awt.Color,text:String:"); MBFunction function = new MBFunction( MapboxTestUtils.object("{'property':'color','type':'identity', 'default':'#FFFFFF'}")); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)|NOT_A_VALID_COLOR|NOT_A_VALID_COLOR"); assertEquals("Default", Color.WHITE, function.color().evaluate(feature, Color.class)); } @Test public void propertyColorStops() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); JSONObject json = MapboxTestUtils.object("{'property':'temperature','type':'exponential','stops':[[0,'blue'],[100,'red']]}"); MBFunction function = new MBFunction(json); assertEquals("temperature", function.getProperty()); assertTrue("property", function.category().contains(MBFunction.FunctionCategory.PROPERTY)); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); Function fn = (Function) function.color(); assertNotNull(fn); assertEquals("Interpolate", fn.getName()); Expression property = fn.getParameters().get(0); assertEquals("temperature", ECQL.toCQL(property)); Literal stop1 = (Literal) fn.getParameters().get(1); assertEquals(new Integer(0), stop1.evaluate(null, Integer.class)); Literal color1 = (Literal) fn.getParameters().get(2); assertEquals(Color.BLUE, color1.evaluate(null, Color.class)); Literal stop2 = (Literal) fn.getParameters().get(3); assertEquals(new Integer(100), stop2.evaluate(null, Integer.class)); Literal color2 = (Literal) fn.getParameters().get(4); assertEquals(Color.RED, color2.evaluate(null, Color.class)); // We are taking care to check that the function method has been supplied Literal method = (Literal) fn.getParameters().get(5); assertEquals("color", method.evaluate(null, String.class)); Color result = fn.evaluate(feature, Color.class); assertEquals(new Color(128, 0, 128), result); } /** * Assert that the an exponential color function correctly falls back to the default value if the input property is not numeric. */ @Test public void propertyColorStopsDefaultValue() throws Exception { 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)"); JSONObject json = MapboxTestUtils.object("{'property':'temperature','type':'exponential','stops':[[0,'blue'],[100,'red']], 'default':'#000000'}"); MBFunction function = new MBFunction(json); Color result = function.color().evaluate(feature, Color.class); assertEquals(new Color(0, 0, 0), result); } @Test public void propertyColorStopsBase() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); JSONObject json = MapboxTestUtils.object("{'property':'temperature','type':'exponential','stops':[[0,'blue'],[100,'red']],'base':1.1}"); MBFunction function = new MBFunction(json); assertEquals("temperature", function.getProperty()); assertTrue("property", function.category().contains(MBFunction.FunctionCategory.PROPERTY)); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); Function fn = (Function) function.color(); assertNotNull(fn); assertEquals("Exponential", fn.getName()); Expression property = fn.getParameters().get(0); assertEquals("temperature", ECQL.toCQL(property)); Literal base = (Literal) fn.getParameters().get(1); assertEquals(new Double(1.1), base.evaluate(null, Double.class)); Literal stop1 = (Literal) fn.getParameters().get(2); assertEquals(new Integer(0), stop1.evaluate(null, Integer.class)); Literal color1 = (Literal) fn.getParameters().get(3); assertEquals(Color.BLUE, color1.evaluate(null, Color.class)); Literal stop2 = (Literal) fn.getParameters().get(4); assertEquals(new Integer(100), stop2.evaluate(null, Integer.class)); Literal color2 = (Literal) fn.getParameters().get(5); assertEquals(Color.RED, color2.evaluate(null, Color.class)); Color result = fn.evaluate(feature, Color.class); // When the base is greater than 1, more growth should be at the end of the range. // So the interpolation on the halfway input value should be less than the halfway output value, which would be (128, 0 , 128). assertEquals(0, result.getGreen()); assertTrue(result.getBlue() > 128); assertTrue(result.getRed() < 128); } @Test public void propertyValueStops() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); JSONObject json = MapboxTestUtils.object("{'type':'exponential', 'property':'temperature','stops':[[0,5],[100,10]]}"); MBFunction function = new MBFunction(json); assertEquals("temperature", function.getProperty()); assertTrue("property", function.category().contains(MBFunction.FunctionCategory.PROPERTY)); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); Function fn = (Function) function.numeric(); assertNotNull(fn); assertEquals("Interpolate", fn.getName()); PropertyName propertyName = (PropertyName) fn.getParameters().get(0); assertEquals("temperature", propertyName.getPropertyName()); Double result = fn.evaluate(feature, Double.class); assertEquals(7.5, result, 0.0); } /** * Tests that a MapBox zoom function (outputting a color) correctly creates a GeoTools interpolation function. * @throws Exception */ @Test public void zoomColorStopsTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); EnvFunction.setGlobalValue("wms_scale_denominator", "2132.729584"); JSONObject json = MapboxTestUtils.object("{'type':'exponential','stops':[[0,'blue'],[6,'red'],[12, 'lime']]}"); MBFunction function = new MBFunction(json); assertTrue("Is a zoom function", EnumSet.of(MBFunction.FunctionCategory.ZOOM).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); Function fn = (Function) function.color(); assertNotNull(fn); assertEquals("Interpolate", fn.getName()); Expression zoomLevel = fn.getParameters().get(0); Number n = zoomLevel.evaluate(null, Number.class); // This should be zoom level 18 assertEquals(18.0, n.doubleValue(), .000001); Literal stop1 = (Literal) fn.getParameters().get(1); assertEquals(new Integer(0), stop1.evaluate(null, Integer.class)); Literal color1 = (Literal) fn.getParameters().get(2); assertEquals(Color.BLUE, color1.evaluate(null, Color.class)); Literal stop2 = (Literal) fn.getParameters().get(3); assertEquals(new Integer(6), stop2.evaluate(null, Integer.class)); Literal color2 = (Literal) fn.getParameters().get(4); assertEquals(Color.RED, color2.evaluate(null, Color.class)); Literal stop3 = (Literal) fn.getParameters().get(5); assertEquals(new Integer(12), stop3.evaluate(null, Integer.class)); Literal color3 = (Literal) fn.getParameters().get(6); assertEquals(Color.GREEN, color3.evaluate(null, Color.class)); // We are taking care to check that the function method has been supplied Literal method = (Literal) fn.getParameters().get(7); assertEquals("color", method.evaluate(null, String.class)); Color result = fn.evaluate(feature, Color.class); assertEquals(new Color(0, 255, 0), result); } /** * Tests that a MapBox linear zoom function (outputting a color) correctly interpolates color values * for zoom levels between stops. * * @throws Exception */ @Test public void zoomColorStopsInterpolationTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); EnvFunction.setGlobalValue("wms_scale_denominator", "69885282.993862"); JSONObject json = MapboxTestUtils.object("{'type':'exponential', 'stops':[[0,'blue'],[6,'red'],[12, 'lime']]}"); MBFunction function = new MBFunction(json); assertTrue("Is a zoom function", EnumSet.of(MBFunction.FunctionCategory.ZOOM).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); Function fn = (Function) function.color(); Expression zoomLevel = fn.getParameters().get(0); Number n = zoomLevel.evaluate(null, Number.class); // This should be zoom level 3 assertEquals(3.0, n.doubleValue(), .000001); Color result = fn.evaluate(feature, Color.class); assertEquals(new Color(127, 0, 128), result); EnvFunction.setGlobalValue("wms_scale_denominator", "8735660.374233"); n = zoomLevel.evaluate(null, Number.class); // This should be zoom level 6 assertEquals(6.0, n.doubleValue(), .000001); result = fn.evaluate(feature, Color.class); assertEquals(new Color(255, 0, 0), result); EnvFunction.setGlobalValue("wms_scale_denominator", "1091957.546779"); n = zoomLevel.evaluate(null, Number.class); // This should be zoom level 9 assertEquals(9.0, n.doubleValue(), .000001); result = fn.evaluate(feature, Color.class); assertEquals(new Color(127, 128, 0), result); EnvFunction.setGlobalValue("wms_scale_denominator", "136494.693347"); n = zoomLevel.evaluate(null, Number.class); // This should be zoom level 12 assertEquals(12.0, n.doubleValue(), .000001); result = fn.evaluate(feature, Color.class); assertEquals(new Color(0, 255, 0), result); } /** * Tests that a MapBox exponential zoom function (outputting a color) correctly interpolates color values * for zoom levels between stops, when the exponential base is > 1. * * @throws Exception */ @Test public void zoomColorStopsExponentialInterpolationTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); // Set scale denominator to the equivalent of zoomLevel 9 String scaleDenomForZoom9 = "1091957.546779"; EnvFunction.setGlobalValue("wms_scale_denominator", scaleDenomForZoom9); // Verify environment has expected scale denominator (and zoom level) assertEquals(1091957.546779, ff.function("env", ff.literal("wms_scale_denominator")) .evaluate(null, Number.class).doubleValue(), .00001); verifyEnvironmentZoomLevel(9); // Create a Mapbox Function String jsonStr = "{'type':'exponential', 'base': 1.9, 'stops':[[0,'blue'],[6,'red'],[12, 'lime']]}"; JSONObject json = MapboxTestUtils.object(jsonStr); MBFunction function = new MBFunction(json); // Assert it is an exponential function with the correct base assertTrue("Is a zoom function", EnumSet.of(MBFunction.FunctionCategory.ZOOM).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); assertEquals(1.9, function.getBase().doubleValue(), .00001); Function fn = (Function) function.color(); //System.out.println("cql: "+ECQL.toCQL( fn )); Color result = fn.evaluate(feature, Color.class); //System.out.println("The interpolated color is: " + result); Expression zoomLevel = fn.getParameters().get(0); Number n = zoomLevel.evaluate(null, Number.class); //System.out.println("(the function's interpolate value was " + n + ")"); assertTrue("Color has no red, but should be interpolated mix of red and green", result.getRed() > 0); assertTrue("Color has full green, but should be interpolated mix of red and green", result.getGreen() < 255); // We are interpolating at the halfway point, so the linear interpolation would have been (128, 128, 0). assertTrue("Exponential interpolation base is > 1, so the interpolated green value should lag behind the linear interpolation.", result.getGreen() < 128); assertTrue("Exponential interpolation base is > 1, so the interpolated red value should lag behind the linear interpolation.", result.getRed() > 128); } /** * Tests that a MapBox exponential zoom function (outputting a color) correctly interpolates color values * for zoom levels between stops, when the exponential base is < 1. * * @throws Exception */ @Test public void zoomColorStopsExponentialInterpolationTestBaseLessThan1() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); // Set scale denominator to the equivalent of zoomLevel 9 String scaleDenomForZoom9 = "1091957.546779"; EnvFunction.setGlobalValue("wms_scale_denominator", scaleDenomForZoom9); // Verify environment has expected scale denominator (and zoom level) assertEquals(1091957.546779, ff.function("env", ff.literal("wms_scale_denominator")) .evaluate(null, Number.class).doubleValue(), .00001); verifyEnvironmentZoomLevel(9); // Create a Mapbox Function String jsonStr = "{'type':'exponential', 'base': 0.1, 'stops':[[0,'blue'],[6,'red'],[12, 'lime']]}"; JSONObject json = MapboxTestUtils.object(jsonStr); MBFunction function = new MBFunction(json); // Assert it is an exponential function with the correct base assertTrue("Is a zoom function", EnumSet.of(MBFunction.FunctionCategory.ZOOM).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); assertEquals(0.1, function.getBase().doubleValue(), .00001); Function fn = (Function) function.color(); Color result = fn.evaluate(feature, Color.class); Expression zoomLevel = fn.getParameters().get(0); Number n = zoomLevel.evaluate(null, Number.class); assertTrue("Color has no red, but should be interpolated mix of red and green", result.getRed() > 0); assertTrue("Color has full green, but should be interpolated mix of red and green", result.getGreen() < 255); // We are interpolating at the halfway point, so the linear interpolation would have been (128, 128, 0). assertTrue("Exponential interpolation base is < 1, so the interpolated green value should lead the linear interpolation.", result.getGreen() > 128); assertTrue("Exponential interpolation base is < 1, so the interpolated red value should lead the linear interpolation.", result.getRed() < 128); } /** * Tests that a MapBox exponential zoom function (outputting a color) correctly interpolates color values * for zoom levels between stops, when the exponential base is == 1 (linear). * * @throws Exception */ @Test public void zoomColorStopsLinearInterpolationTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); // Set scale denominator to the equivalent of zoomLevel 9 String scaleDenomForZoom9 = "1091957.546779"; EnvFunction.setGlobalValue("wms_scale_denominator", scaleDenomForZoom9); // Verify environment has expected scale denominator (and zoom level) assertEquals(1091957.546779, ff.function("env", ff.literal("wms_scale_denominator")) .evaluate(null, Number.class).doubleValue(), .00001); verifyEnvironmentZoomLevel(9); // Create a Mapbox Function String jsonStr = "{'type':'exponential', 'base': 1.0, 'stops':[[0,'blue'],[6,'red'],[12, 'lime']]}"; JSONObject json = MapboxTestUtils.object(jsonStr); MBFunction function = new MBFunction(json); // Assert it is an exponential function with the correct base assertTrue("Is a zoom function", EnumSet.of(MBFunction.FunctionCategory.ZOOM).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); assertEquals(1.0, function.getBase().doubleValue(), .00001); Function fn = (Function) function.color(); Color result = fn.evaluate(feature, Color.class); Expression zoomLevel = fn.getParameters().get(0); Number n = zoomLevel.evaluate(null, Number.class); assertTrue("Color has no red, but should be interpolated mix of red and green", result.getRed() > 0); assertTrue("Color has full green, but should be interpolated mix of red and green", result.getGreen() < 255); // We are interpolating at the halfway point, so the linear interpolation would have been (128, 128, 0). assertEquals("Exponential interpolation base is = 1, so the interpolated green value should equal the linear interpolation.", 128, result.getGreen()); assertEquals("Exponential interpolation base is = 1, so the interpolated red value should equal the linear interpolation.", 127, result.getRed()); } /** * * Test an {@link MBFunction} (type = interval) that returns a color value. * */ @Test public void colorIntervalFunctionTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",numbervalue,location=4326"); java.util.function.Function<Long, SimpleFeature> features = (value)-> DataUtilities.createFeature(SAMPLE, "measure1=A|"+value+"|POINT(0,0)"); String jsonStr = "{'property': 'numbervalue', 'type': 'interval', 'default': '#0F0F0F', 'stops': [[-1000, '#000000'], [-30, '#00FF00'], [0, '#0000FF'], [100, '#FFFFFF']]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"interval\"", MBFunction.FunctionType.INTERVAL, function.getType()); Expression outputExpression = function.color(); // Before the first stop is undefined (return default value) assertThat(outputExpression, evaluatesTo(features.apply(-10000L), Color.class, equalTo(new Color(0x0F0F0F)))); // Test each interval assertThat(outputExpression, evaluatesTo(features.apply(-900L), Color.class, equalTo(Color.BLACK))); assertThat(outputExpression, evaluatesTo(features.apply(-20L), Color.class, equalTo(Color.GREEN))); assertThat(outputExpression, evaluatesTo(features.apply(10L), Color.class, equalTo(Color.BLUE))); assertThat(outputExpression, evaluatesTo(features.apply(500L), Color.class, equalTo(Color.WHITE))); // Test at stops assertThat(outputExpression, evaluatesTo(features.apply(-1000L), Color.class, equalTo(Color.BLACK))); assertThat(outputExpression, evaluatesTo(features.apply(-30L), Color.class, equalTo(Color.GREEN))); assertThat(outputExpression, evaluatesTo(features.apply(0L), Color.class, equalTo(Color.BLUE))); assertThat(outputExpression, evaluatesTo(features.apply(100L), Color.class, equalTo(Color.WHITE))); } /** * * Test an {@link MBFunction} (type = interval) that returns a color value. * */ @Test public void enumIntervalFunctionTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",numbervalue,location=4326"); java.util.function.Function<Long, SimpleFeature> features = (value)-> DataUtilities.createFeature(SAMPLE, "measure1=A|"+value+"|POINT(0,0)"); String jsonStr = "{'property': 'numbervalue', 'type': 'interval', 'default': 1, 'stops': [[-1000, 'INTERVAL'], [-30, 'CATEGORICAL'], [0, 'EXPONENTIAL'], [100, 'IDENTITY']]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"interval\"", MBFunction.FunctionType.INTERVAL, function.getType()); Expression outputExpression = function.enumeration(MBFunction.FunctionType.class); // Before the first stop is undefined assertThat(outputExpression, evaluatesTo(features.apply(-10000L), String.class, any(String.class))); // Test each interval assertThat(outputExpression, evaluatesTo(features.apply(-900L), String.class, equalTo("interval"))); assertThat(outputExpression, evaluatesTo(features.apply(-20L), String.class, equalTo("categorical"))); assertThat(outputExpression, evaluatesTo(features.apply(10L), String.class, equalTo("exponential"))); assertThat(outputExpression, evaluatesTo(features.apply(500L), String.class, equalTo("identity"))); // Test at stops assertThat(outputExpression, evaluatesTo(features.apply(-1000L), String.class, equalTo("interval"))); assertThat(outputExpression, evaluatesTo(features.apply(-30L), String.class, equalTo("categorical"))); assertThat(outputExpression, evaluatesTo(features.apply(0L), String.class, equalTo("exponential"))); assertThat(outputExpression, evaluatesTo(features.apply(100L), String.class, equalTo("identity"))); } @Test public void stringIntervalFunctionTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",numbervalue,location=4326"); java.util.function.Function<Long, SimpleFeature> features = (value)-> DataUtilities.createFeature(SAMPLE, "measure1=A|"+value+"|POINT(0,0)"); String jsonStr = "{'property': 'numbervalue', 'type': 'interval', 'default': 1, 'stops': [[-1000, 'foo'], [-30, 'bar'], [0, 'baz'], [100, 'quux']]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"interval\"", MBFunction.FunctionType.INTERVAL, function.getType()); Expression outputExpression = function.function(String.class); // Before the first stop is undefined assertThat(outputExpression, evaluatesTo(features.apply(-10000L), String.class, any(String.class))); // Test each interval assertThat(outputExpression, evaluatesTo(features.apply(-900L), String.class, equalTo("foo"))); assertThat(outputExpression, evaluatesTo(features.apply(-20L), String.class, equalTo("bar"))); assertThat(outputExpression, evaluatesTo(features.apply(10L), String.class, equalTo("baz"))); assertThat(outputExpression, evaluatesTo(features.apply(500L), String.class, equalTo("quux"))); // Test at stops assertThat(outputExpression, evaluatesTo(features.apply(-1000L), String.class, equalTo("foo"))); assertThat(outputExpression, evaluatesTo(features.apply(-30L), String.class, equalTo("bar"))); assertThat(outputExpression, evaluatesTo(features.apply(0L), String.class, equalTo("baz"))); assertThat(outputExpression, evaluatesTo(features.apply(100L), String.class, equalTo("quux"))); } /** * Tests that a MapBox exponential zoom function (outputting a numeric value) correctly interpolates values * for zoom levels between stops. Tests exponential base == 1 and <> 1. * * @throws Exception */ @Test public void zoomStopsNumericInterpolationTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); // Set scale denominator to the equivalent of zoomLevel 9 String scaleDenomForZoom9 = "1091957.546779"; EnvFunction.setGlobalValue("wms_scale_denominator", scaleDenomForZoom9); // Verify environment has expected scale denominator (and zoom level) assertEquals(1091957.546779, ff.function("env", ff.literal("wms_scale_denominator")) .evaluate(null, Number.class).doubleValue(), .00001); verifyEnvironmentZoomLevel(9); // -------- Test interpolation for base > 1 String jsonStr = "{'type':'exponential', 'base': 1.9, 'stops':[[0,12],[6,24],[12,48]]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); // Assert it is an exponential function with the correct base assertTrue("Is a zoom function", EnumSet.of(MBFunction.FunctionCategory.ZOOM).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); assertEquals(1.9, function.getBase().doubleValue(), .00001); Function fn = (Function) function.numeric(); Number result = fn.evaluate(feature, Number.class); assertTrue("Interpolated value should be above lower stop", result.doubleValue() > 24); assertTrue("Interpolated value should be below midpoint (for base > 1)", result.doubleValue() < 36); // -------- Test interpolation for base < 1 jsonStr = "{'type':'exponential', 'base': 0.1, 'stops':[[0,12],[6,24],[12,48]]}"; function = new MBFunction(MapboxTestUtils.object(jsonStr)); // Assert it is an exponential function with the correct base assertTrue("Is a zoom function", EnumSet.of(MBFunction.FunctionCategory.ZOOM).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); assertEquals(0.1, function.getBase().doubleValue(), .00001); fn = (Function) function.numeric(); result = fn.evaluate(feature, Number.class); assertTrue("Interpolated value should be above lower stop", result.doubleValue() > 24); assertTrue("Interpolated value should be above midpoint (for base < 1)", result.doubleValue() > 36); // -------- Test interpolation for base = 1 jsonStr = "{'type':'exponential', 'base': 1.0, 'stops':[[0,12],[6,24],[12,48]]}"; function = new MBFunction(MapboxTestUtils.object(jsonStr)); // Assert it is an exponential function with the correct base assertTrue("Is a zoom function", EnumSet.of(MBFunction.FunctionCategory.ZOOM).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); assertEquals(1.0, function.getBase().doubleValue(), .00001); fn = (Function) function.numeric(); result = fn.evaluate(feature, Number.class); assertTrue("Interpolated value should be above lower stop", result.doubleValue() > 24); assertEquals("Interpolated value should = midpoint (for base = 1)", result.doubleValue(), 36.0, .0001); } /** * Tests that a MapBox exponential property function (outputting a numeric value) correctly interpolates values * for zoom levels between stops. Tests exponential base == 1 and <> 1. * * @throws Exception */ @Test public void propertyStopsNumericInterpolationTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); // -------- Test interpolation for base > 1 String jsonStr = "{'type':'exponential', 'base': 1.9, 'property':'temperature', 'stops':[[0,12],[30,24],[70,48]]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); // Assert it is an exponential function with the correct base assertTrue("Is a property function", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); assertEquals(1.9, function.getBase().doubleValue(), .00001); Function fn = (Function) function.numeric(); Number result = fn.evaluate(feature, Number.class); assertTrue("Interpolated value should be above lower stop", result.doubleValue() > 24); assertTrue("Interpolated value should be below midpoint (for base > 1)", result.doubleValue() < 36); // -------- Test interpolation for base < 1 jsonStr = "{'type':'exponential', 'base': 0.1, 'property':'temperature', 'stops':[[0,12],[30,24],[70,48]]}"; function = new MBFunction(MapboxTestUtils.object(jsonStr)); // Assert it is an exponential function with the correct base assertTrue("Is a property function", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); assertEquals(0.1, function.getBase().doubleValue(), .00001); fn = (Function) function.numeric(); result = fn.evaluate(feature, Number.class); assertTrue("Interpolated value should be above lower stop", result.doubleValue() > 24); assertTrue("Interpolated value should be above midpoint (for base < 1)", result.doubleValue() > 36); // -------- Test interpolation for base = 1 jsonStr = "{'type':'exponential', 'base': 1.0, 'property':'temperature', 'stops':[[0,12],[30,24],[70,48]]}"; function = new MBFunction(MapboxTestUtils.object(jsonStr)); // Assert it is an exponential function with the correct base assertTrue("Is a property function", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals(MBFunction.FunctionType.EXPONENTIAL, function.getType()); assertEquals(1.0, function.getBase().doubleValue(), .00001); fn = (Function) function.numeric(); result = fn.evaluate(feature, Number.class); assertTrue("Interpolated value should be above lower stop", result.doubleValue() > 24); assertEquals("Interpolated value should = midpoint (for base = 1)", result.doubleValue(), 36.0, .0001); } /** * Assert that the numeric exponential function correctly returns the default value when the property input is not a valid number. */ @Test public void numericExponentialDefaultValueTest() throws Exception { 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)"); String jsonStr = "{'type':'exponential', 'base': 1.9, 'property':'temperature', 'stops':[[0,12],[30,24],[70,48]], 'default':42}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); Expression fn = function.numeric(); Number result = fn.evaluate(feature, Number.class); assertEquals(42, result.intValue()); } /** * Test an MBFunction (type = Identity) that returns a numeric value for a given property. */ @Test public void numericIdentityFunctionTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|50.0|POINT(0,0)"); // Verify the function was created correctly String jsonStr = "{'property':'temperature', 'type':'identity', 'default':-1}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"Identity\"", MBFunction.FunctionType.IDENTITY, function.getType()); // Verify that the output is correct (== input) Expression outputExpression = function.numeric(); Number result = outputExpression.evaluate(feature, Number.class); assertEquals("Numeric identity function output is == input property value", 50.0, result.doubleValue(), .000001); // Check default (for a feature missing the property) feature = DataUtilities.createFeature(SAMPLE, "measure1=A||POINT(0,0)"); result = outputExpression.evaluate(feature, Number.class); assertEquals(-1, result.intValue()); } /** * Test an {@link MBFunction} (type = categorical) that returns a numeric value. */ @Test public void numericCategoricalFunctionTest() throws Exception { SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",roadtype,location=4326"); String jsonStr = "{'property': 'roadtype', 'type': 'categorical', 'default': -1, 'stops': [['trail', 1], ['dirtroad', 2], ['road', 3], ['highway', 4]]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"categorical\"", MBFunction.FunctionType.CATEGORICAL, function.getType()); Expression outputExpression = function.numeric(); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|dirtroad|POINT(0,0)"); Number result = outputExpression.evaluate(feature, Number.class); assertEquals(2, result.intValue()); // Check default feature = DataUtilities.createFeature(SAMPLE, "measure1=A|other|POINT(0,0)"); result = outputExpression.evaluate(feature, Number.class); assertEquals(-1, result.intValue()); } /** * * Test an {@link MBFunction} (type = interval) that returns a numeric value. * */ @Test public void numericIntervalFunctionTest() throws Exception { String jsonStr = "{'property': 'temperature', 'default':0, 'type': 'interval','stops': [[0, 2],[100, 4],[1000, 6]]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertThat(function, categories(containsInAnyOrder(MBFunction.FunctionCategory.PROPERTY))); assertThat(function, hasProperty("type", is(MBFunction.FunctionType.INTERVAL))); Expression outputExpression = function.numeric(); // Test each interval final SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature,location=4326"); java.util.function.Function<Long, SimpleFeature> features = (temp)-> DataUtilities.createFeature(SAMPLE, "measure1=A|"+temp+"|POINT(0,0)"); // Bellow the first stop is undefined (return default value) assertThat(outputExpression, evaluatesTo(features.apply(-1L), Number.class, equalInt(0))); assertThat(outputExpression, evaluatesTo(features.apply(0L), Number.class, equalInt(2))); assertThat(outputExpression, evaluatesTo(features.apply(20L), Number.class, equalInt(2))); assertThat(outputExpression, evaluatesTo(features.apply(100L), Number.class, equalInt(4))); assertThat(outputExpression, evaluatesTo(features.apply(200L), Number.class, equalInt(4))); assertThat(outputExpression, evaluatesTo(features.apply(1000L), Number.class, equalInt(6))); assertThat(outputExpression, evaluatesTo(features.apply(2000L), Number.class, equalInt(6))); } /** * Test a {@link MBFunction} (type = identity) that returns a string value. */ @Test public void stringIdentityFunctionTest() throws Exception { String jsonStr = "{'property': 'textproperty','type': 'identity', 'default':'defaultText'}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"identity\"", MBFunction.FunctionType.IDENTITY, function.getType()); Expression outputExpression = function.function(String.class); SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",textproperty,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|textvalue|POINT(0,0)"); String output = outputExpression.evaluate(feature, String.class); assertEquals("textvalue", output); // Check default (for a feature that's missing the function's property) SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",location=4326"); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|POINT(0,0)"); output = outputExpression.evaluate(feature, String.class); assertEquals("defaultText", output); } /** * Test a {@link MBFunction} (type = identity) that returns an enum value. */ @Test public void enumIdentityFunctionTest() throws Exception { String jsonStr = "{'property': 'linecap','type': 'identity', 'default':'round'}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"identity\"", MBFunction.FunctionType.IDENTITY, function.getType()); Expression outputExpression = function.function(LineJoin.class); SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",linecap,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|square|POINT(0,0)"); String output = outputExpression.evaluate(feature, String.class); assertEquals("square", output); // Check default (for a feature that's missing the function's property) SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",location=4326"); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|POINT(0,0)"); output = outputExpression.evaluate(feature, String.class); assertEquals("round", output); } /** * Test a {@link MBFunction} (type = identity) that returns a boolean value. */ @Test public void booleanIdentityFunctionTest() throws Exception { String jsonStr = "{'property': 'propName','type': 'identity', 'default':'false'}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"identity\"", MBFunction.FunctionType.IDENTITY, function.getType()); Expression outputExpression = function.function(Boolean.class); SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",propName,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|true|POINT(0,0)"); Boolean output = outputExpression.evaluate(feature, Boolean.class); assertTrue(output); // Check default feature = DataUtilities.createFeature(SAMPLE, "measure1=A||POINT(0,0)"); output = outputExpression.evaluate(feature, Boolean.class); assertFalse(output); } @Test public void stringCategoricalFunctionTest() throws Exception { String jsonStr = "{'property': 'roadtype', 'type': 'categorical', 'default': 'defaultStr', 'stops': [['trail', 'string1'], ['dirtroad', 'string2'], ['road', 'string3'], ['highway', 'string4']]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"categorical\"", MBFunction.FunctionType.CATEGORICAL, function.getType()); Expression outputExpression = function.function(String.class); SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",roadtype,location=4326"); // Test for each category SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|trail|POINT(0,0)"); String result = outputExpression.evaluate(feature, String.class); assertEquals("string1", result); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|dirtroad|POINT(0,0)"); result = outputExpression.evaluate(feature, String.class); assertEquals("string2", result); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|road|POINT(0,0)"); result = outputExpression.evaluate(feature, String.class); assertEquals("string3", result); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|highway|POINT(0,0)"); result = outputExpression.evaluate(feature, String.class); assertEquals("string4", result); // Check default feature = DataUtilities.createFeature(SAMPLE, "measure1=A|other|POINT(0,0)"); result = outputExpression.evaluate(feature, String.class); assertEquals("defaultStr", result); } @Test public void booleanIntervalFunctionTest() throws Exception { String jsonStr = "{'property': 'temperature', 'default':'false', 'type': 'interval','stops': [[-1000, 'true'],[0, 'false'],[1000, 'true']]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"interval\"", MBFunction.FunctionType.INTERVAL, function.getType()); Expression outputExpression = function.function(Boolean.class); // Test each interval SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature,location=4326"); SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|-500|POINT(0,0)"); Boolean result = outputExpression.evaluate(feature, Boolean.class); assertEquals(true, result); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|0|POINT(0,0)"); result = outputExpression.evaluate(feature, Boolean.class); assertEquals(false, result); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|500|POINT(0,0)"); result = outputExpression.evaluate(feature, Boolean.class); assertEquals(false, result); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|1000|POINT(0,0)"); result = outputExpression.evaluate(feature, Boolean.class); assertEquals(true, result); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|9999|POINT(0,0)"); result = outputExpression.evaluate(feature, Boolean.class); assertEquals(true, result); // Below lowest stop, the default should be returned. feature = DataUtilities.createFeature(SAMPLE, "measure1=A|-9999|POINT(0,0)"); result = outputExpression.evaluate(feature, Boolean.class); assertEquals(false, result); } @Test public void booleanCategoricalFunctionTest() throws Exception { String jsonStr = "{'property': 'roadtype', 'type': 'categorical', 'default': 'false', 'stops': [['trail', 'true'], ['dirtroad', 'false'], ['road', 'true']]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("Function type is \"categorical\"", MBFunction.FunctionType.CATEGORICAL, function.getType()); Expression outputExpression = function.function(Boolean.class); SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",roadtype,location=4326"); // Test for each category SimpleFeature feature = DataUtilities.createFeature(SAMPLE, "measure1=A|trail|POINT(0,0)"); Boolean result = outputExpression.evaluate(feature, Boolean.class); assertEquals(true, result); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|dirtroad|POINT(0,0)"); result = outputExpression.evaluate(feature, Boolean.class); assertEquals(false, result); feature = DataUtilities.createFeature(SAMPLE, "measure1=A|road|POINT(0,0)"); result = outputExpression.evaluate(feature, Boolean.class); assertEquals(true, result); // Check default feature = DataUtilities.createFeature(SAMPLE, "measure1=A|other|POINT(0,0)"); result = outputExpression.evaluate(feature, Boolean.class); assertEquals(false, result); } /** * Assert that {@link MBFunction} uses the correct default function type when the requested return class is String. */ @Test public void stringDefaultFunctionTest() throws Exception { String jsonStr = "{'property': 'temperature', 'stops': [[0, 'string1'], [100, 'string2'], [200, 'string3'], [300, 'string4']]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("The default function type for String returns should be interval", MBFunction.FunctionType.INTERVAL, function.getTypeWithDefault(String.class)); } /** * Assert that {@link MBFunction} uses the correct default function type when the requested return class is Boolean. */ @Test public void booleanDefaultFunctionTest() throws Exception { String jsonStr = "{'property': 'temperature', 'stops': [[0, 'true'], [100, 'false'], [200, 'true'], [300, 'false']]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertTrue("Function category is \"property\"", EnumSet.of(MBFunction.FunctionCategory.PROPERTY).equals(function.category())); assertEquals("The default function type for Boolean returns should be interval", MBFunction.FunctionType.INTERVAL, function.getTypeWithDefault(Boolean.class)); } /** * Assert that {@link MBFunction} uses the correct default function type when the requested return class is Number. */ @Test public void numericDefaultFunctionTest() throws Exception { String jsonStr = "{'property': 'temperature','stops': [[0, 2],[100, 4],[1000, 6]]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertEquals("The default function type for Number returns should be exponential", FunctionType.EXPONENTIAL, function.getTypeWithDefault(Number.class)); } /** * Assert that {@link MBFunction} uses the correct default function type when the requested return class is Enum. */ @Test public void enumDefaultFunctionTest() throws Exception { String jsonStr = "{'property': 'temperature','stops': [[0, 'ENUM_VAL1'],[100, 'ENUM_VAL2'],[1000, 'ENUM_VAL3']]}"; MBFunction function = new MBFunction(MapboxTestUtils.object(jsonStr)); assertEquals("The default function type for Enums returns should be interval", FunctionType.INTERVAL, function.getTypeWithDefault(Enumeration.class)); } /** * Assert that {@link MBFunction} uses the correct default function type when the requested return class is Color. */ @Test public void colorDefaultFunctionTest() throws Exception { JSONObject json = MapboxTestUtils.object("{'property':'temperature','stops':[[0,'blue'],[100,'red']]}"); MBFunction function = new MBFunction(json); assertEquals("The default function type for Color returns should be exponential", FunctionType.EXPONENTIAL, function.getTypeWithDefault(Color.class)); } public void verifyEnvironmentZoomLevel(int zoomLevel) { Number envZoomLevel = ff.function("zoomLevel", ff.function("env", ff.literal("wms_scale_denominator")), ff.literal("EPSG:3857")) .evaluate(null, Number.class); assertEquals("Zoom level is " + zoomLevel, zoomLevel, envZoomLevel.doubleValue(), .00001); } /** * Test splitting an array function into an array of functions -- one for each dimension in the array. (Exponential function). */ @Test public void testArrayFunction() throws Exception { JSONObject json = MapboxTestUtils.object("{'property':'temperature','type':'exponential', 'base':1.5, 'stops':[ [0,[0,5]], [100,[2,10]] ]}"); MBFunction function = new MBFunction(json); SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326,color:java.awt.Color,text:String:"); SimpleFeature feature1 = DataUtilities.createFeature(SAMPLE, "measure1=A|0|POINT(0,0)|#FF0000|red"); SimpleFeature feature2 = DataUtilities.createFeature(SAMPLE, "measure1=A|100|POINT(0,0)|#FF0000|red"); List<MBFunction> splitFunctions = function.splitArrayFunction(); assertEquals(2, splitFunctions.size()); MBFunction xFn = splitFunctions.get(0); MBFunction yFn = splitFunctions.get(1); assertEquals(0, xFn.numeric().evaluate(feature1, Integer.class).intValue()); assertEquals(5, yFn.numeric().evaluate(feature1, Integer.class).intValue()); assertEquals(2, xFn.numeric().evaluate(feature2, Integer.class).intValue()); assertEquals(10, yFn.numeric().evaluate(feature2, Integer.class).intValue()); } /** * Test splitting an array function into an array of functions -- one for each dimension in the array. (Categorical function, with default). */ @Test public void testArrayFunctionCategoricalWithDefault() throws Exception { JSONObject json = MapboxTestUtils.object("{'property':'character','type':'categorical', 'base':1.5, 'default': [-1,-2], 'stops':[ ['a',[0,5]], ['b',[2,10]] ]}"); MBFunction function = new MBFunction(json); SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326,color:java.awt.Color,character:String:"); SimpleFeature featurea = DataUtilities.createFeature(SAMPLE, "measure1=A|0|POINT(0,0)|#FF0000|a"); SimpleFeature featureb = DataUtilities.createFeature(SAMPLE, "measure1=A|100|POINT(0,0)|#FF0000|b"); SimpleFeature featuredefault = DataUtilities.createFeature(SAMPLE, "measure1=A|100|POINT(0,0)|#FF0000|default"); List<MBFunction> splitFunctions = function.splitArrayFunction(); assertEquals(2, splitFunctions.size()); MBFunction xFn = splitFunctions.get(0); MBFunction yFn = splitFunctions.get(1); assertEquals(0, xFn.numeric().evaluate(featurea, Integer.class).intValue()); assertEquals(5, yFn.numeric().evaluate(featurea, Integer.class).intValue()); assertEquals(2, xFn.numeric().evaluate(featureb, Integer.class).intValue()); assertEquals(10, yFn.numeric().evaluate(featureb, Integer.class).intValue()); assertEquals(-1, xFn.numeric().evaluate(featuredefault, Integer.class).intValue()); assertEquals(-2, yFn.numeric().evaluate(featuredefault, Integer.class).intValue()); } /** * Test that nothing breaks when an array function has output arrays of size one. * @throws Exception */ @Test public void testArrayFunctionSize1() throws Exception { JSONObject json = MapboxTestUtils.object("{'property':'temperature','type':'exponential', 'base':1.5, 'stops':[ [0,[0]], [100,[2]] ]}"); MBFunction function = new MBFunction(json); List<MBFunction> splitFunctions = function.splitArrayFunction(); assertEquals(1, splitFunctions.size()); SimpleFeatureType SAMPLE = DataUtilities.createType("SAMPLE", "id:\"\",temperature:0.0,location=4326,color:java.awt.Color,text:String:"); SimpleFeature feature1 = DataUtilities.createFeature(SAMPLE, "measure1=A|0|POINT(0,0)|#FF0000|red"); SimpleFeature feature2 = DataUtilities.createFeature(SAMPLE, "measure1=A|100|POINT(0,0)|#FF0000|red"); assertEquals(0, splitFunctions.get(0).numeric().evaluate(feature1, Integer.class).intValue()); assertEquals(2, splitFunctions.get(0).numeric().evaluate(feature2, Integer.class).intValue()); } }