Skip to content
Commits on Source (5)
......@@ -10,7 +10,7 @@ JMapViewer
(c) 2010-2011, Michael Vigovsky
(c) 2011-2017, Paul Hartmann
(c) 2011-2016, Gleb Smirnoff
(c) 2011-2018, Vincent Privat
(c) 2011-2019, Vincent Privat
(c) 2011, Jason Huntley
(c) 2012-2016, Simon Legner
(c) 2012, Teemu Koskinen
......
jmapviewer (2.9+dfsg-2) UNRELEASED; urgency=medium
jmapviewer (2.10+dfsg-1~exp1) experimental; urgency=medium
* New upstream release.
* Remove package name from lintian overrides.
* Update copyright years for Vincent Privat.
-- Bas Couwenberg <sebastic@debian.org> Fri, 15 Mar 2019 15:56:05 +0100
-- Bas Couwenberg <sebastic@debian.org> Fri, 31 May 2019 06:02:30 +0200
jmapviewer (2.9+dfsg-1) unstable; urgency=medium
......
......@@ -25,7 +25,7 @@ Copyright: 2007, Tim Haussmann
2011-2017, Paul Hartmann
2017, Robert Scott
2009-2018, Dirk Stöcker
2011-2018, Vincent Privat
2011-2019, Vincent Privat
License: GPL-2+
Files: src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java
......
......@@ -8,17 +8,24 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
/**
* Feature adapter allows to override JMapViewer behaviours from a client application such as JOSM.
*/
public final class FeatureAdapter {
private static BrowserAdapter browserAdapter = new DefaultBrowserAdapter();
private static ImageAdapter imageAdapter = new DefaultImageAdapter();
private static TranslationAdapter translationAdapter = new DefaultTranslationAdapter();
private static LoggingAdapter loggingAdapter = new DefaultLoggingAdapter();
private static SettingsAdapter settingsAdapter = new DefaultSettingsAdapter();
private FeatureAdapter() {
// private constructor for utility classes
......@@ -41,6 +48,28 @@ public final class FeatureAdapter {
BufferedImage read(URL input, boolean readMetadata, boolean enforceTransparency) throws IOException;
}
/**
* Basic settings system allowing to store/retrieve String key/value pairs.
*/
public interface SettingsAdapter {
/**
* Get settings value for a certain key and provide a default value.
* @param key the identifier for the setting
* @param def the default value. For each call of get() with a given key, the
* default value must be the same. {@code def} may be null.
* @return the corresponding value if the property has been set before, {@code def} otherwise
*/
String get(String key, String def);
/**
* Set a value for a certain setting.
* @param key the unique identifier for the setting
* @param value the value of the setting. Can be null or "" which both removes the key-value entry.
* @return {@code true}, if something has changed (i.e. value is different than before)
*/
boolean put(String key, String value);
}
public static void registerBrowserAdapter(BrowserAdapter browserAdapter) {
FeatureAdapter.browserAdapter = Objects.requireNonNull(browserAdapter);
}
......@@ -57,6 +86,15 @@ public final class FeatureAdapter {
FeatureAdapter.loggingAdapter = Objects.requireNonNull(loggingAdapter);
}
/**
* Registers settings adapter.
* @param settingsAdapter settings adapter, must not be null
* @throws NullPointerException if settingsAdapter is null
*/
public static void registerSettingsAdapter(SettingsAdapter settingsAdapter) {
FeatureAdapter.settingsAdapter = Objects.requireNonNull(settingsAdapter);
}
public static void openLink(String url) {
browserAdapter.openLink(url);
}
......@@ -73,6 +111,42 @@ public final class FeatureAdapter {
return loggingAdapter.getLogger(name);
}
public static Logger getLogger(Class<?> klass) {
return loggingAdapter.getLogger(klass.getSimpleName());
}
/**
* Get settings value for a certain key and provide a default value.
* @param key the identifier for the setting
* @param def the default value. For each call of get() with a given key, the
* default value must be the same. {@code def} may be null.
* @return the corresponding value if the property has been set before, {@code def} otherwise
*/
public static String getSetting(String key, String def) {
return settingsAdapter.get(key, def);
}
/**
* Get settings value for a certain key and provide a default value.
* @param key the identifier for the setting
* @param def the default value. For each call of get() with a given key, the
* default value must be the same. {@code def} may be null.
* @return the corresponding value if the property has been set before, {@code def} otherwise
*/
public static int getIntSetting(String key, int def) {
return Integer.parseInt(settingsAdapter.get(key, Integer.toString(def)));
}
/**
* Set a value for a certain setting.
* @param key the unique identifier for the setting
* @param value the value of the setting. Can be null or "" which both removes the key-value entry.
* @return {@code true}, if something has changed (i.e. value is different than before)
*/
public static boolean putSetting(String key, String value) {
return settingsAdapter.put(key, value);
}
public static class DefaultBrowserAdapter implements BrowserAdapter {
@Override
public void openLink(String url) {
......@@ -85,7 +159,7 @@ public final class FeatureAdapter {
e.printStackTrace();
}
} else {
System.err.println(tr("Opening link not supported on current platform (''{0}'')", url));
getLogger(FeatureAdapter.class).log(Level.SEVERE, tr("Opening link not supported on current platform (''{0}'')", url));
}
}
}
......@@ -110,4 +184,21 @@ public final class FeatureAdapter {
return Logger.getLogger(name);
}
}
/**
* Default settings adapter keeping settings in memory only.
*/
public static class DefaultSettingsAdapter implements SettingsAdapter {
private final Map<String, String> settings = new TreeMap<>();
@Override
public String get(String key, String def) {
return settings.getOrDefault(key, def);
}
@Override
public boolean put(String key, String value) {
return !Objects.equals(value == null || value.isEmpty() ? settings.remove(key) : settings.put(key, value), value);
}
}
}
......@@ -13,6 +13,8 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openstreetmap.gui.jmapviewer.interfaces.TileJob;
import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
......@@ -24,7 +26,22 @@ import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
* @author Jan Peter Stotz
*/
public class OsmTileLoader implements TileLoader {
private static final ThreadPoolExecutor jobDispatcher = (ThreadPoolExecutor) Executors.newFixedThreadPool(8);
private static final Logger LOG = FeatureAdapter.getLogger(OsmTileLoader.class);
/** Setting key for number of threads */
public static final String THREADS_SETTING = "jmapviewer.osm-tile-loader.threads";
private static final int DEFAULT_THREADS_NUMBER = 8;
private static int nThreads = DEFAULT_THREADS_NUMBER;
static {
try {
nThreads = FeatureAdapter.getIntSetting(THREADS_SETTING, DEFAULT_THREADS_NUMBER);
} catch (Exception e) {
LOG.log(Level.SEVERE, e.getMessage(), e);
}
}
private static final ThreadPoolExecutor jobDispatcher = (ThreadPoolExecutor) Executors.newFixedThreadPool(nThreads);
private final class OsmTileJob implements TileJob {
private final Tile tile;
......@@ -68,10 +85,10 @@ public class OsmTileLoader implements TileLoader {
listener.tileLoadingFinished(tile, false);
if (input == null) {
try {
System.err.println("Failed loading " + tile.getUrl() +": "
LOG.log(Level.SEVERE, "Failed loading " + tile.getUrl() +": "
+e.getClass() + ": " + e.getMessage());
} catch (IOException ioe) {
ioe.printStackTrace();
LOG.log(Level.SEVERE, ioe.getMessage(), ioe);
}
}
} finally {
......@@ -102,12 +119,17 @@ public class OsmTileLoader implements TileLoader {
protected TileLoaderListener listener;
/**
* Constructs a new {@code OsmTileLoader}.
* @param listener tile loader listener
*/
public OsmTileLoader(TileLoaderListener listener) {
this(listener, null);
}
public OsmTileLoader(TileLoaderListener listener, Map<String, String> headers) {
this.headers.put("Accept", "text/html, image/png, image/jpeg, image/gif, */*");
this.headers.put("User-Agent", "JMapViewer Java/"+System.getProperty("java.version"));
if (headers != null) {
this.headers.putAll(headers);
}
......
......@@ -14,6 +14,8 @@ import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
......@@ -42,7 +44,21 @@ import org.xml.sax.SAXException;
*/
public class BingAerialTileSource extends TMSTileSource {
private static final Logger LOG = FeatureAdapter.getLogger(BingAerialTileSource.class);
/** Setting key for Bing metadata API URL. Must contain {@link #API_KEY_PLACEHOLDER} */
public static final String METADATA_API_SETTING = "jmapviewer.bing.metadata-api-url";
/** Setting key for Bing API key */
public static final String API_KEY_SETTING = "jmapviewer.bing.api-key";
/** Placeholder to specify Bing API key in metadata API URL*/
public static final String API_KEY_PLACEHOLDER = "{apiKey}";
/** Bing Metadata API URL */
private static final String METADATA_API_URL =
"https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&output=xml&key=" + API_KEY_PLACEHOLDER;
/** Original Bing API key created by Potlatch2 developers in 2010 */
private static final String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU";
private static volatile Future<List<Attribution>> attributions; // volatile is required for getAttribution(), see below.
private static String imageUrlTemplate;
private static Integer imageryZoomMax;
......@@ -94,8 +110,8 @@ public class BingAerialTileSource extends TMSTileSource {
}
protected URL getAttributionUrl() throws MalformedURLException {
return new URL("https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&output=xml&key="
+ API_KEY);
return new URL(FeatureAdapter.getSetting(METADATA_API_SETTING, METADATA_API_URL)
.replace(API_KEY_PLACEHOLDER, FeatureAdapter.getSetting(API_KEY_SETTING, API_KEY)));
}
protected List<Attribution> parseAttributionText(InputSource xml) throws IOException {
......@@ -159,11 +175,8 @@ public class BingAerialTileSource extends TMSTileSource {
}
return attributionsList;
} catch (SAXException e) {
System.err.println("Could not parse Bing aerials attribution metadata.");
e.printStackTrace();
} catch (ParserConfigurationException | XPathExpressionException | NumberFormatException e) {
e.printStackTrace();
} catch (SAXException | ParserConfigurationException | XPathExpressionException | NumberFormatException e) {
LOG.log(Level.SEVERE, "Could not parse Bing aerials attribution metadata.", e);
}
return null;
}
......@@ -208,7 +221,7 @@ public class BingAerialTileSource extends TMSTileSource {
}
}
} catch (IOException e) {
System.err.println("Error while retrieving Bing logo: "+e.getMessage());
LOG.log(Level.SEVERE, "Error while retrieving Bing logo: "+e.getMessage());
}
return null;
}
......@@ -241,7 +254,7 @@ public class BingAerialTileSource extends TMSTileSource {
System.out.println("Successfully loaded Bing attribution data.");
return r;
} catch (IOException ex) {
System.err.println("Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds.");
LOG.log(Level.SEVERE, "Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds.");
Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSec));
waitTimeSec *= 2;
}
......@@ -264,11 +277,11 @@ public class BingAerialTileSource extends TMSTileSource {
try {
return attributions.get(0, TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
System.err.println("Bing: attribution data is not yet loaded.");
LOG.log(Level.WARNING, "Bing: attribution data is not yet loaded.");
} catch (ExecutionException ex) {
throw new RuntimeException(ex.getCause());
} catch (InterruptedException ign) {
System.err.println("InterruptedException: " + ign.getMessage());
LOG.log(Level.SEVERE, "InterruptedException: " + ign.getMessage());
}
return null;
}
......