Oleh
Oleh

Reputation: 674

How can I build map with region selection?

I trying to build map which consist of regions(states) and when mouse entered to some region, I need handle it. Have many png images which represent each region separately. I blend my images and got what I want, but I can't handle some region.
For instance:
It's a first region img
enter image description here


This is a second region img
enter image description here


As result I got: enter image description here

Code:

@Override
public void start(Stage primaryStage) throws Exception {
    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("view/MapView.fxml"));
    Pane root = loader.load();
    primaryStage.setTitle("Map");
    primaryStage.setScene(new Scene(root, 700, 700));
    primaryStage.show();
    //First region
    File file = new File("src/res/img/region1.png");
    Canvas canvas = new Canvas(700, 700);
    canvas.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, event -> System.out.println("Region 1"));
    GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
    graphicsContext.drawImage(new Image(file.toURI().toString()), 0, 0);
    root.getChildren().add(canvas);
    //Second region
    file = new File("src/res/img/region2.png");
    canvas = new Canvas(700, 700);
    canvas.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, event -> System.out.println("Region 2"));
    graphicsContext = canvas.getGraphicsContext2D();
    graphicsContext.drawImage(new Image(file.toURI().toString()), 0, 0);
    root.getChildren().add(canvas);
}

In console I got always "Region 2".
Please give me tips for research. Thanks in advance!

Upvotes: 1

Views: 336

Answers (3)

Roland
Roland

Reputation: 18415

You can use an ImageView and setPickOnBounds for that.

Example code:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class LayersWithMouseEvents extends Application {

    @Override
    public void start(Stage primaryStage) {

        // root
        StackPane root = new StackPane();

        // create layers
        Pane region1Layer = new Pane();
        Pane region2Layer = new Pane();

        // add layers
        root.getChildren().addAll(region1Layer, region2Layer);

        // load images
        ImageView region1ImageView = new ImageView( new Image( getClass().getResource("region1.png").toExternalForm()));
        ImageView region2ImageView = new ImageView( new Image( getClass().getResource("region2.png").toExternalForm()));

        // add images
        region1Layer.getChildren().add(region1ImageView);
        region2Layer.getChildren().add(region2ImageView);

        // mouse handler
        region1Layer.setOnMousePressed(e -> System.out.println("Region 1: " + e));
        region2Layer.setOnMousePressed(e -> System.out.println("Region 2: " + e));

        // this is the magic that allows you to click on the separate layers, but ONLY(!) as long as the layer is transparent
        region1Layer.setPickOnBounds(false);
        region2Layer.setPickOnBounds(false);

        primaryStage.setScene(new Scene(root, 800, 600));
        primaryStage.show();

    }

    public static void main(String[] args) {
        launch(args);
    }
}

In the future please create an MCVE with focus on complete. Nobody wants to waste their time in order to make your incomplete example work.

Upvotes: 3

fabian
fabian

Reputation: 82461

The image data can be used to determine the color of the pixels under the mouse cursor. If the pixel is not fully transparent, then the cursor is on that region.

To retrieve this information you need to use a listener for the mouse moved event. For simplicity you can use a property with a listener attached to trigger the region enter/leave events:

The following example assumes you keep references to the images used for the regions named image1 and image2:

PixelReader reader1 = image1.getPixelReader();
PixelReader reader2 = image2.getPixelReader();
SimpleIntegerProperty region = new SimpleIntegerProperty(-1);
region.addListener((observable, oldValue, newValue) -> {
    if (newValue.intValue() < 0) {
        System.out.println("region left");
    } else {
        System.out.println("Region " + (newValue.intValue() + 1));
    }
});
canvas.setOnMouseMoved(event -> {
    int x = (int) event.getX();
    int y = (int) event.getY();
    if (x < image1.getWidth() && y < image1.getHeight() && reader1.getColor(x, y).getOpacity() != 0) {
        region.set(0);
    } else if (x < image2.getWidth() && y < image2.getHeight() && reader2.getColor(x, y).getOpacity() != 0) {
        region.set(1);
    } else {
        region.set(-1);
    }
});

Also there is no need to create multiple Canvas to paint the images.

Upvotes: 2

mipa
mipa

Reputation: 10650

To my opinion using a canvas here is not the right approach. If you would define your regions as polygons and add them to the scene graph you can attach a listener to each of these polygons and then react accordingly if the mouse is over some region. Maybe this is also possible with image views but I have never tried wether a transparent region of an image is also mouse transparent which seems to be necessary in that case. For a program of mine I used polygons and it works nicely.

Upvotes: 2

Related Questions