]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/gui/map/OsmMapSource.java
Allow to use maps with custom format
[GpsPrune.git] / src / tim / prune / gui / map / OsmMapSource.java
1 package tim.prune.gui.map;
2
3 import java.util.regex.Matcher;
4 import tim.prune.I18nManager;
5
6 /**
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)
11  */
12 public class OsmMapSource extends MapSource
13 {
14         /** Name for this source */
15         private String _name = null;
16         /** Base urls */
17         private String[] _baseUrls = null;
18         /** Site names */
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;
24
25
26         /**
27          * Constructor giving single name and url
28          * @param inName source name
29          * @param inUrl base url
30          */
31         public OsmMapSource(String inName, String inUrl)
32         {
33                 this(inName, inUrl, "png", null, null, 18);
34         }
35
36         /**
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
42          */
43         public OsmMapSource(String inName, String inStr1, String inStr2, int inMaxZoom)
44         {
45                 if (inStr2 != null && inStr2.length() == 3)
46                         init(inName, inStr1, inStr2, null, null, inMaxZoom);
47                 else
48                         init(inName, inStr1, "png", inStr2, "png", inMaxZoom);
49         }
50
51         /**
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
59          */
60         public OsmMapSource(String inName, String inUrl1, String inExt1,
61                 String inUrl2, String inExt2, int inMaxZoom)
62         {
63                 init(inName, inUrl1, inExt1, inUrl2, inExt2, inMaxZoom);
64         }
65
66         /**
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
74          */
75         private void init(String inName, String inUrl1, String inExt1,
76                 String inUrl2, String inExt2, int inMaxZoom)
77         {
78                 _name = inName;
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)
91                 {
92                         _baseUrls[0] = _baseUrls[1];
93                         _siteNames[0] = _siteNames[1];
94                         _baseUrls[1] = _siteNames[1] = null;
95                 }
96                 _maxZoom = inMaxZoom;
97         }
98
99         /** Set the API key (if required) */
100         public void setApiKey(String inKey)
101         {
102                 _apiKey = inKey;
103         }
104
105         /**
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
109          */
110         public static OsmMapSource fromConfig(String inConfigString)
111         {
112                 OsmMapSource source = null;
113                 if (inConfigString.startsWith("o:"))
114                 {
115                         String[] items = inConfigString.substring(2).split(";");
116                         try {
117                                 if (items.length == 3) { // single source url
118                                         source = new OsmMapSource(items[0], items[1], null, Integer.parseInt(items[2]));
119                                 }
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]));
122                                 }
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]));
125                                 }
126                         } catch (NumberFormatException nfe) {}
127                 }
128                 return source;
129         }
130
131         /**
132          * @return name
133          */
134         public String getName() {
135                 return _name;
136         }
137
138         /** Number of layers */
139         public int getNumLayers() {
140                 return _baseUrls[1] == null?1:2;
141         }
142
143         /** Base url for this source */
144         public String getBaseUrl(int inLayerNum) {
145                 return _baseUrls[inLayerNum];
146         }
147
148         /** site name without protocol or www. */
149         public String getSiteName(int inLayerNum) {
150                 return _siteNames[inLayerNum];
151         }
152
153         /**
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
160          */
161         public String makeURL(int inLayerNum, int inZoom, int inX, int inY)
162         {
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);
166         }
167
168         public String makeUrl(String baseUrl, int inLayerNum, int inZoom, int inX, int inY)
169         {
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));
175                         return baseUrl;
176                 }
177
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));
182                 if (_apiKey != null)
183                 {
184                         url.append("?apikey=").append(_apiKey);
185                 }
186                 return url.toString();
187         }
188
189         /**
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
196          */
197         public String makeFilePath(int inLayerNum, int inZoom, int inX, int inY)
198         {
199                 String siteName = getSiteName(inLayerNum);
200                 String filePath = makeUrl(siteName, inLayerNum, inZoom, inX, inY);
201                 int indexParam = filePath.indexOf("?");
202                 if (indexParam > 0)
203                 {
204                         filePath = filePath.substring(0, indexParam);
205                 }
206                 return filePath;
207         }
208
209         /**
210          * @return maximum zoom level
211          */
212         public final int getMaxZoomLevel()
213         {
214                 return _maxZoom;
215         }
216
217         /**
218          * If the base url contains something like [1234], then pick a server
219          * @param inBaseUrl base url
220          * @return modified base url
221          */
222         protected static final String pickServerUrl(String inBaseUrl)
223         {
224                 if (inBaseUrl == null || inBaseUrl.indexOf('[') < 0) {
225                         return inBaseUrl;
226                 }
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()) {
232                         return inBaseUrl;
233                 }
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;
238                 if (numMatches > 0)
239                 {
240                         int matchNum = (int) Math.floor(Math.random() * numMatches);
241                         server = "" + match.charAt(matchNum);
242                 }
243                 final String result = matcher.group(1) + (server==null?"":server) + matcher.group(3);
244                 return result;
245         }
246
247         /**
248          * @return semicolon-separated list of all fields
249          */
250         public String getConfigString()
251         {
252                 return "o:" +  getName() + ";" + getSiteStrings() + getMaxZoomLevel();
253         }
254 }