Changeset 36174 in osm
- Timestamp:
- 2023-10-17T15:05:06+02:00 (14 months ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
r35456 r36174 19 19 import java.util.stream.Collectors; 20 20 21 import javax.xml.XMLConstants; 21 22 import javax.xml.parsers.DocumentBuilder; 22 23 import javax.xml.parsers.DocumentBuilderFactory; … … 51 52 /** Setting key for Bing API key */ 52 53 public static final String API_KEY_SETTING = "jmapviewer.bing.api-key"; 53 /** Placeholder to specify Bing API key in metadata API URL */54 /** Placeholder to specify Bing API key in metadata API URL */ 54 55 public static final String API_KEY_PLACEHOLDER = "{apikey}"; 56 /** Placeholder to specify Bing API layer in metadata API URL */ 57 private static final String API_KEY_LAYER = "{layer}"; 55 58 56 59 /** Bing Metadata API URL */ 57 60 private static final String METADATA_API_URL = 58 "https://dev.virtualearth.net/REST/v1/Imagery/Metadata/ Aerial?include=ImageryProviders&output=xml&key=" + API_KEY_PLACEHOLDER;61 "https://dev.virtualearth.net/REST/v1/Imagery/Metadata/{layer}?include=ImageryProviders&output=xml&key=" + API_KEY_PLACEHOLDER; 59 62 /** Original Bing API key created by Potlatch2 developers in 2010 */ 60 63 private static final String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU"; 61 64 62 private staticvolatile Future<List<Attribution>> attributions; // volatile is required for getAttribution(), see below.63 private staticString imageUrlTemplate;64 private static Integer imageryZoomMax;65 private staticString[] subdomains;66 67 private static final Pattern subdomainPattern = Pattern.compile("\\{subdomain \\}");68 private static final Pattern quadkeyPattern = Pattern.compile("\\{quadkey \\}");69 private static final Pattern culturePattern = Pattern.compile("\\{culture \\}");65 private volatile Future<List<Attribution>> attributions; // volatile is required for getAttribution(), see below. 66 private String imageUrlTemplate; 67 private int imageryZoomMax = Integer.MIN_VALUE; 68 private String[] subdomains; 69 70 private static final Pattern subdomainPattern = Pattern.compile("\\{subdomain}"); 71 private static final Pattern quadkeyPattern = Pattern.compile("\\{quadkey}"); 72 private static final Pattern culturePattern = Pattern.compile("\\{culture}"); 70 73 private String brandLogoUri; 74 private String layer = "Aerial"; 71 75 72 76 /** … … 110 114 } 111 115 116 /** 117 * Set the layer for this Bing tile source 118 * @param layer The layer to use. See 119 * <a href="https://learn.microsoft.com/en-us/bingmaps/rest-services/imagery/get-imagery-metadata#template-parameters"> 120 * get-imagery-metadata 121 * </a> for valid layers. 122 * @since JMapViewer 2.18 123 */ 124 protected void setLayer(String layer) { 125 this.layer = layer; 126 } 127 112 128 protected URL getAttributionUrl() throws MalformedURLException { 113 129 return new URL(FeatureAdapter.getSetting(METADATA_API_SETTING, METADATA_API_URL) 114 .replace(API_KEY_PLACEHOLDER, FeatureAdapter.getSetting(API_KEY_SETTING, API_KEY))); 130 .replace(API_KEY_PLACEHOLDER, FeatureAdapter.getSetting(API_KEY_SETTING, API_KEY)) 131 .replace(API_KEY_LAYER, this.layer)); 115 132 } 116 133 … … 118 135 try { 119 136 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 137 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 138 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 139 factory.setXIncludeAware(false); 120 140 DocumentBuilder builder = factory.newDocumentBuilder(); 121 141 Document document = builder.parse(xml); … … 123 143 XPathFactory xPathFactory = XPathFactory.newInstance(); 124 144 XPath xpath = xPathFactory.newXPath(); 125 imageUrlTemplate = xpath.compile("//ImageryMetadata/ImageUrl/text()").evaluate(document).replace( 126 "http://ecn.{subdomain}.tiles.virtualearth.net/", 127 "https://ecn.{subdomain}.tiles.virtualearth.net/"); 128 imageUrlTemplate = culturePattern.matcher(imageUrlTemplate).replaceAll(Locale.getDefault().toString()); 129 imageryZoomMax = Integer.valueOf(xpath.compile("//ImageryMetadata/ZoomMax/text()").evaluate(document)); 145 setImageUrlTemplate(xpath.compile("//ImageryMetadata/ImageUrl/text()").evaluate(document)); 146 setImageryZoomMax(Integer.parseInt(xpath.compile("//ImageryMetadata/ZoomMax/text()").evaluate(document))); 130 147 131 148 NodeList subdomainTxt = (NodeList) xpath.compile("//ImageryMetadata/ImageUrlSubdomains/string/text()") 132 149 .evaluate(document, XPathConstants.NODESET); 133 subdomains = new String[subdomainTxt.getLength()]; 134 for (int i = 0; i < subdomainTxt.getLength(); i++) { 135 subdomains[i] = subdomainTxt.item(i).getNodeValue(); 136 } 150 setSubdomains(subdomainTxt); 137 151 138 152 brandLogoUri = xpath.compile("/Response/BrandLogoUri/text()").evaluate(document); … … 164 178 attr.minZoom = Integer.parseInt(zoomMinXpath.evaluate(areaNode)); 165 179 166 Double southLat = Double.valueOf(southLatXpath.evaluate(areaNode));167 Double northLat = Double.valueOf(northLatXpath.evaluate(areaNode));168 Double westLon = Double.valueOf(westLonXpath.evaluate(areaNode));169 Double eastLon = Double.valueOf(eastLonXpath.evaluate(areaNode));180 double southLat = Double.parseDouble(southLatXpath.evaluate(areaNode)); 181 double northLat = Double.parseDouble(northLatXpath.evaluate(areaNode)); 182 double westLon = Double.parseDouble(westLonXpath.evaluate(areaNode)); 183 double eastLon = Double.parseDouble(eastLonXpath.evaluate(areaNode)); 170 184 attr.min = new Coordinate(southLat, westLon); 171 185 attr.max = new Coordinate(northLat, eastLon); … … 184 198 @Override 185 199 public int getMaxZoom() { 186 if (imageryZoomMax != null)200 if (imageryZoomMax != Integer.MIN_VALUE) 187 201 return imageryZoomMax; 188 202 else … … 210 224 } else { 211 225 // Some Linux distributions (like Debian) will remove Bing logo from sources, so get it at runtime 212 for (int i = 0; i < 5 && getAttribution() == null; i++) {226 for (int i = 0; i < 5 && (getAttribution() == null || getAttribution().isEmpty()); i++) { 213 227 // Makes sure attribution is loaded 214 228 if (JMapViewer.debug) { 215 System.out.println("Bing attribution attempt " + (i+1));229 LOG.log(Level.FINE, "Bing attribution attempt {0}", (i + 1)); 216 230 } 217 231 } 218 232 if (brandLogoUri != null && !brandLogoUri.isEmpty()) { 219 System.out.println("Reading Bing logo from "+brandLogoUri);233 LOG.log(Level.FINE, "Reading Bing logo from {0}", brandLogoUri); 220 234 return FeatureAdapter.readImage(new URL(brandLogoUri)); 221 235 } … … 243 257 244 258 protected Callable<List<Attribution>> getAttributionLoaderCallable() { 245 return new Callable<List<Attribution>>() { 246 247 @Override 248 public List<Attribution> call() throws Exception { 249 int waitTimeSec = 1; 250 while (true) { 251 try { 252 InputSource xml = new InputSource(getAttributionUrl().openStream()); 253 List<Attribution> r = parseAttributionText(xml); 254 System.out.println("Successfully loaded Bing attribution data."); 255 return r; 256 } catch (IOException ex) { 257 LOG.log(Level.SEVERE, "Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds."); 258 Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSec)); 259 waitTimeSec *= 2; 260 } 259 return () -> { 260 int waitTimeSec = 1; 261 while (true) { 262 try { 263 InputSource xml = new InputSource(getAttributionUrl().openStream()); 264 List<Attribution> r = parseAttributionText(xml); 265 LOG.log(Level.FINE, "Successfully loaded Bing attribution data."); 266 return r; 267 } catch (IOException ex) { 268 LOG.log(Level.SEVERE, "Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds."); 269 LOG.log(Level.FINE, ex.getMessage(), ex); 270 Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSec)); 271 waitTimeSec *= 2; 261 272 } 262 273 } … … 278 289 return attributions.get(); 279 290 } catch (ExecutionException ex) { 280 throw new RuntimeException(ex .getCause());291 throw new RuntimeException(ex); 281 292 } catch (InterruptedException ign) { 282 LOG.log(Level.SEVERE, "InterruptedException: " + ign.getMessage()); 293 LOG.log(Level.SEVERE, "InterruptedException: {0}", ign.getMessage()); 294 LOG.log(Level.FINE, ign.getMessage(), ign); 295 Thread.currentThread().interrupt(); 283 296 } 284 297 return null; … … 298 311 .collect(Collectors.joining(" ")); 299 312 } catch (RuntimeException e) { 300 e.printStackTrace();313 LOG.log(Level.SEVERE, e.getMessage(), e); 301 314 } 302 315 return "Error loading Bing attribution data"; 316 } 317 318 private void setImageUrlTemplate(String template) { 319 String noHttpTemplate = template.replace("http://ecn.{subdomain}.tiles.virtualearth.net/", 320 "https://ecn.{subdomain}.tiles.virtualearth.net/"); 321 this.imageUrlTemplate = culturePattern.matcher(noHttpTemplate).replaceAll(Locale.getDefault().toString()); 322 } 323 324 private void setImageryZoomMax(int maxZoom) { 325 imageryZoomMax = maxZoom; 326 } 327 328 private void setSubdomains(NodeList subdomainTxt) { 329 subdomains = new String[subdomainTxt.getLength()]; 330 for (int i = 0; i < subdomainTxt.getLength(); i++) { 331 subdomains[i] = subdomainTxt.item(i).getNodeValue(); 332 } 303 333 } 304 334
Note:
See TracChangeset
for help on using the changeset viewer.