package tim.prune.gui.map; import java.util.regex.Matcher; import tim.prune.I18nManager; /** * Class to provide a map source for all OSM-like sources * (eg mapnik, opencyclemap, openpistemap etc). * These can be single-layer or double-layer sources with tiles * in various formats (default png) */ public class OsmMapSource extends MapSource { /** Name for this source */ private String _name = null; /** Base urls */ private String[] _baseUrls = null; /** Site names */ private String[] _siteNames = null; /** Maximum zoom level */ private int _maxZoom = 0; /** API key, usually remains empty */ private String _apiKey = null; /** * Constructor giving single name and url * @param inName source name * @param inUrl base url */ public OsmMapSource(String inName, String inUrl) { this(inName, inUrl, "png", null, null, 18); } /** * Constructor giving name, two strings and maximum zoom * @param inName source name * @param inStr1 base layer url * @param inStr2 either base layer extension or upper layer url * @param inMaxZoom maximum zoom level */ public OsmMapSource(String inName, String inStr1, String inStr2, int inMaxZoom) { if (inStr2 != null && inStr2.length() == 3) init(inName, inStr1, inStr2, null, null, inMaxZoom); else init(inName, inStr1, "png", inStr2, "png", inMaxZoom); } /** * Constructor giving name, urls, extensions and maximum zoom * @param inName source name * @param inUrl1 base layer url * @param inExt1 extension for base layer * @param inUrl2 upper layer url * @param inExt2 extension for top layer * @param inMaxZoom maximum zoom level */ public OsmMapSource(String inName, String inUrl1, String inExt1, String inUrl2, String inExt2, int inMaxZoom) { init(inName, inUrl1, inExt1, inUrl2, inExt2, inMaxZoom); } /** * Initialisation giving name, urls, extensions and maximum zoom * @param inName source name * @param inUrl1 base layer url * @param inExt1 extension for base layer * @param inUrl2 upper layer url * @param inExt2 extension for top layer * @param inMaxZoom maximum zoom level */ private void init(String inName, String inUrl1, String inExt1, String inUrl2, String inExt2, int inMaxZoom) { _name = inName; if (_name == null || _name.trim().equals("")) {_name = I18nManager.getText("mapsource.unknown");} _baseUrls = new String[2]; _baseUrls[0] = fixBaseUrl(inUrl1); _baseUrls[1] = fixBaseUrl(inUrl2); _extensions = new String[2]; _extensions[0] = inExt1; _extensions[1] = inExt2; _siteNames = new String[2]; _siteNames[0] = fixSiteName(_baseUrls[0]); _siteNames[1] = fixSiteName(_baseUrls[1]); // Swap layers if second layer given without first if (_baseUrls[0] == null && _baseUrls[1] != null) { _baseUrls[0] = _baseUrls[1]; _siteNames[0] = _siteNames[1]; _baseUrls[1] = _siteNames[1] = null; } _maxZoom = inMaxZoom; } /** Set the API key (if required) */ public void setApiKey(String inKey) { _apiKey = inKey; } /** * Construct a new map source from its config string * @param inConfigString string from Config, separated by semicolons * @return new map source, or null if not parseable */ public static OsmMapSource fromConfig(String inConfigString) { OsmMapSource source = null; if (inConfigString.startsWith("o:")) { String[] items = inConfigString.substring(2).split(";"); try { if (items.length == 3) { // single source url source = new OsmMapSource(items[0], items[1], null, Integer.parseInt(items[2])); } else if (items.length == 4) { // two urls or one url plus extension source = new OsmMapSource(items[0], items[1], items[2], Integer.parseInt(items[3])); } else if (items.length == 6) { // two urls and two extensions source = new OsmMapSource(items[0], items[1], items[2], items[3], items[4], Integer.parseInt(items[5])); } } catch (NumberFormatException nfe) {} } return source; } /** * @return name */ public String getName() { return _name; } /** Number of layers */ public int getNumLayers() { return _baseUrls[1] == null?1:2; } /** Base url for this source */ public String getBaseUrl(int inLayerNum) { return _baseUrls[inLayerNum]; } /** site name without protocol or www. */ public String getSiteName(int inLayerNum) { return _siteNames[inLayerNum]; } /** * Make the URL to get the specified tile */ public String makeURL(int inLayerNum, int inZoom, int inX, int inY) { // Check if the base url has a [1234], if so replace at random StringBuffer url = new StringBuffer(); url.append(pickServerUrl(_baseUrls[inLayerNum])); url.append(inZoom).append('/').append(inX).append('/').append(inY); url.append('.').append(getFileExtension(inLayerNum)); if (_apiKey != null) { url.append("?apikey=").append(_apiKey); } return url.toString(); } /** * @return maximum zoom level */ public final int getMaxZoomLevel() { return _maxZoom; } /** * If the base url contains something like [1234], then pick a server * @param inBaseUrl base url * @return modified base url */ protected static final String pickServerUrl(String inBaseUrl) { if (inBaseUrl == null || inBaseUrl.indexOf('[') < 0) { return inBaseUrl; } // Check for [.*] (once only) // Only need to support one, make things a bit easier final Matcher matcher = WILD_PATTERN.matcher(inBaseUrl); // if not, return base url unchanged if (!matcher.matches()) { return inBaseUrl; } // if so, pick one at random and replace in the String final String match = matcher.group(2); final int numMatches = match.length(); String server = null; if (numMatches > 0) { int matchNum = (int) Math.floor(Math.random() * numMatches); server = "" + match.charAt(matchNum); } final String result = matcher.group(1) + (server==null?"":server) + matcher.group(3); return result; } /** * @return semicolon-separated list of all fields */ public String getConfigString() { return "o:" + getName() + ";" + getSiteStrings() + getMaxZoomLevel(); } }