/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2015, 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.renderer.lite; import static java.awt.RenderingHints.KEY_ANTIALIASING; import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import org.geotools.data.property.PropertyDataStore; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.image.test.ImageAssert; import org.geotools.map.FeatureLayer; import org.geotools.map.MapContent; import org.geotools.renderer.RenderListener; import org.geotools.styling.FeatureTypeStyle; import org.geotools.styling.Style; import org.geotools.test.TestData; import org.junit.Before; import org.junit.Test; import org.opengis.feature.simple.SimpleFeature; /** * Z-order rendering test making use of FeatureTypeStyle sortBy to refine query used for styling. * * @source $URL$ */ public class ZOrderTest { private static final long TIME = 40000; private static final int THRESHOLD = 100; SimpleFeatureSource zsquares; SimpleFeatureSource zroads; ReferencedEnvelope bounds; SimpleFeatureSource zbuildings; @Before public void setUp() throws Exception { File property = new File(TestData.getResource(this, "zorder/zsquares.properties").toURI()); PropertyDataStore ds = new PropertyDataStore(property.getParentFile()); zsquares = ds.getFeatureSource("zsquares"); zroads = ds.getFeatureSource("zroads"); zbuildings = ds.getFeatureSource("zbuildings"); bounds = zsquares.getBounds(); bounds.expandBy(0.2, 0.2); // System.setProperty("org.geotools.test.interactive", "true"); } @Test public void testSingleZAscending() throws Exception { runZSquaresTest("zorder/zpolygon.sld", "z", "z-ascending"); } @Test public void testSingleZDescending() throws Exception { runZSquaresTest("zorder/zpolygon.sld", "z D", "z-descending"); } @Test public void testSingleCatDescendingZDescending() throws Exception { runZSquaresTest("zorder/zpolygon.sld", "cat D,z D", "cat-desc-z-desc"); } @Test public void testSingleCatDescendingZAscending() throws Exception { runZSquaresTest("zorder/zpolygon.sld", "cat D,z A", "cat-desc-z-asc"); } @Test public void testCrossRoadsNoGroup() throws Exception { runRoadsTest("zorder/zroads.sld", "z", null, "roads-no-group"); } @Test public void testCrossRoadsGrouped() throws Exception { runRoadsTest("zorder/zroads.sld", "z", "theGroup", "roads-group"); } @Test public void testCrossRoadsComplexSort() throws Exception { runRoadsTest("zorder/zroads.sld", "cat D, name D", "theGroup", "roads-group-complex-sort"); } @Test public void testCrossRoadsBuildingsOnZ() throws Exception { runRoadsBuildingTest("z", "theGroup", "roads-buildings-group-z"); } @Test public void testCrossRoadsBuildingsZoomedOut() throws Exception { bounds.expandBy(10, 10); runRoadsBuildingTest("z", "theGroup", "roads-buildings-group-z-zoomedout"); } private void runZSquaresTest(String styleName, String sortBy, String referenceImageName) throws Exception { Style style = RendererBaseTest.loadStyle(this, styleName); forceSortBy(style, sortBy); MapContent mc = new MapContent(); mc.addLayer(new FeatureLayer(zsquares, style)); runImageComparison(referenceImageName, mc); } private void runRoadsTest(String styleName, String sortBy, String sortByGroup, String referenceImageName) throws Exception { Style style = RendererBaseTest.loadStyle(this, styleName); forceSortBy(style, sortBy); forceSortByGroup(style, sortByGroup); MapContent mc = new MapContent(); mc.addLayer(new FeatureLayer(zroads, style)); runImageComparison(referenceImageName, mc); } private void runRoadsBuildingTest(String sortBy, String sortByGroup, String referenceImageName) throws Exception { Style roadsStyle = RendererBaseTest.loadStyle(this, "zorder/zroads.sld"); forceSortBy(roadsStyle, sortBy); forceSortByGroup(roadsStyle, sortByGroup); Style buildingsStyle = RendererBaseTest.loadStyle(this, "zorder/zbuildings.sld"); forceSortBy(buildingsStyle, sortBy); forceSortByGroup(buildingsStyle, sortByGroup); MapContent mc = new MapContent(); mc.addLayer(new FeatureLayer(zroads, roadsStyle)); mc.addLayer(new FeatureLayer(zbuildings, buildingsStyle)); runImageComparison(referenceImageName, mc); } private void runImageComparison(String referenceImageName, MapContent mc) throws Exception, IOException { StreamingRenderer renderer = new StreamingRenderer(); renderer.setMapContent(mc); renderer.setJava2DHints(new RenderingHints(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON)); renderer.addRenderListener(new RenderListener() { @Override public void featureRenderer(SimpleFeature feature) { // nothing to do } @Override public void errorOccurred(Exception e) { fail("No rendering errors expected, but got one: " + e.getMessage()); } }); BufferedImage image = RendererBaseTest.showRender(referenceImageName, renderer, TIME, bounds); File reference = new File( "./src/test/resources/org/geotools/renderer/lite/test-data/zorder/" + referenceImageName + ".png"); ImageAssert.assertEquals(reference, image, THRESHOLD); mc.dispose(); } private void forceSortBy(Style style, String sortBy) { if (sortBy != null) { for (FeatureTypeStyle fts : style.featureTypeStyles()) { fts.getOptions().put(FeatureTypeStyle.SORT_BY, sortBy); } } } private void forceSortByGroup(Style style, String sortByGroup) { if (sortByGroup != null) { for (FeatureTypeStyle fts : style.featureTypeStyles()) { fts.getOptions().put(FeatureTypeStyle.SORT_BY_GROUP, sortByGroup); } } } @Test public void testInvalidAttribute() throws Exception { final AtomicInteger failureCount = new AtomicInteger(0); runZRoadsFailureTest("zorder/zpolygon.sld", "not-there", null, new RenderListener() { @Override public void featureRenderer(SimpleFeature feature) { // nothing to do } @Override public void errorOccurred(Exception e) { failureCount.incrementAndGet(); assertTrue(e instanceof IllegalArgumentException); assertTrue(e.getMessage().contains("not-there")); } }); assertEquals(1, failureCount.get()); } @Test public void testInvalidSortBySyntax() throws Exception { final AtomicInteger failureCount = new AtomicInteger(0); runZRoadsFailureTest("zorder/zpolygon.sld", "z upsideDown", null, new RenderListener() { @Override public void featureRenderer(SimpleFeature feature) { // nothing to do } @Override public void errorOccurred(Exception e) { failureCount.incrementAndGet(); assertTrue(e instanceof IllegalArgumentException); assertTrue(e.getMessage().contains("z upsideDown")); } }); assertEquals(1, failureCount.get()); } @Test public void testNonComparable() throws Exception { final AtomicInteger failureCount = new AtomicInteger(0); runZRoadsFailureTest("zorder/zpolygon.sld", "color", null, new RenderListener() { @Override public void featureRenderer(SimpleFeature feature) { // nothing to do } @Override public void errorOccurred(Exception e) { failureCount.incrementAndGet(); assertTrue(e instanceof IllegalArgumentException); assertTrue(e.getMessage().contains("color")); assertTrue(e.getMessage().contains("sort")); } }); assertEquals(1, failureCount.get()); } @Test public void testIncompatibleAttributes() throws Exception { final AtomicInteger failureCount = new AtomicInteger(0); runRoadsBuildFailureTest("zorder/zpolygon.sld", "z", "cat", "theGroup", new RenderListener() { @Override public void featureRenderer(SimpleFeature feature) { // nothing to do } @Override public void errorOccurred(Exception e) { failureCount.incrementAndGet(); assertTrue(e instanceof IllegalArgumentException); assertTrue(e.getMessage().contains("z")); assertTrue(e.getMessage().contains("cat")); assertTrue(e.getMessage().contains("incompatible")); } }); assertEquals(1, failureCount.get()); } @Test public void testIncompatibleDirections() throws Exception { final AtomicInteger failureCount = new AtomicInteger(0); runRoadsBuildFailureTest("zorder/zpolygon.sld", "z A", "z D", "theGroup", new RenderListener() { @Override public void featureRenderer(SimpleFeature feature) { // nothing to do } @Override public void errorOccurred(Exception e) { failureCount.incrementAndGet(); assertTrue(e instanceof IllegalArgumentException); assertTrue(e.getMessage().contains("order")); } }); assertEquals(1, failureCount.get()); } @Test public void testDifferentLenghts() throws Exception { final AtomicInteger failureCount = new AtomicInteger(0); runRoadsBuildFailureTest("zorder/zpolygon.sld", "z, cat", "z", "theGroup", new RenderListener() { @Override public void featureRenderer(SimpleFeature feature) { // nothing to do } @Override public void errorOccurred(Exception e) { failureCount.incrementAndGet(); assertTrue(e instanceof IllegalArgumentException); assertTrue(e.getMessage().contains("number of attributes")); } }); assertEquals(1, failureCount.get()); } private void runZRoadsFailureTest(String styleName, String sortBy, String sortByGroup, RenderListener listener) throws Exception { Style style = RendererBaseTest.loadStyle(this, styleName); forceSortBy(style, sortBy); forceSortByGroup(style, sortByGroup); MapContent mc = new MapContent(); mc.addLayer(new FeatureLayer(zroads, style)); runFailureTest(listener, mc); } private void runFailureTest(RenderListener listener, MapContent mc) throws Exception { StreamingRenderer renderer = new StreamingRenderer(); renderer.setMapContent(mc); renderer.setJava2DHints(new RenderingHints(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON)); renderer.addRenderListener(listener); RendererBaseTest.showRender("failures", renderer, TIME, bounds); mc.dispose(); } private void runRoadsBuildFailureTest(String styleName, String sortByRoads, String sortByBuildings, String sortByGroup, RenderListener listener) throws Exception { Style roadsStyle = RendererBaseTest.loadStyle(this, "zorder/zroads.sld"); forceSortBy(roadsStyle, sortByRoads); forceSortByGroup(roadsStyle, sortByGroup); Style buildingsStyle = RendererBaseTest.loadStyle(this, "zorder/zbuildings.sld"); forceSortBy(buildingsStyle, sortByBuildings); forceSortByGroup(buildingsStyle, sortByGroup); MapContent mc = new MapContent(); mc.addLayer(new FeatureLayer(zroads, roadsStyle)); mc.addLayer(new FeatureLayer(zbuildings, buildingsStyle)); runFailureTest(listener, mc); } @Test public void testZOrderComposite() throws Exception { Style roadsStyle = RendererBaseTest.loadStyle(this, "zorder/zroads.sld"); forceSortBy(roadsStyle, "z"); forceSortByGroup(roadsStyle, "theGroup"); FeatureTypeStyle fts = roadsStyle.featureTypeStyles().get(0); fts.getOptions().put(FeatureTypeStyle.COMPOSITE_BASE, "true"); fts.getOptions().put(FeatureTypeStyle.COMPOSITE, "destination-in"); Style buildingsStyle = RendererBaseTest.loadStyle(this, "zorder/zbuildings.sld"); MapContent mc = new MapContent(); mc.addLayer(new FeatureLayer(zbuildings, buildingsStyle)); mc.addLayer(new FeatureLayer(zroads, roadsStyle)); runImageComparison("zorder-composite.png", mc); } }