Ticket #6869: JMapViewer.java

File JMapViewer.java, 31.8 KB (added by jhuntley, 14 years ago)
Line 
1package org.openstreetmap.gui.jmapviewer;
2
3//License: GPL. Copyright 2008 by Jan Peter Stotz
4
5import java.awt.Color;
6import java.awt.Dimension;
7import java.awt.Font;
8import java.awt.Graphics;
9import java.awt.Image;
10import java.awt.Insets;
11import java.awt.Point;
12import java.awt.Rectangle;
13import java.awt.event.ActionEvent;
14import java.awt.event.ActionListener;
15import java.awt.event.MouseEvent;
16import java.awt.font.TextAttribute;
17import java.awt.geom.Rectangle2D;
18import java.util.HashMap;
19import java.util.LinkedList;
20import java.util.List;
21
22import javax.swing.ImageIcon;
23import javax.swing.JButton;
24import javax.swing.JPanel;
25import javax.swing.JSlider;
26import javax.swing.event.ChangeEvent;
27import javax.swing.event.ChangeListener;
28import javax.swing.event.EventListenerList;
29
30import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent;
31import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent.COMMAND;
32import org.openstreetmap.gui.jmapviewer.interfaces.JMapViewerEventListener;
33import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
34import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon;
35import org.openstreetmap.gui.jmapviewer.interfaces.MapObject;
36import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle;
37import org.openstreetmap.gui.jmapviewer.interfaces.TileCache;
38import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
39import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
40import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
41import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource;
42
43import com.houghtonassociates.nexus.core.math.ACPMMathUtil;
44
45/**
46 *
47 * Provides a simple panel that displays pre-rendered map tiles loaded from the
48 * OpenStreetMap project.
49 *
50 * @author Jan Peter Stotz
51 *
52 */
53public class JMapViewer extends JPanel implements TileLoaderListener {
54
55 private static final long serialVersionUID = 1L;
56
57 /**
58 * Vectors for clock-wise tile painting
59 */
60 protected static final Point[] move = { new Point(1, 0), new Point(0, 1), new Point(-1, 0), new Point(0, -1) };
61
62 public static final int MAX_ZOOM = 22;
63 public static final int MIN_ZOOM = 0;
64
65 protected List<MapMarker> mapMarkerList;
66 protected List<MapRectangle> mapRectangleList;
67
68 protected List<MapPolygon> mapPolygonList;
69 protected List<MapObject> mapObjectList;
70
71 protected boolean mapMarkersVisible;
72 protected boolean mapRectanglesVisible;
73 protected boolean mapPolygonsVisible;
74
75 protected boolean tileGridVisible;
76
77 protected TileController tileController;
78
79 /**
80 * x- and y-position of the center of this map-panel on the world map
81 * denoted in screen pixel regarding the current zoom level.
82 */
83 protected Point center;
84
85 /**
86 * Current zoom level
87 */
88 protected int zoom;
89
90 protected JSlider zoomSlider;
91 protected JButton zoomInButton;
92 protected JButton zoomOutButton;
93
94 private TileSource tileSource;
95
96 // Attribution
97 private Image attrImage;
98 private String attrTermsUrl;
99 public static final Font ATTR_FONT = new Font("Arial", Font.PLAIN, 10);
100 public static final Font ATTR_LINK_FONT;
101
102 protected Rectangle attrTextBounds = null;
103 protected Rectangle attrToUBounds = null;
104 protected Rectangle attrImageBounds = null;
105
106 static {
107 HashMap<TextAttribute, Integer> aUnderline = new HashMap<TextAttribute, Integer>();
108 aUnderline.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
109 ATTR_LINK_FONT = ATTR_FONT.deriveFont(aUnderline);
110 }
111
112 /**
113 * Creates a standard {@link JMapViewer} instance that can be controlled via
114 * mouse: hold right mouse button for moving, double click left mouse button
115 * or use mouse wheel for zooming. Loaded tiles are stored the
116 * {@link MemoryTileCache} and the tile loader uses 4 parallel threads for
117 * retrieving the tiles.
118 */
119 public JMapViewer() {
120 this(new MemoryTileCache(), 4);
121 new DefaultMapController(this);
122 }
123
124 public JMapViewer(TileCache tileCache, int downloadThreadCount) {
125 super();
126 tileSource = new OsmTileSource.Mapnik();
127 tileController = new TileController(tileSource, tileCache, this);
128 mapMarkerList = new LinkedList<MapMarker>();
129 mapPolygonList = new LinkedList<MapPolygon>();
130 mapObjectList = new LinkedList<MapObject>();
131 mapRectangleList = new LinkedList<MapRectangle>();
132 mapMarkersVisible = true;
133 mapRectanglesVisible = true;
134 mapPolygonsVisible = true;
135 tileGridVisible = false;
136 setLayout(null);
137 initializeZoomSlider();
138 setMinimumSize(new Dimension(tileSource.getTileSize(), tileSource.getTileSize()));
139 setPreferredSize(new Dimension(400, 400));
140 setDisplayPositionByLatLon(50, 9, 3);
141 //setToolTipText("");
142 }
143
144 @Override
145 public String getToolTipText(MouseEvent event) {
146 // Point screenPoint = event.getLocationOnScreen();
147 // Coordinate c = getPosition(screenPoint);
148 return super.getToolTipText(event);
149 }
150
151 protected void initializeZoomSlider() {
152 zoomSlider = new JSlider(MIN_ZOOM, tileController.getTileSource().getMaxZoom());
153 zoomSlider.setOrientation(JSlider.VERTICAL);
154 zoomSlider.setBounds(10, 10, 30, 150);
155 zoomSlider.setOpaque(false);
156 zoomSlider.addChangeListener(new ChangeListener() {
157 public void stateChanged(ChangeEvent e) {
158 setZoom(zoomSlider.getValue());
159 }
160 });
161 add(zoomSlider);
162 int size = 18;
163 try {
164 ImageIcon icon = new ImageIcon(getClass().getResource("images/plus.png"));
165 zoomInButton = new JButton(icon);
166 } catch (Exception e) {
167 zoomInButton = new JButton("+");
168 zoomInButton.setFont(new Font("sansserif", Font.BOLD, 9));
169 zoomInButton.setMargin(new Insets(0, 0, 0, 0));
170 }
171 zoomInButton.setBounds(4, 155, size, size);
172 zoomInButton.addActionListener(new ActionListener() {
173
174 public void actionPerformed(ActionEvent e) {
175 zoomIn();
176 }
177 });
178 add(zoomInButton);
179 try {
180 ImageIcon icon = new ImageIcon(getClass().getResource("images/minus.png"));
181 zoomOutButton = new JButton(icon);
182 } catch (Exception e) {
183 zoomOutButton = new JButton("-");
184 zoomOutButton.setFont(new Font("sansserif", Font.BOLD, 9));
185 zoomOutButton.setMargin(new Insets(0, 0, 0, 0));
186 }
187 zoomOutButton.setBounds(8 + size, 155, size, size);
188 zoomOutButton.addActionListener(new ActionListener() {
189
190 public void actionPerformed(ActionEvent e) {
191 zoomOut();
192 }
193 });
194 add(zoomOutButton);
195 }
196
197 /**
198 * Changes the map pane so that it is centered on the specified coordinate
199 * at the given zoom level.
200 *
201 * @param lat
202 * latitude of the specified coordinate
203 * @param lon
204 * longitude of the specified coordinate
205 * @param zoom
206 * {@link #MIN_ZOOM} <= zoom level <= {@link #MAX_ZOOM}
207 */
208 public void setDisplayPositionByLatLon(double lat, double lon, int zoom) {
209 setDisplayPositionByLatLon(new Point(getWidth() / 2, getHeight() / 2), lat, lon, zoom);
210 }
211
212 /**
213 * Changes the map pane so that the specified coordinate at the given zoom
214 * level is displayed on the map at the screen coordinate
215 * <code>mapPoint</code>.
216 *
217 * @param mapPoint
218 * point on the map denoted in pixels where the coordinate should
219 * be set
220 * @param lat
221 * latitude of the specified coordinate
222 * @param lon
223 * longitude of the specified coordinate
224 * @param zoom
225 * {@link #MIN_ZOOM} <= zoom level <=
226 * {@link TileSource#getMaxZoom()}
227 */
228 public void setDisplayPositionByLatLon(Point mapPoint, double lat, double lon, int zoom) {
229 int x = OsmMercator.LonToX(lon, zoom);
230 int y = OsmMercator.LatToY(lat, zoom);
231 setDisplayPosition(mapPoint, x, y, zoom);
232 }
233
234 public void setDisplayPosition(int x, int y, int zoom) {
235 setDisplayPosition(new Point(getWidth() / 2, getHeight() / 2), x, y, zoom);
236 }
237
238 public void setDisplayPosition(Point mapPoint, int x, int y, int zoom) {
239 if (zoom > tileController.getTileSource().getMaxZoom() || zoom < MIN_ZOOM)
240 return;
241
242 // Get the plain tile number
243 Point p = new Point();
244 p.x = x - mapPoint.x + getWidth() / 2;
245 p.y = y - mapPoint.y + getHeight() / 2;
246 center = p;
247 setIgnoreRepaint(true);
248 try {
249 int oldZoom = this.zoom;
250 this.zoom = zoom;
251 if (oldZoom != zoom) {
252 zoomChanged(oldZoom);
253 }
254 if (zoomSlider.getValue() != zoom) {
255 zoomSlider.setValue(zoom);
256 }
257 } finally {
258 setIgnoreRepaint(false);
259 repaint();
260 }
261 }
262
263 /**
264 * Sets the displayed map pane and zoom level so that all map markers are
265 * visible.
266 */
267 public void setDisplayToFitMapElements(boolean markers, boolean rectangles, boolean polygons) {
268 int nbElemToCheck = 0;
269 if (markers && mapMarkerList != null)
270 nbElemToCheck += mapMarkerList.size();
271 if (rectangles && mapRectangleList != null)
272 nbElemToCheck += mapRectangleList.size();
273 if (polygons && mapPolygonList != null)
274 nbElemToCheck += mapPolygonList.size();
275 if (nbElemToCheck == 0)
276 return;
277
278 int x_min = Integer.MAX_VALUE;
279 int y_min = Integer.MAX_VALUE;
280 int x_max = Integer.MIN_VALUE;
281 int y_max = Integer.MIN_VALUE;
282 int mapZoomMax = tileController.getTileSource().getMaxZoom();
283
284 if (markers) {
285 for (MapMarker marker : mapMarkerList) {
286 int x = OsmMercator.LonToX(marker.getLon(), mapZoomMax);
287 int y = OsmMercator.LatToY(marker.getLat(), mapZoomMax);
288 x_max = Math.max(x_max, x);
289 y_max = Math.max(y_max, y);
290 x_min = Math.min(x_min, x);
291 y_min = Math.min(y_min, y);
292 }
293 }
294
295 if (polygons) {
296 for (MapPolygon polygon : mapPolygonList) {
297 for (Coordinate c : polygon.getPoints()) {
298 int x = OsmMercator.LonToX(c.getLon(), mapZoomMax);
299 int y = OsmMercator.LatToY(c.getLat(), mapZoomMax);
300 x_max = Math.max(x_max, x);
301 y_max = Math.max(y_max, y);
302 x_min = Math.min(x_min, x);
303 y_min = Math.min(y_min, y);
304 }
305 }
306 }
307
308 int height = Math.max(0, getHeight());
309 int width = Math.max(0, getWidth());
310 int newZoom = mapZoomMax;
311 int x = x_max - x_min;
312 int y = y_max - y_min;
313 while (x > width || y > height) {
314 newZoom--;
315 x >>= 1;
316 y >>= 1;
317 }
318 x = x_min + (x_max - x_min) / 2;
319 y = y_min + (y_max - y_min) / 2;
320 int z = 1 << (mapZoomMax - newZoom);
321 x /= z;
322 y /= z;
323 setDisplayPosition(x, y, newZoom);
324 }
325
326
327 /**
328 * Sets the displayed map pane and zoom level so that all map markers are
329 * visible.
330 */
331 public void setDisplayToFitMapMarkers() {
332 setDisplayToFitMapElements(true, false, false);
333 }
334
335 /**
336 * Sets the displayed map pane and zoom level so that all map rectangles are
337 * visible.
338 */
339 public void setDisplayToFitMapRectangles() {
340 setDisplayToFitMapElements(false, true, false);
341 }
342
343 /**
344 * Sets the displayed map pane and zoom level so that all map polygons are
345 * visible.
346 */
347 public void setDisplayToFitMapPolygons() {
348 setDisplayToFitMapElements(false, false, true);
349 }
350
351 /**
352 * @return the center
353 */
354 public Point getCenter() {
355 return center;
356 }
357
358 /**
359 * @param center the center to set
360 */
361 public void setCenter(Point center) {
362 this.center = center;
363 }
364
365 /**
366 * Calculates the latitude/longitude coordinate of the center of the
367 * currently displayed map area.
368 *
369 * @return latitude / longitude
370 */
371 public Coordinate getPosition() {
372 double lon = OsmMercator.XToLon(center.x, zoom);
373 double lat = OsmMercator.YToLat(center.y, zoom);
374 return new Coordinate(lat, lon);
375 }
376
377 /**
378 * Converts the relative pixel coordinate (regarding the top left corner of
379 * the displayed map) into a latitude / longitude coordinate
380 *
381 * @param mapPoint
382 * relative pixel coordinate regarding the top left corner of the
383 * displayed map
384 * @return latitude / longitude
385 */
386 public Coordinate getPosition(Point mapPoint) {
387 return getPosition(mapPoint.x, mapPoint.y);
388 }
389
390 /**
391 * Converts the relative pixel coordinate (regarding the top left corner of
392 * the displayed map) into a latitude / longitude coordinate
393 *
394 * @param mapPointX
395 * @param mapPointY
396 * @return
397 */
398 public Coordinate getPosition(int mapPointX, int mapPointY) {
399 int x = center.x + mapPointX - getWidth() / 2;
400 int y = center.y + mapPointY - getHeight() / 2;
401 double lon = OsmMercator.XToLon(x, zoom);
402 double lat = OsmMercator.YToLat(y, zoom);
403 return new Coordinate(lat, lon);
404 }
405
406 /**
407 * Calculates the position on the map of a given coordinate
408 *
409 * @param lat
410 * @param lon
411 * @param checkOutside
412 * @return point on the map or <code>null</code> if the point is not visible
413 * and checkOutside set to <code>true</code>
414 */
415 public Point getMapPosition(double lat, double lon, boolean checkOutside) {
416 int x = OsmMercator.LonToX(lon, zoom);
417 int y = OsmMercator.LatToY(lat, zoom);
418 x -= center.x - getWidth() / 2;
419 y -= center.y - getHeight() / 2;
420 if (checkOutside) {
421 if (x < 0 || y < 0 || x > getWidth() || y > getHeight())
422 return null;
423 }
424 return new Point(x, y);
425 }
426
427 /**
428 * Calculates the position on the map of a given coordinate
429 *
430 * @param lat
431 * @param lon
432 * @return point on the map or <code>null</code> if the point is not visible
433 */
434 public Point getMapPosition(double lat, double lon) {
435 return getMapPosition(lat, lon, true);
436 }
437
438 /**
439 * Calculates the position on the map of a given coordinate
440 *
441 * @param coord
442 * @return point on the map or <code>null</code> if the point is not visible
443 */
444 public Point getMapPosition(Coordinate coord) {
445 if (coord != null)
446 return getMapPosition(coord.getLat(), coord.getLon());
447 else
448 return null;
449 }
450
451 /**
452 * Calculates the position on the map of a given coordinate
453 *
454 * @param coord
455 * @return point on the map or <code>null</code> if the point is not visible
456 * and checkOutside set to <code>true</code>
457 */
458 public Point getMapPosition(Coordinate coord, boolean checkOutside) {
459 if (coord != null)
460 return getMapPosition(coord.getLat(), coord.getLon(), checkOutside);
461 else
462 return null;
463 }
464
465 /**
466 * Gets the meter per pixel.
467 *
468 * @return the meter per pixel
469 * @author Jason Huntley
470 */
471 public double getMeterPerPixel() {
472 Point origin=new Point(5,5);
473 Point center=new Point(getWidth()/2, getHeight()/2);
474
475 double pDistance=center.distance(origin);
476
477 Coordinate originCoord=getPosition(origin);
478 Coordinate centerCoord=getPosition(center);
479
480 double kmDistance=OsmMercator.getDistance(originCoord.getLat(), originCoord.getLon(),
481 centerCoord.getLat(), centerCoord.getLon());
482
483 double mDistance=OsmMercator.kmToMeters(kmDistance);
484
485 return mDistance/pDistance;
486 }
487
488 @Override
489 protected void paintComponent(Graphics g) {
490 super.paintComponent(g);
491
492 int iMove = 0;
493
494 int tilesize = tileSource.getTileSize();
495 int tilex = center.x / tilesize;
496 int tiley = center.y / tilesize;
497 int off_x = (center.x % tilesize);
498 int off_y = (center.y % tilesize);
499
500 int w2 = getWidth() / 2;
501 int h2 = getHeight() / 2;
502 int posx = w2 - off_x;
503 int posy = h2 - off_y;
504
505 int diff_left = off_x;
506 int diff_right = tilesize - off_x;
507 int diff_top = off_y;
508 int diff_bottom = tilesize - off_y;
509
510 boolean start_left = diff_left < diff_right;
511 boolean start_top = diff_top < diff_bottom;
512
513 if (start_top) {
514 if (start_left) {
515 iMove = 2;
516 } else {
517 iMove = 3;
518 }
519 } else {
520 if (start_left) {
521 iMove = 1;
522 } else {
523 iMove = 0;
524 }
525 } // calculate the visibility borders
526 int x_min = -tilesize;
527 int y_min = -tilesize;
528 int x_max = getWidth();
529 int y_max = getHeight();
530
531 // paint the tiles in a spiral, starting from center of the map
532 boolean painted = true;
533 int x = 0;
534 while (painted) {
535 painted = false;
536 for (int i = 0; i < 4; i++) {
537 if (i % 2 == 0) {
538 x++;
539 }
540 for (int j = 0; j < x; j++) {
541 if (x_min <= posx && posx <= x_max && y_min <= posy && posy <= y_max) {
542 // tile is visible
543 Tile tile = tileController.getTile(tilex, tiley, zoom);
544 if (tile != null) {
545 painted = true;
546 tile.paint(g, posx, posy);
547 if (tileGridVisible) {
548 g.drawRect(posx, posy, tilesize, tilesize);
549 }
550 }
551 }
552 Point p = move[iMove];
553 posx += p.x * tilesize;
554 posy += p.y * tilesize;
555 tilex += p.x;
556 tiley += p.y;
557 }
558 iMove = (iMove + 1) % move.length;
559 }
560 }
561 // outer border of the map
562 int mapSize = tilesize << zoom;
563 g.drawRect(w2 - center.x, h2 - center.y, mapSize, mapSize);
564
565 // g.drawString("Tiles in cache: " + tileCache.getTileCount(), 50, 20);
566
567 if (mapPolygonsVisible && mapPolygonList != null) {
568 for (MapPolygon polygon : mapPolygonList) {
569 paintPolygon(g, polygon);
570 }
571 }
572
573 if (mapRectanglesVisible && mapRectangleList != null) {
574 for (MapRectangle rectangle : mapRectangleList) {
575 paintRectangle(g, rectangle);
576 }
577 }
578
579 if (mapObjectList != null) {
580 for (MapObject obj : mapObjectList) {
581 paintObject(g, obj);
582 }
583 }
584
585 if (mapMarkersVisible && mapMarkerList != null) {
586 for (MapMarker marker : mapMarkerList) {
587 paintMarker(g, marker);
588 }
589 }
590
591 paintAttribution(g);
592 }
593
594 /**
595 * Paint a single object.
596 */
597 protected void paintObject(Graphics g, MapObject obj) {
598 Point p = getMapPosition(obj.getLat(), obj.getLon(),false);
599 if (p != null) {
600 obj.paint(g, p, this);
601 }
602 }
603
604 /**
605 * Paint a single marker.
606 */
607 protected void paintMarker(Graphics g, MapMarker marker) {
608 Point p = getMapPosition(marker.getLat(), marker.getLon());
609 if (p != null) {
610 marker.paint(g, p);
611 }
612 }
613
614 /**
615 * Paint a single rectangle.
616 */
617 protected void paintRectangle(Graphics g, MapRectangle rectangle) {
618 Coordinate topLeft = rectangle.getTopLeft();
619 Coordinate bottomRight = rectangle.getBottomRight();
620 if (topLeft != null && bottomRight != null) {
621 Point pTopLeft = getMapPosition(topLeft, false);
622 Point pBottomRight = getMapPosition(bottomRight, false);
623 if (pTopLeft != null && pBottomRight != null) {
624 rectangle.paint(g, pTopLeft, pBottomRight);
625 }
626 }
627 }
628
629 /**
630 * Paint a single polygon.
631 */
632 protected void paintPolygon(Graphics g, MapPolygon polygon) {
633 List<Coordinate> coords = polygon.getPoints();
634 if (coords != null && coords.size() >= 3) {
635 List<Point> points = new LinkedList<Point>();
636 for (Coordinate c : coords) {
637 Point p = getMapPosition(c, false);
638 if (p == null) {
639 return;
640 }
641 points.add(p);
642 }
643 polygon.paint(g, points);
644 }
645 }
646
647 /**
648 * Moves the visible map pane.
649 *
650 * @param x
651 * horizontal movement in pixel.
652 * @param y
653 * vertical movement in pixel
654 */
655 public void moveMap(int x, int y) {
656 center.x += x;
657 center.y += y;
658 repaint();
659 }
660
661 /**
662 * @return the current zoom level
663 */
664 public int getZoom() {
665 return zoom;
666 }
667
668 /**
669 * Increases the current zoom level by one
670 */
671 public void zoomIn() {
672 setZoom(zoom + 1);
673 }
674
675 /**
676 * Increases the current zoom level by one
677 */
678 public void zoomIn(Point mapPoint) {
679 setZoom(zoom + 1, mapPoint);
680 }
681
682 /**
683 * Decreases the current zoom level by one
684 */
685 public void zoomOut() {
686 setZoom(zoom - 1);
687 }
688
689 /**
690 * Decreases the current zoom level by one
691 */
692 public void zoomOut(Point mapPoint) {
693 setZoom(zoom - 1, mapPoint);
694 }
695
696 public void setZoom(int zoom, Point mapPoint) {
697 if (zoom > tileController.getTileSource().getMaxZoom() || zoom < tileController.getTileSource().getMinZoom()
698 || zoom == this.zoom)
699 return;
700 Coordinate zoomPos = getPosition(mapPoint);
701 tileController.cancelOutstandingJobs(); // Clearing outstanding load
702 // requests
703 setDisplayPositionByLatLon(mapPoint, zoomPos.getLat(), zoomPos.getLon(), zoom);
704
705 this.fireJMVEvent(new JMVCommandEvent(COMMAND.ZOOM, this));
706 }
707
708 public void setZoom(int zoom) {
709 setZoom(zoom, new Point(getWidth() / 2, getHeight() / 2));
710 }
711
712 /**
713 * Every time the zoom level changes this method is called. Override it in
714 * derived implementations for adapting zoom dependent values. The new zoom
715 * level can be obtained via {@link #getZoom()}.
716 *
717 * @param oldZoom
718 * the previous zoom level
719 */
720 protected void zoomChanged(int oldZoom) {
721 zoomSlider.setToolTipText("Zoom level " + zoom);
722 zoomInButton.setToolTipText("Zoom to level " + (zoom + 1));
723 zoomOutButton.setToolTipText("Zoom to level " + (zoom - 1));
724 zoomOutButton.setEnabled(zoom > tileController.getTileSource().getMinZoom());
725 zoomInButton.setEnabled(zoom < tileController.getTileSource().getMaxZoom());
726 }
727
728 public boolean isTileGridVisible() {
729 return tileGridVisible;
730 }
731
732 public void setTileGridVisible(boolean tileGridVisible) {
733 this.tileGridVisible = tileGridVisible;
734 repaint();
735 }
736
737 public boolean getMapMarkersVisible() {
738 return mapMarkersVisible;
739 }
740
741 /**
742 * Enables or disables painting of the {@link MapMarker}
743 *
744 * @param mapMarkersVisible
745 * @see #addMapMarker(MapMarker)
746 * @see #getMapMarkerList()
747 */
748 public void setMapMarkerVisible(boolean mapMarkersVisible) {
749 this.mapMarkersVisible = mapMarkersVisible;
750 repaint();
751 }
752
753 public void setMapMarkerList(List<MapMarker> mapMarkerList) {
754 this.mapMarkerList = mapMarkerList;
755 repaint();
756 }
757
758 public List<MapMarker> getMapMarkerList() {
759 return mapMarkerList;
760 }
761
762 public void setMapRectangleList(List<MapRectangle> mapRectangleList) {
763 this.mapRectangleList = mapRectangleList;
764 repaint();
765 }
766
767 public List<MapRectangle> getMapRectangleList() {
768 return mapRectangleList;
769 }
770
771 public void setMapPolygonList(List<MapPolygon> mapPolygonList) {
772 this.mapPolygonList = mapPolygonList;
773 repaint();
774 }
775
776 public List<MapPolygon> getMapPolygonList() {
777 return mapPolygonList;
778 }
779
780
781 public void addMapObject(MapObject obj) {
782 mapObjectList.add(obj);
783 repaint();
784 }
785
786 public void removeMapObject(MapObject obj) {
787 mapMarkerList.remove(obj);
788 repaint();
789 }
790
791 public void addMapMarker(MapMarker marker) {
792 mapMarkerList.add(marker);
793 repaint();
794 }
795
796 public void removeMapMarker(MapMarker marker) {
797 mapMarkerList.remove(marker);
798 repaint();
799 }
800
801 public void removeAllMapMarkers() {
802 mapMarkerList.clear();
803 repaint();
804 }
805
806 public void addMapRectangle(MapRectangle rectangle) {
807 mapRectangleList.add(rectangle);
808 repaint();
809 }
810
811 public void removeMapRectangle(MapRectangle rectangle) {
812 mapRectangleList.remove(rectangle);
813 repaint();
814 }
815
816 public void removeAllMapRectangles() {
817 mapRectangleList.clear();
818 repaint();
819 }
820
821 public void addMapPolygon(MapPolygon polygon) {
822 mapPolygonList.add(polygon);
823 repaint();
824 }
825
826 public void removeMapPolygon(MapPolygon polygon) {
827 mapPolygonList.remove(polygon);
828 repaint();
829 }
830
831 public void removeAllMapPolygons() {
832 mapPolygonList.clear();
833 repaint();
834 }
835
836 public void setZoomContolsVisible(boolean visible) {
837 zoomSlider.setVisible(visible);
838 zoomInButton.setVisible(visible);
839 zoomOutButton.setVisible(visible);
840 }
841
842 public boolean getZoomContolsVisible() {
843 return zoomSlider.isVisible();
844 }
845
846 public TileSource getTileSource() {
847 return tileSource;
848 }
849
850 public void setTileSource(TileSource tileSource) {
851 if (tileSource.getMaxZoom() > MAX_ZOOM)
852 throw new RuntimeException("Maximum zoom level too high");
853 if (tileSource.getMinZoom() < MIN_ZOOM)
854 throw new RuntimeException("Minumim zoom level too low");
855 this.tileSource = tileSource;
856 tileController.setTileSource(tileSource);
857 zoomSlider.setMinimum(tileSource.getMinZoom());
858 zoomSlider.setMaximum(tileSource.getMaxZoom());
859 tileController.cancelOutstandingJobs();
860 if (zoom > tileSource.getMaxZoom()) {
861 setZoom(tileSource.getMaxZoom());
862 }
863 boolean requireAttr = tileSource.requiresAttribution();
864 if (requireAttr) {
865 attrImage = tileSource.getAttributionImage();
866 attrTermsUrl = tileSource.getTermsOfUseURL();
867 } else {
868 attrImage = null;
869 attrTermsUrl = null;
870 }
871 repaint();
872 }
873
874 public void tileLoadingFinished(Tile tile, boolean success) {
875 repaint();
876 }
877
878 public boolean isMapRectanglesVisible() {
879 return mapRectanglesVisible;
880 }
881
882 /**
883 * Enables or disables painting of the {@link MapRectangle}
884 *
885 * @param mapRectanglesVisible
886 * @see #addMapRectangle(MapRectangle)
887 * @see #getMapRectangleList()
888 */
889 public void setMapRectanglesVisible(boolean mapRectanglesVisible) {
890 this.mapRectanglesVisible = mapRectanglesVisible;
891 repaint();
892 }
893
894 public boolean isMapPolygonsVisible() {
895 return mapPolygonsVisible;
896 }
897
898 /**
899 * Enables or disables painting of the {@link MapPolygon}
900 *
901 * @param mapPolygonsVisible
902 * @see #addMapPolygon(MapPolygon)
903 * @see #getMapPolygonList()
904 */
905 public void setMapPolygonsVisible(boolean mapPolygonsVisible) {
906 this.mapPolygonsVisible = mapPolygonsVisible;
907 repaint();
908 }
909
910 /*
911 * (non-Javadoc)
912 *
913 * @see
914 * org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener#getTileCache
915 * ()
916 */
917 public TileCache getTileCache() {
918 return tileController.getTileCache();
919 }
920
921 public void setTileLoader(TileLoader loader) {
922 tileController.setTileLoader(loader);
923 }
924
925 private void paintAttribution(Graphics g) {
926 if (!tileSource.requiresAttribution())
927 return;
928 // Draw attribution
929 Font font = g.getFont();
930 g.setFont(ATTR_LINK_FONT);
931
932 Rectangle2D termsStringBounds = g.getFontMetrics().getStringBounds("Background Terms of Use", g);
933 int textRealHeight = (int) termsStringBounds.getHeight();
934 int textHeight = textRealHeight - 5;
935 int textWidth = (int) termsStringBounds.getWidth();
936 int termsTextY = getHeight() - textHeight;
937 if (attrTermsUrl != null) {
938 int x = 2;
939 int y = getHeight() - textHeight;
940 attrToUBounds = new Rectangle(x, y-textHeight, textWidth, textRealHeight);
941 g.setColor(Color.black);
942 g.drawString("Background Terms of Use", x + 1, y + 1);
943 g.setColor(Color.white);
944 g.drawString("Background Terms of Use", x, y);
945 }
946
947 // Draw attribution logo
948 if (attrImage != null) {
949 int x = 2;
950 int imgWidth = attrImage.getWidth(this);
951 int height = attrImage.getHeight(null);
952 int y = termsTextY - height - textHeight - 5;
953 attrImageBounds = new Rectangle(x, y, imgWidth, height);
954 g.drawImage(attrImage, x, y, null);
955 }
956
957 g.setFont(ATTR_FONT);
958 Coordinate topLeft = getPosition(0, 0);
959 Coordinate bottomRight = getPosition(getWidth(), getHeight());
960 String attributionText = tileSource.getAttributionText(zoom, topLeft, bottomRight);
961 if (attributionText != null) {
962 Rectangle2D stringBounds = g.getFontMetrics().getStringBounds(attributionText, g);
963 int x = getWidth() - (int) stringBounds.getWidth();
964 int y = getHeight() - textHeight;
965 g.setColor(Color.black);
966 g.drawString(attributionText, x + 1, y + 1);
967 g.setColor(Color.white);
968 g.drawString(attributionText, x, y);
969 attrTextBounds = new Rectangle(x, y-textHeight, textWidth, textRealHeight);
970 }
971
972 g.setFont(font);
973 }
974
975 protected EventListenerList listenerList = new EventListenerList();
976
977 /**
978 * @param listener to set
979 */
980 public void addJMVListener(JMapViewerEventListener listener) {
981 listenerList.add(JMapViewerEventListener.class, listener);
982 }
983
984 public void removeJMVListener(JMapViewerEventListener listener) {
985 listenerList.remove(JMapViewerEventListener.class, listener);
986 }
987
988 void fireJMVEvent(JMVCommandEvent evt) {
989 Object[] listeners = listenerList.getListenerList();
990 for (int i=0; i<listeners.length; i+=2) {
991 if (listeners[i]==JMapViewerEventListener.class) {
992 ((JMapViewerEventListener)listeners[i+1]).processCommand(evt);
993 }
994 }
995 }
996}