package aimax.osm.gui.fx.viewer;
import aimax.osm.data.OsmMap;
import aimax.osm.data.entities.EntityAttribute;
import aimax.osm.data.entities.MapEntity;
import aimax.osm.data.entities.MapNode;
import aimax.osm.data.entities.WayRef;
import aimax.osm.data.impl.DefaultMap;
import aimax.osm.viewer.AbstractEntityRenderer;
import aimax.osm.viewer.CoordTransformer;
import aimax.osm.viewer.UnifiedMapDrawer;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.input.*;
import javafx.scene.layout.StackPane;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Created by rlunde on 28.10.2016.
*/
public class MapPaneCtrl {
private StackPane pane;
private Canvas currCanvas;
private Canvas osCanvas;
private UnifiedMapDrawer<Canvas> mapDrawer;
private boolean scaleToFit = true;
public MapPaneCtrl(StackPane pane) {
this.pane = pane;
mapDrawer = new UnifiedMapDrawer<Canvas>(new FXImageBuilder(), createMap());
mapDrawer.getMap().addMapDataEventListener(ev -> update());
pane.widthProperty().addListener((obs, o, n) -> { scaleToFit = true; update(); });
pane.heightProperty().addListener((obs, o, n) -> { scaleToFit = true; update(); });
pane.setOnMouseEntered(ev -> {if (currCanvas != null) currCanvas.requestFocus();});
pane.setOnMousePressed(this::handleMouseEvent);
pane.setOnMouseDragged(this::handleMouseEvent);
pane.setOnMouseClicked(this::handleMouseEvent);
pane.setOnScroll(this::handleScrollEvent);
pane.setOnKeyPressed(this::handleKeyEvent);
pane.setMinSize(0, 0);
}
protected OsmMap createMap() { return new DefaultMap(); }
public OsmMap getMap() {
return mapDrawer.getMap();
}
public void loadMap(InputStream stream) {
mapDrawer.loadMap(stream);
scaleToFit = true;
}
public void loadMap(File file) {
mapDrawer.loadMap(file);
scaleToFit = true;
}
public AbstractEntityRenderer getRenderer() {
return mapDrawer.getRenderer();
}
public void setRenderer(AbstractEntityRenderer renderer) { mapDrawer.setRenderer(renderer); }
public CoordTransformer getTransformer() { return mapDrawer.getTransformer(); }
public void update() {
Canvas canvas = osCanvas;
if (canvas == null
|| Math.abs(canvas.getWidth() - pane.getWidth()) > 0.01
|| Math.abs(canvas.getHeight() - pane.getHeight()) > 0.1) {
canvas = new Canvas(pane.getWidth(), pane.getHeight());
canvas.setFocusTraversable(true);
}
mapDrawer.drawMap(canvas, scaleToFit);
pane.getChildren().add(canvas);
if (pane.getChildren().size() > 1)
pane.getChildren().remove(0);
osCanvas = currCanvas;
currCanvas = canvas;
scaleToFit = false;
}
/**
* Multiples the current scale with the specified factor and adjusts the
* view so that the objects shown at the specified view focus keep at their
* position.
*/
public void zoom(float factor, int focusX, int focusY) {
getTransformer().zoom(factor, focusX, focusY);
// paintPreview((int) ((1 - factor) * focusX),
// (int) ((1 - factor) * focusY), factor);
update();
}
public void multiplyDisplayFactorWith(float fac) {
getRenderer().setDisplayFactor(getRenderer().getDisplayFactor() * fac);
update();
}
/**
* Adjusts the view.
*
* @param dx
* Number of pixels for horizontal shift.
* @param dy
* Number of pixels for vertical shift.
*/
public void adjust(double dx, double dy) {
getTransformer().adjust((int) dx, (int) dy);
//paintPreview(dx, dy, 1f);
update();
}
private double xDrag;
private double yDrag;
private boolean dragActive;
protected void handleMouseEvent(MouseEvent event) {
if (event.getEventType() == MouseEvent.MOUSE_PRESSED) {
xDrag = event.getX();
yDrag = event.getY();
dragActive = false;
} else if (event.getEventType() == MouseEvent.MOUSE_DRAGGED) {
adjust(event.getX() - xDrag, event.getY() - yDrag);
xDrag = event.getX();
yDrag = event.getY();
dragActive = true;
} else if (event.getEventType() == MouseEvent.MOUSE_CLICKED && !dragActive) {
if (event.getButton() == MouseButton.PRIMARY) {
CoordTransformer tr = getTransformer();
getMap().addMarker(tr.lat((int) event.getY()), tr.lon((int) event.getX()));
} else if (event.getButton() == MouseButton.SECONDARY) {
getMap().clearMarkersAndTracks();
} else if (event.getButton() == MouseButton.MIDDLE) {
MapNode mNode = getRenderer().getNextNode((int) event.getX(), (int) event.getY());
if (mNode != null)
showMapEntityInfoDialog(mNode, true);
}
}
currCanvas.requestFocus(); // hack...
}
protected void handleScrollEvent(ScrollEvent event) {
float fac = event.getDeltaY() > 0 ? 1.2f : 1.0f / 1.2f;
if (event.isAltDown())
multiplyDisplayFactorWith(fac);
else
zoom(fac, (int) event.getX(), (int) event.getY());
}
protected void handleKeyEvent(KeyEvent event) {
if (event.getCode() == KeyCode.LEFT)
adjust(30, 0);
else if (event.getCode() == KeyCode.RIGHT)
adjust(-30, 0);
else if (event.getCode() == KeyCode.UP)
adjust(0, 30);
else if (event.getCode() == KeyCode.DOWN)
adjust(0, -30);
else if (event.getCode() == KeyCode.PLUS)
if (event.isAltDown())
multiplyDisplayFactorWith(1.5f);
else
zoom(1.5f, (int) pane.getWidth() / 2, (int) pane.getHeight() / 2);
else if (event.getCode() == KeyCode.MINUS)
if (event.isAltDown())
multiplyDisplayFactorWith(1.0f/1.5f);
else
zoom(0.7f, (int) pane.getWidth() / 2, (int) pane.getHeight() / 2);
else
return;
event.consume();
currCanvas.requestFocus(); // hack...
}
/**
* Finds the visible entity next to the specified view coordinates and shows
* informations about it.
*
* @param debug
* Enables a more detailed view.
*/
private void showMapEntityInfoDialog(MapEntity entity, boolean debug) {
List<MapEntity> entities = new ArrayList<>();
if (entity.getName() != null || entity.getAttributes().length > 0
|| debug)
entities.add(entity);
if (entity instanceof MapNode) {
MapNode mNode = (MapNode) entity;
for (WayRef ref : mNode.getWayRefs()) {
MapEntity me = ref.getWay();
if (me.getName() != null || me.getAttributes().length > 0
|| debug)
entities.add(me);
}
}
for (MapEntity me : entities) {
String header = (me.getName() != null) ? me.getName() : "";
StringBuilder content = new StringBuilder();
if (debug)
header += " (" + ((me instanceof MapNode) ? "Node " : "Way ")
+ me.getId() + ")";
if (me instanceof MapNode) {
content.append("Lat: ")
.append(((MapNode) me).getLat())
.append(" Lon: ")
.append(((MapNode) me).getLon())
.append(" ");
}
if (me.getAttributes().length > 0) {
EntityAttribute[] atts = me.getAttributes();
content.append("Attributs: ");
for (EntityAttribute att : atts) {
content.append(att.getKey())
.append("=")
.append(att.getValue())
.append(" ");
}
}
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Map Entity Info");
alert.setHeaderText(header);
alert.setContentText(content.toString());
Optional<ButtonType> result = alert.showAndWait();
if (!result.isPresent())
break;
}
}
}