source: osm/applications/editors/josm/plugins/slippymap/slippymap/SlippyMapLayer.java@ 4740

Last change on this file since 4740 was 4740, checked in by frederik, 18 years ago

new features for slippymap plugin: show tile details, request update, load all tiles

File size: 13.2 KB
Line 
1package slippymap;
2
3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.awt.Color;
6import java.awt.Component;
7import java.awt.Graphics;
8import java.awt.Image;
9import java.awt.Point;
10import java.awt.Rectangle;
11import java.awt.event.ActionEvent;
12import java.awt.event.ActionListener;
13import java.awt.event.MouseAdapter;
14import java.awt.event.MouseEvent;
15import java.awt.image.ImageObserver;
16import java.io.File;
17import java.util.ArrayList;
18import java.util.Collection;
19import java.util.Date;
20import java.util.HashMap;
21
22import javax.swing.AbstractAction;
23import javax.swing.Action;
24import javax.swing.Icon;
25import javax.swing.JColorChooser;
26import javax.swing.JLabel;
27import javax.swing.JMenuItem;
28import javax.swing.JOptionPane;
29import javax.swing.JPopupMenu;
30import javax.swing.JSeparator;
31import javax.swing.SwingUtilities;
32
33import org.openstreetmap.josm.Main;
34import org.openstreetmap.josm.actions.JosmAction;
35import org.openstreetmap.josm.actions.RenameLayerAction;
36import org.openstreetmap.josm.data.coor.EastNorth;
37import org.openstreetmap.josm.data.coor.LatLon;
38import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
39import org.openstreetmap.josm.data.projection.Mercator;
40import org.openstreetmap.josm.data.projection.Projection;
41import org.openstreetmap.josm.gui.MapFrame;
42import org.openstreetmap.josm.gui.MapView;
43import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
44import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
45import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
46import org.openstreetmap.josm.gui.layer.Layer;
47import org.openstreetmap.josm.gui.layer.RawGpsLayer;
48import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
49import org.openstreetmap.josm.gui.layer.markerlayer.Marker;
50import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
51import org.openstreetmap.josm.tools.ColorHelper;
52import org.openstreetmap.josm.tools.ImageProvider;
53
54/**
55 * Class that displays a slippy map layer.
56 *
57 * @author Frederik Ramm <frederik@remote.org>
58 *
59 */
60public class SlippyMapLayer extends Layer implements ImageObserver {
61
62 ArrayList<HashMap<Integer, SlippyMapTile>> tileStorage = new ArrayList<HashMap<Integer, SlippyMapTile>>(20);
63 Point[][] pixelpos = new Point[21][21];
64 LatLon lastTopLeft;
65 LatLon lastBotRight;
66
67 int z12x0, z12x1, z12y0, z12y1;
68
69 private Image bufferImage;
70 private SlippyMapTile clickedTile;
71 private boolean needRedraw;
72
73 private JPopupMenu tileOptionMenu;
74
75 public SlippyMapLayer()
76 {
77 super("Slippy Map");
78 for (int i=0; i<18; i++)
79 tileStorage.add(new HashMap<Integer, SlippyMapTile>());
80
81 tileOptionMenu = new JPopupMenu();
82 tileOptionMenu.add(new JMenuItem(new AbstractAction("Load Tile") {
83 public void actionPerformed(ActionEvent ae) {
84 if (clickedTile != null)
85 {
86 clickedTile.loadImage();
87 needRedraw = true;
88 Main.map.repaint();
89 }
90 }
91 }));
92
93 tileOptionMenu.add(new JMenuItem(new AbstractAction("Show Tile Status") {
94 public void actionPerformed(ActionEvent ae) {
95 if (clickedTile != null)
96 {
97 clickedTile.loadMetadata();
98 needRedraw = true;
99 Main.map.repaint();
100 }
101 }
102 }));
103
104 tileOptionMenu.add(new JMenuItem(new AbstractAction("Request Update") {
105 public void actionPerformed(ActionEvent ae) {
106 if (clickedTile != null)
107 {
108 clickedTile.requestUpdate();
109 needRedraw = true;
110 Main.map.repaint();
111 }
112 }
113 }));
114
115 tileOptionMenu.add(new JMenuItem(new AbstractAction("Load All Tiles") {
116 public void actionPerformed(ActionEvent ae) {
117 loadAllTiles();
118 needRedraw = true;
119 Main.map.repaint();
120 }
121 }));
122
123 SwingUtilities.invokeLater(new Runnable(){
124 public void run() {
125 Main.map.mapView.addMouseListener(new MouseAdapter() {
126 @Override public void mouseClicked(MouseEvent e) {
127 if (e.getButton() != MouseEvent.BUTTON3)
128 return;
129 clickedTile = getTileForPixelpos(e.getX(), e.getY());
130 tileOptionMenu.show(e.getComponent(), e.getX(), e.getY());
131 }
132 });
133
134 Main.map.mapView.addLayerChangeListener(new MapView.LayerChangeListener(){
135 public void activeLayerChange(Layer oldLayer, Layer newLayer) {}
136 public void layerAdded(Layer newLayer) {}
137 public void layerRemoved(Layer oldLayer) {
138 Main.pref.listener.remove(SlippyMapLayer.this);
139 }
140 });
141 }
142 });
143 }
144
145 void loadAllTiles()
146 {
147 MapView mv = Main.map.mapView;
148 LatLon topLeft = mv.getLatLon(0, 0);
149 LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
150 z12x0 = lonToTileX(topLeft.lon());
151 z12x1 = lonToTileX(botRight.lon());
152 z12y0 = latToTileY(topLeft.lat());
153 z12y1 = latToTileY(botRight.lat());
154 if (z12x0>z12x1) { int tmp = z12x0; z12x0=z12x1; z12x1=tmp; }
155 if (z12y0>z12y1) { int tmp = z12y0; z12y0=z12y1; z12y1=tmp; }
156 if (z12x1-z12x0 > 18) return;
157 if (z12y1-z12y0 > 18) return;
158 for(int x = z12x0-1; x <= z12x1; x++)
159 {
160 for (int y=z12y0-1; y <= z12y1; y++)
161 {
162 int key = (y <<19) + x;
163 SlippyMapTile tile = tileStorage.get(12).get(key);
164 if (tile.getImage() == null) tile.loadImage();
165 }
166 }
167 }
168
169
170 /**
171 */
172 @Override public void paint(Graphics g, MapView mv)
173 {
174 LatLon topLeft = mv.getLatLon(0, 0);
175 LatLon botRight = mv.getLatLon(mv.getWidth(), mv.getHeight());
176 Graphics oldg = g;
177
178 if (lastTopLeft != null &&
179 lastBotRight != null &&
180 topLeft.equalsEpsilon(lastTopLeft) &&
181 botRight.equalsEpsilon(lastBotRight) &&
182 bufferImage != null &&
183 mv.getWidth() == bufferImage.getWidth(null) &&
184 mv.getHeight() == bufferImage.getHeight(null) &&
185 !needRedraw)
186 {
187
188 g.drawImage(bufferImage, 0, 0, null);
189 return;
190 }
191
192 needRedraw = false;
193 lastTopLeft = topLeft;
194 lastBotRight = botRight;
195 bufferImage = mv.createImage(mv.getWidth(), mv.getHeight());
196 g = bufferImage.getGraphics();
197
198 z12x0 = lonToTileX(topLeft.lon());
199 z12x1 = lonToTileX(botRight.lon());
200 z12y0 = latToTileY(topLeft.lat());
201 z12y1 = latToTileY(botRight.lat());
202
203 if (z12x0>z12x1) { int tmp = z12x0; z12x0=z12x1; z12x1=tmp; }
204 if (z12y0>z12y1) { int tmp = z12y0; z12y0=z12y1; z12y1=tmp; }
205
206 if (z12x1-z12x0 > 18) return;
207 if (z12y1-z12y0 > 18) return;
208
209 for (int x = z12x0 -1; x <= z12x1+1; x++)
210 {
211 double lon = tileXToLon(x);
212 for (int y = z12y0 -1; y <= z12y1+1; y++)
213 {
214 LatLon tmpLL = new LatLon(tileYToLat(y), lon);
215 pixelpos[x-z12x0+1][y-z12y0+1] = mv.getPoint(Main.proj.latlon2eastNorth(tmpLL));
216 }
217 }
218
219 int fontHeight = g.getFontMetrics().getHeight();
220
221 g.setColor(Color.DARK_GRAY);
222
223 for (int x = z12x0 -1; x <= z12x1; x++)
224 {
225 for (int y = z12y0 -1; y <= z12y1; y++)
226 {
227 int key = (y << 19) + x;
228 SlippyMapTile tile = tileStorage.get(12).get(key);
229
230 if (tile == null)
231 {
232 tileStorage.get(12).put(key, tile = new SlippyMapTile(x, y, 12));
233 }
234 Image img = tile.getImage();
235
236 if (img != null)
237 {
238 Point p = pixelpos[x-z12x0+1][y-z12y0+1];
239 Point p2 = pixelpos[x-z12x0+2][y-z12y0+2];
240 g.drawImage(img, p.x, p.y, p2.x-p.x, p2.y-p.y, this);
241 }
242 }
243 }
244
245 for (int x = z12x0 -1; x <= z12x1; x++)
246 {
247 Point p = pixelpos[x-z12x0+1][0];
248
249 if (x % 32 == 0)
250 {
251 // level 7 tile boundary
252 g.fillRect(p.x - 1, 0, 3, mv.getHeight());
253 }
254 else
255 {
256 g.drawLine(p.x, 0, p.x, mv.getHeight());
257 }
258
259 for (int y = z12y0 -1; y <= z12y1; y++)
260 {
261 int key = (y <<19) + x;
262 int texty = p.y + 2 + fontHeight;
263 SlippyMapTile tile = tileStorage.get(12).get(key);
264 p = pixelpos[x-z12x0+1][y-z12y0+2];
265 g.drawString("x=" + x + " y="+ y + " z=12", p.x + 2, texty);
266 texty += 1 + fontHeight;
267 if ((x % 32 == 0) && (y % 32 == 0))
268 {
269 g.drawString("x=" + x/32 + " y="+ y/32 + " z=7", p.x+2, texty);
270 texty += 1 + fontHeight;
271 }
272
273 String md = tile.getMetadata();
274 if (md != null)
275 {
276 g.drawString(md, p.x+2, texty);
277 texty += 1 + fontHeight;
278 }
279
280 if (tile.getImage() == null)
281 {
282 g.drawString("image not loaded", p.x+2, texty);
283 texty += 1 + fontHeight;
284 }
285
286 if (x == z12x0-1)
287 {
288 if (y % 32 == 31)
289 {
290 g.fillRect(0, p.y - 1, mv.getWidth(), 3);
291 }
292 else
293 {
294 g.drawLine(0, p.y, mv.getWidth(), p.y);
295 }
296 }
297 }
298 }
299
300 oldg.drawImage(bufferImage, 0, 0, null);
301
302 }
303
304 SlippyMapTile getTileForPixelpos(int px, int py)
305 {
306 int tilex=z12x1;
307 int tiley=z12y1;
308 for (int x = z12x0; x <= z12x1; x++)
309 {
310
311 if (pixelpos[x-z12x0+1][0].x > px)
312 {
313 tilex = x-1;
314 break;
315 }
316 }
317 if (tilex==-1) return null;
318 for (int y = z12y0; y <= z12y1; y++)
319 {
320
321 if (pixelpos[0][y-z12y0+1].y > py)
322 {
323 tiley = y-1;
324 break;
325 }
326 }
327 if (tiley==-1) return null;
328
329 int key = (tiley <<19) + tilex;
330 SlippyMapTile tile = tileStorage.get(12).get(key);
331 if (tile==null) tileStorage.get(12).put(key, tile = new SlippyMapTile(tilex, tiley, 12));
332 return tile;
333 }
334
335 @Override
336 public Icon getIcon() {
337 // TODO Auto-generated method stub
338 return ImageProvider.get("slippymap");
339 }
340
341 @Override
342 public Object getInfoComponent() {
343 // TODO Auto-generated method stub
344 return null;
345 }
346
347 @Override public Component[] getMenuEntries() {
348 /*
349 JMenuItem color = new JMenuItem(tr("Customize Color"), ImageProvider.get("colorchooser"));
350 color.addActionListener(new ActionListener(){
351 public void actionPerformed(ActionEvent e) {
352 String col = Main.pref.get("color.layer "+name, Main.pref.get("color.gps marker", ColorHelper.color2html(Color.gray)));
353 JColorChooser c = new JColorChooser(ColorHelper.html2color(col));
354 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")};
355 int answer = JOptionPane.showOptionDialog(Main.parent, c, tr("Choose a color"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
356 switch (answer) {
357 case 0:
358 Main.pref.put("color.layer "+name, ColorHelper.color2html(c.getColor()));
359 break;
360 case 1:
361 return;
362 case 2:
363 Main.pref.put("color.layer "+name, null);
364 break;
365 }
366 Main.map.repaint();
367 }
368 });
369 */
370
371 return new Component[] {
372 new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
373 new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
374 new JSeparator(),
375 // color,
376 new JMenuItem(new RenameLayerAction(associatedFile, this)),
377 new JSeparator(),
378 new JMenuItem(new LayerListPopup.InfoAction(this))
379 };
380 }
381
382 @Override
383 public String getToolTipText() {
384 // TODO Auto-generated method stub
385 return null;
386 }
387
388 @Override
389 public boolean isMergable(Layer other) {
390 return false;
391 }
392
393 @Override
394 public void mergeFrom(Layer from) {
395 }
396
397 @Override
398 public void visitBoundingBox(BoundingXYVisitor v) {
399 // TODO Auto-generated method stub
400 }
401
402 private int latToTileY(double lat) {
403 double l = lat / 180 * Math.PI;
404 double pf = Math.log(Math.tan(l) + (1/Math.cos(l)));
405 return (int) (2048.0 * (Math.PI - pf) / Math.PI);
406 }
407
408 private int lonToTileX(double lon) {
409 return (int) (512.0 * (lon + 180.0) / 45.0);
410 }
411
412 private double tileYToLat(int y) {
413 return Math.atan(Math.sinh(Math.PI - (Math.PI*y / 2048.0))) * 180 / Math.PI;
414 }
415
416 private double tileXToLon(int x) {
417 return x * 45.0 / 512.0 - 180.0;
418 }
419
420 public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
421
422 boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
423 // Repaint immediately if we are done, otherwise batch up
424 // repaint requests every 100 milliseconds
425 needRedraw = true;
426 Main.map.repaint(done ? 0 : 100);
427 return !done;
428 }
429}
Note: See TracBrowser for help on using the repository browser.