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

3

365

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.

Rothmuller answered 23/5, 2017 at 9:8 Comment(7)
For reference: reddit.com/r/ProgrammerHumor/comments/6e6wu6/…Rendarender
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.Laise
Could it be, that the JavaFX thing has problems using ajax to receive calls/data from non-HTTP served endpoints like your test.json?Kwasi
@Kwasi 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 loadingRothmuller
@GökhanKurt would you read the answer aboveRothmuller
@ChandlerBing Then the problem might be in the reload code. Use WebEngine's reload method like this.webView.getEngine().reload(). Or you can use the executeScript method to reload marker data without refreshing the page. For reference see: docs.oracle.com/javase/8/javafx/api/javafx/scene/web/…Laise
@GökhanKurt reload() doesn't work, its useless. I will try the executeScript thoughRothmuller
F
74

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.

Footsore answered 30/5, 2017 at 16:49 Comment(2)
Hi there, so you think that the problem is with ajax calls. Ok, I will try to find another way to send the GPS positions to javascript (using executescript).Rothmuller
No its not the ajax calls. The problem is that I can't refresh the webview to show the new marker. When I open the app, the map is displayed which means that the ajax calls work fine.Rothmuller
P
21

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.

Pedersen answered 30/5, 2017 at 18:12 Comment(4)
No thats not the problem, I display the map when I open the app usign that line. But when I click on a button, I want to put a marker on the map. But I can't refresh the map to show the marker on it.Rothmuller
Ah, gotcha. I misunderstood. Are you saying the basic page is loading but no markers are getting loaded? Is the map showing up in your webView at least? I think you may be on to something as you mentioned initMap above. If it is only calling that function on page load then it probably is not getting edits to the json. You would have to refresh the page within the webview or tell the page to call initMap again somehow --maybe through ajax? Unless JavaFX can get the webView to call javascript functionsPedersen
When the app is opened everything that exists in the json file is loaded. But when I change a value while the app is open, I want to refresh the webview to see the change. But it just doesn't work ! This is the problem.Rothmuller
See my edit -- does adding a manual call from Java to the initMap function help?Pedersen
B
5

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.

Bootlace answered 2/6, 2017 at 9:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.