【问题标题】:Disable google maps tile fading or detect if map is fully rendered?禁用谷歌地图瓷砖褪色或检测地图是否完全呈现?
【发布时间】:2016-01-11 06:46:20
【问题描述】:

问题

有没有办法禁用谷歌地图图块的褪色?或者有没有办法检测地图是否完全渲染?

问题

我想在地图完全加载(并渲染)时获取一个事件并截取屏幕截图。我按照几个答案中的建议尝试了这些事件

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

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

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

但是即使在加载完所有内容并触发了上述事件之后,图块仍在褪色。

看起来像这样:左边是谷歌地图,右边是事件触发后自动截取的截图:

代码

代码在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: javafx.scene.web.WebEngine$LoadWorker@6954b271, name: state, value: SCHEDULED]
Webpage loaded
Latitude = 38.897676, Longitude = -77.036483
[Running] true
[State] ReadOnlyObjectProperty [bean: javafx.scene.web.WebEngine$LoadWorker@6954b271, 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: javafx.scene.web.WebEngine$LoadWorker@6954b271, name: state, value: SUCCEEDED]
[Running] false
[javascript] tilesloaded
[javascript] onMapLoadingFinished
taking screenshot

当然可以稍等片刻,然后截取屏幕截图,但这不是一个合适的解决方案。

【问题讨论】:

    标签: google-maps google-maps-api-3 javafx


    【解决方案1】:

    谷歌地图事件非常独立。即使地图可能正在平移,tilesloaded 侦听器也会特别触发。这可以通过尝试在此侦听器示例 here987654321@

    中强调地图来看出

    此外,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);
    });
    

    【讨论】:

    • 谢谢,但正如问题中所述,我希望有一个适当的解决方案,而不是猜测地图何时可以完全加载和渲染。计时器不是一种选择。
    • 这是我必须采用的解决方案,因为似乎没有合适的解决方案。我认为 200 毫秒的超时就足够了。注意:在google api源码中有一个属性“tileFadeMode”,但是没有关于它的描述并且代码被混淆了。
    【解决方案2】:

    我无法找到确定地图何时完全加载的方法,但您可以首先使用谷歌地图静态 apihttps://developers.google.com/maps/documentation/static-maps/intro 将地图作为图像获取。

    这是一个加载静态地图的代码sn-p:

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

    【讨论】:

    • 非常感谢!但不幸的是,这仅限于 640x640 分辨率。
    • @Roland,是的,除非您有高级帐户。当我在 Streetview 完成加载时寻找通知时,我开始使用该方法。当时没有分辨率限制。我对 SO 进行了广泛的搜索,所有答案都表明当瓷砖完成加载时没有触发事件。
    • 是的,但即使使用高级帐户也只有 2048x2048。
    【解决方案3】:

    您使用的是什么 Google 地图 API 版本?

    您可以使用“tilesloaded”事件进行检查。

    还有一个称为“空闲”的事件,一旦您的地图处于空闲状态,就会调用该事件。

    当地图进入空闲状态时会调用“空闲”事件 - 一切都已完全加载或发生错误并且地图无法加载。 我认为“idle”比 tileloaded/bounds_changed 更可靠,并且使用 addListenerOnce 方法,在第一次触发“idle”时执行闭包中的代码,然后分离事件。

    Link查看有关上述事件的描述

    【讨论】:

    • 谢谢,但正如问题中所述,我已经尝试过这些。 API 版本为 3。
    • 这可能与您的 Internet 速度或浏览器性能有关。你能发布任何关于它如何加载的视频,或者你是否可以提供任何代码?
    • 我添加了代码,它在 JavaFX 中。不知道能不能用上。无论如何,感谢您的帮助:-) 互联网速度应该不是问题。事件必须仅在全部完成后触发。我的猜测是,在触发 tileloaded 或 idle 事件时,一些 javascript fading 仍在同时运行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 2013-08-01
    • 1970-01-01
    • 2014-06-27
    • 1970-01-01
    相关资源
    最近更新 更多