禁用谷歌地图平铺淡出或检测地图是否完全呈现?

时间:2016-01-11 06:46:20

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

问题

有没有办法禁用Google地图图块的淡化?或者有没有办法检测地图是否完全渲染?

问题

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

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

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

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

但是即使加载了所有事件并且上面提到的事件被触发,瓷砖仍在消失。

它看起来像这样:左边是谷歌地图,右边是在事件发生后拍摄的自动截图:

a docker container

代码

代码在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

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

3 个答案:

答案 0 :(得分:3)

谷歌地图事件非常独立。即使地图可能正在平移,tilesloaded听众也会特别开火。通过尝试在此侦听器示例中here施加压力地图,可以看出这一点。{/ p>

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

答案 1 :(得分:3)

我无法找到确定地图何时完全加载的方法,但您可以首先使用google maps static 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>

答案 2 :(得分:2)

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

您可以查看&#34; tilesloaded&#34;事件

还有一个名为&#34; idle&#34;一旦你的地图处于闲置状态,它就会被调用。

&#34;空闲&#34;当地图进入空闲状态时调用事件 - 完全加载所有内容或发生错误并且映射无法加载。 我认为&#34;闲置&#34;比tilesload / bounds_changed更可靠,并且使用addListenerOnce方法,闭包中的代码第一次执行&#34; idle&#34;被解雇然后分离事件。

Link

检查有关上述事件的说明