2016-01-11 20 views
11

DomandaDisabilita la dissolvenza delle tile di google maps o rileva se la mappa è completamente renderizzata?

C'è un modo per disattivare la dissolvenza di Maps Google piastrelle? O c'è un modo per rilevare se una mappa è completamente renderizzata?

Problema

mi piacerebbe ottenere un evento quando una mappa è a pieno carico (e reso) e prendere uno screenshot. Ho provato a questi eventi come suggerito in varie risposte

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

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

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

ma le tessere sono ancora svanendo, anche dopo tutto è caricato e gli eventi di cui sopra licenziato.

Sembra che questo: a sinistra è Google Maps, a destra è uno screenshot automatizzato che viene assunto dopo gli eventi sparati:

enter image description here

Codice

Il codice è in html e 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); 
    } 
} 

output della console

[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 

Naturalmente si poteva aspettare un secondo e prendere screenshot allora, ma non è una soluzione adeguata.

risposta

3

mappe di Google gli eventi sono legati molto individualmente. Il listener tilesloaded si attiva in particolare anche mentre la mappa potrebbe eseguire panoramiche. Questo può essere visto cercando di sottolineare la mappa in questo ascoltatori esempio here

Inoltre, l'ascoltatore idle viene licenziato più volte durante una padella. Potrebbe essere meglio posizionare un timer con una quantità di tempo prestabilita che determinerebbe che l'utente ha completamente interrotto il panning/zooming e quindi acquisisce lo screenshot.Ecco un esempio di base:

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

Grazie, ma come indicato nella domanda mi piacerebbe avere una soluzione adeguata e non un'ipotesi quando la mappa potrebbe essere completamente caricata e renderizzata. Un timer non è un'opzione. – Roland

+1

Questa è la soluzione con cui devo andare, poiché non sembra esserci una soluzione adeguata. Ho pensato che fosse sufficiente un timeout di 200 ms. Nota: nell'origine di google api esiste una proprietà "tileFadeMode", ma non esiste una descrizione e il codice è offuscato. – Roland

2

Quale versione dell'API della mappa di Google si sta utilizzando?

È possibile verificare con l'evento "tilesloaded".

Inoltre, vi è un evento chiamato "inattivo" che verrà chiamato una volta che la mappa è inattiva.

L'evento "inattivo" viene chiamato quando la mappa passa allo stato di inattività - tutto è stato caricato completamente o è successo qualcosa di sbagliato e la mappa non è stata caricata. Penso che "idle" sia più affidabile di tilesloaded/bounds_changed e che utilizzi il metodo addListenerOnce il codice nella chiusura sia eseguito la prima volta che "idle" viene attivato e quindi l'evento è staccato.

check descrizione di eventi di cui sopra a Link

+0

Grazie, ma come dichiarato nella domanda che ho già provato questi. La versione API è 3. – Roland

+0

Ciò può essere dovuto alla velocità di Internet o alle prestazioni del browser. Puoi pubblicare qualsiasi video di come viene caricato o se puoi fornire qualsiasi codice? –

+1

Ho aggiunto il codice, è in JavaFX. Non so se puoi farne uso. Comunque, grazie per l'aiuto :-) La velocità di internet non dovrebbe essere un problema. L'evento deve sparare solo quando tutto è finito. La mia ipotesi è che alcuni sbiadimenti in javascript continuino a essere eseguiti simultaneamente mentre vengono attivati ​​gli eventi caricati o inattivi. – Roland

3

non sono stato in grado di trovare un modo per determinare quando la mappa è a pieno carico, ma è possibile ottenere la mappa come immagine, in primo luogo, utilizzando il google maps static api https://developers.google.com/maps/documentation/static-maps/intro.

Ecco un frammento di codice che carica una mappa statica:

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

Grazie mille! Ma sfortunatamente è limitato a una risoluzione 640x640. – Roland

+0

@Roland, Sì, a meno che tu non abbia un account premium. Ho iniziato a utilizzare quel metodo quando stavo cercando una notifica quando Streetview aveva terminato il caricamento. A quel tempo non c'erano limiti di risoluzione. Ho fatto una ricerca approfondita di SO, e tutte le risposte hanno indicato che non si verifica alcun evento quando le tessere finiscono di caricare. – brenzy

+0

Sì, ma anche con un account premium sarebbe solo 2048x2048. – Roland