![JAR search and dependency download from the Maven repository](/logo.png)
eu.limetri.client.mapviewer.fx.JFXMapPane Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mapviewer-javafx Show documentation
Show all versions of mapviewer-javafx Show documentation
JXMapViewer JavaFX implementation
/**
* Copyright (C) 2008-2012 AgroSense Foundation.
*
* AgroSense is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* There are special exceptions to the terms and conditions of the GPLv3 as it is applied to
* this software, see the FLOSS License Exception
* .
*
* AgroSense is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AgroSense. If not, see .
*/
package eu.limetri.client.mapviewer.fx;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import eu.limetri.client.mapviewer.data.AbstractMapViewer;
import eu.limetri.client.mapviewer.data.GeoPosition;
import eu.limetri.client.mapviewer.data.MapViewer;
import eu.limetri.client.mapviewer.data.Tile;
import eu.limetri.client.mapviewer.data.TileFactory;
import eu.limetri.client.mapviewer.data.TileFactoryInfo;
import eu.limetri.client.mapviewer.data.common.TileFactoryInfoSelectionEventHandler;
import eu.limetri.client.mapviewer.data.tilefactory.OSMTileFactoryInfo;
import eu.limetri.client.mapviewer.fx.impl.OfflineTileFactoryFX;
/**
* Map browser component
*
* @author Frantisek Post
*/
public class JFXMapPane extends StackPane implements MapViewer>{
private static final int TILE_SIZE = 256;
public static final String ZOOM = "zoom";
private Canvas canvas = new Canvas();
private AbstractMapViewer> abstractMapViewer;
private TileLoadListener tileLoadListener = new TileLoadListener();
private Image loadingImage;
private double prevX;
private double prevY;
private boolean zoomSelectActive = false;
private double zoomX;
private double zoomY;
public static final int MIN_ZOOM = 1;
public static final int MAX_ZOOM = 17;
private WritableImage savedCanvas;
private PropertyChangeSupport propertyChangeSupport;
private boolean zoomEnabled = false;
private boolean typeSelectionEnabled = false;
private AnchorPane rightPane = new AnchorPane();
private ZoomPanel zoomPanel;
private MapTypeSelectionPanelFX typeSelectionPanel;
private final class TileLoadListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
if ("loaded".equals(evt.getPropertyName())
&& Boolean.TRUE.equals(evt.getNewValue())) {
Tile> t = (Tile>) evt.getSource();
if (t.getZoom() == abstractMapViewer.getZoom()) {
Platform.runLater(new Runnable() {
@Override
public void run() {
paintTiles();
}
});
}
}
}
}
/**
* Constructor
*/
public JFXMapPane() {
super();
canvas.setBlendMode(BlendMode.MULTIPLY);
canvas.widthProperty().bind(widthProperty());
canvas.heightProperty().bind(heightProperty());
getChildren().add(canvas);
initMapViewer();
loadingImage = new Image(getClass().getResourceAsStream("loading.png"));
setOnMousePressed(new EventHandler() {
@Override
public void handle(MouseEvent event) {
prevX = event.getX();
prevY = event.getY();
savedCanvas = null;
}
});
setOnMouseReleased(new EventHandler() {
@Override
public void handle(MouseEvent event) {
if (zoomSelectActive) {
zoomSelectActive = false;
abstractMapViewer.zoomToCoordinates(prevX, prevY, event.getX(), event.getY());
}
}
});
setOnMouseDragged(new EventHandler() {
@Override
public void handle(MouseEvent event) {
if (event.isControlDown()) {
zoomSelectActive = true;
zoomX = event.getX();
zoomY = event.getY();
paintZoomRectangle();
} else {
zoomSelectActive = false;
double mouseX = event.getX();
double mouseY = event.getY();
double x = abstractMapViewer.getCenter().getX()
- (mouseX - prevX);
double y = abstractMapViewer.getCenter().getY()
- (mouseY - prevY);
int maxHeight = (int) (abstractMapViewer.getTileFactory()
.getMapSize(abstractMapViewer.getZoom()).getHeight() * abstractMapViewer
.getTileFactory().getTileSize(
abstractMapViewer.getZoom()));
if (y > maxHeight) {
y = maxHeight;
}
abstractMapViewer.setCenter(new Point2D.Double(x, y));
paintTiles();
prevX = mouseX;
prevY = mouseY;
}
}
});
this.canvas.setOnScroll(new EventHandler() {
@Override
public void handle(ScrollEvent me) {
if (me.getDeltaY() > 0) {
if (getZoom() > MIN_ZOOM) {
zoomOut();
}
} else {
if (getZoom() < MAX_ZOOM) {
zoomIn();
}
}
}
});
ChangeListener changeListener = new ChangeListener() {
@Override
public void changed(ObservableValue extends Number> observable, Number oldValue, Number newValue) {
//TODO in demo invoked only when resized to bigger?
refreshScene();
}
};
widthProperty().addListener(changeListener);
heightProperty().addListener(changeListener);
propertyChangeSupport = new PropertyChangeSupport(this);
rightPane = new AnchorPane();
getChildren().add(rightPane);
setZoomEnabled(true);
}
private void refreshScene() {
paintTiles();
}
private void initMapViewer() {
abstractMapViewer = new AbstractMapViewer>() {
@Override
protected void repaint() {
paintTiles();
}
@Override
protected int getWidth() {
return (int) JFXMapPane.this.getWidth();
}
@Override
protected java.awt.Insets getInsets() {
return new java.awt.Insets(0, 0, 0, 0);
}
@Override
protected int getHeight() {
return (int) JFXMapPane.this.getHeight();
}
@Override
protected void firePropertyChange(String name, Object oldValue, Object newValue) {
JFXMapPane.this.firePropertyChange(name, oldValue, newValue);
}
};
abstractMapViewer.setTileFactory(new OfflineTileFactoryFX(new OSMTileFactoryInfo()));
}
protected void paintTiles() {
//correcting map bounds
Point2D center = abstractMapViewer.getCenter();
double viewerHeight = getHeight();
int maxHeight = (int) (getTileFactory().getMapSize(getZoom()).getHeight() * getTileFactory().getTileSize(getZoom()));
double y = center.getY();
if (y < (viewerHeight/2)) {
y = viewerHeight / 2;
}
if (y > maxHeight - (viewerHeight/2)) {
y = maxHeight - (viewerHeight/2);
}
if (y != center.getY()) {
abstractMapViewer.setCenter(new Point2D.Double(center.getX(), y));
}
int zoom = abstractMapViewer.getZoom();
Rectangle viewportBounds = abstractMapViewer.getViewportBounds();
drawMapTiles(zoom, viewportBounds);
// drawOverlays(zoom, g, viewportBounds);
}
private void drawMapTiles(int zoom, Rectangle viewportBounds) {
int size = abstractMapViewer.getTileFactory().getTileSize(zoom);
// calculate the "visible" viewport area in tiles
int numWide = viewportBounds.width / size + 2;
int numHigh = viewportBounds.height / size + 2;
TileFactoryInfo info = abstractMapViewer.getTileFactory().getInfo();
int tpx = (int) Math.floor(viewportBounds.getX() / info.getTileSize(0));
int tpy = (int) Math.floor(viewportBounds.getY() / info.getTileSize(0));
GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
graphicsContext.setFill(Color.GRAY);
graphicsContext.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
// p("top tile = " + topLeftTile);
// fetch the tiles from the factory and store them in the tiles cache
// attach the tileLoadListener
for (int x = 0; x <= numWide; x++) {
for (int y = 0; y <= numHigh; y++) {
int itpx = x + tpx;// topLeftTile.getX();
int itpy = y + tpy;// topLeftTile.getY();
Tile tile = abstractMapViewer.getTileFactory().getTile(itpx,
itpy, zoom);
tile.addUniquePropertyChangeListener("loaded", tileLoadListener);
int ox = ((itpx * abstractMapViewer.getTileFactory()
.getTileSize(zoom)) - viewportBounds.x);
int oy = ((itpy * abstractMapViewer.getTileFactory()
.getTileSize(zoom)) - viewportBounds.y);
if (tile.isLoaded()) {
graphicsContext.drawImage(tile.getImage(), ox, oy);
} else {
//show resized image, while loading
Tile superTile = abstractMapViewer.getTileFactory().getTile(itpx / 2, itpy / 2, zoom + 1);
if (superTile.isLoaded()) {
int offX = (itpx % 2) * size / 2;
int offY = (itpy % 2) * size / 2;
graphicsContext.drawImage(superTile.getImage(), offX, offY, size / 2, size / 2, ox, oy, size, size);
} else {
Image image = getLoadingImage();
double h = image.getHeight();
double w = image.getWidth();
double lx = TILE_SIZE / 2 - (w / 2);
double ly = TILE_SIZE / 2 - (h / 2);
graphicsContext.setFill(Color.GRAY);
graphicsContext.fillRect(ox, oy, TILE_SIZE, TILE_SIZE);
graphicsContext.drawImage(getLoadingImage(), ox + lx, oy + ly);
}
}
}
}
}
private void paintZoomRectangle() {
if (zoomSelectActive) {
GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
if (savedCanvas == null) {
savedCanvas = canvas.snapshot(null, null);
} else {
graphicsContext.drawImage(savedCanvas, 0, 0);
}
abstractMapViewer.fireRepaintCallbacks();
graphicsContext.setLineWidth(3); //TODO color and width
graphicsContext.setStroke(Color.RED);
graphicsContext.strokeRect(Math.min(prevX, zoomX), Math.min(prevY, zoomY), Math.abs(zoomX-prevX), Math.abs(zoomY-prevY));
}
}
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
/**
* Set zoom level
*
* @param zoom
*/
public void setZoom(int zoom) {
abstractMapViewer.setZoom(zoom);
}
/**
* Set location of the center of the map
*
* @param addressLocation
*/
public void setAddressLocation(GeoPosition addressLocation) {
abstractMapViewer.setAddressLocation(addressLocation);
}
public Image getLoadingImage() {
return loadingImage;
}
/**
* Returns current zoom level
*
* @return
*/
public int getZoom() {
return abstractMapViewer.getZoom();
}
/**
* Increase zoom level by one
*/
public void zoomIn() {
if (getZoom() < MAX_ZOOM) {
abstractMapViewer.setZoom(abstractMapViewer.getZoom() + 1);
}
}
/**
* Decrease zoom level by one
*/
public void zoomOut() {
if (getZoom() > MIN_ZOOM) {
abstractMapViewer.setZoom(abstractMapViewer.getZoom() - 1);
}
}
@Override
public TileFactory> getTileFactory() {
return abstractMapViewer.getTileFactory();
}
@Override
public Rectangle2D getViewportBounds() {
return abstractMapViewer.getViewportBounds();
}
public void setTileFactory(TileFactory> factory) {
abstractMapViewer.setTileFactory(factory);
paintTiles();
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
}
public void setZoomEnabled(boolean zoomEnabled) {
if (this.zoomEnabled != zoomEnabled) {
this.zoomEnabled= zoomEnabled;
if (zoomEnabled) {
enableZoom();
} else {
disableZoom();
}
}
}
public boolean isZoomEnabled() {
return zoomEnabled ;
}
private void enableZoom() {
zoomPanel = new ZoomPanel(this);
zoomPanel.attach();
AnchorPane.setTopAnchor(zoomPanel, 30d);
AnchorPane.setRightAnchor(zoomPanel, 2d);
rightPane.getChildren().add(zoomPanel);
}
private void disableZoom() {
zoomPanel.detach();
rightPane.getChildren().remove(zoomPanel);
}
public void setTypeSelectionEnabled(boolean typeSelectionEnabled, TileFactoryInfo...tileFactoryInfos) {
if (this.typeSelectionEnabled != typeSelectionEnabled) {
this.typeSelectionEnabled= typeSelectionEnabled;
if (typeSelectionEnabled) {
enableTypeSelection(tileFactoryInfos);
} else {
disableTypeSelection();
}
}
}
private void enableTypeSelection(TileFactoryInfo...tileFactoryInfos) {
typeSelectionPanel = new MapTypeSelectionPanelFX();
typeSelectionPanel.setItems(tileFactoryInfos);
typeSelectionPanel.setOnSelectionChange(new TileFactoryInfoSelectionEventHandler() {
@Override
public void itemSelected(TileFactoryInfo tileFactoryInfo) {
setTileFactory(new OfflineTileFactoryFX(tileFactoryInfo));
}
});
typeSelectionPanel.setActiveTileFactoryInfo(getTileFactory().getInfo());
AnchorPane.setTopAnchor(typeSelectionPanel, 2d);
AnchorPane.setRightAnchor(typeSelectionPanel, 2d);
rightPane.getChildren().add(typeSelectionPanel);
}
private void disableTypeSelection() {
rightPane.getChildren().remove(typeSelectionPanel);
}
public boolean isTypeSelectionEnabled() {
return typeSelectionEnabled;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy