source: josm/trunk/test/unit/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreferenceTestIT.java@ 14521

Last change on this file since 14521 was 14521, checked in by Don-vip, 5 years ago

see #16073 - handle entries where centroid does not lie in shape (like Canadian 'British Columbia Mosaic')

  • Property svn:eol-style set to native
File size: 9.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences.imagery;
3
4import static org.junit.Assert.assertTrue;
5
6import java.io.IOException;
7import java.net.URL;
8import java.util.ArrayList;
9import java.util.Collections;
10import java.util.HashSet;
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14import java.util.TreeMap;
15
16import org.junit.Rule;
17import org.junit.Test;
18import org.openstreetmap.gui.jmapviewer.Coordinate;
19import org.openstreetmap.gui.jmapviewer.TileXY;
20import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
21import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource;
22import org.openstreetmap.gui.jmapviewer.tilesources.BingAerialTileSource;
23import org.openstreetmap.gui.jmapviewer.tilesources.ScanexTileSource;
24import org.openstreetmap.gui.jmapviewer.tilesources.TemplatedTMSTileSource;
25import org.openstreetmap.josm.data.Bounds;
26import org.openstreetmap.josm.data.coor.LatLon;
27import org.openstreetmap.josm.data.imagery.CoordinateConversion;
28import org.openstreetmap.josm.data.imagery.ImageryInfo;
29import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
30import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
31import org.openstreetmap.josm.data.imagery.Shape;
32import org.openstreetmap.josm.data.imagery.TemplatedWMSTileSource;
33import org.openstreetmap.josm.data.imagery.WMSEndpointTileSource;
34import org.openstreetmap.josm.data.imagery.WMTSTileSource;
35import org.openstreetmap.josm.data.imagery.WMTSTileSource.WMTSGetCapabilitiesException;
36import org.openstreetmap.josm.data.projection.ProjectionRegistry;
37import org.openstreetmap.josm.testutils.JOSMTestRules;
38import org.openstreetmap.josm.tools.HttpClient;
39import org.openstreetmap.josm.tools.HttpClient.Response;
40import org.openstreetmap.josm.tools.Logging;
41import org.openstreetmap.josm.tools.Utils;
42
43import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
44
45/**
46 * Integration tests of {@link ImageryPreference} class.
47 */
48public class ImageryPreferenceTestIT {
49
50 /**
51 * Setup rule
52 */
53 @Rule
54 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
55 public JOSMTestRules test = new JOSMTestRules().https().projection().projectionNadGrids().timeout(10000*60);
56
57 private final Map<String, Map<ImageryInfo, List<String>>> errors = Collections.synchronizedMap(new TreeMap<>());
58 private final Set<String> workingURLs = Collections.synchronizedSet(new HashSet<>());
59
60 private boolean addError(ImageryInfo info, String error) {
61 return errors.computeIfAbsent(info.getCountryCode(), x -> Collections.synchronizedMap(new TreeMap<>()))
62 .computeIfAbsent(info, x -> Collections.synchronizedList(new ArrayList<>()))
63 .add(error);
64 }
65
66 private void checkUrl(ImageryInfo info, String url) {
67 if (url != null && !workingURLs.contains(url)) {
68 try {
69 Response response = HttpClient.create(new URL(url)).connect();
70 if (response.getResponseCode() >= 400) {
71 addError(info, url + " -> HTTP " + response.getResponseCode());
72 } else if (response.getResponseCode() >= 300) {
73 Logging.warn(url + " -> HTTP " + response.getResponseCode());
74 } else {
75 workingURLs.add(url);
76 }
77 response.disconnect();
78 } catch (IOException e) {
79 addError(info, url + " -> " + e);
80 }
81 }
82 }
83
84 private void checkTileUrl(ImageryInfo info, AbstractTileSource tileSource, ICoordinate center, int zoom)
85 throws IOException {
86 TileXY xy = tileSource.latLonToTileXY(center, zoom);
87 for (int i = 0; i < 3; i++) {
88 try {
89 checkUrl(info, tileSource.getTileUrl(zoom, xy.getXIndex(), xy.getYIndex()));
90 return;
91 } catch (IOException e) {
92 // Try up to three times max to allow Bing source to initialize itself
93 // and avoid random network errors
94 Logging.trace(e);
95 if (i == 2) {
96 throw e;
97 }
98 try {
99 Thread.sleep(500);
100 } catch (InterruptedException ex) {
101 Logging.warn(ex);
102 }
103 }
104 }
105 }
106
107 private static LatLon getPointInShape(Shape shape) {
108 final Coordinate p1 = shape.getPoints().get(0);
109 final Bounds bounds = new Bounds(p1.getLat(), p1.getLon(), p1.getLat(), p1.getLon());
110 shape.getPoints().forEach(p -> bounds.extend(p.getLat(), p.getLon()));
111
112 final double w = bounds.getWidth();
113 final double h = bounds.getHeight();
114
115 final double x2 = bounds.getMinLon() + (w / 2.0);
116 final double y2 = bounds.getMinLat() + (h / 2.0);
117
118 final LatLon center = new LatLon(y2, x2);
119
120 // check to see if center is inside shape
121 if (shape.contains(center)) {
122 return center;
123 }
124
125 // if center position (C) is not inside shape, try naively some other positions as follows:
126 final double x1 = bounds.getMinLon() + (.25 * w);
127 final double x3 = bounds.getMinLon() + (.75 * w);
128 final double y1 = bounds.getMinLat() + (.25 * h);
129 final double y3 = bounds.getMinLat() + (.75 * h);
130 // +-----------+
131 // | 5 1 6 |
132 // | 4 C 2 |
133 // | 8 3 7 |
134 // +-----------+
135 for (LatLon candidate : new LatLon[] {
136 new LatLon(y1, x2),
137 new LatLon(y2, x3),
138 new LatLon(y3, x2),
139 new LatLon(y2, x1),
140 new LatLon(y1, x1),
141 new LatLon(y1, x3),
142 new LatLon(y3, x3),
143 new LatLon(y3, x1)
144 }) {
145 if (shape.contains(candidate)) {
146 return candidate;
147 }
148 }
149 return center;
150 }
151
152 private static LatLon getCenter(ImageryBounds bounds) {
153 List<Shape> shapes = bounds.getShapes();
154 return shapes != null && !shapes.isEmpty() ? getPointInShape(shapes.get(0)) : bounds.getCenter();
155 }
156
157 private void checkEntry(ImageryInfo info) {
158 Logging.info("Checking "+ info);
159
160 if (info.getAttributionImageRaw() != null && info.getAttributionImage() == null) {
161 addError(info, "Can't fetch attribution image: " + info.getAttributionImageRaw());
162 }
163
164 checkUrl(info, info.getAttributionImageURL());
165 checkUrl(info, info.getAttributionLinkURL());
166 String eula = info.getEulaAcceptanceRequired();
167 if (eula != null) {
168 checkUrl(info, eula.replaceAll("\\{lang\\}", ""));
169 }
170 checkUrl(info, info.getPermissionReferenceURL());
171 checkUrl(info, info.getTermsOfUseURL());
172
173 try {
174 ImageryBounds bounds = info.getBounds();
175 // Some imagery sources do not define tiles at (0,0). So pickup Greenwich Royal Observatory for global sources
176 ICoordinate center = CoordinateConversion.llToCoor(bounds != null ? getCenter(bounds) : new LatLon(51.47810, -0.00170));
177 AbstractTileSource tileSource = getTileSource(info);
178 checkTileUrl(info, tileSource, center, info.getMinZoom());
179 // checking max zoom for real is complex, see https://josm.openstreetmap.de/ticket/16073#comment:27
180 if (info.getMaxZoom() > 0) {
181 checkTileUrl(info, tileSource, center, Utils.clamp(12, info.getMinZoom() + 1, info.getMaxZoom()));
182 }
183 } catch (IOException | WMTSGetCapabilitiesException | IllegalArgumentException e) {
184 addError(info, e.toString());
185 }
186
187 for (ImageryInfo mirror : info.getMirrors()) {
188 checkEntry(mirror);
189 }
190 }
191
192 private static AbstractTileSource getTileSource(ImageryInfo info) throws IOException, WMTSGetCapabilitiesException {
193 switch (info.getImageryType()) {
194 case BING:
195 return new BingAerialTileSource(info);
196 case SCANEX:
197 return new ScanexTileSource(info);
198 case TMS:
199 return new TemplatedTMSTileSource(info);
200 case WMS:
201 return new TemplatedWMSTileSource(info, ProjectionRegistry.getProjection());
202 case WMS_ENDPOINT:
203 return new WMSEndpointTileSource(info, ProjectionRegistry.getProjection());
204 case WMTS:
205 return new WMTSTileSource(info, ProjectionRegistry.getProjection());
206 default:
207 throw new UnsupportedOperationException(info.toString());
208 }
209 }
210
211 /**
212 * Test that available imagery entries are valid.
213 * @throws Exception in case of error
214 */
215 @Test
216 public void testValidityOfAvailableImageryEntries() throws Exception {
217 ImageryLayerInfo.instance.load(false);
218 ImageryLayerInfo.instance.getDefaultLayers().parallelStream().forEach(this::checkEntry);
219 assertTrue(errors.toString().replaceAll("\\}, ", "\n\\}, ").replaceAll(", ImageryInfo\\{", "\n ,ImageryInfo\\{"),
220 errors.isEmpty());
221 }
222}
Note: See TracBrowser for help on using the repository browser.