Google Maps & JavaFX: Display marker on the map after clicking JavaFX button

JavascriptGoogle MapsJavafxJavafx Webengine

Javascript Problem Overview


I have been trying to display a marker on the map when I click on a Button of my JavaFX application. So what happens is when I click on that button, I write the position in a JSON file, this file will be loaded in the html file that contains the map. The problem is that it works perfectly when I open the html page in the browser, but nothing happens in the JavaFX's web view, and I don't know why !

This is the html file:

<!DOCTYPE html>
<html>
  <head>
  <title>Simple Map</title>
  <meta name="viewport" content="initial-scale=1.0">
  <meta charset="utf-8">
  <style>
  /* Always set the map height explicitly to define the size of the div
   * element that contains the map. */
  /*#map {
    height: 100%;
  }*/
  #map{width:100%;height:100%;margin:auto;}
  /* Optional: Makes the sample page fill the window. */
  html, body {
    height: 100%;
    margin: 0;
    padding: 0;
  }
</style>
</head>
<body>
<div id="map"></div>
<script>
  var map;
  var marker;
  // Multiple Markers
  var markers = [];
  var pos = {lat: 46.662388, lng: 0.3599617};
  var itinerary_markers = [];
  
  function initMap() {

    var currentLat, currentLng;//Latitude et longtitude courante

    $.ajax({
      url: 'https://maps.googleapis.com/maps/api/geocode/json?address=My+ADDRESS&key=MY_KEY',
      async: false,
      dataType: 'json',
      success: function (data) {
        currentLat = data.results[0].geometry.location.lat;
        currentLng = data.results[0].geometry.location.lng;
      }
    });

    map = new google.maps.Map(document.getElementById('map'), {
      center: {lat: currentLat, lng: currentLng},
      zoom: 15,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });


    /*MARQUEUR*/ 
    $.ajax({
        async: false,
        url: 'test.json',
        data: "",
        accepts:'application/json',
        dataType: 'json',
        success: function (data) {
            for (var i = 0; i < data.hydrants.length; i++) {
                markers.push( data.hydrants[i]);
            }
        }
    });

      var posi = new google.maps.LatLng(markers[0].Lat, markers[0].Lng);
      marker = new google.maps.Marker({
          position: posi,
          map: map,
          //title: markers[i][0]
          title: markers[0].Name
        });
        
  }
</script>

<script
    src="https://code.jquery.com/jquery-3.2.1.min.js"
    integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
    crossorigin="anonymous">
</script>


<script src="https://maps.googleapis.com/maps/api/js?key=MY_KEY&callback=initMap&language=fr"
async defer></script>

</body>
</html>

When I click the button, I fill the JSON file (which works perfectly) and then I execute this to refresh the webview:

this.webView.getEngine().load(getClass().getResource("/data/index.html").toString());

As I said before, when I open the file on the browser I see the expected result, but I don't know what is the problem with the JavaFX. If there is a better way to do this please tell me.

EDIT:

I found a solution to the problem by sending directly the data (the GPS coordinates) from JavaFX to Javascript using the executeScript() method, so I don't need a json file as bridge between the two platforms. So this is an example of how the code looks like:

eng.executeScript("updateMarker(" + lat + ", " + lng + ")");//eng is a WebEngine instance

And here is the Javascript:

/*The initial latitude and longtitude*/
var currentLat = the latitude;
var currentLng = the longtitude;

function initMap() {

    map = new google.maps.Map(document.getElementById('map'), {
      center: {lat: currentLat, lng: currentLng},
      zoom: 15,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });

    var posi = new google.maps.LatLng(currentLat, currentLng);
    marker = new google.maps.Marker({
        position: posi,
        map: map,
        visible: false
    });
  }

/*The method that is I call from JavaFX*/
function updateMarker(_lat, _lng){
    marker.setPosition({lat: _lat, lng: _lng});
    map.setCenter(new google.maps.LatLng(_lat, _lng));
    marker.setVisible(true);
  }

Thank you for your comments and answers, and a special shootout to reddit.

Javascript Solutions


Solution 1 - Javascript

If I had to guess - one of two things is happening:

Either A) your javaFX is not supporting cross site ajax calls or B) it is not waiting for the asynchronous ajax response/something else is going wrong.

So let's do some testing together. Firstly can we clean this up to nest the ajax calls? Then can you add in some console.log statements to find out what each is sending back? If you miss some output we know where it's going wrong and that'll help us fix things.

Note I've changed success to the 'done' additions because success is a bit out of date, and everything is nested to eliminate the question around whether any blanks are being sent in to the next calls (synchronicity issues):

$.ajax({
	url: 'https://maps.googleapis.com/maps/api/geocode/json?address=My+ADDRESS&key=MY_KEY',
	async: false,
	dataType: 'json'
}).done(function(data) {
	currentLat = data.results[0].geometry.location.lat;
	currentLng = data.results[0].geometry.location.lng;
	console.log(currentLat);
	console.log(currentLng);
	// Multiple Markers
	var markers = [];
	var pos = {lat: 46.662388, lng: 0.3599617};
	var itinerary_markers = [];
	var map = new google.maps.Map(document.getElementById('map'), {
		center: {lat: currentLat, lng: currentLng},
		zoom: 15,
		mapTypeId: google.maps.MapTypeId.ROADMAP
	});
	console.log(map);
	/*MARQUEUR*/ 
	$.ajax({
		async: false,
		url: 'test.json',
		data: "",
		accepts:'application/json',
		dataType: 'json'
	}).done(function(data) {
		for (var i = 0; i < data.hydrants.length; i++) {
			markers.push( data.hydrants[i]);
		}
		console.log(markers);
		var posi = new google.maps.LatLng(markers[0].Lat, markers[0].Lng);
		console.log(posi);
		var marker = new google.maps.Marker({
			position: posi,
			map: map,
			//title: markers[i][0]
			title: markers[0].Name
		});
		console.log(marker);
	}).fail(function(jqXHR, testStatus){
		console.log(textStatus);
	});
}).fail(function(jqXHR, testStatus){
	console.log(textStatus);
});

Here is a link on getting the console.log output in to System.out in Java if this is an issue: https://stackoverflow.com/questions/28687640/javafx-8-webengine-how-to-get-console-log-from-javascript-to-system-out-in-ja

...Also hello from reddit.

Solution 2 - Javascript

In the line:

this.webView.getEngine().load(getClass().getResource("/data/index.html").toString());

I would try double-checking the path to the file is correct. Reading other answers on StackOverflow, it looks like this is supposed to be relative to the package root and either with or without the leading '/'. i.e. getResource("data/index.html"). But, then again, maybe you would already be seeing errors related to getResource()...

My next go to, for debugging purposes, would be to comment out the part where you write the JSON and just manually write some good JSON and just try to get it to show up in the webView. The fewer moving parts, the better. If you can get it to work with your pre-written JSON then you can assume it is some problem with the JSON you are writing with Java and then it being loaded to the HTML.

Edit: I dug a bit deeper. This could be totally wrong again but maybe you can try manually calling the initMap() function from Java that your web browser normally calls onload. https://stackoverflow.com/questions/19420753/how-to-call-a-javascript-function-from-a-javafx-webview-on-button-click has some more details. Try this.webView.getEngine().executeScript("initMap()"); after you edit the JSON with your button.

Edit 2 As an aside, too, it might make sense to split initMap into an initMap and updateMap function for making the map to begin with and then setting the markers on the map. Though this is hardly breaking anything.

Solution 3 - Javascript

If your mouse-wheel is used to zoom the map out or in and the marker appears, then you are experiencing the same issue that I did.

Try manually zooming the mapview to restore the markers. I also had to employ this technique when displaying a route from the Directions Service, otherwise the waypoint markers were not displaying correctly.

This is the code in my Javafx controller class to do so:

KeyFrame kf1 = new KeyFrame(Duration.seconds(0.75), e -> map.setZoom(map.getZoom() - 1));
KeyFrame kf2 = new KeyFrame(Duration.seconds(1.5), e -> map.setZoom(map.getZoom() + 1));
Timeline timeline = new Timeline(kf1, kf2);
Platform.runLater(timeline::play);

This was using GMapsFX, which is just a thin Java wrapper around javascript engine calls on a JavaFX WebView. Hopefully it helps.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChandler BingView Question on Stackoverflow
Solution 1 - JavascriptDavid GView Answer on Stackoverflow
Solution 2 - JavascriptMattView Answer on Stackoverflow
Solution 3 - JavascriptFraserView Answer on Stackoverflow