2016-01-11 36 views
11

問題禁用谷歌地圖平鋪褪色或檢測地圖是否完全呈現?

有沒有辦法來禁用谷歌地圖瓷磚的褪色?或者有沒有辦法檢測地圖是否完全呈現?

問題

我想獲得一個事件時,地圖完全加載(和渲染),並採取截圖。我想這些事件在幾個答案

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

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

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

建議,但瓷磚仍然褪色,即使在所有裝載和上述事件被解僱。

它看起來像這樣:左邊是谷歌地圖,右邊是採取事件發生後發射一個自動截圖:

enter image description here

代碼

的代碼是HTML和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); 
    } 
} 

控制檯輸出

[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 

當然一個可以等待第二個,並採取截圖的話,但是這並不是一個妥善的解決辦法。

回答

3

關於上述事件

檢查說明谷歌地圖事件很獨立並列。即使在地圖可能正在平移的情況下,tilesloaded聽衆也會特別點燃。這可以通過試圖強調在這個聽衆例如here

另外圖中可以看出,idle聽衆被泛期間進行幾次。最好將定時器放置一段時間,以確定用戶已完全停止平移/縮放,然後截取屏幕截圖。這裏有一個簡單的例子:

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

謝謝,但正如問題中所述,我希望有一個適當的解決方案,而不是猜測地圖可以完全加載和渲染。計時器不是一個選項。 – Roland

+1

這是我必須解決的解決方案,因爲似乎沒有一個合適的解決方案。我認爲200ms的超時就足夠了。注意:在google api源代碼中有一個屬性「tileFadeMode」,但沒有關於它的描述,代碼被混淆了。 – Roland

2

你使用的是什麼谷歌地圖API版本?

您可以選擇「tilesloaded」事件。

還有一個事件叫做「空閒」,一旦你的地圖處於空閒狀態,這個事件就會被調用。

地圖進入閒置狀態時會調用「空閒」事件 - 所有加載完全或發生錯誤的地方都會發生並且地圖加載失敗。 我認爲「空閒」更可靠,然後tilesloaded/bounds_changed和使用addListenerOnce方法閉包中的代碼被執行第一次「空閒」被觸發,然後事件被分離。在Link

+0

謝謝你,但在這個問題說我已經嘗試過這些。 API版本爲3. – Roland

+0

這可能是由於您的Internet速度或瀏覽器性能。你可以發佈任何視頻如何加載,或者你是否可以提供任何代碼? –

+1

我添加了代碼,它在JavaFX中。不知道你是否可以利用它。無論如何,感謝您的幫助:-)互聯網速度不應該是一個問題。只有完成所有事件後,事件纔會開始。我的猜測是,當tileloaded或idle事件被觸發時,一些JavaScript淡入淡出仍然同時運行。 – Roland

3

我一直沒能找到一種方法來確定何時地圖滿載,但你可以得到的地圖在第一位置的圖片,使用谷歌映射靜態api https://developers.google.com/maps/documentation/static-maps/intro

下面的代碼片段加載一個靜態地圖:

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

非常感謝!但不幸的是,這僅限於640x640分辨率。 – Roland

+0

@羅蘭,是的,除非你有一個高級賬戶。當Streetview完成加載時我正在查找通知時,我開始使用該方法。當時沒有解決方案的限制。我對SO進行了廣泛的搜索,所有答案都表明,瓷磚裝載完成後不會發生任何事件。 – brenzy

+0

是的,但即使有高級賬戶,它也只有2048x2048。 – Roland