2016-01-11 8 views
11

PytanieWyłączyć blokowanie kafelków map Google lub wykryć, czy mapa jest w pełni renderowana?

Czy istnieje sposób na wyłączenie zacierania się kafelków map Google? Czy istnieje sposób na wykrycie, czy mapa jest w pełni renderowana?

Problem

Chciałbym uzyskać zdarzenia, gdy mapa jest w pełni załadowany (i renderowane) i zrzut ekranu. Próbowałem te wydarzenia jak sugeruje w kilku odpowiedziach

google.maps.event.addListenerOnce(map, 'tilesloaded', function(){ 
    // screenshot 
}); 

google.maps.event.addListener(map, 'idle', function(){ 
    // screenshot 
}); 

window.onload = function(e){ 
    // screenshot 
}; 

ale płytki są nadal blaknięcie, nawet po załadowaniu i powyższe wydarzenia zwolniony.

Wygląda to tak: po lewej to google maps, prawda jest zautomatyzowany screenshot że zostanie podjęta po wydarzeniach opalane:

enter image description here

Kod

Kod jest w html i JavaFX

demo.html

<!DOCTYPE html> 
<html> 
<head> 

    <script src="http://maps.google.com/maps/api/js?sensor=false"></script> 

    <style> 
     html, body { 
     height: 100%; 
     margin: 0; 
     padding: 0; 
     } 
    #mapcanvas { 
     /* height: 4000px; we need the value from java */ 
     width: 100% 
    } 
    </style> 


</head> 
<body> 
<div id="mapcanvas"></div> 

<script type="text/javascript"> 

     console.log("Loading map tiles"); 

     // set map canvas height 
     document.getElementById('mapcanvas').style.height = window.mapCanvasHeight; 

     document.map = new google.maps.Map(document.getElementById("mapcanvas")); 

     // the global window object is used to set variables via java 
     var latlng = new google.maps.LatLng(window.lat, window.lon); 

     // https://developers.google.com/maps/documentation/javascript/reference?hl=en 
     var Options = { 
      zoom: 17, 
      center: latlng, 
      mapTypeId: google.maps.MapTypeId.SATELLITE, 
      disableDefaultUI: true, 
     }; 

     var map = new google.maps.Map(document.getElementById("mapcanvas"), Options); 
     document.goToLocation = function(x, y) { 

      console.log("goToLocation, lat: " + x +", lon:" + y); 

      var latLng = new google.maps.LatLng(x, y); 
      map.setCenter(latLng); 

     } 

     // this doesn't work properly because some tiles fade in and hence you get a snapshot with faded tiles 
     google.maps.event.addListenerOnce(map, 'tilesloaded', function(){ 

      console.log("tilesloaded"); 

      java.onMapLoadingFinished(); 

     }); 

     // This event is fired when the map becomes idle after panning or zooming. 
     // it works after all tiles were first loaded and you zoom afterwards (but doesn't work a 100% all the time) 
     // initially you still get faded tiles 
     google.maps.event.addListener(map, 'idle', function(){ 

      console.log("idle"); 

      // java.onMapLoadingFinished(); 

     }); 

     window.onload = function(e){ 

      console.log("window.onload"); 

      // java.onMapLoadingFinished(); 

     }; 

</script> 
</body> 
</html> 

GoogleApp.java

import java.net.URL; 

import javafx.application.Application; 
import javafx.beans.value.ChangeListener; 
import javafx.concurrent.Worker.State; 
import javafx.scene.Scene; 
import javafx.scene.SnapshotParameters; 
import javafx.scene.control.ScrollPane; 
import javafx.scene.control.SplitPane; 
import javafx.scene.control.TextField; 
import javafx.scene.image.ImageView; 
import javafx.scene.image.WritableImage; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.Pane; 
import javafx.scene.paint.Color; 
import javafx.scene.web.WebEngine; 
import javafx.scene.web.WebView; 
import javafx.stage.Stage; 
import netscape.javascript.JSObject; 

/** 
* Load google maps map and take a snapshot from it 
*/ 
public class GoogleApp extends Application { 

    // initial lat/lon 
    double lat = 38.897676; 
    double lon = -77.036483; 

    double browserWidth = 1024; // this is set to 100% in the html css for the map canvas 
    double browserHeight = 8000; // this is used in the html map canvas; IMPORTANT: when the app freezes during zoom in, then the problem is probably the height; it works without problems with height of e. g. 360 

    MyBrowser webBrowser; 
    TextField latitudeTextField; 
    TextField longitudeTextField; 

    private ScrollPane snapshotScrollPane; 

    @Override 
    public void start(Stage stage) throws Exception { 

     webBrowser = new MyBrowser(browserWidth, browserHeight); 

     ScrollPane browserScrollPane = new ScrollPane(webBrowser); 
     snapshotScrollPane = new ScrollPane(); 

     SplitPane splitPane = new SplitPane(browserScrollPane, snapshotScrollPane); 

     BorderPane borderPane = new BorderPane(); 
     borderPane.setCenter(splitPane); 
     borderPane.setRight(snapshotScrollPane); 

     Scene scene = new Scene(borderPane, 1024, 768); 
     stage.setScene(scene); 
     stage.show(); 

    } 

    private void createSnapshot() { 

     SnapshotParameters parameters = new SnapshotParameters(); 
     parameters.setFill(Color.TRANSPARENT); 

     // new image from clipped image 
     WritableImage wim = null; 
     wim = webBrowser.snapshot(parameters, wim); 

     snapshotScrollPane.setContent(new ImageView(wim)); 
    } 

    public class JavaBridge { 

     public void onMapLoadingFinished() { 

      System.out.println("[javascript] onMapLoadingFinished"); 

      createSnapshot(); 

     } 

     public void log(String text) { 
      System.out.println("[javascript] " + text); 
     } 

    } 

    private class MyBrowser extends Pane { 

     WebView webView; 
     WebEngine webEngine; 

     public MyBrowser(double width, double height) { 

      webView = new WebView(); 
      webView.setPrefWidth(width); 
      webView.setPrefHeight(height); 

      webEngine = webView.getEngine(); 

      webEngine.getLoadWorker().stateProperty().addListener((ChangeListener<State>) (observable, oldState, newState) -> { 

       System.out.println("[State] " + observable); 

       if (newState == State.SCHEDULED) { 

        System.out.println("Webpage loaded"); 

        // inject "java" object 
        JSObject window = (JSObject) webEngine.executeScript("window"); 
        JavaBridge bridge = new JavaBridge(); 
        window.setMember("java", bridge); 

        // console.log 
        webEngine.executeScript("console.log = function(message)\n" + "{\n" + " java.log(message);\n" + "};"); 

        // initialize variables 

        // canvas height 
        webEngine.executeScript("window.mapCanvasHeight = '" + browserHeight + "px'"); 

        System.out.println("Latitude = " + lat + ", Longitude = " + lon); 

        webEngine.executeScript("window.lat = " + lat + ";" + "window.lon = " + lon + ";"); 

       } 

       if (newState == State.SUCCEEDED) { 
        // createSnapshot(); 
       } 
      }); 

      // logging other properties 
      webEngine.getLoadWorker().exceptionProperty().addListener((ChangeListener<Throwable>) (ov, t, t1) -> System.out.println("[Exception] " + t1.getMessage())); 
      webEngine.getLoadWorker().progressProperty().addListener((ChangeListener<Number>) (observable, oldState, newState) -> System.out.println("[Progress] " + newState)); 
      webEngine.getLoadWorker().workDoneProperty().addListener((ChangeListener<Number>) (observable, oldState, newState) -> System.out.println("[WorkDone] " + newState)); 
      webEngine.getLoadWorker().runningProperty().addListener((ChangeListener<Boolean>) (observable, oldState, newState) -> System.out.println("[Running] " + newState)); 
      webEngine.getLoadWorker().messageProperty().addListener((ChangeListener<String>) (observable, oldState, newState) -> System.out.println("[Message] " + newState)); 


      final URL urlGoogleMaps = getClass().getResource("demo.html"); 
      webEngine.load(urlGoogleMaps.toExternalForm()); 

      // TODO: how should that work? it doesn't do anything when we invoke an alert 
      webEngine.setOnAlert(e -> System.out.println("Alert: " + e.toString())); 

      getChildren().add(webView); 

     } 

    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

wyjście konsola

[WorkDone] 0.0 
[Progress] 0.0 
[State] ReadOnlyObjectProperty [bean: [email protected], name: state, value: SCHEDULED] 
Webpage loaded 
Latitude = 38.897676, Longitude = -77.036483 
[Running] true 
[State] ReadOnlyObjectProperty [bean: [email protected], name: state, value: RUNNING] 
[WorkDone] 50.0 
[Progress] 0.5 
[WorkDone] 66.66666666666667 
[Progress] 0.6666666666666667 
[WorkDone] 76.59016927083334 
[Progress] 0.7659016927083334 
[javascript] Loading map tiles 
[WorkDone] 79.69424280262338 
[Progress] 0.7969424280262337 
[WorkDone] 82.91479192680356 
[Progress] 0.8291479192680357 
[WorkDone] 86.13534105098375 
[Progress] 0.8613534105098376 
[WorkDone] 87.9566062307981 
[Progress] 0.879566062307981 
[WorkDone] 89.554165026828 
[Progress] 0.89554165026828 
[WorkDone] 89.62836770069038 
[Progress] 0.8962836770069037 
[javascript] idle 
[WorkDone] 89.70492380394815 
[Progress] 0.8970492380394814 
[WorkDone] 89.78964107398804 
[Progress] 0.8978964107398804 
[WorkDone] 89.85311355504936 
[Progress] 0.8985311355504936 
[WorkDone] 89.91528395017954 
[Progress] 0.8991528395017955 
[WorkDone] 89.9528416875862 
[Progress] 0.899528416875862 
[javascript] window.onload 
[Message] Loading complete 
[WorkDone] 100.0 
[Progress] 1.0 
[State] ReadOnlyObjectProperty [bean: [email protected], name: state, value: SUCCEEDED] 
[Running] false 
[javascript] tilesloaded 
[javascript] onMapLoadingFinished 
taking screenshot 

jednego kursu mógłby poczekać sekundę, a następnie podjąć zrzut ekranu, ale nie jest to właściwe rozwiązanie.

Odpowiedz

3

Google Maps wydarzenia związane są bardzo indywidualnie. Słuchacz tilesloaded szczególnie strzela nawet podczas panoramowania mapy. Można to zaobserwować, próbując podkreślić mapę w tym przykładzie słuchaczy here

Dodatkowo idle słuchacz zostaje zwolniony kilka razy podczas patelni. Najlepiej jest umieścić timer z ustalonym czasem, który określałby, że użytkownik całkowicie przestawił panoramowanie/powiększanie, a następnie wykonał zrzut ekranu.Oto prosty przykład:

google.maps.event.addListener(map, "idle", function() { 
    var mapMoveTimer, tileLoadedListener; 
    var moveDetect = google.maps.event.addListener(map, "bounds_changed", function() { 
     clearTimeout(mapMoveTimer); 
    }); 

    mapMoveTimer = setTimeout(function() { 
     google.maps.event.removeListener(moveDetect); 

     // here is your code 
     tileLoadedListener = google.maps.event.addListenerOnce(map, 'tilesloaded', function(){ 
      console.log("tilesloaded"); 
      java.onMapLoadingFinished(); 

      // make sure to remove the listener so it isn't fired multiple times. 
      google.maps.event.removeListener(tileLoadedListener); 
     }); 
    }); 

    }, 1000); 
}); 
+0

Dziękuję, ale jak stwierdzono w pytaniu, chciałbym mieć odpowiednie rozwiązanie, a nie odgadnąć, kiedy mapa może być w pełni załadowana i renderowana. Timer nie jest opcją. – Roland

+1

Jest to rozwiązanie, z którym muszę się uporać, ponieważ nie wydaje się, aby był właściwy. Pomyślałem, że limit czasu wynoszący 200ms jest wystarczający. Uwaga: w źródle Google api znajduje się właściwość "tileFadeMode", ale nie ma w niej opisu, a kod jest zaciemniany. – Roland

2

Jakiej wersji interfejsu API mapy Google używasz?

Możesz sprawdzić za pomocą zdarzenia "tilesloaded".

Istnieje także zdarzenie zwane "bezczynnością", które zostanie wywołane, gdy mapa będzie w stanie bezczynności.

Zdarzenie "bezczynności" jest wywoływane, gdy mapa przechodzi w stan bezczynności - wszystko zostało całkowicie załadowane lub wystąpiło coś nie tak i mapa nie powiodła się. Myślę, że "idle" jest bardziej niezawodny niż tilesloaded/bounds_changed i przy użyciu metody addListenerOnce kod w zamknięciu jest wykonywany przy pierwszym uruchomieniu "bezczynności", a następnie zdarzenie zostaje odłączone.

Sprawdź opis o powyższych wydarzeniach w Link

+0

Dziękuję, ale jak wskazano w pytaniu Próbowałem już tych. Wersja interfejsu API to 3. – Roland

+0

Może to być spowodowane szybkością Internetu lub wydajnością przeglądarki. Czy możesz zamieścić dowolne wideo o tym, jak się ładuje lub czy możesz podać dowolny kod? –

+1

Dodałem kod, to jest w JavaFX. Nie wiem, czy możesz z tego skorzystać. W każdym razie, dzięki za pomoc :-) Szybkość internetu nie powinna stanowić problemu. Zdarzenie musi wystrzelić dopiero po zakończeniu wszystkich. Domyślam się, że niektóre blaknięcie javascripta nadal działa jednocześnie, gdy zdarzenia tileloaded lub bezczynne są uruchamiane. – Roland

3

nie udało się znaleźć sposób, aby określić, kiedy mapa jest w pełni załadowany, ale można dostać mapę jako obraz w pierwszej kolejności, przy użyciu Google mapy statyczne api https://developers.google.com/maps/documentation/static-maps/intro.

Oto fragment kodu, który ładuje statyczną mapę:

<!DOCTYPE html> 
 
<html> 
 
<head lang="en"> 
 
    <meta charset="UTF-8"> 
 
    <title></title> 
 
</head> 
 
<body> 
 

 
<div id="staticView" style="width:400px; height:400px"></div> 
 

 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> 
 

 
<script type="text/javascript"> 
 
    $(document).ready(function() { 
 
    var mapImage = new Image(); 
 
    var eltStaticView = $("#staticView"); 
 
    $(mapImage).one('load', function() { 
 
     eltStaticView.css('background-image', 'url(' + mapImage.src + ')'); 
 
     console.log("Image Has Loaded"); 
 
     $(mapImage).unbind(); 
 
    }); 
 
    $(mapImage).error(function() { 
 
     $(mapImage).unbind(); 
 
    }); 
 
    var imageSrc = "https://maps.googleapis.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=12&size=400x400&maptype=satellite"; //+ "&key=" + YOUR_API_KEY 
 
    mapImage.src = imageSrc; 
 
    }); 
 
</script> 
 
</body> 
 
</html>

+0

Dziękuję bardzo! Ale niestety jest to ograniczone do rozdzielczości 640x640. – Roland

+0

@Roland, tak, chyba że masz konto premium. Zacząłem używać tej metody, gdy szukałem powiadomienia po zakończeniu ładowania Street View. W tym czasie nie było limitu rozdzielczości. Zrobiłem obszerne wyszukiwanie SO, a wszystkie odpowiedzi wskazywały, że nie ma zdarzenia wystrzelonego, gdy płytki zakończą ładowanie. – brenzy

+0

Tak, ale nawet z kontem premium będzie to tylko 2048x2048. – Roland

Powiązane problemy