360
votes

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.

3
Although the script is not written in the best way possible, there doesn't seem anything wrong with it. There are couple possibilities that may make it not work. Ensure that the new marker is added to the top of the json file. Pass cache: false to the ajax call in case it is cached, and make sure you target the right file. Also some logs here and there would help identify the problem.Gökhan Kurt
Could it be, that the JavaFX thing has problems using ajax to receive calls/data from non-HTTP served endpoints like your test.json?Sebastian
@Sebastian Whats driving me crazy is that when I open the app the json file is loaded correctly and I see the expected result. The problem is when I update the json file with another value (while the app is open) it just doesn't want to refresh to display the new marker ! So there is no problem with the ajax calls. But can it be the callback thing? because the initMap() is called while the page is loadingChandler Bing
@GökhanKurt would you read the answer aboveChandler Bing

3 Answers

70
votes

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: JavaFX 8 WebEngine: How to get console.log() from javascript to System.out in java?

...Also hello from reddit.

19
votes

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. 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.

4
votes

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.