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();
}
}

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;
}
WebView
) in JavaFX, which may work better for your purposes.