skip to Main Content

I’ve been trying to create a Java desktop app using Java, JavaScript and JavaFX and one part of the app should be able to display a location on the map using the location of the venue taken from the database (street + city, no postal code) and once I run the app it seems to show the location correctly for a second or two and then it completely disapears like it’s shown in the images below.
Before

After

I used Maps JavaScript API and Geocoding API. I’m trying to get the app to consistently show the location without it disappearing without it running into an issue. This is the method that I’m currently using to display the map:

public void showMap(Venue venue) {
  WebEngine webEngine   = mapView.getEngine();

  Location location     = venue.getLocation();
  String address        = location.toString();
  String encodedAddress = URLEncoder.encode(address, StandardCharsets.UTF_8);

  String mapHTML = "<html>" +
    "<head>" +
    "<script src="https://maps.googleapis.com/maps/api/js?key=API_KEY"></script>" +
    "<script>" +
    "function initMap() {" +
    "  var geocoder = new google.maps.Geocoder();" +
    "  geocoder.geocode({'address': '" + encodedAddress + "'}, function(results, status) {" +
    "    if (status === 'OK') {" +
    "      var mapOptions = {" +
    "        zoom: 15," +
    "        center: results[0].geometry.location," +
    "        mapTypeId: google.maps.MapTypeId.ROADMAP" +
    "      };" +
    "      var map = new google.maps.Map(document.getElementById('map'), mapOptions);" +
    "      new google.maps.Marker({" +
    "        position: results[0].geometry.location," +
    "        map: map" +
    "      });" +
    "    } else {" +
    "      alert('Geocode was not successful for the following reason: ' + status);" +
    "    }" +
    "  });" +
    "}" +
    "</script>" +
    "</head>" +
    "<body onload='initMap()'>" +
    "<div id='map' style='width:100%;height:100%;'></div>" +
    "</body>" +
    "</html>";

  webEngine.loadContent(mapHTML);
}

I used WebView on JavaFX to add the map as well. Anybody knows what could I add to the code to fix this. Should I maybe add another class that would do the geocoding for the given location or is there something wrong with the displayed code. Anything helps

2

Answers


  1. There are several questions on this site describing Google Maps not working in JavaFX WebView. It seems the Google Maps API does some fairly sophisticated browser checking which causes it to fail running in that context.

    Another solution is to use the Gluon Maps Project, which provides a "native" (i.e. not relying on WebView and HTML/Javascript embedding) JavaFX component displaying Open Street Maps.

    Gluon maps requires specifying the latitude and longitude of the location to display, so you will need a separate geocoding service if you want to search addresses. Nominatim from Open Street Maps seems to work well enough.

    Here’s a Geocoding implementation based on Nominatim:

    Geocoder.java

    package org.jamesd.examples.maps;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    import java.io.IOException;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.net.URLEncoder;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.nio.charset.StandardCharsets;
    import java.util.concurrent.Executor;
    import java.util.function.Consumer;
    
    public class Geocoder {
        public record LatLon(double lat, double lon) {}
    
        private final static String GEOCODE_URL = "https://nominatim.openstreetmap.org/search";
    
        public LatLon geocode(String address) throws URISyntaxException, IOException, InterruptedException {
            HttpClient client = HttpClient.newBuilder().build();
            HttpRequest request = buildRequest(address);
            HttpResponse.BodyHandler<String> handler = HttpResponse.BodyHandlers.ofString();
            HttpResponse<String> response = client.send(request, handler);
            return latLonFromResponse(response);
        }
        public void geocodeAsync(String address, Consumer<LatLon> onRetrieved, Executor exec) throws URISyntaxException {
            HttpClient client = HttpClient.newBuilder().build();
            HttpResponse.BodyHandler<String> handler = HttpResponse.BodyHandlers.ofString();
            client.sendAsync(buildRequest(address), handler)
                    .thenApply(this::latLonFromResponse)
                    .thenAcceptAsync(onRetrieved, exec);
        }
        private HttpRequest buildRequest(String address) throws URISyntaxException {
            URI uri = buildURI(address);
            return HttpRequest.newBuilder()
                    .uri(uri)
                    .GET()
                    .build();
        }
    
        private LatLon latLonFromResponse(HttpResponse<String> response) {
            try {
                ObjectMapper objectMapper = new ObjectMapper();
                JsonNode root = objectMapper.readTree(response.body());
                JsonNode result = root.elements().next();
                double lat = result.at("/lat").asDouble();
                double lon = result.at("/lon").asDouble();
                return new LatLon(lat, lon);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
        
    
        private URI buildURI(String address) throws URISyntaxException {
            String encodedAddress = URLEncoder.encode(address, StandardCharsets.UTF_8);
            StringBuilder uri = new StringBuilder(GEOCODE_URL)
                    .append("?q=").append(encodedAddress)
                    .append("&format=json")
                    .append("&addressdetails=1")
                    .append("&limit=1");
            return new URI(uri.toString());
        }
    }
    
    

    And a simple demo app displaying the address you displayed in Google maps (or close to it), along with a location marker, using Gluon maps:

    package org.jamesd.examples.maps;
    
    import com.gluonhq.maps.MapLayer;
    import com.gluonhq.maps.MapPoint;
    import com.gluonhq.maps.MapView;
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.geometry.Point2D;
    import javafx.scene.Scene;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.stage.Stage;
    
    public class HelloApplication extends Application {
    
    
    
        @Override
        public void start(Stage stage) throws Exception {
    
            MapView map = new MapView();
            map.setZoom(19);
    
            ObjectProperty<Geocoder.LatLon> latlon = new SimpleObjectProperty<>(new Geocoder.LatLon(0, 0));
            latlon.subscribe(ll -> map.setCenter(ll.lat(), ll.lon()));
    
            Geocoder geocoder = new Geocoder();
            geocoder.geocodeAsync(
                    "Ribnjak 2, 10000, Zagreb, Croatia",
                    latlon::set,
                    Platform::runLater
            );
    
    
            MapLayer poiLayer = new MapLayer() {
                private Circle marker = new Circle(5, Color.RED);
                {
                    getChildren().add(marker);
                    latlon.subscribe(this::markDirty);
                }
    
                @Override
                protected void layoutLayer() {
                    super.layoutLayer();
                    Geocoder.LatLon ll = latlon.get();
                    if (ll == null) {
                        marker.setVisible(false);
                    } else {
                        MapPoint mapPoint = new MapPoint(latlon.get().lat(), latlon.get().lon());
                        Point2D point = getMapPoint(mapPoint.getLatitude(), mapPoint.getLongitude());
                        marker.setCenterX(point.getX());
                        marker.setCenterY(point.getY());
                        marker.setVisible(true);
                    }
                }
    
            };
            map.addLayer(poiLayer);
    
            Scene scene = new Scene(map, 800, 500);
            stage.setScene(scene);
            stage.show();
        }
    
        public static void main(String[] args) {
            launch();
        }
    }
    

    enter image description here

    For completeness, the pom.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.jamesd.examples</groupId>
        <artifactId>maps</artifactId>
        <version>1.0-SNAPSHOT</version>
        <name>maps</name>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <javafx.version>22.0.1</javafx.version>
            <junit.version>5.8.2</junit.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-controls</artifactId>
                <version>22.0.1</version>
            </dependency>
            <dependency>
                <groupId>com.gluonhq</groupId>
                <artifactId>maps</artifactId>
                <version>2.0.0-ea+6</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.openjfx</groupId>
                        <artifactId>javafx-controls</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.openjfx</groupId>
                        <artifactId>javafx-graphics</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.openjfx</groupId>
                        <artifactId>javafx-base</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.17.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter-api</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter-engine</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.10.1</version>
                    <configuration>
                        <source>22</source>
                        <target>22</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

    and module-info.java:

    module org.jamesd.examples.maps {
        requires javafx.controls;
        requires com.gluonhq.maps;
        requires java.net.http;
        requires com.fasterxml.jackson.core;
        requires com.fasterxml.jackson.databind;
    
        exports org.jamesd.examples.maps;
    }
    
    Login or Signup to reply.
  2. I had the same issue a while ago and my (maybe only temporary) solution was to add the api version 3.55 to the script call
    src="http://maps.google.com/maps/api/js?v=3.55&key=API_KEY&libraries=maps,geometry,visualization,marker&quot;
    As everythings works when using mozilla I think it is a problem with the WebView ar the WebView Support. I am looking forward to a permanent solution, because I don’t know how long this version will be available.
    Any advice is welcome…

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search