Premier Depot

This commit is contained in:
antoine.gagneux 2023-02-21 17:04:04 +01:00
commit b7d843338f
39 changed files with 3562 additions and 0 deletions

10
.classpath Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/bin/

17
.project Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ExempleJPanelOSM</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,12 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=1.8

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,8 @@
public class ClassePrincipale {
public static void main(String[] args) {
new MaJFrameDemo().setVisible(true);
new MaJFrameSimple().setVisible(true);
}
}

192
src/MaJFrameDemo.java Normal file
View File

@ -0,0 +1,192 @@
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.openstreetmap.gui.jmapviewer.JMapViewer;
import org.openstreetmap.gui.jmapviewer.MapMarkerDot;
import org.openstreetmap.gui.jmapviewer.MapMarkerImage;
import org.openstreetmap.gui.jmapviewer.MapMarkerLabel;
import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
/**
*
* Demonstrates the usage of {@link JMapViewer}
*
* @author Jan Peter Stotz
*
*/
public class MaJFrameDemo extends JFrame {
private static final long serialVersionUID = 1L;
private final JMapViewer map;
static {
System.setProperty("http.agent", "Gluon Mobile/1.0.3");
}
public MaJFrameDemo() {
super("OSM Demo");
setSize(400, 400);
map = new JMapViewer();
getContentPane().setLayout(new BorderLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setExtendedState(JFrame.MAXIMIZED_BOTH);
JPanel panel = new JPanel();
JPanel helpPanel = new JPanel();
getContentPane().add(panel, BorderLayout.NORTH);
getContentPane().add(helpPanel, BorderLayout.SOUTH);
JLabel helpLabel = new JLabel("Utilisez le bouton droit de la souris pour vous déplacer,\n"
+"Double-clic gauche ou molette de la souris pour zoomer.");
helpPanel.add(helpLabel);
panel.setLayout(new GridLayout(0, 1, 0, 0));
JPanel panel_1 = new JPanel();
panel.add(panel_1);
JCheckBox showMapMarker = new JCheckBox("Marqueurs de carte visibles");
showMapMarker.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
map.setMapMarkerVisible(showMapMarker.isSelected());
}
});
showMapMarker.setSelected(true);
panel_1.add(showMapMarker);
JCheckBox showTileGrid = new JCheckBox("Grille de tuiles visible");
showTileGrid.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
map.setTileGridVisible(showTileGrid.isSelected());
}
});
showTileGrid.setSelected(false);
panel_1.add(showTileGrid);
JCheckBox showZoomControls = new JCheckBox("Afficher les contrôles de zoom");
showZoomControls.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
map.setZoomContolsVisible(showZoomControls.isSelected());
}
});
showZoomControls.setSelected(true);
panel_1.add(showZoomControls);
JPanel panel_2 = new JPanel();
panel.add(panel_2);
JButton buttonFit = new JButton("Adapter la carte aux marqueurs");
buttonFit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
map.setDisplayToFitMapMarkers();
}
});
panel_2.add(buttonFit);
JButton buttonMarkerPoint = new JButton("Marqueurs points");
buttonMarkerPoint.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addMarkerDot(100);
}
});
panel_2.add(buttonMarkerPoint);
JButton buttonMarkerImg = new JButton("Marqueurs images");
buttonMarkerImg.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addMarkerImg(50);
}
});
panel_2.add(buttonMarkerImg);
JButton buttonMarkerLabel = new JButton("Marqueurs labels");
buttonMarkerLabel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addMarkerLabel(50);
}
});
panel_2.add(buttonMarkerLabel);
JButton buttonCleanMarkers = new JButton("Supprimer tous les marqueurs");
buttonCleanMarkers.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cleanMarkers();
}
});
panel_2.add(buttonCleanMarkers);
getContentPane().add(map, BorderLayout.CENTER);
//
addMarkerDot(10);
map.addPropertyChangeListener(getName(), new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
// TODO Auto-generated method stub
System.out.println("on property change");
}
});
// map.setDisplayPositionByLatLon(49.807, 8.6, 11);
// map.setTileGridVisible(true);
}
private void cleanMarkers() {
List<MapMarker> list = new ArrayList<>(map.getMapMarkerList());
Iterator<MapMarker> it = list.iterator();
while (it.hasNext()) {
map.removeMapMarker(it.next());
}
}
private void addMarkerLabel(int nbMarkers) {
Random r = new Random();
for (int i=0;i<nbMarkers;i++) {
double latitude = (r.nextDouble() * -180.0) + 90.0;
double longitude = (r.nextDouble() * -360.0) + 180.0;
Color color = new Color((int)(Math.random() * 0x1000000));
map.addMapMarker(new MapMarkerLabel(color, "label" + i, 16, latitude, longitude));
}
}
private void addMarkerImg(int nbMarkers) {
Random r = new Random();
for (int i=0;i<nbMarkers;i++) {
double latitude = (r.nextDouble() * -180.0) + 90.0;
double longitude = (r.nextDouble() * -360.0) + 180.0;
map.addMapMarker(new MapMarkerImage("region-auvergne-rhone-alpes_logo.png", 10, latitude, longitude));
}
}
private void addMarkerDot(int nbMarkers) {
Random r = new Random();
for (int i=0;i<nbMarkers;i++) {
double latitude = (r.nextDouble() * -180.0) + 90.0;
double longitude = (r.nextDouble() * -360.0) + 180.0;
Color color = new Color((int)(Math.random() * 0x1000000));
map.addMapMarker(new MapMarkerDot(color, 10, latitude, longitude));
}
}
}

35
src/MaJFrameSimple.java Normal file
View File

@ -0,0 +1,35 @@
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import org.openstreetmap.gui.jmapviewer.JMapViewer;
import javax.swing.JButton;
public class MaJFrameSimple extends JFrame {
private JPanel contentPane;
private JMapViewer panel_1;
static {
System.setProperty("http.agent", "Gluon Mobile/1.0.3");
}
/**
* Create the frame.
*/
public MaJFrameSimple() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
panel_1 = new JMapViewer();
contentPane.add(panel_1, BorderLayout.CENTER);
}
}

View File

@ -0,0 +1,55 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2009 by Stefan Zeller
import java.awt.geom.Point2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* This class encapsulates a Point2D.Double and provide access
* via <tt>lat</tt> and <tt>lon</tt>.
*
* @author Jan Peter Stotz
*
*/
public class Coordinate implements Serializable {
private transient Point2D.Double data;
public Coordinate(double lat, double lon) {
data = new Point2D.Double(lon, lat);
}
public double getLat() {
return data.y;
}
public void setLat(double lat) {
data.y = lat;
}
public double getLon() {
return data.x;
}
public void setLon(double lon) {
data.x = lon;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeObject(data.x);
out.writeObject(data.y);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
data = new Point2D.Double();
data.x = (Double) in.readObject();
data.y = (Double) in.readObject();
}
public String toString() {
return "Coordinate[" + data.y + ", " + data.x + "]";
}
}

View File

@ -0,0 +1,180 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
/**
* Default map controller which implements map moving by pressing the right
* mouse button and zooming by double click or by mouse wheel.
*
* @author Jan Peter Stotz
*
*/
public class DefaultMapController extends JMapController implements MouseListener, MouseMotionListener,
MouseWheelListener {
private static final int MOUSE_BUTTONS_MASK = MouseEvent.BUTTON3_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK
| MouseEvent.BUTTON2_DOWN_MASK;
private static final int MAC_MOUSE_BUTTON3_MASK = MouseEvent.CTRL_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK;
public DefaultMapController(JMapViewer map) {
super(map);
}
private Point lastDragPoint;
private boolean isMoving = false;
private boolean movementEnabled = true;
private int movementMouseButton = MouseEvent.BUTTON3;
private int movementMouseButtonMask = MouseEvent.BUTTON3_DOWN_MASK;
private boolean wheelZoomEnabled = true;
private boolean doubleClickZoomEnabled = true;
public void mouseDragged(MouseEvent e) {
if (!movementEnabled || !isMoving)
return;
// Is only the selected mouse button pressed?
if ((e.getModifiersEx() & MOUSE_BUTTONS_MASK) == movementMouseButtonMask) {
Point p = e.getPoint();
if (lastDragPoint != null) {
int diffx = lastDragPoint.x - p.x;
int diffy = lastDragPoint.y - p.y;
map.moveMap(diffx, diffy);
}
lastDragPoint = p;
}
}
public void mouseClicked(MouseEvent e) {
if (doubleClickZoomEnabled && e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
map.zoomIn(e.getPoint());
}
}
public void mousePressed(MouseEvent e) {
if (e.getButton() == movementMouseButton || isPlatformOsx() && e.getModifiersEx() == MAC_MOUSE_BUTTON3_MASK) {
lastDragPoint = null;
isMoving = true;
}
}
public void mouseReleased(MouseEvent e) {
if (e.getButton() == movementMouseButton || isPlatformOsx() && e.getButton() == MouseEvent.BUTTON1) {
lastDragPoint = null;
isMoving = false;
}
}
public void mouseWheelMoved(MouseWheelEvent e) {
if (wheelZoomEnabled) {
map.setZoom(map.getZoom() - e.getWheelRotation(), e.getPoint());
}
}
public boolean isMovementEnabled() {
return movementEnabled;
}
/**
* Enables or disables that the map pane can be moved using the mouse.
*
* @param movementEnabled
*/
public void setMovementEnabled(boolean movementEnabled) {
this.movementEnabled = movementEnabled;
}
public int getMovementMouseButton() {
return movementMouseButton;
}
/**
* Sets the mouse button that is used for moving the map. Possible values
* are:
* <ul>
* <li>{@link MouseEvent#BUTTON1} (left mouse button)</li>
* <li>{@link MouseEvent#BUTTON2} (middle mouse button)</li>
* <li>{@link MouseEvent#BUTTON3} (right mouse button)</li>
* </ul>
*
* @param movementMouseButton
*/
public void setMovementMouseButton(int movementMouseButton) {
this.movementMouseButton = movementMouseButton;
switch (movementMouseButton) {
case MouseEvent.BUTTON1:
movementMouseButtonMask = MouseEvent.BUTTON1_DOWN_MASK;
break;
case MouseEvent.BUTTON2:
movementMouseButtonMask = MouseEvent.BUTTON2_DOWN_MASK;
break;
case MouseEvent.BUTTON3:
movementMouseButtonMask = MouseEvent.BUTTON3_DOWN_MASK;
break;
default:
throw new RuntimeException("Unsupported button");
}
}
public boolean isWheelZoomEnabled() {
return wheelZoomEnabled;
}
public void setWheelZoomEnabled(boolean wheelZoomEnabled) {
this.wheelZoomEnabled = wheelZoomEnabled;
}
public boolean isDoubleClickZoomEnabled() {
return doubleClickZoomEnabled;
}
public void setDoubleClickZoomEnabled(boolean doubleClickZoomEnabled) {
this.doubleClickZoomEnabled = doubleClickZoomEnabled;
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
// Mac OSX simulates with ctrl + mouse 1 the second mouse button hence no dragging events get fired.
//
if (isPlatformOsx()) {
if (!movementEnabled || !isMoving)
return;
// Is only the selected mouse button pressed?
if (e.getModifiersEx() == MouseEvent.CTRL_DOWN_MASK) {
Point p = e.getPoint();
if (lastDragPoint != null) {
int diffx = lastDragPoint.x - p.x;
int diffy = lastDragPoint.y - p.y;
map.moveMap(diffx, diffy);
}
lastDragPoint = p;
}
}
}
/**
* Replies true if we are currently running on OSX
*
* @return true if we are currently running on OSX
*/
public static boolean isPlatformOsx() {
String os = System.getProperty("os.name");
return os != null && os.toLowerCase().startsWith("mac os x");
}
}

View File

@ -0,0 +1,35 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelListener;
/**
* Abstract base class for all mouse controller implementations. For
* implementing your own controller create a class that derives from this one
* and implements one or more of the following interfaces:
* <ul>
* <li>{@link MouseListener}</li>
* <li>{@link MouseMotionListener}</li>
* <li>{@link MouseWheelListener}</li>
* </ul>
*
* @author Jan Peter Stotz
*/
public abstract class JMapController {
protected JMapViewer map;
public JMapController(JMapViewer map) {
this.map = map;
if (this instanceof MouseListener)
map.addMouseListener((MouseListener) this);
if (this instanceof MouseWheelListener)
map.addMouseWheelListener((MouseWheelListener) this);
if (this instanceof MouseMotionListener)
map.addMouseMotionListener((MouseMotionListener) this);
}
}

View File

@ -0,0 +1,790 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
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.awt.font.TextAttribute;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
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 org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
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
*
*/
public class JMapViewer extends JPanel implements TileLoaderListener {
private static final long serialVersionUID = 1L;
/**
* Vectors for clock-wise tile painting
*/
protected static final Point[] move = { new Point(1, 0), new Point(0, 1), new Point(-1, 0), new Point(0, -1) };
public static final int MAX_ZOOM = 22;
public static final int MIN_ZOOM = 0;
protected List<MapMarker> mapMarkerList;
protected List<MapRectangle> mapRectangleList;
protected boolean mapMarkersVisible;
protected boolean mapRectanglesVisible;
protected boolean tileGridVisible;
protected 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;
private TileSource tileSource;
// Attribution
private Image attrImage;
private String attrTermsUrl;
public static final Font ATTR_FONT = new Font("Arial", Font.PLAIN, 10);
public static final Font ATTR_LINK_FONT;
static {
HashMap<TextAttribute, Integer> aUnderline = new HashMap<TextAttribute, Integer>();
aUnderline.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
ATTR_LINK_FONT = ATTR_FONT.deriveFont(aUnderline);
}
/**
* 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 the
* {@link MemoryTileCache} and the tile loader uses 4 parallel threads for
* retrieving the tiles.
*/
public JMapViewer() {
this(new MemoryTileCache(), 4);
new DefaultMapController(this);
}
public JMapViewer(TileCache tileCache, int downloadThreadCount) {
super();
tileSource = new OsmTileSource.Mapnik();
tileController = new TileController(tileSource, tileCache, this);
mapMarkerList = new LinkedList<MapMarker>();
mapRectangleList = new LinkedList<MapRectangle>();
mapMarkersVisible = true;
mapRectanglesVisible = true;
tileGridVisible = false;
setLayout(null);
initializeZoomSlider();
setMinimumSize(new Dimension(tileSource.getTileSize(), tileSource.getTileSize()));
setPreferredSize(new Dimension(400, 400));
setDisplayPositionByLatLon(50, 9, 3);
//setToolTipText("");
}
@Override
public String getToolTipText(MouseEvent event) {
// Point screenPoint = event.getLocationOnScreen();
// Coordinate c = getPosition(screenPoint);
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() {
public void stateChanged(ChangeEvent e) {
setZoom(zoomSlider.getValue());
}
});
add(zoomSlider);
int size = 18;
try {
ImageIcon icon = new ImageIcon(getClass().getResource("images/plus.png"));
zoomInButton = new JButton(icon);
} catch (Exception e) {
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() {
public void actionPerformed(ActionEvent e) {
zoomIn();
}
});
add(zoomInButton);
try {
ImageIcon icon = new ImageIcon(getClass().getResource("images/minus.png"));
zoomOutButton = new JButton(icon);
} catch (Exception e) {
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() {
public void actionPerformed(ActionEvent e) {
zoomOut();
}
});
add(zoomOutButton);
}
/**
* Changes the map pane so that it is centered on the specified coordinate
* at the given zoom level.
*
* @param lat
* latitude of the specified coordinate
* @param lon
* longitude of the specified coordinate
* @param zoom
* {@link #MIN_ZOOM} <= zoom level <= {@link #MAX_ZOOM}
*/
public void setDisplayPositionByLatLon(double lat, double lon, int zoom) {
setDisplayPositionByLatLon(new Point(getWidth() / 2, getHeight() / 2), lat, lon, 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 lat
* latitude of the specified coordinate
* @param lon
* longitude of the specified coordinate
* @param zoom
* {@link #MIN_ZOOM} <= zoom level <=
* {@link TileSource#getMaxZoom()}
*/
public void setDisplayPositionByLatLon(Point mapPoint, double lat, double lon, int zoom) {
int x = OsmMercator.LonToX(lon, zoom);
int y = OsmMercator.LatToY(lat, zoom);
setDisplayPosition(mapPoint, x, y, zoom);
}
public void setDisplayPosition(int x, int y, int zoom) {
setDisplayPosition(new Point(getWidth() / 2, getHeight() / 2), x, y, 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 map markers are
* visible.
*/
public void setDisplayToFitMapMarkers() {
if (mapMarkerList == null || mapMarkerList.size() == 0)
return;
int x_min = Integer.MAX_VALUE;
int y_min = Integer.MAX_VALUE;
int x_max = Integer.MIN_VALUE;
int y_max = Integer.MIN_VALUE;
int mapZoomMax = tileController.getTileSource().getMaxZoom();
for (MapMarker marker : mapMarkerList) {
int x = OsmMercator.LonToX(marker.getLon(), mapZoomMax);
int y = OsmMercator.LatToY(marker.getLat(), mapZoomMax);
x_max = Math.max(x_max, x);
y_max = Math.max(y_max, y);
x_min = Math.min(x_min, x);
y_min = Math.min(y_min, y);
}
int height = Math.max(0, getHeight());
int width = Math.max(0, getWidth());
// System.out.println(x_min + " < x < " + x_max);
// System.out.println(y_min + " < y < " + y_max);
// System.out.println("tiles: " + width + " " + height);
int newZoom = mapZoomMax;
int x = x_max - x_min;
int y = y_max - y_min;
while (x > width || y > height) {
// System.out.println("zoom: " + zoom + " -> " + x + " " + y);
newZoom--;
x >>= 1;
y >>= 1;
}
x = x_min + (x_max - x_min) / 2;
y = y_min + (y_max - y_min) / 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 setDisplayToFitMapRectangle() {
if (mapRectangleList == null || mapRectangleList.size() == 0)
return;
int x_min = Integer.MAX_VALUE;
int y_min = Integer.MAX_VALUE;
int x_max = Integer.MIN_VALUE;
int y_max = Integer.MIN_VALUE;
int mapZoomMax = tileController.getTileSource().getMaxZoom();
for (MapRectangle rectangle : mapRectangleList) {
x_max = Math.max(x_max, OsmMercator.LonToX(rectangle.getBottomRight().getLon(), mapZoomMax));
y_max = Math.max(y_max, OsmMercator.LatToY(rectangle.getTopLeft().getLat(), mapZoomMax));
x_min = Math.min(x_min, OsmMercator.LonToX(rectangle.getTopLeft().getLon(), mapZoomMax));
y_min = Math.min(y_min, OsmMercator.LatToY(rectangle.getBottomRight().getLat(), mapZoomMax));
}
int height = Math.max(0, getHeight());
int width = Math.max(0, getWidth());
// System.out.println(x_min + " < x < " + x_max);
// System.out.println(y_min + " < y < " + y_max);
// System.out.println("tiles: " + width + " " + height);
int newZoom = mapZoomMax;
int x = x_max - x_min;
int y = y_max - y_min;
while (x > width || y > height) {
// System.out.println("zoom: " + zoom + " -> " + x + " " + y);
newZoom--;
x >>= 1;
y >>= 1;
}
x = x_min + (x_max - x_min) / 2;
y = y_min + (y_max - y_min) / 2;
int z = 1 << (mapZoomMax - newZoom);
x /= z;
y /= z;
setDisplayPosition(x, y, newZoom);
}
/**
* Calculates the latitude/longitude coordinate of the center of the
* currently displayed map area.
*
* @return latitude / longitude
*/
public Coordinate getPosition() {
double lon = OsmMercator.XToLon(center.x, zoom);
double lat = OsmMercator.YToLat(center.y, zoom);
return new Coordinate(lat, lon);
}
/**
* 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 Coordinate 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
* @param mapPointY
* @return
*/
public Coordinate getPosition(int mapPointX, int mapPointY) {
int x = center.x + mapPointX - getWidth() / 2;
int y = center.y + mapPointY - getHeight() / 2;
double lon = OsmMercator.XToLon(x, zoom);
double lat = OsmMercator.YToLat(y, zoom);
return new Coordinate(lat, lon);
}
/**
* Calculates the position on the map of a given coordinate
*
* @param lat
* @param lon
* @param checkOutside
* @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) {
int x = OsmMercator.LonToX(lon, zoom);
int y = OsmMercator.LatToY(lat, zoom);
x -= center.x - getWidth() / 2;
y -= center.y - getHeight() / 2;
if (checkOutside) {
if (x < 0 || y < 0 || x > getWidth() || y > getHeight())
return null;
}
return new Point(x, y);
}
/**
* Calculates the position on the map of a given coordinate
*
* @param lat
* @param lon
* @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 coord
* @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
* @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(Coordinate coord, boolean checkOutside) {
if (coord != null)
return getMapPosition(coord.getLat(), coord.getLon(), checkOutside);
else
return null;
}
@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 off_x = (center.x % tilesize);
int off_y = (center.y % tilesize);
int w2 = getWidth() / 2;
int h2 = getHeight() / 2;
int posx = w2 - off_x;
int posy = h2 - off_y;
int diff_left = off_x;
int diff_right = tilesize - off_x;
int diff_top = off_y;
int diff_bottom = tilesize - off_y;
boolean start_left = diff_left < diff_right;
boolean start_top = diff_top < diff_bottom;
if (start_top) {
if (start_left) {
iMove = 2;
} else {
iMove = 3;
}
} else {
if (start_left) {
iMove = 1;
} else {
iMove = 0;
}
} // calculate the visibility borders
int x_min = -tilesize;
int y_min = -tilesize;
int x_max = getWidth();
int y_max = getHeight();
// 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 (x_min <= posx && posx <= x_max && y_min <= posy && posy <= y_max) {
// tile is visible
Tile tile = tileController.getTile(tilex, tiley, zoom);
if (tile != null) {
painted = true;
tile.paint(g, posx, posy);
if (tileGridVisible) {
g.drawRect(posx, posy, tilesize, tilesize);
}
}
}
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;
g.drawRect(w2 - center.x, h2 - center.y, mapSize, mapSize);
// g.drawString("Tiles in cache: " + tileCache.getTileCount(), 50, 20);
if (mapRectanglesVisible && mapRectangleList != null) {
for (MapRectangle rectangle : mapRectangleList) {
Coordinate topLeft = rectangle.getTopLeft();
Coordinate bottomRight = rectangle.getBottomRight();
if (topLeft != null && bottomRight != null) {
Point pTopLeft = getMapPosition(topLeft.getLat(), topLeft.getLon(), false);
Point pBottomRight = getMapPosition(bottomRight.getLat(), bottomRight.getLon(), false);
if (pTopLeft != null && pBottomRight != null) {
rectangle.paint(g, pTopLeft, pBottomRight);
}
}
}
}
if (mapMarkersVisible && mapMarkerList != null) {
for (MapMarker marker : mapMarkerList) {
paintMarker(g, marker);
}
}
paintAttribution(g);
}
/**
* Paint a single marker.
*/
protected void paintMarker(Graphics g, MapMarker marker) {
Point p = getMapPosition(marker.getLat(), marker.getLon());
if (p != null) {
marker.paint(g, p);
}
}
/**
* Moves the visible map pane.
*
* @param x
* horizontal movement in pixel.
* @param y
* vertical movement in pixel
*/
public void moveMap(int x, int y) {
center.x += x;
center.y += y;
repaint();
}
/**
* @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
*/
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
*/
public void zoomOut(Point mapPoint) {
setZoom(zoom - 1, mapPoint);
}
public void setZoom(int zoom, Point mapPoint) {
if (zoom > tileController.getTileSource().getMaxZoom() || zoom < tileController.getTileSource().getMinZoom()
|| zoom == this.zoom)
return;
Coordinate zoomPos = getPosition(mapPoint);
tileController.cancelOutstandingJobs(); // Clearing outstanding load
// requests
setDisplayPositionByLatLon(mapPoint, zoomPos.getLat(), zoomPos.getLon(), zoom);
}
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());
}
public boolean isTileGridVisible() {
return tileGridVisible;
}
public void setTileGridVisible(boolean tileGridVisible) {
this.tileGridVisible = tileGridVisible;
repaint();
}
public boolean getMapMarkersVisible() {
return mapMarkersVisible;
}
/**
* Enables or disables painting of the {@link MapMarker}
*
* @param mapMarkersVisible
* @see #addMapMarker(MapMarker)
* @see #getMapMarkerList()
*/
public void setMapMarkerVisible(boolean mapMarkersVisible) {
this.mapMarkersVisible = mapMarkersVisible;
repaint();
}
public void setMapMarkerList(List<MapMarker> mapMarkerList) {
this.mapMarkerList = mapMarkerList;
repaint();
}
public List<MapMarker> getMapMarkerList() {
return mapMarkerList;
}
public void setMapRectangleList(List<MapRectangle> mapRectangleList) {
this.mapRectangleList = mapRectangleList;
repaint();
}
public List<MapRectangle> getMapRectangleList() {
return mapRectangleList;
}
public void addMapMarker(MapMarker marker) {
mapMarkerList.add(marker);
repaint();
}
public void removeMapMarker(MapMarker marker) {
mapMarkerList.remove(marker);
repaint();
}
public void addMapRectangle(MapRectangle rectangle) {
mapRectangleList.add(rectangle);
repaint();
}
public void removeMapRectangle(MapRectangle rectangle) {
mapRectangleList.remove(rectangle);
repaint();
}
public void setZoomContolsVisible(boolean visible) {
zoomSlider.setVisible(visible);
zoomInButton.setVisible(visible);
zoomOutButton.setVisible(visible);
}
public boolean getZoomContolsVisible() {
return zoomSlider.isVisible();
}
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("Minumim zoom level too low");
this.tileSource = tileSource;
tileController.setTileSource(tileSource);
zoomSlider.setMinimum(tileSource.getMinZoom());
zoomSlider.setMaximum(tileSource.getMaxZoom());
tileController.cancelOutstandingJobs();
if (zoom > tileSource.getMaxZoom()) {
setZoom(tileSource.getMaxZoom());
}
boolean requireAttr = tileSource.requiresAttribution();
if (requireAttr) {
attrImage = tileSource.getAttributionImage();
attrTermsUrl = tileSource.getTermsOfUseURL();
} else {
attrImage = null;
attrTermsUrl = null;
}
repaint();
}
public void tileLoadingFinished(Tile tile, boolean success) {
repaint();
}
public boolean isMapRectanglesVisible() {
return mapRectanglesVisible;
}
/**
* Enables or disables painting of the {@link MapRectangle}
*
* @param mapMarkersVisible
* @see #addMapRectangle(MapRectangle)
* @see #getMapRectangleList()
*/
public void setMapRectanglesVisible(boolean mapRectanglesVisible) {
this.mapRectanglesVisible = mapRectanglesVisible;
repaint();
}
/*
* (non-Javadoc)
*
* @see
* org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener#getTileCache
* ()
*/
public TileCache getTileCache() {
return tileController.getTileCache();
}
public void setTileLoader(TileLoader loader) {
tileController.setTileLoader(loader);
}
private void paintAttribution(Graphics g) {
if (!tileSource.requiresAttribution())
return;
// Draw attribution
Font font = g.getFont();
g.setFont(ATTR_LINK_FONT);
Rectangle2D termsStringBounds = g.getFontMetrics().getStringBounds("Background Terms of Use", g);
int textHeight = (int) termsStringBounds.getHeight() - 5;
int termsTextY = getHeight() - textHeight;
if (attrTermsUrl != null) {
int x = 2;
int y = getHeight() - textHeight;
g.setColor(Color.black);
g.drawString("Background Terms of Use", x + 1, y + 1);
g.setColor(Color.white);
g.drawString("Background Terms of Use", x, y);
}
// Draw attribution logo
if (attrImage != null) {
int x = 2;
int height = attrImage.getHeight(null);
int y = termsTextY - height - textHeight - 5;
g.drawImage(attrImage, x, y, null);
}
g.setFont(ATTR_FONT);
Coordinate topLeft = getPosition(0, 0);
Coordinate bottomRight = getPosition(getWidth(), getHeight());
String attributionText = tileSource.getAttributionText(zoom, topLeft, bottomRight);
if (attributionText != null) {
Rectangle2D stringBounds = g.getFontMetrics().getStringBounds(attributionText, g);
int x = getWidth() - (int) stringBounds.getWidth();
int y = getHeight() - textHeight;
g.setColor(Color.black);
g.drawString(attributionText, x + 1, y + 1);
g.setColor(Color.white);
g.drawString(attributionText, x, y);
}
g.setFont(font);
}
}

View File

@ -0,0 +1,133 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* A generic class that processes a list of {@link Runnable} one-by-one using
* one or more {@link Thread}-instances. The number of instances varies between
* 1 and {@link #WORKER_THREAD_MAX_COUNT} (default: 8). If an instance is idle
* more than {@link #WORKER_THREAD_TIMEOUT} seconds (default: 30), the instance
* ends itself.
*
* @author Jan Peter Stotz
*/
public class JobDispatcher {
private static final JobDispatcher instance = new JobDispatcher();
/**
* @return the singelton instance of the {@link JobDispatcher}
*/
public static JobDispatcher getInstance() {
return instance;
}
private JobDispatcher() {
addWorkerThread().firstThread = true;
}
protected BlockingQueue<Runnable> jobQueue = new LinkedBlockingQueue<Runnable>();
public static int WORKER_THREAD_MAX_COUNT = 8;
/**
* Specifies the time span in seconds that a worker thread waits for new
* jobs to perform. If the time span has elapsed the worker thread
* terminates itself. Only the first worker thread works differently, it
* ignores the timeout and will never terminate itself.
*/
public static int WORKER_THREAD_TIMEOUT = 30;
/**
* Total number of worker threads currently idle or active
*/
protected int workerThreadCount = 0;
/**
* Number of worker threads currently idle
*/
protected int workerThreadIdleCount = 0;
/**
* Just an id for identifying an worker thread instance
*/
protected int workerThreadId = 0;
/**
* Removes all jobs from the queue that are currently not being processed.
*/
public void cancelOutstandingJobs() {
jobQueue.clear();
}
public void addJob(Runnable job) {
try {
jobQueue.put(job);
if (workerThreadIdleCount == 0 && workerThreadCount < WORKER_THREAD_MAX_COUNT)
addWorkerThread();
} catch (InterruptedException e) {
}
}
protected JobThread addWorkerThread() {
JobThread jobThread = new JobThread(++workerThreadId);
synchronized (this) {
workerThreadCount++;
}
jobThread.start();
return jobThread;
}
public class JobThread extends Thread {
Runnable job;
boolean firstThread = false;
public JobThread(int threadId) {
super("OSMJobThread " + threadId);
setDaemon(true);
job = null;
}
@Override
public void run() {
executeJobs();
synchronized (instance) {
workerThreadCount--;
}
}
protected void executeJobs() {
while (!isInterrupted()) {
try {
synchronized (instance) {
workerThreadIdleCount++;
}
if (firstThread)
job = jobQueue.take();
else
job = jobQueue.poll(WORKER_THREAD_TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException e1) {
return;
} finally {
synchronized (instance) {
workerThreadIdleCount--;
}
}
if (job == null)
return;
try {
job.run();
job = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,60 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
/**
* A simple implementation of the {@link MapMarker} interface. Each map marker
* is painted as a circle with a black border line and filled with a specified
* color.
*
* @author Jan Peter Stotz
*
*/
public class MapMarkerDot implements MapMarker {
private double lat;
private double lon;
private Color color;
private int size;
public MapMarkerDot(double lat, double lon) {
this(Color.YELLOW, 10, lat, lon);
}
public MapMarkerDot(Color color, int size, double lat, double lon) {
super();
this.color = color;
this.lat = lat;
this.lon = lon;
this.size = size;
}
public double getLat() {
return lat;
}
public double getLon() {
return lon;
}
public void paint(Graphics g, Point position) {
g.setColor(color);
g.fillOval(position.x - size/2, position.y - size/2, size, size);
g.setColor(Color.BLACK);
g.drawOval(position.x - size/2, position.y - size/2, size, size);
}
@Override
public String toString() {
return "MapMarker at " + lat + " " + lon;
}
}

View File

@ -0,0 +1,66 @@
package org.openstreetmap.gui.jmapviewer;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
/**
* A simple implementation of the {@link MapMarker} interface. Each map marker
* is painted as a circle with a black border line and filled with a specified
* color.
*
* @author Jan Peter Stotz
*
*/
public class MapMarkerImage implements MapMarker {
private double lat;
private double lon;
private BufferedImage image;
private int size;
public MapMarkerImage(double lat, double lon) {
this("region-auvergne-rhone-alpes_logo.png", 10, lat, lon);
}
public MapMarkerImage(String img, int size, double lat, double lon) {
super();
this.lat = lat;
this.lon = lon;
this.size = size;
try {
image = ImageIO.read(new File(img));
} catch (IOException e) {
e.printStackTrace();
}
}
public double getLat() {
return lat;
}
public double getLon() {
return lon;
}
public void paint(Graphics g, Point position) {
g.drawImage(image, position.x - size/2, position.y - size/2, size, size, null);
}
@Override
public String toString() {
return "MapMarker at " + lat + " " + lon;
}
}

View File

@ -0,0 +1,62 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
/**
* A simple implementation of the {@link MapMarker} interface. Each map marker
* is painted as a circle with a black border line and filled with a specified
* color.
*
* @author Jan Peter Stotz
*
*/
public class MapMarkerLabel implements MapMarker {
private double lat;
private double lon;
private Color color;
private String label;
private int sizeFont;
public MapMarkerLabel(double lat, double lon) {
this(Color.YELLOW, "Label", 16, lat, lon);
}
public MapMarkerLabel(Color color, String label, int sizeFont, double lat, double lon) {
super();
this.color = color;
this.lat = lat;
this.lon = lon;
this.label = label;
this.sizeFont = sizeFont;
}
public double getLat() {
return lat;
}
public double getLon() {
return lon;
}
public void paint(Graphics g, Point position) {
g.setColor(color);
g.setFont(new Font("default", Font.BOLD, sizeFont));
g.drawString(label, position.x , position.y );
}
@Override
public String toString() {
return "MapMarker at " + lat + " " + lon;
}
}

View File

@ -0,0 +1,224 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.util.Hashtable;
import java.util.logging.Logger;
import org.openstreetmap.gui.jmapviewer.interfaces.TileCache;
import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
/**
* {@link TileCache} implementation that stores all {@link Tile} objects in
* memory up to a certain limit ({@link #getCacheSize()}). If the limit is
* exceeded the least recently used {@link Tile} objects will be deleted.
*
* @author Jan Peter Stotz
*/
public class MemoryTileCache implements TileCache {
protected static final Logger log = Logger.getLogger(MemoryTileCache.class.getName());
/**
* Default cache size
*/
protected int cacheSize = 200;
protected Hashtable<String, CacheEntry> hashtable;
/**
* List of all tiles in their last recently used order
*/
protected CacheLinkedListElement lruTiles;
public MemoryTileCache() {
hashtable = new Hashtable<String, CacheEntry>(cacheSize);
lruTiles = new CacheLinkedListElement();
}
public void addTile(Tile tile) {
CacheEntry entry = createCacheEntry(tile);
hashtable.put(tile.getKey(), entry);
lruTiles.addFirst(entry);
if (hashtable.size() > cacheSize)
removeOldEntries();
}
public Tile getTile(TileSource source, int x, int y, int z) {
CacheEntry entry = hashtable.get(Tile.getTileKey(source, x, y, z));
if (entry == null)
return null;
// We don't care about placeholder tiles and hourglass image tiles, the
// important tiles are the loaded ones
if (entry.tile.isLoaded())
lruTiles.moveElementToFirstPos(entry);
return entry.tile;
}
/**
* Removes the least recently used tiles
*/
protected void removeOldEntries() {
synchronized (lruTiles) {
try {
while (lruTiles.getElementCount() > cacheSize) {
removeEntry(lruTiles.getLastElement());
}
} catch (Exception e) {
log.warning(e.getMessage());
}
}
}
protected void removeEntry(CacheEntry entry) {
hashtable.remove(entry.tile.getKey());
lruTiles.removeEntry(entry);
}
protected CacheEntry createCacheEntry(Tile tile) {
return new CacheEntry(tile);
}
/**
* Clears the cache deleting all tiles from memory
*/
public void clear() {
synchronized (lruTiles) {
hashtable.clear();
lruTiles.clear();
}
}
public int getTileCount() {
return hashtable.size();
}
public int getCacheSize() {
return cacheSize;
}
/**
* Changes the maximum number of {@link Tile} objects that this cache holds.
*
* @param cacheSize
* new maximum number of tiles
*/
public void setCacheSize(int cacheSize) {
this.cacheSize = cacheSize;
if (hashtable.size() > cacheSize)
removeOldEntries();
}
/**
* Linked list element holding the {@link Tile} and links to the
* {@link #next} and {@link #prev} item in the list.
*/
protected static class CacheEntry {
Tile tile;
CacheEntry next;
CacheEntry prev;
protected CacheEntry(Tile tile) {
this.tile = tile;
}
public Tile getTile() {
return tile;
}
public CacheEntry getNext() {
return next;
}
public CacheEntry getPrev() {
return prev;
}
}
/**
* Special implementation of a double linked list for {@link CacheEntry}
* elements. It supports element removal in constant time - in difference to
* the Java implementation which needs O(n).
*
* @author Jan Peter Stotz
*/
protected static class CacheLinkedListElement {
protected CacheEntry firstElement = null;
protected CacheEntry lastElement;
protected int elementCount;
public CacheLinkedListElement() {
clear();
}
public synchronized void clear() {
elementCount = 0;
firstElement = null;
lastElement = null;
}
/**
* Add the element to the head of the list.
*
* @param new element to be added
*/
public synchronized void addFirst(CacheEntry element) {
if (elementCount == 0) {
firstElement = element;
lastElement = element;
element.prev = null;
element.next = null;
} else {
element.next = firstElement;
firstElement.prev = element;
element.prev = null;
firstElement = element;
}
elementCount++;
}
/**
* Removes the specified elemntent form the list.
*
* @param element
* to be removed
*/
public synchronized void removeEntry(CacheEntry element) {
if (element.next != null) {
element.next.prev = element.prev;
}
if (element.prev != null) {
element.prev.next = element.next;
}
if (element == firstElement)
firstElement = element.next;
if (element == lastElement)
lastElement = element.prev;
element.next = null;
element.prev = null;
elementCount--;
}
public synchronized void moveElementToFirstPos(CacheEntry entry) {
if (firstElement == entry)
return;
removeEntry(entry);
addFirst(entry);
}
public int getElementCount() {
return elementCount;
}
public CacheEntry getLastElement() {
return lastElement;
}
public CacheEntry getFirstElement() {
return firstElement;
}
}
}

View File

@ -0,0 +1,457 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
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.interfaces.TileSource.TileUpdate;
/**
* A {@link TileLoader} implementation that loads tiles from OSM via HTTP and
* saves all loaded files in a directory located in the the temporary directory.
* If a tile is present in this file cache it will not be loaded from OSM again.
*
* @author Jan Peter Stotz
* @author Stefan Zeller
*/
public class OsmFileCacheTileLoader extends OsmTileLoader {
private static final Logger log = Logger.getLogger(OsmFileCacheTileLoader.class.getName());
private static final String ETAG_FILE_EXT = ".etag";
private static final String TAGS_FILE_EXT = ".tags";
private static final Charset TAGS_CHARSET = Charset.forName("UTF-8");
public static final long FILE_AGE_ONE_DAY = 1000 * 60 * 60 * 24;
public static final long FILE_AGE_ONE_WEEK = FILE_AGE_ONE_DAY * 7;
protected String cacheDirBase;
protected long maxCacheFileAge = FILE_AGE_ONE_WEEK;
protected long recheckAfter = FILE_AGE_ONE_DAY;
public static File getDefaultCacheDir() throws SecurityException {
String tempDir = null;
String userName = System.getProperty("user.name");
try {
tempDir = System.getProperty("java.io.tmpdir");
} catch (SecurityException e) {
log.log(Level.WARNING,
"Failed to access system property ''java.io.tmpdir'' for security reasons. Exception was: "
+ e.toString());
throw e; // rethrow
}
try {
if (tempDir == null)
throw new IOException("No temp directory set");
String subDirName = "JMapViewerTiles";
// On Linux/Unix systems we do not have a per user tmp directory.
// Therefore we add the user name for getting a unique dir name.
if (userName != null && userName.length() > 0) {
subDirName += "_" + userName;
}
File cacheDir = new File(tempDir, subDirName);
return cacheDir;
} catch (Exception e) {
}
return null;
}
/**
* Create a OSMFileCacheTileLoader with given cache directory.
* If cacheDir is not set or invalid, IOException will be thrown.
* @param map
* @param cacheDir
*/
public OsmFileCacheTileLoader(TileLoaderListener map, File cacheDir) throws IOException {
super(map);
if (cacheDir == null || (!cacheDir.exists() && !cacheDir.mkdirs()))
throw new IOException("Cannot access cache directory");
log.finest("Tile cache directory: " + cacheDir);
cacheDirBase = cacheDir.getAbsolutePath();
}
/**
* Create a OSMFileCacheTileLoader with system property temp dir.
* If not set an IOException will be thrown.
* @param map
*/
public OsmFileCacheTileLoader(TileLoaderListener map) throws SecurityException, IOException {
this(map, getDefaultCacheDir());
}
@Override
public Runnable createTileLoaderJob(final TileSource source, final int tilex, final int tiley, final int zoom) {
return new FileLoadJob(source, tilex, tiley, zoom);
}
protected class FileLoadJob implements Runnable {
InputStream input = null;
int tilex, tiley, zoom;
Tile tile;
TileSource source;
File tileCacheDir;
File tileFile = null;
long fileAge = 0;
boolean fileTilePainted = false;
public FileLoadJob(TileSource source, int tilex, int tiley, int zoom) {
this.source = source;
this.tilex = tilex;
this.tiley = tiley;
this.zoom = zoom;
}
public void run() {
TileCache cache = listener.getTileCache();
synchronized (cache) {
tile = cache.getTile(source, tilex, tiley, zoom);
if (tile == null || tile.isLoaded() || tile.loading)
return;
tile.loading = true;
}
tileCacheDir = new File(cacheDirBase, source.getName().replaceAll("[\\\\/:*?\"<>|]", "_"));
if (!tileCacheDir.exists()) {
tileCacheDir.mkdirs();
}
if (loadTileFromFile())
return;
if (fileTilePainted) {
Runnable job = new Runnable() {
public void run() {
loadOrUpdateTile();
}
};
JobDispatcher.getInstance().addJob(job);
} else {
loadOrUpdateTile();
}
}
protected void loadOrUpdateTile() {
try {
// log.finest("Loading tile from OSM: " + tile);
URLConnection urlConn = loadTileFromOsm(tile);
if (tileFile != null) {
switch (source.getTileUpdate()) {
case IfModifiedSince:
urlConn.setIfModifiedSince(fileAge);
break;
case LastModified:
if (!isOsmTileNewer(fileAge)) {
log.finest("LastModified test: local version is up to date: " + tile);
tile.setLoaded(true);
tileFile.setLastModified(System.currentTimeMillis() - maxCacheFileAge + recheckAfter);
return;
}
break;
}
}
if (source.getTileUpdate() == TileUpdate.ETag || source.getTileUpdate() == TileUpdate.IfNoneMatch) {
String fileETag = tile.getValue("etag");
if (fileETag != null) {
switch (source.getTileUpdate()) {
case IfNoneMatch:
urlConn.addRequestProperty("If-None-Match", fileETag);
break;
case ETag:
if (hasOsmTileETag(fileETag)) {
tile.setLoaded(true);
tileFile.setLastModified(System.currentTimeMillis() - maxCacheFileAge
+ recheckAfter);
return;
}
}
}
tile.putValue("etag", urlConn.getHeaderField("ETag"));
}
if (urlConn instanceof HttpURLConnection && ((HttpURLConnection)urlConn).getResponseCode() == 304) {
// If we are isModifiedSince or If-None-Match has been set
// and the server answers with a HTTP 304 = "Not Modified"
log.finest("ETag test: local version is up to date: " + tile);
tile.setLoaded(true);
tileFile.setLastModified(System.currentTimeMillis() - maxCacheFileAge + recheckAfter);
return;
}
loadTileMetadata(tile, urlConn);
saveTagsToFile();
if ("no-tile".equals(tile.getValue("tile-info")))
{
tile.setError("No tile at this zoom level");
listener.tileLoadingFinished(tile, true);
} else {
byte[] buffer = loadTileInBuffer(urlConn);
if (buffer != null) {
tile.loadImage(new ByteArrayInputStream(buffer));
tile.setLoaded(true);
listener.tileLoadingFinished(tile, true);
saveTileToFile(buffer);
}
}
} catch (Exception e) {
tile.setError(e.getMessage());
listener.tileLoadingFinished(tile, false);
if (input == null) {
System.err.println("failed loading " + zoom + "/" + tilex + "/" + tiley + " " + e.getMessage());
}
} finally {
tile.loading = false;
tile.setLoaded(true);
}
}
protected boolean loadTileFromFile() {
FileInputStream fin = null;
try {
tileFile = getTileFile();
loadTagsFromFile();
if ("no-tile".equals(tile.getValue("tile-info")))
{
tile.setError("No tile at this zoom level");
if (tileFile.exists()) {
tileFile.delete();
}
tileFile = getTagsFile();
} else {
fin = new FileInputStream(tileFile);
if (fin.available() == 0)
throw new IOException("File empty");
tile.loadImage(fin);
fin.close();
}
fileAge = tileFile.lastModified();
boolean oldTile = System.currentTimeMillis() - fileAge > maxCacheFileAge;
// System.out.println("Loaded from file: " + tile);
if (!oldTile) {
tile.setLoaded(true);
listener.tileLoadingFinished(tile, true);
fileTilePainted = true;
return true;
}
listener.tileLoadingFinished(tile, true);
fileTilePainted = true;
} catch (Exception e) {
try {
if (fin != null) {
fin.close();
tileFile.delete();
}
} catch (Exception e1) {
}
tileFile = null;
fileAge = 0;
}
return false;
}
protected byte[] loadTileInBuffer(URLConnection urlConn) throws IOException {
input = urlConn.getInputStream();
ByteArrayOutputStream bout = new ByteArrayOutputStream(input.available());
byte[] buffer = new byte[2048];
boolean finished = false;
do {
int read = input.read(buffer);
if (read >= 0) {
bout.write(buffer, 0, read);
} else {
finished = true;
}
} while (!finished);
if (bout.size() == 0)
return null;
return bout.toByteArray();
}
/**
* Performs a <code>HEAD</code> request for retrieving the
* <code>LastModified</code> header value.
*
* Note: This does only work with servers providing the
* <code>LastModified</code> header:
* <ul>
* <li>{@link OsmTileLoader#MAP_OSMA} - supported</li>
* <li>{@link OsmTileLoader#MAP_MAPNIK} - not supported</li>
* </ul>
*
* @param fileAge
* @return <code>true</code> if the tile on the server is newer than the
* file
* @throws IOException
*/
protected boolean isOsmTileNewer(long fileAge) throws IOException {
URL url;
url = new URL(tile.getUrl());
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
prepareHttpUrlConnection(urlConn);
urlConn.setRequestMethod("HEAD");
urlConn.setReadTimeout(30000); // 30 seconds read timeout
// System.out.println("Tile age: " + new
// Date(urlConn.getLastModified()) + " / "
// + new Date(fileAge));
long lastModified = urlConn.getLastModified();
if (lastModified == 0)
return true; // no LastModified time returned
return (lastModified > fileAge);
}
protected boolean hasOsmTileETag(String eTag) throws IOException {
URL url;
url = new URL(tile.getUrl());
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
prepareHttpUrlConnection(urlConn);
urlConn.setRequestMethod("HEAD");
urlConn.setReadTimeout(30000); // 30 seconds read timeout
// System.out.println("Tile age: " + new
// Date(urlConn.getLastModified()) + " / "
// + new Date(fileAge));
String osmETag = urlConn.getHeaderField("ETag");
if (osmETag == null)
return true;
return (osmETag.equals(eTag));
}
protected File getTileFile() {
return new File(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile() + "_" + tile.getYtile() + "."
+ source.getTileType());
}
protected File getTagsFile() {
return new File(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile() + "_" + tile.getYtile()
+ TAGS_FILE_EXT);
}
protected void saveTileToFile(byte[] rawData) {
try {
FileOutputStream f = new FileOutputStream(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile()
+ "_" + tile.getYtile() + "." + source.getTileType());
f.write(rawData);
f.close();
// System.out.println("Saved tile to file: " + tile);
} catch (Exception e) {
System.err.println("Failed to save tile content: " + e.getLocalizedMessage());
}
}
protected void saveTagsToFile() {
File tagsFile = getTagsFile();
if (tile.getMetadata() == null) {
tagsFile.delete();
return;
}
try {
final PrintWriter f = new PrintWriter(new OutputStreamWriter(new FileOutputStream(tagsFile),
TAGS_CHARSET));
for (Entry<String, String> entry : tile.getMetadata().entrySet()) {
f.println(entry.getKey() + "=" + entry.getValue());
}
f.close();
} catch (Exception e) {
System.err.println("Failed to save tile tags: " + e.getLocalizedMessage());
}
}
/** Load backward-compatiblity .etag file and if it exists move it to new .tags file*/
private void loadOldETagfromFile() {
File etagFile = new File(tileCacheDir, tile.getZoom() + "_"
+ tile.getXtile() + "_" + tile.getYtile() + ETAG_FILE_EXT);
if (!etagFile.exists()) return;
try {
FileInputStream f = new FileInputStream(etagFile);
byte[] buf = new byte[f.available()];
f.read(buf);
f.close();
String etag = new String(buf, TAGS_CHARSET.name());
tile.putValue("etag", etag);
if (etagFile.delete()) {
saveTagsToFile();
}
} catch (IOException e) {
System.err.println("Failed to load compatiblity etag: " + e.getLocalizedMessage());
}
}
protected void loadTagsFromFile() {
loadOldETagfromFile();
File tagsFile = getTagsFile();
try {
final BufferedReader f = new BufferedReader(new InputStreamReader(new FileInputStream(tagsFile),
TAGS_CHARSET));
for (String line = f.readLine(); line != null; line = f.readLine()) {
final int i = line.indexOf('=');
if (i == -1 || i == 0) {
System.err.println("Malformed tile tag in file '" + tagsFile.getName() + "':" + line);
continue;
}
tile.putValue(line.substring(0,i),line.substring(i+1));
}
f.close();
} catch (FileNotFoundException e) {
} catch (Exception e) {
System.err.println("Failed to load tile tags: " + e.getLocalizedMessage());
}
}
}
public long getMaxFileAge() {
return maxCacheFileAge;
}
/**
* Sets the maximum age of the local cached tile in the file system. If a
* local tile is older than the specified file age
* {@link OsmFileCacheTileLoader} will connect to the tile server and check
* if a newer tile is available using the mechanism specified for the
* selected tile source/server.
*
* @param maxFileAge
* maximum age in milliseconds
* @see #FILE_AGE_ONE_DAY
* @see #FILE_AGE_ONE_WEEK
* @see TileSource#getTileUpdate()
*/
public void setCacheMaxFileAge(long maxFileAge) {
this.maxCacheFileAge = maxFileAge;
}
public String getCacheDirBase() {
return cacheDirBase;
}
public void setTileCacheDir(String tileCacheDir) {
File dir = new File(tileCacheDir);
dir.mkdirs();
this.cacheDirBase = dir.getAbsolutePath();
}
}

View File

@ -0,0 +1,136 @@
package org.openstreetmap.gui.jmapviewer;
// License: GPL. Copyright 2007 by Tim Haussmann
/**
* This class implements the Mercator Projection as it is used by Openstreetmap
* (and google). It provides methods to translate coordinates from 'map space'
* into latitude and longitude (on the WGS84 ellipsoid) and vice versa. Map
* space is measured in pixels. The origin of the map space is the top left
* corner. The map space origin (0,0) has latitude ~85 and longitude -180
*
* @author Tim Haussmann
*
*/
public class OsmMercator {
private static int TILE_SIZE = 256;
public static final double MAX_LAT = 85.05112877980659;
public static final double MIN_LAT = -85.05112877980659;
public static double radius(int aZoomlevel) {
return (TILE_SIZE * (1 << aZoomlevel)) / (2.0 * Math.PI);
}
/**
* Returns the absolut number of pixels in y or x, defined as: 2^Zoomlevel *
* TILE_WIDTH where TILE_WIDTH is the width of a tile in pixels
*
* @param aZoomlevel
* @return
*/
public static int getMaxPixels(int aZoomlevel) {
return TILE_SIZE * (1 << aZoomlevel);
}
public static int falseEasting(int aZoomlevel) {
return getMaxPixels(aZoomlevel) / 2;
}
public static int falseNorthing(int aZoomlevel) {
return (-1 * getMaxPixels(aZoomlevel) / 2);
}
/**
* Transform longitude to pixelspace
*
* <p>
* Mathematical optimization<br>
* <code>
* x = radius(aZoomlevel) * toRadians(aLongitude) + falseEasting(aZoomLevel)<br>
* x = getMaxPixels(aZoomlevel) / (2 * PI) * (aLongitude * PI) / 180 + getMaxPixels(aZoomlevel) / 2<br>
* x = getMaxPixels(aZoomlevel) * aLongitude / 360 + 180 * getMaxPixels(aZoomlevel) / 360<br>
* x = getMaxPixels(aZoomlevel) * (aLongitude + 180) / 360<br>
* </code>
* </p>
*
* @param aLongitude
* [-180..180]
* @return [0..2^Zoomlevel*TILE_SIZE[
* @author Jan Peter Stotz
*/
public static int LonToX(double aLongitude, int aZoomlevel) {
int mp = getMaxPixels(aZoomlevel);
int x = (int) ((mp * (aLongitude + 180l)) / 360l);
x = Math.min(x, mp - 1);
return x;
}
/**
* Transforms latitude to pixelspace
* <p>
* Mathematical optimization<br>
* <code>
* log(u) := log((1.0 + sin(toRadians(aLat))) / (1.0 - sin(toRadians(aLat))<br>
*
* y = -1 * (radius(aZoomlevel) / 2 * log(u)))) - falseNorthing(aZoomlevel))<br>
* y = -1 * (getMaxPixel(aZoomlevel) / 2 * PI / 2 * log(u)) - -1 * getMaxPixel(aZoomLevel) / 2<br>
* y = getMaxPixel(aZoomlevel) / (-4 * PI) * log(u)) + getMaxPixel(aZoomLevel) / 2<br>
* y = getMaxPixel(aZoomlevel) * ((log(u) / (-4 * PI)) + 1/2)<br>
* </code>
* </p>
* @param aLat
* [-90...90]
* @return [0..2^Zoomlevel*TILE_SIZE[
* @author Jan Peter Stotz
*/
public static int LatToY(double aLat, int aZoomlevel) {
if (aLat < MIN_LAT)
aLat = MIN_LAT;
else if (aLat > MAX_LAT)
aLat = MAX_LAT;
double sinLat = Math.sin(Math.toRadians(aLat));
double log = Math.log((1.0 + sinLat) / (1.0 - sinLat));
int mp = getMaxPixels(aZoomlevel);
int y = (int) (mp * (0.5 - (log / (4.0 * Math.PI))));
y = Math.min(y, mp - 1);
return y;
}
/**
* Transforms pixel coordinate X to longitude
*
* <p>
* Mathematical optimization<br>
* <code>
* lon = toDegree((aX - falseEasting(aZoomlevel)) / radius(aZoomlevel))<br>
* lon = 180 / PI * ((aX - getMaxPixels(aZoomlevel) / 2) / getMaxPixels(aZoomlevel) / (2 * PI)<br>
* lon = 180 * ((aX - getMaxPixels(aZoomlevel) / 2) / getMaxPixels(aZoomlevel))<br>
* lon = 360 / getMaxPixels(aZoomlevel) * (aX - getMaxPixels(aZoomlevel) / 2)<br>
* lon = 360 * aX / getMaxPixels(aZoomlevel) - 180<br>
* </code>
* </p>
* @param aX
* [0..2^Zoomlevel*TILE_WIDTH[
* @return ]-180..180[
* @author Jan Peter Stotz
*/
public static double XToLon(int aX, int aZoomlevel) {
return ((360d * aX) / getMaxPixels(aZoomlevel)) - 180.0;
}
/**
* Transforms pixel coordinate Y to latitude
*
* @param aY
* [0..2^Zoomlevel*TILE_WIDTH[
* @return [MIN_LAT..MAX_LAT] is about [-85..85]
*/
public static double YToLat(int aY, int aZoomlevel) {
aY += falseNorthing(aZoomlevel);
double latitude = (Math.PI / 2) - (2 * Math.atan(Math.exp(-1.0 * aY / radius(aZoomlevel))));
return -1 * Math.toDegrees(latitude);
}
}

View File

@ -0,0 +1,113 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
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;
/**
* A {@link TileLoader} implementation that loads tiles from OSM.
*
* @author Jan Peter Stotz
*/
public class OsmTileLoader implements TileLoader {
/**
* Holds the used user agent used for HTTP requests. If this field is
* <code>null</code>, the default Java user agent is used.
*/
public static String USER_AGENT = null;
public static String ACCEPT = "text/html, image/png, image/jpeg, image/gif, */*";
protected TileLoaderListener listener;
public OsmTileLoader(TileLoaderListener listener) {
this.listener = listener;
}
public Runnable createTileLoaderJob(final TileSource source, final int tilex, final int tiley, final int zoom) {
return new Runnable() {
InputStream input = null;
public void run() {
TileCache cache = listener.getTileCache();
Tile tile;
synchronized (cache) {
tile = cache.getTile(source, tilex, tiley, zoom);
if (tile == null || tile.isLoaded() || tile.loading)
return;
tile.loading = true;
}
try {
// Thread.sleep(500);
URLConnection conn = loadTileFromOsm(tile);
loadTileMetadata(tile, conn);
if ("no-tile".equals(tile.getValue("tile-info"))) {
tile.setError("No tile at this zoom level");
} else {
input = conn.getInputStream();
tile.loadImage(input);
input.close();
input = null;
}
tile.setLoaded(true);
listener.tileLoadingFinished(tile, true);
} catch (Exception e) {
tile.setError(e.getMessage());
listener.tileLoadingFinished(tile, false);
if (input == null) {
System.err.println("failed loading " + zoom + "/" + tilex + "/" + tiley + " " + e.getMessage());
}
} finally {
tile.loading = false;
tile.setLoaded(true);
}
}
};
}
protected URLConnection loadTileFromOsm(Tile tile) throws IOException {
URL url;
url = new URL(tile.getUrl());
URLConnection urlConn = url.openConnection();
if (urlConn instanceof HttpURLConnection) {
prepareHttpUrlConnection((HttpURLConnection)urlConn);
}
urlConn.setReadTimeout(30000); // 30 seconds read timeout
return urlConn;
}
protected void loadTileMetadata(Tile tile, URLConnection urlConn) {
String str = urlConn.getHeaderField("X-VE-TILEMETA-CaptureDatesRange");
if (str != null) {
tile.putValue("capture-date", str);
}
str = urlConn.getHeaderField("X-VE-Tile-Info");
if (str != null) {
tile.putValue("tile-info", str);
}
}
protected void prepareHttpUrlConnection(HttpURLConnection urlConn) {
if (USER_AGENT != null) {
urlConn.setRequestProperty("User-agent", USER_AGENT);
}
urlConn.setRequestProperty("Accept", ACCEPT);
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}

View File

@ -0,0 +1,305 @@
package org.openstreetmap.gui.jmapviewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import org.openstreetmap.gui.jmapviewer.interfaces.TileCache;
import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
/**
* Holds one map tile. Additionally the code for loading the tile image and
* painting it is also included in this class.
*
* @author Jan Peter Stotz
*/
public class Tile {
/**
* Hourglass image that is displayed until a map tile has been loaded
*/
public static BufferedImage LOADING_IMAGE;
public static BufferedImage ERROR_IMAGE;
static {
try {
LOADING_IMAGE = ImageIO.read(JMapViewer.class.getResourceAsStream("images/hourglass.png"));
ERROR_IMAGE = ImageIO.read(JMapViewer.class.getResourceAsStream("images/error.png"));
} catch (Exception e1) {
LOADING_IMAGE = null;
ERROR_IMAGE = null;
}
}
protected TileSource source;
protected int xtile;
protected int ytile;
protected int zoom;
protected BufferedImage image;
protected String key;
protected boolean loaded = false;
protected boolean loading = false;
protected boolean error = false;
protected String error_message;
/** TileLoader-specific tile metadata */
protected Map<String, String> metadata;
/**
* Creates a tile with empty image.
*
* @param source
* @param xtile
* @param ytile
* @param zoom
*/
public Tile(TileSource source, int xtile, int ytile, int zoom) {
super();
this.source = source;
this.xtile = xtile;
this.ytile = ytile;
this.zoom = zoom;
this.image = LOADING_IMAGE;
this.key = getTileKey(source, xtile, ytile, zoom);
}
public Tile(TileSource source, int xtile, int ytile, int zoom, BufferedImage image) {
this(source, xtile, ytile, zoom);
this.image = image;
}
/**
* Tries to get tiles of a lower or higher zoom level (one or two level
* difference) from cache and use it as a placeholder until the tile has
* been loaded.
*/
public void loadPlaceholderFromCache(TileCache cache) {
BufferedImage tmpImage = new BufferedImage(source.getTileSize(), source.getTileSize(), BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) tmpImage.getGraphics();
// g.drawImage(image, 0, 0, null);
for (int zoomDiff = 1; zoomDiff < 5; zoomDiff++) {
// first we check if there are already the 2^x tiles
// of a higher detail level
int zoom_high = zoom + zoomDiff;
if (zoomDiff < 3 && zoom_high <= JMapViewer.MAX_ZOOM) {
int factor = 1 << zoomDiff;
int xtile_high = xtile << zoomDiff;
int ytile_high = ytile << zoomDiff;
double scale = 1.0 / factor;
g.setTransform(AffineTransform.getScaleInstance(scale, scale));
int paintedTileCount = 0;
for (int x = 0; x < factor; x++) {
for (int y = 0; y < factor; y++) {
Tile tile = cache.getTile(source, xtile_high + x, ytile_high + y, zoom_high);
if (tile != null && tile.isLoaded()) {
paintedTileCount++;
tile.paint(g, x * source.getTileSize(), y * source.getTileSize());
}
}
}
if (paintedTileCount == factor * factor) {
image = tmpImage;
return;
}
}
int zoom_low = zoom - zoomDiff;
if (zoom_low >= JMapViewer.MIN_ZOOM) {
int xtile_low = xtile >> zoomDiff;
int ytile_low = ytile >> zoomDiff;
int factor = (1 << zoomDiff);
double scale = factor;
AffineTransform at = new AffineTransform();
int translate_x = (xtile % factor) * source.getTileSize();
int translate_y = (ytile % factor) * source.getTileSize();
at.setTransform(scale, 0, 0, scale, -translate_x, -translate_y);
g.setTransform(at);
Tile tile = cache.getTile(source, xtile_low, ytile_low, zoom_low);
if (tile != null && tile.isLoaded()) {
tile.paint(g, 0, 0);
image = tmpImage;
return;
}
}
}
}
public TileSource getSource() {
return source;
}
/**
* @return tile number on the x axis of this tile
*/
public int getXtile() {
return xtile;
}
/**
* @return tile number on the y axis of this tile
*/
public int getYtile() {
return ytile;
}
/**
* @return zoom level of this tile
*/
public int getZoom() {
return zoom;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
public void loadImage(InputStream input) throws IOException {
image = ImageIO.read(input);
}
/**
* @return key that identifies a tile
*/
public String getKey() {
return key;
}
public boolean isLoaded() {
return loaded;
}
public boolean isLoading() {
return loading;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public String getUrl() throws IOException {
return source.getTileUrl(zoom, xtile, ytile);
}
/**
* Paints the tile-image on the {@link Graphics} <code>g</code> at the
* position <code>x</code>/<code>y</code>.
*
* @param g
* @param x
* x-coordinate in <code>g</code>
* @param y
* y-coordinate in <code>g</code>
*/
public void paint(Graphics g, int x, int y) {
if (image == null)
return;
g.drawImage(image, x, y, null);
}
@Override
public String toString() {
return "Tile " + key;
}
/**
* Note that the hash code does not include the {@link #source}.
* Therefore a hash based collection can only contain tiles
* of one {@link #source}.
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + xtile;
result = prime * result + ytile;
result = prime * result + zoom;
return result;
}
/**
* Compares this object with <code>obj</code> based on
* the fields {@link #xtile}, {@link #ytile} and
* {@link #zoom}.
* The {@link #source} field is ignored.
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tile other = (Tile) obj;
if (xtile != other.xtile)
return false;
if (ytile != other.ytile)
return false;
if (zoom != other.zoom)
return false;
return true;
}
public static String getTileKey(TileSource source, int xtile, int ytile, int zoom) {
return zoom + "/" + xtile + "/" + ytile + "@" + source.getName();
}
public String getStatus() {
if (this.error)
return "error";
if (this.loaded)
return "loaded";
if (this.loading)
return "loading";
return "new";
}
public boolean hasError() {
return error;
}
public String getErrorMessage() {
return error_message;
}
public void setError(String message) {
error = true;
setImage(ERROR_IMAGE);
error_message = message;
}
public void putValue(String key, String value) {
if (value == null || "".equals(value)) {
if (metadata != null) {
metadata.remove(key);
}
return;
}
if (metadata == null) {
metadata = new HashMap<String,String>();
}
metadata.put(key, value);
}
public String getValue(String key) {
if (metadata == null) return null;
return metadata.get(key);
}
public Map<String,String> getMetadata() {
return metadata;
}
}

View File

@ -0,0 +1,84 @@
package org.openstreetmap.gui.jmapviewer;
import org.openstreetmap.gui.jmapviewer.JobDispatcher.JobThread;
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;
public class TileController {
protected TileLoader tileLoader;
protected TileCache tileCache;
protected TileSource tileSource;
JobDispatcher jobDispatcher;
public TileController(TileSource source, TileCache tileCache, TileLoaderListener listener) {
tileSource = new OsmTileSource.Mapnik();
tileLoader = new OsmTileLoader(listener);
this.tileCache = tileCache;
jobDispatcher = JobDispatcher.getInstance();
}
/**
* retrieves a tile from the cache. If the tile is not present in the cache
* a load job is added to the working queue of {@link JobThread}.
*
* @param tilex
* @param tiley
* @param zoom
* @return specified tile from the cache or <code>null</code> if the tile
* was not found in the cache.
*/
public Tile getTile(int tilex, int tiley, int zoom) {
int max = (1 << zoom);
if (tilex < 0 || tilex >= max || tiley < 0 || tiley >= max)
return null;
Tile tile = tileCache.getTile(tileSource, tilex, tiley, zoom);
if (tile == null) {
tile = new Tile(tileSource, tilex, tiley, zoom);
tileCache.addTile(tile);
tile.loadPlaceholderFromCache(tileCache);
}
if (!tile.isLoaded()) {
jobDispatcher.addJob(tileLoader.createTileLoaderJob(tileSource, tilex, tiley, zoom));
}
return tile;
}
public TileCache getTileCache() {
return tileCache;
}
public void setTileCache(TileCache tileCache) {
this.tileCache = tileCache;
}
public TileLoader getTileLoader() {
return tileLoader;
}
public void setTileLoader(TileLoader tileLoader) {
this.tileLoader = tileLoader;
}
public TileSource getTileLayerSource() {
return tileSource;
}
public TileSource getTileSource() {
return tileSource;
}
public void setTileSource(TileSource tileSource) {
this.tileSource = tileSource;
}
/**
*
*/
public void cancelOutstandingJobs() {
jobDispatcher.cancelOutstandingJobs();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

View File

@ -0,0 +1,37 @@
package org.openstreetmap.gui.jmapviewer.interfaces;
//License: GPL. Copyright 2008 by Jan Peter Stotz
import java.awt.Graphics;
import java.awt.Point;
import org.openstreetmap.gui.jmapviewer.JMapViewer;
/**
* Interface to be implemented by all one dimensional elements that can be displayed on the map.
*
* @author Jan Peter Stotz
* @see JMapViewer#addMapMarker(MapMarker)
* @see JMapViewer#getMapMarkerList()
*/
public interface MapMarker {
/**
* @return Latitude of the map marker position
*/
public double getLat();
/**
* @return Longitude of the map marker position
*/
public double getLon();
/**
* Paints the map marker on the map. The <code>position</code> specifies the
* coordinates within <code>g</code>
*
* @param g
* @param position
*/
public void paint(Graphics g, Point position);
}

View File

@ -0,0 +1,39 @@
package org.openstreetmap.gui.jmapviewer.interfaces;
//License: GPL. Copyright 2009 by Stefan Zeller
import java.awt.Graphics;
import java.awt.Point;
import org.openstreetmap.gui.jmapviewer.Coordinate;
import org.openstreetmap.gui.jmapviewer.JMapViewer;
/**
* Interface to be implemented by rectangles that can be displayed on the map.
*
* @author Stefan Zeller
* @see JMapViewer#addMapRectangle(MapRectangle)
* @see JMapViewer#getMapRectangleList()
* @date 21.06.2009S
*/
public interface MapRectangle {
/**
* @return Latitude/Longitude of top left of rectangle
*/
public Coordinate getTopLeft();
/**
* @return Latitude/Longitude of bottom right of rectangle
*/
public Coordinate getBottomRight();
/**
* Paints the map rectangle on the map. The <code>topLeft</code> and
* <code>bottomRight</code> are specifying the coordinates within <code>g</code>
*
* @param g
* @param position
*/
public void paint(Graphics g, Point topLeft, Point bottomRight);
}

View File

@ -0,0 +1,45 @@
package org.openstreetmap.gui.jmapviewer.interfaces;
import org.openstreetmap.gui.jmapviewer.JMapViewer;
import org.openstreetmap.gui.jmapviewer.Tile;
//License: GPL. Copyright 2008 by Jan Peter Stotz
/**
* Implement this interface for creating your custom tile cache for
* {@link JMapViewer}.
*
* @author Jan Peter Stotz
*/
public interface TileCache {
/**
* Retrieves a tile from the cache if present, otherwise <code>null</code>
* will be returned.
*
* @param source
* @param x
* tile number on the x axis of the tile to be retrieved
* @param y
* tile number on the y axis of the tile to be retrieved
* @param z
* zoom level of the tile to be retrieved
* @return the requested tile or <code>null</code> if the tile is not
* present in the cache
*/
public Tile getTile(TileSource source, int x, int y, int z);
/**
* Adds a tile to the cache. How long after adding a tile can be retrieved
* via {@link #getTile(int, int, int)} is unspecified and depends on the
* implementation.
*
* @param tile
*/
public void addTile(Tile tile);
/**
* @return the number of tiles hold by the cache
*/
public int getTileCount();
}

View File

@ -0,0 +1,26 @@
package org.openstreetmap.gui.jmapviewer.interfaces;
//License: GPL. Copyright 2008 by Jan Peter Stotz
/**
* Interface for implementing a tile loader. Tiles are usually loaded via HTTP
* or from a file.
*
* @author Jan Peter Stotz
*/
public interface TileLoader {
/**
* A typical {@link #createTileLoaderJob(int, int, int)} implementation
* should create and return a new {@link Job} instance that performs the
* load action.
*
* @param tileLayerSource
* @param tilex
* @param tiley
* @param zoom
* @returns {@link Runnable} implementation that performs the desired load
* action.
*/
public Runnable createTileLoaderJob(TileSource tileLayerSource, int tilex, int tiley, int zoom);
}

View File

@ -0,0 +1,18 @@
package org.openstreetmap.gui.jmapviewer.interfaces;
import org.openstreetmap.gui.jmapviewer.Tile;
//License: GPL. Copyright 2008 by Jan Peter Stotz
public interface TileLoaderListener {
/**
* Will be called if a new {@link Tile} has been loaded successfully.
* Loaded can mean downloaded or loaded from file cache.
*
* @param tile
*/
public void tileLoadingFinished(Tile tile, boolean success);
public TileCache getTileCache();
}

View File

@ -0,0 +1,134 @@
package org.openstreetmap.gui.jmapviewer.interfaces;
import java.awt.Image;
import java.io.IOException;
import org.openstreetmap.gui.jmapviewer.Coordinate;
import org.openstreetmap.gui.jmapviewer.JMapViewer;
//License: GPL. Copyright 2008 by Jan Peter Stotz
/**
*
* @author Jan Peter Stotz
*/
public interface TileSource {
/**
* Specifies the different mechanisms for detecting updated tiles
* respectively only download newer tiles than those stored locally.
*
* <ul>
* <li>{@link #IfNoneMatch} Server provides ETag header entry for all tiles
* and <b>supports</b> conditional download via <code>If-None-Match</code>
* header entry.</li>
* <li>{@link #ETag} Server provides ETag header entry for all tiles but
* <b>does not support</b> conditional download via
* <code>If-None-Match</code> header entry.</li>
* <li>{@link #IfModifiedSince} Server provides Last-Modified header entry
* for all tiles and <b>supports</b> conditional download via
* <code>If-Modified-Since</code> header entry.</li>
* <li>{@link #LastModified} Server provides Last-Modified header entry for
* all tiles but <b>does not support</b> conditional download via
* <code>If-Modified-Since</code> header entry.</li>
* <li>{@link #None} The server does not support any of the listed
* mechanisms.</li>
* </ul>
*
*/
public enum TileUpdate {
IfNoneMatch, ETag, IfModifiedSince, LastModified, None
}
/**
* Specifies the maximum zoom value. The number of zoom levels is [0..
* {@link #getMaxZoom()}].
*
* @return maximum zoom value that has to be smaller or equal to
* {@link JMapViewer#MAX_ZOOM}
*/
public int getMaxZoom();
/**
* Specifies the minimum zoom value. This value is usually 0.
* Only for maps that cover a certain region up to a limited zoom level
* this method should return a value different than 0.
*
* @return minimum zoom value - usually 0
*/
public int getMinZoom();
/**
* @return The supported tile update mechanism
* @see TileUpdate
*/
public TileUpdate getTileUpdate();
/**
* A tile layer name has to be unique and has to consist only of characters
* valid for filenames.
*
* @return Name of the tile layer
*/
public String getName();
/**
* Constructs the tile url.
*
* @param zoom
* @param tilex
* @param tiley
* @return fully qualified url for downloading the specified tile image
*/
public String getTileUrl(int zoom, int tilex, int tiley) throws IOException;
/**
* Specifies the tile image type. For tiles rendered by Mapnik or
* Osmarenderer this is usually <code>"png"</code>.
*
* @return file extension of the tile image type
*/
public String getTileType();
/**
* Specifies how large each tile is.
* @return The size of a single tile in pixels.
*/
public int getTileSize();
/**
* @return True if the tile source requires attribution in text or image form.
*/
public boolean requiresAttribution();
/**
* @param zoom The optional zoom level for the view.
* @param botRight The bottom right of the bounding box for attribution.
* @param topLeft The top left of the bounding box for attribution.
* @return Attribution text for the image source.
*/
public String getAttributionText(int zoom, Coordinate topLeft, Coordinate botRight);
/**
* @return The URL for the attribution image. Null if no image should be displayed.
*/
public Image getAttributionImage();
/**
* @return The URL to open when the user clicks the attribution image.
*/
public String getAttributionLinkURL();
/**
* @return The URL to open when the user clicks the attribution "Terms of Use" text.
*/
public String getTermsOfUseURL();
public double latToTileY(double lat, int zoom);
public double lonToTileX(double lon, int zoom);
public double tileYToLat(int y, int zoom);
public double tileXToLon(int x, int zoom);
}

View File

@ -0,0 +1,14 @@
<html>
<body>
<h1>org.openstreetmap.gui.jmapviewer</h1>
<p>This package and all sub-packages are belonging to the Java
component <a href="http://wiki.openstreetmap.org/wiki/JMapViewer">JMapViewer</a>
</p>
<p>JMapViewer is designed to run as stand-alone component without
any further requirements. Therefore <b>please do not add any code that
depends on other libraries or applications</b>. Only functions and methods
provided by the runtime library of Java 5 should be used.</p>
<h2>In particular, this concerns the developer of JOSM!</h2>
<p>2009-08-10 Jan Peter Stotz jpstotz@gmx.de (maintainer of JMapViewer)</p>
</body>
</html>

View File

@ -0,0 +1,114 @@
/**
*
*/
package org.openstreetmap.gui.jmapviewer.tilesources;
import java.awt.Image;
import java.io.IOException;
import javax.swing.ImageIcon;
import org.openstreetmap.gui.jmapviewer.Coordinate;
import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
public abstract class AbstractOsmTileSource implements TileSource {
protected String name;
protected String baseUrl;
protected String attrImgUrl;
public AbstractOsmTileSource(String name, String base_url) {
this(name, base_url, null);
}
public AbstractOsmTileSource(String name, String base_url, String attr_img_url) {
this.name = name;
this.baseUrl = base_url;
attrImgUrl = attr_img_url;
}
public String getName() {
return name;
}
public int getMaxZoom() {
return 18;
}
public int getMinZoom() {
return 0;
}
public String getExtension() {
return "png";
}
/**
* @throws IOException when subclass cannot return the tile URL
*/
public String getTilePath(int zoom, int tilex, int tiley) throws IOException {
return "/" + zoom + "/" + tilex + "/" + tiley + "." + getExtension();
}
public String getBaseUrl() {
return this.baseUrl;
}
public String getTileUrl(int zoom, int tilex, int tiley) throws IOException {
return this.getBaseUrl() + getTilePath(zoom, tilex, tiley);
}
@Override
public String toString() {
return getName();
}
public String getTileType() {
return "png";
}
public int getTileSize() {
return 256;
}
public Image getAttributionImage() {
if (attrImgUrl != null)
return new ImageIcon(attrImgUrl).getImage();
else
return null;
}
public boolean requiresAttribution() {
return true;
}
public String getAttributionText(int zoom, Coordinate topLeft, Coordinate botRight) {
return "© OpenStreetMap contributors, CC-BY-SA ";
}
public String getAttributionLinkURL() {
return "http://openstreetmap.org/";
}
public String getTermsOfUseURL() {
return "http://www.openstreetmap.org/copyright";
}
public double latToTileY(double lat, int zoom) {
double l = lat / 180 * Math.PI;
double pf = Math.log(Math.tan(l) + (1 / Math.cos(l)));
return Math.pow(2.0, zoom - 1) * (Math.PI - pf) / Math.PI;
}
public double lonToTileX(double lon, int zoom) {
return Math.pow(2.0, zoom - 3) * (lon + 180.0) / 45.0;
}
public double tileYToLat(int y, int zoom) {
return Math.atan(Math.sinh(Math.PI - (Math.PI * y / Math.pow(2.0, zoom - 1)))) * 180 / Math.PI;
}
public double tileXToLon(int x, int zoom) {
return x * 45.0 / Math.pow(2.0, zoom - 3) - 180.0;
}
}

View File

@ -0,0 +1,29 @@
package org.openstreetmap.gui.jmapviewer.tilesources;
public class OfflineOsmTileSource extends AbstractOsmTileSource {
private final int minZoom;
private final int maxZoom;
public OfflineOsmTileSource(String path, int minZoom, int maxZoom) {
super("Offline from "+path, path);
this.minZoom = minZoom;
this.maxZoom = maxZoom;
}
@Override
public int getMaxZoom() {
return maxZoom;
}
@Override
public int getMinZoom() {
return minZoom;
}
@Override
public TileUpdate getTileUpdate() {
return TileUpdate.None;
}
}

View File

@ -0,0 +1,83 @@
package org.openstreetmap.gui.jmapviewer.tilesources;
public class OsmTileSource {
public static final String MAP_MAPNIK = "http://tile.openstreetmap.org";
public static final String MAP_OSMA = "http://tah.openstreetmap.org/Tiles";
public static class Mapnik extends AbstractOsmTileSource {
public Mapnik() {
super("Mapnik", MAP_MAPNIK);
}
public TileUpdate getTileUpdate() {
return TileUpdate.IfNoneMatch;
}
}
public static class CycleMap extends AbstractOsmTileSource {
private static final String PATTERN = "http://%s.tile.opencyclemap.org/cycle";
private static final String[] SERVER = { "a", "b", "c" };
private int SERVER_NUM = 0;
public CycleMap() {
super("OSM Cycle Map", PATTERN);
}
@Override
public String getBaseUrl() {
String url = String.format(this.baseUrl, new Object[] { SERVER[SERVER_NUM] });
SERVER_NUM = (SERVER_NUM + 1) % SERVER.length;
return url;
}
@Override
public int getMaxZoom() {
return 17;
}
public TileUpdate getTileUpdate() {
return TileUpdate.LastModified;
}
}
public static abstract class OsmaSource extends AbstractOsmTileSource {
String osmaSuffix;
public OsmaSource(String name, String osmaSuffix) {
super(name, MAP_OSMA);
this.osmaSuffix = osmaSuffix;
}
@Override
public int getMaxZoom() {
return 17;
}
@Override
public String getBaseUrl() {
return MAP_OSMA + "/" + osmaSuffix;
}
public TileUpdate getTileUpdate() {
return TileUpdate.IfModifiedSince;
}
}
public static class TilesAtHome extends OsmaSource {
public TilesAtHome() {
super("TilesAtHome", "tile");
}
}
public static class Maplint extends OsmaSource {
public Maplint() {
super("Maplint", "maplint");
}
}
}

View File

@ -0,0 +1,20 @@
package org.openstreetmap.gui.jmapviewer.tilesources;
public class TMSTileSource extends AbstractOsmTileSource {
private int maxZoom;
public TMSTileSource(String name, String url, int maxZoom) {
super(name, url);
this.maxZoom = maxZoom;
}
@Override
public int getMaxZoom() {
return (maxZoom == 0) ? super.getMaxZoom() : maxZoom;
}
public TileUpdate getTileUpdate() {
return TileUpdate.IfNoneMatch;
}
}

View File

@ -0,0 +1,28 @@
package org.openstreetmap.gui.jmapviewer.tilesources;
public class TemplatedTMSTileSource extends AbstractOsmTileSource {
private int maxZoom;
public TemplatedTMSTileSource(String name, String url, int maxZoom) {
super(name, url);
this.maxZoom = maxZoom;
}
public String getTileUrl(int zoom, int tilex, int tiley) {
return this.baseUrl
.replaceAll("\\{zoom\\}", Integer.toString(zoom))
.replaceAll("\\{x\\}", Integer.toString(tilex))
.replaceAll("\\{y\\}", Integer.toString(tiley));
}
@Override
public int getMaxZoom() {
return (maxZoom == 0) ? super.getMaxZoom() : maxZoom;
}
public TileUpdate getTileUpdate() {
return TileUpdate.IfNoneMatch;
}
}