1 package tim.prune.gui.map;
3 import java.util.regex.Matcher;
4 import tim.prune.I18nManager;
7 * Class to provide a map source for all OSM-like sources
8 * (eg mapnik, opencyclemap, openpistemap etc).
9 * These can be single-layer or double-layer sources with tiles
10 * in various formats (default png)
12 public class OsmMapSource extends MapSource
14 /** Name for this source */
15 private String _name = null;
17 private String[] _baseUrls = null;
19 private String[] _siteNames = null;
20 /** Maximum zoom level */
21 private int _maxZoom = 0;
22 /** API key, usually remains empty */
23 private String _apiKey = null;
27 * Constructor giving single name and url
28 * @param inName source name
29 * @param inUrl base url
31 public OsmMapSource(String inName, String inUrl)
33 this(inName, inUrl, "png", null, null, 18);
37 * Constructor giving name, two strings and maximum zoom
38 * @param inName source name
39 * @param inStr1 base layer url
40 * @param inStr2 either base layer extension or upper layer url
41 * @param inMaxZoom maximum zoom level
43 public OsmMapSource(String inName, String inStr1, String inStr2, int inMaxZoom)
45 if (inStr2 != null && inStr2.length() == 3)
46 init(inName, inStr1, inStr2, null, null, inMaxZoom);
48 init(inName, inStr1, "png", inStr2, "png", inMaxZoom);
52 * Constructor giving name, urls, extensions and maximum zoom
53 * @param inName source name
54 * @param inUrl1 base layer url
55 * @param inExt1 extension for base layer
56 * @param inUrl2 upper layer url
57 * @param inExt2 extension for top layer
58 * @param inMaxZoom maximum zoom level
60 public OsmMapSource(String inName, String inUrl1, String inExt1,
61 String inUrl2, String inExt2, int inMaxZoom)
63 init(inName, inUrl1, inExt1, inUrl2, inExt2, inMaxZoom);
67 * Initialisation giving name, urls, extensions and maximum zoom
68 * @param inName source name
69 * @param inUrl1 base layer url
70 * @param inExt1 extension for base layer
71 * @param inUrl2 upper layer url
72 * @param inExt2 extension for top layer
73 * @param inMaxZoom maximum zoom level
75 private void init(String inName, String inUrl1, String inExt1,
76 String inUrl2, String inExt2, int inMaxZoom)
79 if (_name == null || _name.trim().equals("")) {_name = I18nManager.getText("mapsource.unknown");}
80 _baseUrls = new String[2];
81 _baseUrls[0] = fixBaseUrl(inUrl1);
82 _baseUrls[1] = fixBaseUrl(inUrl2);
83 _extensions = new String[2];
84 _extensions[0] = inExt1;
85 _extensions[1] = inExt2;
86 _siteNames = new String[2];
87 _siteNames[0] = fixSiteName(_baseUrls[0]);
88 _siteNames[1] = fixSiteName(_baseUrls[1]);
89 // Swap layers if second layer given without first
90 if (_baseUrls[0] == null && _baseUrls[1] != null)
92 _baseUrls[0] = _baseUrls[1];
93 _siteNames[0] = _siteNames[1];
94 _baseUrls[1] = _siteNames[1] = null;
99 /** Set the API key (if required) */
100 public void setApiKey(String inKey)
106 * Construct a new map source from its config string
107 * @param inConfigString string from Config, separated by semicolons
108 * @return new map source, or null if not parseable
110 public static OsmMapSource fromConfig(String inConfigString)
112 OsmMapSource source = null;
113 if (inConfigString.startsWith("o:"))
115 String[] items = inConfigString.substring(2).split(";");
117 if (items.length == 3) { // single source url
118 source = new OsmMapSource(items[0], items[1], null, Integer.parseInt(items[2]));
120 else if (items.length == 4) { // two urls or one url plus extension
121 source = new OsmMapSource(items[0], items[1], items[2], Integer.parseInt(items[3]));
123 else if (items.length == 6) { // two urls and two extensions
124 source = new OsmMapSource(items[0], items[1], items[2], items[3], items[4], Integer.parseInt(items[5]));
126 } catch (NumberFormatException nfe) {}
134 public String getName() {
138 /** Number of layers */
139 public int getNumLayers() {
140 return _baseUrls[1] == null?1:2;
143 /** Base url for this source */
144 public String getBaseUrl(int inLayerNum) {
145 return _baseUrls[inLayerNum];
148 /** site name without protocol or www. */
149 public String getSiteName(int inLayerNum) {
150 return _siteNames[inLayerNum];
154 * Make the URL to get the specified tile
155 * @param inLayerNum layer number
156 * @param inZoom zoom level
157 * @param inX x coordinate
158 * @param inY y coordinate
159 * @return relative file path as String
161 public String makeURL(int inLayerNum, int inZoom, int inX, int inY)
163 // Check if the base url has a [1234], if so replace at random
164 String baseUrl = pickServerUrl(_baseUrls[inLayerNum]);
165 return makeUrl(baseUrl, inLayerNum, inZoom, inX, inY);
168 public String makeUrl(String baseUrl, int inLayerNum, int inZoom, int inX, int inY)
170 // If the base URL has {x}/{y} placeholders, use them
171 if (baseUrl.contains("{x}")) {
172 baseUrl = baseUrl.replace("{z}", Integer.toString(inZoom))
173 .replace("{x}", Integer.toString(inX))
174 .replace("{y}", Integer.toString(inY));
178 // Else simply append the tile indices and file extension
179 StringBuffer url = new StringBuffer(baseUrl);
180 url.append(inZoom).append('/').append(inX).append('/').append(inY);
181 url.append('.').append(getFileExtension(inLayerNum));
184 url.append("?apikey=").append(_apiKey);
186 return url.toString();
190 * Make a relative file path from the base directory including site name
191 * @param inLayerNum layer number
192 * @param inZoom zoom level
193 * @param inX x coordinate
194 * @param inY y coordinate
195 * @return relative file path as String
197 public String makeFilePath(int inLayerNum, int inZoom, int inX, int inY)
199 String siteName = getSiteName(inLayerNum);
200 String filePath = makeUrl(siteName, inLayerNum, inZoom, inX, inY);
201 int indexParam = filePath.indexOf("?");
204 filePath = filePath.substring(0, indexParam);
210 * @return maximum zoom level
212 public final int getMaxZoomLevel()
218 * If the base url contains something like [1234], then pick a server
219 * @param inBaseUrl base url
220 * @return modified base url
222 protected static final String pickServerUrl(String inBaseUrl)
224 if (inBaseUrl == null || inBaseUrl.indexOf('[') < 0) {
227 // Check for [.*] (once only)
228 // Only need to support one, make things a bit easier
229 final Matcher matcher = WILD_PATTERN.matcher(inBaseUrl);
230 // if not, return base url unchanged
231 if (!matcher.matches()) {
234 // if so, pick one at random and replace in the String
235 final String match = matcher.group(2);
236 final int numMatches = match.length();
237 String server = null;
240 int matchNum = (int) Math.floor(Math.random() * numMatches);
241 server = "" + match.charAt(matchNum);
243 final String result = matcher.group(1) + (server==null?"":server) + matcher.group(3);
248 * @return semicolon-separated list of all fields
250 public String getConfigString()
252 return "o:" + getName() + ";" + getSiteStrings() + getMaxZoomLevel();