1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.gui.mappaint;
|
---|
3 |
|
---|
4 | import static org.junit.Assert.assertEquals;
|
---|
5 | import static org.junit.Assert.fail;
|
---|
6 |
|
---|
7 | import java.awt.Point;
|
---|
8 | import java.awt.image.BufferedImage;
|
---|
9 | import java.io.File;
|
---|
10 | import java.io.FileInputStream;
|
---|
11 | import java.io.FileNotFoundException;
|
---|
12 | import java.io.IOException;
|
---|
13 | import java.util.Arrays;
|
---|
14 | import java.util.Collection;
|
---|
15 | import java.util.stream.Collectors;
|
---|
16 | import java.util.stream.Stream;
|
---|
17 |
|
---|
18 | import javax.imageio.ImageIO;
|
---|
19 |
|
---|
20 | import org.junit.Ignore;
|
---|
21 | import org.junit.Rule;
|
---|
22 | import org.junit.Test;
|
---|
23 | import org.junit.runner.RunWith;
|
---|
24 | import org.junit.runners.Parameterized;
|
---|
25 | import org.junit.runners.Parameterized.Parameters;
|
---|
26 | import org.openstreetmap.josm.TestUtils;
|
---|
27 | import org.openstreetmap.josm.data.Bounds;
|
---|
28 | import org.openstreetmap.josm.data.osm.DataSet;
|
---|
29 | import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
|
---|
30 | import org.openstreetmap.josm.gui.NavigatableComponent;
|
---|
31 | import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
|
---|
32 | import org.openstreetmap.josm.gui.preferences.SourceEntry;
|
---|
33 | import org.openstreetmap.josm.io.IllegalDataException;
|
---|
34 | import org.openstreetmap.josm.io.OsmReader;
|
---|
35 | import org.openstreetmap.josm.testutils.JOSMTestRules;
|
---|
36 |
|
---|
37 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
---|
38 |
|
---|
39 | /**
|
---|
40 | * Test cases for {@link StyledMapRenderer} and the MapCSS classes.
|
---|
41 | * <p>
|
---|
42 | * This test uses the data and reference files stored in the test data directory {@value #TEST_DATA_BASE}
|
---|
43 | * @author Michael Zangl
|
---|
44 | */
|
---|
45 | @RunWith(Parameterized.class)
|
---|
46 | public class MapCSSRendererTest {
|
---|
47 | private static final String TEST_DATA_BASE = "/renderer/";
|
---|
48 | /**
|
---|
49 | * lat = 0..1, lon = 0..1
|
---|
50 | */
|
---|
51 | private static final Bounds AREA_DEFAULT = new Bounds(0, 0, 1, 1);
|
---|
52 | private static final int IMAGE_SIZE = 256;
|
---|
53 |
|
---|
54 | /**
|
---|
55 | * Minimal test rules required
|
---|
56 | */
|
---|
57 | @Rule
|
---|
58 | @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
|
---|
59 | public JOSMTestRules test = new JOSMTestRules().preferences().projection();
|
---|
60 |
|
---|
61 | private TestConfig testConfig;
|
---|
62 |
|
---|
63 | /**
|
---|
64 | * The different configurations of this test.
|
---|
65 | * @return The parameters.
|
---|
66 | */
|
---|
67 | @Parameters
|
---|
68 | public static Collection<Object[]> runs() {
|
---|
69 | return Stream.of(
|
---|
70 | /** Tests for StyledMapRenderer#drawNodeSymbol */
|
---|
71 | new TestConfig("node-shapes", AREA_DEFAULT),
|
---|
72 |
|
---|
73 | /** Tests that StyledMapRenderer#drawWay respects width */
|
---|
74 | new TestConfig("way-width", AREA_DEFAULT)
|
---|
75 |
|
---|
76 | ).map(e -> new Object[] {e})
|
---|
77 | .collect(Collectors.toList());
|
---|
78 | }
|
---|
79 |
|
---|
80 | /**
|
---|
81 | * @param testConfig The config to use for this test.
|
---|
82 | */
|
---|
83 | public MapCSSRendererTest(TestConfig testConfig) {
|
---|
84 | this.testConfig = testConfig;
|
---|
85 | }
|
---|
86 |
|
---|
87 | /**
|
---|
88 | * Run the test using {@link #testConfig}
|
---|
89 | * @throws Exception if an error occurs
|
---|
90 | */
|
---|
91 | @Test
|
---|
92 | @Ignore("not ready")
|
---|
93 | public void testRender() throws Exception {
|
---|
94 | // load the data
|
---|
95 | DataSet dataSet = testConfig.getOsmDataSet();
|
---|
96 |
|
---|
97 | // load the style
|
---|
98 | MapCSSStyleSource.STYLE_SOURCE_LOCK.writeLock().lock();
|
---|
99 | try {
|
---|
100 | MapPaintStyles.getStyles().clear();
|
---|
101 |
|
---|
102 | MapCSSStyleSource source = new MapCSSStyleSource(testConfig.getStyleSourceEntry());
|
---|
103 | source.loadStyleSource();
|
---|
104 | if (!source.getErrors().isEmpty()) {
|
---|
105 | fail("Failed to load style file. Errors: " + source.getErrors());
|
---|
106 | }
|
---|
107 | MapPaintStyles.getStyles().setStyleSources(Arrays.asList(source));
|
---|
108 |
|
---|
109 | } finally {
|
---|
110 | MapCSSStyleSource.STYLE_SOURCE_LOCK.writeLock().unlock();
|
---|
111 | }
|
---|
112 |
|
---|
113 | // create the renderer
|
---|
114 | BufferedImage image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE, BufferedImage.TYPE_INT_ARGB);
|
---|
115 | NavigatableComponent nc = new NavigatableComponent() {
|
---|
116 | {
|
---|
117 | setBounds(0, 0, IMAGE_SIZE, IMAGE_SIZE);
|
---|
118 | updateLocationState();
|
---|
119 | }
|
---|
120 |
|
---|
121 | @Override
|
---|
122 | protected boolean isVisibleOnScreen() {
|
---|
123 | return true;
|
---|
124 | }
|
---|
125 |
|
---|
126 | @Override
|
---|
127 | public Point getLocationOnScreen() {
|
---|
128 | return new Point(0, 0);
|
---|
129 | }
|
---|
130 | };
|
---|
131 | nc.zoomTo(testConfig.testArea);
|
---|
132 | dataSet.allPrimitives().stream().forEach(n -> n.setHighlighted(n.isKeyTrue("highlight")));
|
---|
133 | new StyledMapRenderer(image.createGraphics(), nc, false).render(dataSet, false, testConfig.testArea);
|
---|
134 |
|
---|
135 | BufferedImage reference = testConfig.getReference();
|
---|
136 |
|
---|
137 | // now compute differences:
|
---|
138 | assertEquals(IMAGE_SIZE, reference.getWidth());
|
---|
139 | assertEquals(IMAGE_SIZE, reference.getHeight());
|
---|
140 |
|
---|
141 | StringBuilder differences = new StringBuilder();
|
---|
142 |
|
---|
143 | for (int y = 0; y < reference.getHeight(); y++) {
|
---|
144 | for (int x = 0; x < reference.getWidth(); x++) {
|
---|
145 | int expected = reference.getRGB(x, y);
|
---|
146 | int result = image.getRGB(x, y);
|
---|
147 | if (expected != result && differences.length() < 500) {
|
---|
148 | differences.append("\nDifference at ")
|
---|
149 | .append(x)
|
---|
150 | .append(",")
|
---|
151 | .append(y)
|
---|
152 | .append(": Expected ")
|
---|
153 | .append(Integer.toHexString(expected))
|
---|
154 | .append(" but got ")
|
---|
155 | .append(Integer.toHexString(result));
|
---|
156 | }
|
---|
157 | }
|
---|
158 | }
|
---|
159 |
|
---|
160 | if (differences.length() > 0) {
|
---|
161 | // You can use this to debug:
|
---|
162 | ImageIO.write(image, "png", new File(testConfig.getTestDirectory() + "/test-output.png"));
|
---|
163 | fail("Images for test " + testConfig.testDirectory + " differ: " + differences.toString());
|
---|
164 | }
|
---|
165 | }
|
---|
166 |
|
---|
167 | private static class TestConfig {
|
---|
168 | private final String testDirectory;
|
---|
169 | private final Bounds testArea;
|
---|
170 |
|
---|
171 | TestConfig(String testDirectory, Bounds testArea) {
|
---|
172 | this.testDirectory = testDirectory;
|
---|
173 | this.testArea = testArea;
|
---|
174 | }
|
---|
175 |
|
---|
176 | public BufferedImage getReference() throws IOException {
|
---|
177 | return ImageIO.read(new File(getTestDirectory() + "/reference.png"));
|
---|
178 | }
|
---|
179 |
|
---|
180 | private String getTestDirectory() {
|
---|
181 | return TestUtils.getTestDataRoot() + TEST_DATA_BASE + testDirectory;
|
---|
182 | }
|
---|
183 |
|
---|
184 | public SourceEntry getStyleSourceEntry() {
|
---|
185 | return new SourceEntry(getTestDirectory() + "/style.mapcss",
|
---|
186 | "test style", "a test style", true // active
|
---|
187 | );
|
---|
188 | }
|
---|
189 |
|
---|
190 | public DataSet getOsmDataSet() throws FileNotFoundException, IllegalDataException {
|
---|
191 | return OsmReader.parseDataSet(new FileInputStream(getTestDirectory() + "/data.osm"), null);
|
---|
192 | }
|
---|
193 |
|
---|
194 | @Override
|
---|
195 | public String toString() {
|
---|
196 | return "TestConfig [testDirectory=" + testDirectory + ", testArea=" + testArea + ']';
|
---|
197 | }
|
---|
198 | }
|
---|
199 | }
|
---|