// License: GPL. For details, see Readme.txt file.
package org.openstreetmap.gui.jmapviewer;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent;
import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent.COMMAND;
import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
import org.openstreetmap.gui.jmapviewer.interfaces.JMapViewerEventListener;
import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon;
import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle;
import org.openstreetmap.gui.jmapviewer.interfaces.TileCache;
import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource;
/**
* Provides a simple panel that displays pre-rendered map tiles loaded from the
* OpenStreetMap project.
*
* @author Jan Peter Stotz
* @author Jason Huntley
*/
public class JMapViewer extends JPanel implements TileLoaderListener {
/** whether debug mode is enabled or not */
public static boolean debug;
/** option to reverse zoom direction with mouse wheel */
public static boolean zoomReverseWheel;
/**
* Vectors for clock-wise tile painting
*/
private static final Point[] move = {new Point(1, 0), new Point(0, 1), new Point(-1, 0), new Point(0, -1)};
/** Maximum zoom level */
public static final int MAX_ZOOM = 22;
/** Minimum zoom level */
public static final int MIN_ZOOM = 0;
protected transient List<MapMarker> mapMarkerList;
protected transient List<MapRectangle> mapRectangleList;
protected transient List<MapPolygon> mapPolygonList;
protected boolean mapMarkersVisible;
protected boolean mapRectanglesVisible;
protected boolean mapPolygonsVisible;
protected boolean tileGridVisible;
protected boolean scrollWrapEnabled;
protected transient TileController tileController;
/**
* x- and y-position of the center of this map-panel on the world map
* denoted in screen pixel regarding the current zoom level.
*/
protected Point center;
/**
* Current zoom level
*/
protected int zoom;
protected JSlider zoomSlider;
protected JButton zoomInButton;
protected JButton zoomOutButton;
/**
* Apparence of zoom controls.
*/
public enum ZOOM_BUTTON_STYLE {
/** Zoom buttons are displayed horizontally (default) */
HORIZONTAL,
/** Zoom buttons are displayed vertically */
VERTICAL
}
protected ZOOM_BUTTON_STYLE zoomButtonStyle;
protected transient TileSource tileSource;
protected transient AttributionSupport attribution = new AttributionSupport();
protected EventListenerList evtListenerList = new EventListenerList();
/**
* Creates a standard {@link JMapViewer} instance that can be controlled via
* mouse: hold right mouse button for moving, double click left mouse button
* or use mouse wheel for zooming. Loaded tiles are stored in a
* {@link MemoryTileCache} and the tile loader uses 4 parallel threads for
* retrieving the tiles.
*/
public JMapViewer() {
this(new MemoryTileCache());
new DefaultMapController(this);
}
/**
* Creates a new {@link JMapViewer} instance.
* @param tileCache The cache where to store tiles
* @param downloadThreadCount not used anymore
* @deprecated use {@link #JMapViewer(TileCache)}
*/
@Deprecated
public JMapViewer(TileCache tileCache, int downloadThreadCount) {
this(tileCache);
}
/**
* Creates a new {@link JMapViewer} instance.
* @param tileCache The cache where to store tiles
*
*/
public JMapViewer(TileCache tileCache) {
tileSource = new OsmTileSource.Mapnik();
tileController = new TileController(tileSource, tileCache, this);
mapMarkerList = Collections.synchronizedList(new LinkedList<MapMarker>());
mapPolygonList = Collections.synchronizedList(new LinkedList<MapPolygon>());
mapRectangleList = Collections.synchronizedList(new LinkedList<MapRectangle>());
mapMarkersVisible = true;
mapRectanglesVisible = true;
mapPolygonsVisible = true;
tileGridVisible = false;
setLayout(null);
initializeZoomSlider();
setMinimumSize(new Dimension(tileSource.getTileSize(), tileSource.getTileSize()));
setPreferredSize(new Dimension(400, 400));
setDisplayPosition(new Coordinate(50, 9), 3);
}
@Override
public String getToolTipText(MouseEvent event) {
return super.getToolTipText(event);
}
protected void initializeZoomSlider() {
zoomSlider = new JSlider(MIN_ZOOM, tileController.getTileSource().getMaxZoom());
zoomSlider.setOrientation(JSlider.VERTICAL);
zoomSlider.setBounds(10, 10, 30, 150);
zoomSlider.setOpaque(false);
zoomSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
setZoom(zoomSlider.getValue());
}
});
zoomSlider.setFocusable(false);
add(zoomSlider);
int size = 18;
URL url = JMapViewer.class.getResource("images/plus.png");
if (url != null) {
ImageIcon icon = new ImageIcon(url);
zoomInButton = new JButton(icon);
} else {
zoomInButton = new JButton("+");
zoomInButton.setFont(new Font("sansserif", Font.BOLD, 9));
zoomInButton.setMargin(new Insets(0, 0, 0, 0));
}
zoomInButton.setBounds(4, 155, size, size);
zoomInButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
zoomIn();
}
});
zoomInButton.setFocusable(false);
add(zoomInButton);
url = JMapViewer.class.getResource("images/minus.png");
if (url != null) {
ImageIcon icon = new ImageIcon(url);
zoomOutButton = new JButton(icon);
} else {
zoomOutButton = new JButton("-");
zoomOutButton.setFont(new Font("sansserif", Font.BOLD, 9));
zoomOutButton.setMargin(new Insets(0, 0, 0, 0));
}
zoomOutButton.setBounds(8 + size, 155, size, size);
zoomOutButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
zoomOut();
}
});
zoomOutButton.setFocusable(false);
add(zoomOutButton);
}
/**
* Changes the map pane so that it is centered on the specified coordinate
* at the given zoom level.
*
* @param to
* specified coordinate
* @param zoom
* {@link #MIN_ZOOM} <= zoom level <= {@link #MAX_ZOOM}
*/
public void setDisplayPosition(ICoordinate to, int zoom) {
setDisplayPosition(new Point(getWidth() / 2, getHeight() / 2), to, zoom);
}
/**
* Changes the map pane so that the specified coordinate at the given zoom
* level is displayed on the map at the screen coordinate
* <code>mapPoint</code>.
*
* @param mapPoint
* point on the map denoted in pixels where the coordinate should
* be set
* @param to
* specified coordinate
* @param zoom
* {@link #MIN_ZOOM} <= zoom level <=
* {@link TileSource#getMaxZoom()}
*/
public void setDisplayPosition(Point mapPoint, ICoordinate to, int zoom) {
Point p = tileSource.latLonToXY(to, zoom);
setDisplayPosition(mapPoint, p.x, p.y, zoom);
}
/**
* Sets the display position.
* @param x X coordinate
* @param y Y coordinate
* @param zoom zoom level, between {@link #MIN_ZOOM} and {@link #MAX_ZOOM}
*/
public void setDisplayPosition(int x, int y, int zoom) {
setDisplayPosition(new Point(getWidth() / 2, getHeight() / 2), x, y, zoom);
}
/**
* Sets the display position.
* @param mapPoint map point
* @param x X coordinate
* @param y Y coordinate
* @param zoom zoom level, between {@link #MIN_ZOOM} and {@link #MAX_ZOOM}
*/
public void setDisplayPosition(Point mapPoint, int x, int y, int zoom) {
if (zoom > tileController.getTileSource().getMaxZoom() || zoom < MIN_ZOOM)
return;
// Get the plain tile number
Point p = new Point();
p.x = x - mapPoint.x + getWidth() / 2;
p.y = y - mapPoint.y + getHeight() / 2;
center = p;
setIgnoreRepaint(true);
try {
int oldZoom = this.zoom;
this.zoom = zoom;
if (oldZoom != zoom) {
zoomChanged(oldZoom);
}
if (zoomSlider.getValue() != zoom) {
zoomSlider.setValue(zoom);
}
} finally {
setIgnoreRepaint(false);
repaint();
}
}
/**
* Sets the displayed map pane and zoom level so that all chosen map elements are visible.
* @param markers whether to consider markers
* @param rectangles whether to consider rectangles
* @param polygons whether to consider polygons
*/
public void setDisplayToFitMapElements(boolean markers, boolean rectangles, boolean polygons) {
int nbElemToCheck = 0;
if (markers && mapMarkerList != null)
nbElemToCheck += mapMarkerList.size();
if (rectangles && mapRectangleList != null)
nbElemToCheck += mapRectangleList.size();
if (polygons && mapPolygonList != null)
nbElemToCheck += mapPolygonList.size();
if (nbElemToCheck == 0)
return;
int xMin = Integer.MAX_VALUE;
int yMin = Integer.MAX_VALUE;
int xMax = Integer.MIN_VALUE;
int yMax = Integer.MIN_VALUE;
int mapZoomMax = tileController.getTileSource().getMaxZoom();
if (markers && mapMarkerList != null) {
synchronized (this) {
for (MapMarker marker : mapMarkerList) {
if (marker.isVisible()) {
Point p = tileSource.latLonToXY(marker.getCoordinate(), mapZoomMax);
xMax = Math.max(xMax, p.x);
yMax = Math.max(yMax, p.y);
xMin = Math.min(xMin, p.x);
yMin = Math.min(yMin, p.y);
}
}
}
}
if (rectangles && mapRectangleList != null) {
synchronized (this) {
for (MapRectangle rectangle : mapRectangleList) {
if (rectangle.isVisible()) {
Point bottomRight = tileSource.latLonToXY(rectangle.getBottomRight(), mapZoomMax);
Point topLeft = tileSource.latLonToXY(rectangle.getTopLeft(), mapZoomMax);
xMax = Math.max(xMax, bottomRight.x);
yMax = Math.max(yMax, topLeft.y);
xMin = Math.min(xMin, topLeft.x);
yMin = Math.min(yMin, bottomRight.y);
}
}
}
}
if (polygons && mapPolygonList != null) {
synchronized (this) {
for (MapPolygon polygon : mapPolygonList) {
if (polygon.isVisible()) {
for (ICoordinate c : polygon.getPoints()) {
Point p = tileSource.latLonToXY(c, mapZoomMax);
xMax = Math.max(xMax, p.x);
yMax = Math.max(yMax, p.y);
xMin = Math.min(xMin, p.x);
yMin = Math.min(yMin, p.y);
}
}
}
}
}
int height = Math.max(0, getHeight());
int width = Math.max(0, getWidth());
int newZoom = mapZoomMax;
int x = xMax - xMin;
int y = yMax - yMin;
while (x > width || y > height) {
newZoom--;
x >>= 1;
y >>= 1;
}
x = xMin + (xMax - xMin) / 2;
y = yMin + (yMax - yMin) / 2;
int z = 1 << (mapZoomMax - newZoom);
x /= z;
y /= z;
setDisplayPosition(x, y, newZoom);
}
/**
* Sets the displayed map pane and zoom level so that all map markers are visible.
*/
public void setDisplayToFitMapMarkers() {
setDisplayToFitMapElements(true, false, false);
}
/**
* Sets the displayed map pane and zoom level so that all map rectangles are visible.
*/
public void setDisplayToFitMapRectangles() {
setDisplayToFitMapElements(false, true, false);
}
/**
* Sets the displayed map pane and zoom level so that all map polygons are visible.
*/
public void setDisplayToFitMapPolygons() {
setDisplayToFitMapElements(false, false, true);
}
/**
* @return the center
*/
public Point getCenter() {
return center;
}
/**
* @param center the center to set
*/
public void setCenter(Point center) {
this.center = center;
}
/**
* Calculates the latitude/longitude coordinate of the center of the
* currently displayed map area.
*
* @return latitude / longitude
*/
public ICoordinate getPosition() {
return tileSource.xyToLatLon(center, zoom);
}
/**
* Converts the relative pixel coordinate (regarding the top left corner of
* the displayed map) into a latitude / longitude coordinate
*
* @param mapPoint
* relative pixel coordinate regarding the top left corner of the
* displayed map
* @return latitude / longitude
*/
public ICoordinate getPosition(Point mapPoint) {
return getPosition(mapPoint.x, mapPoint.y);
}
/**
* Converts the relative pixel coordinate (regarding the top left corner of
* the displayed map) into a latitude / longitude coordinate
*
* @param mapPointX X coordinate
* @param mapPointY Y coordinate
* @return latitude / longitude
*/
public ICoordinate getPosition(int mapPointX, int mapPointY) {
int x = center.x + mapPointX - getWidth() / 2;
int y = center.y + mapPointY - getHeight() / 2;
return tileSource.xyToLatLon(x, y, zoom);
}
/**
* Calculates the position on the map of a given coordinate
*
* @param lat latitude
* @param lon longitude
* @param checkOutside check if the point is outside the displayed area
* @return point on the map or <code>null</code> if the point is not visible
* and checkOutside set to <code>true</code>
*/
public Point getMapPosition(double lat, double lon, boolean checkOutside) {
Point p = tileSource.latLonToXY(lat, lon, zoom);
p.translate(-(center.x - getWidth() / 2), -(center.y - getHeight() /2));
if (checkOutside && (p.x < 0 || p.y < 0 || p.x > getWidth() || p.y > getHeight())) {
return null;
}
return p;
}
/**
* Calculates the position on the map of a given coordinate
*
* @param lat latitude
* @param lon longitude
* @return point on the map or <code>null</code> if the point is not visible
*/
public Point getMapPosition(double lat, double lon) {
return getMapPosition(lat, lon, true);
}
/**
* Calculates the position on the map of a given coordinate
*
* @param lat Latitude
* @param lon longitude
* @param offset Offset respect Latitude
* @param checkOutside check if the point is outside the displayed area
* @return Integer the radius in pixels
*/
public Integer getLatOffset(double lat, double lon, double offset, boolean checkOutside) {
Point p = tileSource.latLonToXY(lat + offset, lon, zoom);
int y = p.y - (center.y - getHeight() / 2);
if (checkOutside && (y < 0 || y > getHeight())) {
return null;
}
return y;
}
/**
* Calculates the position on the map of a given coordinate
*
* @param marker MapMarker object that define the x,y coordinate
* @param p coordinate
* @return Integer the radius in pixels
*/
public Integer getRadius(MapMarker marker, Point p) {
if (marker.getMarkerStyle() == MapMarker.STYLE.FIXED)
return (int) marker.getRadius();
else if (p != null) {
Integer radius = getLatOffset(marker.getLat(), marker.getLon(), marker.getRadius(), false);
radius = radius == null ? null : p.y - radius.intValue();
return radius;
} else
return null;
}
/**
* Calculates the position on the map of a given coordinate
*
* @param coord coordinate
* @return point on the map or <code>null</code> if the point is not visible
*/
public Point getMapPosition(Coordinate coord) {
if (coord != null)
return getMapPosition(coord.getLat(), coord.getLon());
else
return null;
}
/**
* Calculates the position on the map of a given coordinate
*
* @param coord coordinate
* @param checkOutside check if the point is outside the displayed area
* @return point on the map or <code>null</code> if the point is not visible
* and checkOutside set to <code>true</code>
*/
public Point getMapPosition(ICoordinate coord, boolean checkOutside) {
if (coord != null)
return getMapPosition(coord.getLat(), coord.getLon(), checkOutside);
else
return null;
}
/**
* Gets the meter per pixel.
*
* @return the meter per pixel
*/
public double getMeterPerPixel() {
Point origin = new Point(5, 5);
Point center = new Point(getWidth() / 2, getHeight() / 2);
double pDistance = center.distance(origin);
ICoordinate originCoord = getPosition(origin);
ICoordinate centerCoord = getPosition(center);
double mDistance = tileSource.getDistance(originCoord.getLat(), originCoord.getLon(),
centerCoord.getLat(), centerCoord.getLon());
return mDistance / pDistance;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int iMove = 0;
int tilesize = tileSource.getTileSize();
int tilex = center.x / tilesize;
int tiley = center.y / tilesize;
int offsx = center.x % tilesize;
int offsy = center.y % tilesize;
int w2 = getWidth() / 2;
int h2 = getHeight() / 2;
int posx = w2 - offsx;
int posy = h2 - offsy;
int diffLeft = offsx;
int diffRight = tilesize - offsx;
int diffTop = offsy;
int diffBottom = tilesize - offsy;
boolean startLeft = diffLeft < diffRight;
boolean startTop = diffTop < diffBottom;
if (startTop) {
if (startLeft) {
iMove = 2;
} else {
iMove = 3;
}
} else {
if (startLeft) {
iMove = 1;
} else {
iMove = 0;
}
} // calculate the visibility borders
int xMin = -tilesize;
int yMin = -tilesize;
int xMax = getWidth();
int yMax = getHeight();
// calculate the length of the grid (number of squares per edge)
int gridLength = 1 << zoom;
// paint the tiles in a spiral, starting from center of the map
boolean painted = true;
int x = 0;
while (painted) {
painted = false;
for (int i = 0; i < 4; i++) {
if (i % 2 == 0) {
x++;
}
for (int j = 0; j < x; j++) {
if (xMin <= posx && posx <= xMax && yMin <= posy && posy <= yMax) {
// tile is visible
Tile tile;
if (scrollWrapEnabled) {
// in case tilex is out of bounds, grab the tile to use for wrapping
int tilexWrap = ((tilex % gridLength) + gridLength) % gridLength;
tile = tileController.getTile(tilexWrap, tiley, zoom);
} else {
tile = tileController.getTile(tilex, tiley, zoom);
}
if (tile != null) {
tile.paint(g, posx, posy, tilesize, tilesize);
if (tileGridVisible) {
g.drawRect(posx, posy, tilesize, tilesize);
}
}
painted = true;
}
Point p = move[iMove];
posx += p.x * tilesize;
posy += p.y * tilesize;
tilex += p.x;
tiley += p.y;
}
iMove = (iMove + 1) % move.length;
}
}
// outer border of the map
int mapSize = tilesize << zoom;
if (scrollWrapEnabled) {
g.drawLine(0, h2 - center.y, getWidth(), h2 - center.y);
g.drawLine(0, h2 - center.y + mapSize, getWidth(), h2 - center.y + mapSize);
} else {
g.drawRect(w2 - center.x, h2 - center.y, mapSize, mapSize);
}
// g.drawString("Tiles in cache: " + tileCache.getTileCount(), 50, 20);
// keep x-coordinates from growing without bound if scroll-wrap is enabled
if (scrollWrapEnabled) {
center.x = center.x % mapSize;
}
if (mapPolygonsVisible && mapPolygonList != null) {
synchronized (this) {
for (MapPolygon polygon : mapPolygonList) {
if (polygon.isVisible())
paintPolygon(g, polygon);
}
}
}
if (mapRectanglesVisible && mapRectangleList != null) {
synchronized (this) {
for (MapRectangle rectangle : mapRectangleList) {
if (rectangle.isVisible())
paintRectangle(g, rectangle);
}
}
}
if (mapMarkersVisible && mapMarkerList != null) {
synchronized (this) {
for (MapMarker marker : mapMarkerList) {
if (marker.isVisible())
paintMarker(g, marker);
}
}
}
attribution.paintAttribution(g, getWidth(), getHeight(), getPosition(0, 0), getPosition(getWidth(), getHeight()), zoom, this);
}
/**
* Paint a single marker.
* @param g Graphics used for painting
* @param marker marker to paint
*/
protected void paintMarker(Graphics g, MapMarker marker) {
Point p = getMapPosition(marker.getLat(), marker.getLon(), marker.getMarkerStyle() == MapMarker.STYLE.FIXED);
Integer radius = getRadius(marker, p);
if (scrollWrapEnabled) {
int tilesize = tileSource.getTileSize();
int mapSize = tilesize << zoom;
if (p == null) {
p = getMapPosition(marker.getLat(), marker.getLon(), false);
radius = getRadius(marker, p);
}
marker.paint(g, p, radius);
int xSave = p.x;
int xWrap = xSave;
// overscan of 15 allows up to 30-pixel markers to gracefully scroll off the edge of the panel
while ((xWrap -= mapSize) >= -15) {
p.x = xWrap;
marker.paint(g, p, radius);
}
xWrap = xSave;
while ((xWrap += mapSize) <= getWidth() + 15) {
p.x = xWrap;
marker.paint(g, p, radius);
}
} else {
if (p != null) {
marker.paint(g, p, radius);
}
}
}
/**
* Paint a single rectangle.
* @param g Graphics used for painting
* @param rectangle rectangle to paint
*/
protected void paintRectangle(Graphics g, MapRectangle rectangle) {
Coordinate topLeft = rectangle.getTopLeft();
Coordinate bottomRight = rectangle.getBottomRight();
if (topLeft != null && bottomRight != null) {
Point pTopLeft = getMapPosition(topLeft, false);
Point pBottomRight = getMapPosition(bottomRight, false);
if (pTopLeft != null && pBottomRight != null) {
rectangle.paint(g, pTopLeft, pBottomRight);
if (scrollWrapEnabled) {
int tilesize = tileSource.getTileSize();
int mapSize = tilesize << zoom;
int xTopLeftSave = pTopLeft.x;
int xTopLeftWrap = xTopLeftSave;
int xBottomRightSave = pBottomRight.x;
int xBottomRightWrap = xBottomRightSave;
while ((xBottomRightWrap -= mapSize) >= 0) {
xTopLeftWrap -= mapSize;
pTopLeft.x = xTopLeftWrap;
pBottomRight.x = xBottomRightWrap;
rectangle.paint(g, pTopLeft, pBottomRight);
}
xTopLeftWrap = xTopLeftSave;
xBottomRightWrap = xBottomRightSave;
while ((xTopLeftWrap += mapSize) <= getWidth()) {
xBottomRightWrap += mapSize;
pTopLeft.x = xTopLeftWrap;
pBottomRight.x = xBottomRightWrap;
rectangle.paint(g, pTopLeft, pBottomRight);
}
}
}
}
}
/**
* Paint a single polygon.
* @param g Graphics used for painting
* @param polygon polygon to paint
*/
protected void paintPolygon(Graphics g, MapPolygon polygon) {
List<? extends ICoordinate> coords = polygon.getPoints();
if (coords != null && coords.size() >= 3) {
List<Point> points = new LinkedList<>();
for (ICoordinate c : coords) {
Point p = getMapPosition(c, false);
if (p == null) {
return;
}
points.add(p);
}
polygon.paint(g, points);
if (scrollWrapEnabled) {
int tilesize = tileSource.getTileSize();
int mapSize = tilesize << zoom;
List<Point> pointsWrapped = new LinkedList<>(points);
boolean keepWrapping = true;
while (keepWrapping) {
for (Point p : pointsWrapped) {
p.x -= mapSize;
if (p.x < 0) {
keepWrapping = false;
}
}
polygon.paint(g, pointsWrapped);
}
pointsWrapped = new LinkedList<>(points);
keepWrapping = true;
while (keepWrapping) {
for (Point p : pointsWrapped) {
p.x += mapSize;
if (p.x > getWidth()) {
keepWrapping = false;
}
}
polygon.paint(g, pointsWrapped);
}
}
}
}
/**
* Moves the visible map pane.
*
* @param x
* horizontal movement in pixel.
* @param y
* vertical movement in pixel
*/
public void moveMap(int x, int y) {
tileController.cancelOutstandingJobs(); // Clear outstanding load
center.x += x;
center.y += y;
repaint();
this.fireJMVEvent(new JMVCommandEvent(COMMAND.MOVE, this));
}
/**
* @return the current zoom level
*/
public int getZoom() {
return zoom;
}
/**
* Increases the current zoom level by one
*/
public void zoomIn() {
setZoom(zoom + 1);
}
/**
* Increases the current zoom level by one
* @param mapPoint point to choose as center for new zoom level
*/
public void zoomIn(Point mapPoint) {
setZoom(zoom + 1, mapPoint);
}
/**
* Decreases the current zoom level by one
*/
public void zoomOut() {
setZoom(zoom - 1);
}
/**
* Decreases the current zoom level by one
*
* @param mapPoint point to choose as center for new zoom level
*/
public void zoomOut(Point mapPoint) {
setZoom(zoom - 1, mapPoint);
}
/**
* Set the zoom level and center point for display
*
* @param zoom new zoom level
* @param mapPoint point to choose as center for new zoom level
*/
public void setZoom(int zoom, Point mapPoint) {
if (zoom > tileController.getTileSource().getMaxZoom() || zoom < tileController.getTileSource().getMinZoom()
|| zoom == this.zoom)
return;
ICoordinate zoomPos = getPosition(mapPoint);
tileController.cancelOutstandingJobs(); // Clearing outstanding load
// requests
setDisplayPosition(mapPoint, zoomPos, zoom);
this.fireJMVEvent(new JMVCommandEvent(COMMAND.ZOOM, this));
}
/**
* Set the zoom level
*
* @param zoom new zoom level
*/
public void setZoom(int zoom) {
setZoom(zoom, new Point(getWidth() / 2, getHeight() / 2));
}
/**
* Every time the zoom level changes this method is called. Override it in
* derived implementations for adapting zoom dependent values. The new zoom
* level can be obtained via {@link #getZoom()}.
*
* @param oldZoom the previous zoom level
*/
protected void zoomChanged(int oldZoom) {
zoomSlider.setToolTipText("Zoom level " + zoom);
zoomInButton.setToolTipText("Zoom to level " + (zoom + 1));
zoomOutButton.setToolTipText("Zoom to level " + (zoom - 1));
zoomOutButton.setEnabled(zoom > tileController.getTileSource().getMinZoom());
zoomInButton.setEnabled(zoom < tileController.getTileSource().getMaxZoom());
}
/**
* Determines whether the tile grid is visible or not.
* @return {@code true} if the tile grid is visible, {@code false} otherwise
*/
public boolean isTileGridVisible() {
return tileGridVisible;
}
/**
* Sets whether the tile grid is visible or not.
* @param tileGridVisible {@code true} if the tile grid is visible, {@code false} otherwise
*/
public void setTileGridVisible(boolean tileGridVisible) {
this.tileGridVisible = tileGridVisible;
repaint();
}
/**
* Determines whether {@link MapMarker}s are painted or not.
* @return {@code true} if {@link MapMarker}s are painted, {@code false} otherwise
*/
public boolean getMapMarkersVisible() {
return mapMarkersVisible;
}
/**
* Enables or disables painting of the {@link MapMarker}
*
* @param mapMarkersVisible {@code true} to enable painting of markers
* @see #addMapMarker(MapMarker)
* @see #getMapMarkerList()
*/
public void setMapMarkerVisible(boolean mapMarkersVisible) {
this.mapMarkersVisible = mapMarkersVisible;
repaint();
}
/**
* Sets the list of {@link MapMarker}s.
* @param mapMarkerList list of {@link MapMarker}s
*/
public void setMapMarkerList(List<MapMarker> mapMarkerList) {
this.mapMarkerList = mapMarkerList;
repaint();
}
/**
* Returns the list of {@link MapMarker}s.
* @return list of {@link MapMarker}s
*/
public List<MapMarker> getMapMarkerList() {
return mapMarkerList;
}
/**
* Sets the list of {@link MapRectangle}s.
* @param mapRectangleList list of {@link MapRectangle}s
*/
public void setMapRectangleList(List<MapRectangle> mapRectangleList) {
this.mapRectangleList = mapRectangleList;
repaint();
}
/**
* Returns the list of {@link MapRectangle}s.
* @return list of {@link MapRectangle}s
*/
public List<MapRectangle> getMapRectangleList() {
return mapRectangleList;
}
/**
* Sets the list of {@link MapPolygon}s.
* @param mapPolygonList list of {@link MapPolygon}s
*/
public void setMapPolygonList(List<MapPolygon> mapPolygonList) {
this.mapPolygonList = mapPolygonList;
repaint();
}
/**
* Returns the list of {@link MapPolygon}s.
* @return list of {@link MapPolygon}s
*/
public List<MapPolygon> getMapPolygonList() {
return mapPolygonList;
}
/**
* Add a {@link MapMarker}.
* @param marker map marker to add
*/
public void addMapMarker(MapMarker marker) {
mapMarkerList.add(marker);
repaint();
}
/**
* Remove a {@link MapMarker}.
* @param marker map marker to remove
*/
public void removeMapMarker(MapMarker marker) {
mapMarkerList.remove(marker);
repaint();
}
/**
* Remove all {@link MapMarker}s.
*/
public void removeAllMapMarkers() {
mapMarkerList.clear();
repaint();
}
/**
* Add a {@link MapRectangle}.
* @param rectangle map rectangle to add
*/
public void addMapRectangle(MapRectangle rectangle) {
mapRectangleList.add(rectangle);
repaint();
}
/**
* Remove a {@link MapRectangle}.
* @param rectangle map rectangle to remove
*/
public void removeMapRectangle(MapRectangle rectangle) {
mapRectangleList.remove(rectangle);
repaint();
}
/**
* Remove all {@link MapRectangle}s.
*/
public void removeAllMapRectangles() {
mapRectangleList.clear();
repaint();
}
/**
* Add a {@link MapPolygon}.
* @param polygon map polygon to add
*/
public void addMapPolygon(MapPolygon polygon) {
mapPolygonList.add(polygon);
repaint();
}
/**
* Remove a {@link MapPolygon}.
* @param polygon map polygon to remove
*/
public void removeMapPolygon(MapPolygon polygon) {
mapPolygonList.remove(polygon);
repaint();
}
/**
* Remove all {@link MapPolygon}s.
*/
public void removeAllMapPolygons() {
mapPolygonList.clear();
repaint();
}
/**
* Sets whether zoom controls are displayed or not.
* @param visible {@code true} if zoom controls are displayed, {@code false} otherwise
*/
public void setZoomContolsVisible(boolean visible) {
zoomSlider.setVisible(visible);
zoomInButton.setVisible(visible);
zoomOutButton.setVisible(visible);
}
/**
* Determines whether zoom controls are displayed or not.
* @return {@code true} if zoom controls are displayed, {@code false} otherwise
*/
public boolean getZoomControlsVisible() {
return zoomSlider.isVisible();
}
/**
* Sets the tile source.
* @param tileSource tile source
*/
public void setTileSource(TileSource tileSource) {
if (tileSource.getMaxZoom() > MAX_ZOOM)
throw new RuntimeException("Maximum zoom level too high");
if (tileSource.getMinZoom() < MIN_ZOOM)
throw new RuntimeException("Minimum zoom level too low");
ICoordinate position = getPosition();
this.tileSource = tileSource;
tileController.setTileSource(tileSource);
zoomSlider.setMinimum(tileSource.getMinZoom());
zoomSlider.setMaximum(tileSource.getMaxZoom());
tileController.cancelOutstandingJobs();
if (zoom > tileSource.getMaxZoom()) {
setZoom(tileSource.getMaxZoom());
}
attribution.initialize(tileSource);
setDisplayPosition(position, zoom);
repaint();
}
@Override
public void tileLoadingFinished(Tile tile, boolean success) {
tile.setLoaded(success);
repaint();
}
/**
* Determines whether the {@link MapRectangle}s are painted or not.
* @return {@code true} if the {@link MapRectangle}s are painted, {@code false} otherwise
*/
public boolean isMapRectanglesVisible() {
return mapRectanglesVisible;
}
/**
* Enables or disables painting of the {@link MapRectangle}s.
*
* @param mapRectanglesVisible {@code true} to enable painting of rectangles
* @see #addMapRectangle(MapRectangle)
* @see #getMapRectangleList()
*/
public void setMapRectanglesVisible(boolean mapRectanglesVisible) {
this.mapRectanglesVisible = mapRectanglesVisible;
repaint();
}
/**
* Determines whether the {@link MapPolygon}s are painted or not.
* @return {@code true} if the {@link MapPolygon}s are painted, {@code false} otherwise
*/
public boolean isMapPolygonsVisible() {
return mapPolygonsVisible;
}
/**
* Enables or disables painting of the {@link MapPolygon}s.
*
* @param mapPolygonsVisible {@code true} to enable painting of polygons
* @see #addMapPolygon(MapPolygon)
* @see #getMapPolygonList()
*/
public void setMapPolygonsVisible(boolean mapPolygonsVisible) {
this.mapPolygonsVisible = mapPolygonsVisible;
repaint();
}
/**
* Determines whether scroll wrap is enabled or not.
* @return {@code true} if scroll wrap is enabled, {@code false} otherwise
*/
public boolean isScrollWrapEnabled() {
return scrollWrapEnabled;
}
/**
* Sets whether scroll wrap is enabled or not.
* @param scrollWrapEnabled {@code true} if scroll wrap is enabled, {@code false} otherwise
*/
public void setScrollWrapEnabled(boolean scrollWrapEnabled) {
this.scrollWrapEnabled = scrollWrapEnabled;
repaint();
}
/**
* Returns the zoom controls apparence style (horizontal/vertical).
* @return {@link ZOOM_BUTTON_STYLE#VERTICAL} or {@link ZOOM_BUTTON_STYLE#HORIZONTAL}
*/
public ZOOM_BUTTON_STYLE getZoomButtonStyle() {
return zoomButtonStyle;
}
/**
* Sets the zoom controls apparence style (horizontal/vertical).
* @param style {@link ZOOM_BUTTON_STYLE#VERTICAL} or {@link ZOOM_BUTTON_STYLE#HORIZONTAL}
*/
public void setZoomButtonStyle(ZOOM_BUTTON_STYLE style) {
zoomButtonStyle = style;
if (zoomSlider == null || zoomInButton == null || zoomOutButton == null) {
return;
}
switch (style) {
case VERTICAL:
zoomSlider.setBounds(10, 27, 30, 150);
zoomInButton.setBounds(14, 8, 20, 20);
zoomOutButton.setBounds(14, 176, 20, 20);
break;
case HORIZONTAL:
default:
zoomSlider.setBounds(10, 10, 30, 150);
zoomInButton.setBounds(4, 155, 18, 18);
zoomOutButton.setBounds(26, 155, 18, 18);
break;
}
repaint();
}
/**
* Returns the tile controller.
* @return the tile controller
*/
public TileController getTileController() {
return tileController;
}
/**
* Return tile information caching class
* @return tile cache
* @see TileController#getTileCache()
*/
public TileCache getTileCache() {
return tileController.getTileCache();
}
/**
* Sets the tile loader.
* @param loader tile loader
*/
public void setTileLoader(TileLoader loader) {
tileController.setTileLoader(loader);
}
/**
* Returns attribution.
* @return attribution
*/
public AttributionSupport getAttribution() {
return attribution;
}
/**
* @param listener listener to set
*/
public void addJMVListener(JMapViewerEventListener listener) {
evtListenerList.add(JMapViewerEventListener.class, listener);
}
/**
* @param listener listener to remove
*/
public void removeJMVListener(JMapViewerEventListener listener) {
evtListenerList.remove(JMapViewerEventListener.class, listener);
}
/**
* Send an update to all objects registered with viewer
*
* @param evt event to dispatch
*/
private void fireJMVEvent(JMVCommandEvent evt) {
Object[] listeners = evtListenerList.getListenerList();
for (int i = 0; i < listeners.length; i += 2) {
if (listeners[i] == JMapViewerEventListener.class) {
((JMapViewerEventListener) listeners[i + 1]).processCommand(evt);
}
}
}
}