Since you are referring to the PoiLayer
class in the Gluon Map's demo package, you can modify it easily to:
update the icon position accordingly when you click on one location of the map, without requiring zoom updates.
show a tooltip on your icon.
Just add this method to PoiLayer
to update a given point to a new lat
and lon
coordinates:
public void updatePoint(MapPoint p, double lat, double lon) {
for (Pair<MapPoint, Node> candidate : points) {
MapPoint point = candidate.getKey();
if (point.equals(p)) {
p.update(lat, lon);
markDirty();
break;
}
}
}
Note the call to markDirty()
, that will force a layout pass and will update the icon position.
If you add a Tooltip
to the icon, you won't need to take care of updating its position:
private MapPoint mapPointIcon;
private PoiLayer createDemoLayer() {
PoiLayer answer = new PoiLayer();
Node icon = new Circle(7, Color.BLUE);
Tooltip.install(icon, new Tooltip("Pruebas"));
mapPointIcon = new MapPoint(40.4, -3.7);
answer.addPoint(mapPointIcon, icon);
return answer;
}
The only part missing is getting the latitude and longitude coordinates from the scene. This question covers that.
So based on it, you can add this method to the PoiLayer
class:
public MapPoint getMapPosition(double x, double y) {
double z = baseMap.zoom().get();
double latrad = Math.PI - (2 * Math.PI * (y - baseMap.getTranslateY())) / (Math.pow(2, z) * 256.0);
double mlat = Math.toDegrees(Math.atan(Math.sinh(latrad)));
double mlon = (x - this.baseMap.getTranslateX()) / (256.0 * Math.pow(2, z)) * 360.0 - 180.0;
return new MapPoint(mlat, mlon);
}
Finally, add a listener to your view:
@Override
public void start(Stage stage) throws Exception {
MapView view = new MapView();
PoiLayer myDemoLayer = createDemoLayer();
view.setZoom(4);
view.addLayer(myDemoLayer);
Scene scene = new Scene(view, 600, 700);
stage.setScene(scene);
stage.show();
view.flyTo(0, mapPointIcon, 1.);
view.setOnMouseClicked(e -> {
MapPoint position = myDemoLayer.getMapPosition(e.getX(), e.getY());
myDemoLayer.updatePoint(mapPointIcon, position.getLatitude(), position.getLongitude());
});
}
This works fine, but if you have a layer with several points, you'll need a way to select which point you want to move.
EDIT
Based in the OP request of having a permanent popup instead of a tooltip, this is a very simple popup implementation. For a better solution, ControlsFX PopOver
control is recommended.
This can be added to the PoiLayer
class:
public void addPoint(MapPoint p, Node icon) {
final Pair<MapPoint, Node> pair = new Pair(p, icon);
icon.setOnMouseClicked(e -> {
e.consume();
showPopup(pair);
});
points.add(pair);
this.getChildren().add(icon);
this.markDirty();
}
private void showPopup(Pair<MapPoint, Node> pair) {
Node icon = pair.getValue();
MapPoint point = pair.getKey();
final Stage primaryStage = (Stage) icon.getScene().getWindow();
final Stage popupStage = new Stage();
popupStage.initStyle(StageStyle.UNDECORATED);
popupStage.initModality(Modality.APPLICATION_MODAL);
popupStage.initOwner(primaryStage);
VBox box = new VBox(5);
box.setPadding(new Insets(5));
box.getChildren().addAll(new Label("Popup"),
new Label("Lat: " + String.format("%2.6fº", point.getLatitude())),
new Label("Lon: " + String.format("%2.6fº", point.getLongitude())));
Label close = new Label("X");
close.setOnMouseClicked(a -> popupStage.close());
StackPane.setAlignment(close, Pos.TOP_RIGHT);
final StackPane stackPane = new StackPane(box, close);
stackPane.setPadding(new Insets(5));
stackPane.setStyle("-fx-background-color: lightgreen;");
Scene scene = new Scene(stackPane, 150, 100);
Bounds iconBounds = icon.localToScene(icon.getBoundsInLocal());
popupStage.setX(primaryStage.getX() + primaryStage.getScene().getX() + iconBounds.getMaxX() );
popupStage.setY(primaryStage.getY() + primaryStage.getScene().getY() + iconBounds.getMaxY());
popupStage.setScene(scene);
popupStage.show();
}
Whenever you click in the icon, a modal popup will show up (you need to close it before going back to the map). This can be changed, of course, to have a non modal and auto closable popup.
