简体   繁体   English

OpenLayers 3重新加载图层

[英]OpenLayers 3 Reload Layer(s)

I am working on a project using OL3 in which I need to be able to manually (by button press) or automatically (time based) reload vector layers IF they have been updated since the last load using HTTP conditional GETs (304 headers and such). 我正在使用OL3进行项目,我需要能够手动(通过按下按钮)或自动(基于时间)重新加载矢量图层,如果它们自上次加载后使用HTTP条件GET(304标题等)已更新。

I found this very old post ( https://gis.stackexchange.com/questions/333/how-to-dynamically-refresh-reload-a-kml-layer-in-openlayers ) for KML layers, but it appears to use variables no longer found in OL3 and I am not sure that it would allow for only loading files that have been modified since the last load. 我发现这个非常古老的帖子( https://gis.stackexchange.com/questions/333/how-to-dynamically-refresh-reload-a-kml-layer-in-openlayers )用于KML图层,但它似乎使用了在OL3中找不到变量,我不确定它是否只允许加载自上次加载以来已修改过的文件。 At first glance it appears that a full reload is forced, even if the file has not been modified. 乍一看,即使文件尚未修改,也会强制执行完全重新加载。

There does not seem to be anything in the API that resembles a reload function for the map or layer objects in OL3. API中似乎没有任何类似于OL3中的地图或图层对象的重新加载功能。 Is there a way to do this? 有没有办法做到这一点?

Update 1: 更新1:

I found a possible way to do this as an answer in this question: https://gis.stackexchange.com/questions/125074/openlayers3-how-to-reload-a-layer-from-geoserver-when-underlying-data-change using the code: 我在这个问题中找到了一种可能的方法来解决这个问题: https//gis.stackexchange.com/questions/125074/openlayers3-how-to-reload-a-layer-from-geoserver-when-underlying-data使用代码进行更改

layer.getSource().updateParams({"time": Date.now()});

however when I run this code I get the error: 但是,当我运行此代码时,我收到错误:

TypeError: selectedLayer.getSource(...).updateParams is not a function

Upon checking the API Reference for OL3 it appears that no such functions exist. 在检查OL3的API参考时,似乎不存在这样的功能。 The closest is setProperties() or setAttributions(). 最接近的是setProperties()或setAttributions()。 Neither of which work. 两者都不奏效。 It also appears that not all layer types implement getSource(). 似乎并非所有图层类型都实现了getSource()。

Update 2: 更新2:

The refresh() reloads the tiles, but does not appear to be requesting them from the server. refresh()重新加载磁贴,但似乎没有从服务器请求它们。 Rather, it appears they are being loaded from a cache (but not the HTTP cache). 相反,它似乎是从缓存(但不是HTTP缓存)加载。 No requests are made, no HTTP 304s or anything like that. 没有请求,没有HTTP 304或类似的东西。 Will be trying a variant of the KML approach and posting the results soon. 将尝试使用KML方法的变体并尽快发布结果。

Update 3: 更新3:

After trying LOTS of different solutions I accidentally stumbled upon something that worked for vector layers. 在尝试了很多不同的解决方案后,我不小心偶然发现了一些适用于矢量图层的东西。 By calling the layer source.clear() function and then calling Map.updateSize(), the layer is automagically reloaded from it's source URL. 通过调用图层source.clear()函数然后调用Map.updateSize(),该图层将从其源URL自动重新加载。 An XHR GET request is issued and if the source file has changed, it will be reloaded from the file. 发出XHR GET请求,如果源文件已更改,将从文件重新加载。 If the source file has not changed, a 304 will be issued and the source will be reloaded from the cache. 如果源文件未更改,则将发出304并且将从缓存重新加载源。

Below is a function that should uses this method to reload a given layer: 下面是一个应该使用此方法重新加载给定图层的函数:

function refreshLayer(selectedLayer)
{
    var selectedLayerSource = selectedLayer.getSource();

    if(selectedLayerSource instanceof ol.source.Vector)
    {
        //do vector reload
        selectedLayerSource.clear();
        map.updateSize();
    }
    else
    {
        //reload the entire page
        window.location.reload();
    }
}

However, it appears that on the first few tries (depending on the browser) the request is sent, a 200 code is sent back, but the layer does not reflect any changes. 但是,似乎在前几次尝试(取决于浏览器)发送请求时,会发回200代码,但该层不反映任何更改。 After a few tries (and reloading the page a few times) it works. 经过几次尝试(并重新加载页面几次),它可以工作。 Once it starts working for a layer it continues to work as often as the source file is changed. 一旦它开始为一个图层工作,它就会在源文件发生变化时继续工作。 Does anyone have any idea what is going on? 有谁知道发生了什么事?

Update 4: 更新4:

Using an adaptation of Jonatas' answer I am getting better results. 使用Jonatas答案的改编我得到了更好的结果。 New features pop up instantly on a reload. 重新加载时会立即弹出新功能。 However, old features are not removed from the map and many features that have moved locations are shown on the map twice. 但是,旧功能不会从地图中删除,许多已移动位置的功能会在地图上显示两次。 Below is my code: 以下是我的代码:

function refreshSelectedLayer()
{
    console.log("This feature is still in the process of being implemented. Refresh may not actually occur.");

    var selectedLayerSource = selectedLayer.getSource();

    if(selectedLayerSource instanceof ol.source.Vector)
    {
        var now = Date.now();
        var format = selectedLayerSource.getFormat();
        var url = selectedLayerSource.getUrl();
        url = url + '?t=' + now;

        loader = ol.featureloader.xhr(url, format);

        selectedLayerSource.clear();
        loader.call(selectedLayerSource, [], 1, 'EPSG:3857');

        map.updateSize();
    }
    else if(selectedLayerSource instanceof ol.source.Tile)
    {
        selectedLayerSource.changed();
        selectedLayerSource.refresh();
    }
}

Note that the var selectedLayer is set elsewhere in the code. 请注意,var selectedLayer在代码中的其他位置设置。 Any ideas why these very odd results are occuring? 任何想法为什么会出现这些非常奇怪的结果?

Update 5: 更新5:

I noticed that if I remove all other code besides the: 我注意到如果我除了以下所有其他代码:

source.clear();

call an XHR GET request is made and the features do not disappear. 调用XHR GET请求并且功能不会消失。 Why is clearing the source not removing all of the features? 为什么清除源不删除所有功能?

Update 6: 更新6:

After discovering that ol.source.clear() was not actually removing features from a given data source/layer I replaced it using the following code: 在发现ol.source.clear()实际上没有从给定数据源/层中删除功能后,我使用以下代码替换它:

selectedLayerSource.forEachFeature(function(feature){
        selectedLayerSource.removeFeature(feature);
    });

By outputting the features in the layer before and after each step, I got this: 通过在每个步骤之前和之后输出图层中的要素,我得到了:

var now = Date.now();
    var format = selectedLayerSource.getFormat();
    var url = selectedLayerSource.getUrl();
    url = url + '?t=' + now;

    console.log("time: "+now+"  format: "+format+"  url: "+url);

    loader = ol.featureloader.xhr(url, format);

    console.log(selectedLayerSource.getFeatures());
    console.log("Deleting features...");

    /*
        Try adding code here to manually remove all features from source
    */
    selectedLayerSource.forEachFeature(function(feature){
        selectedLayerSource.removeFeature(feature);
    });

    console.log(selectedLayerSource.getFeatures());

    console.log("Loading features from file...");

    loader.call(selectedLayerSource, [], 1, 'EPSG:3857');

    window.setTimeout(function(){
        console.log(selectedLayerSource.getFeatures());
        map.updateSize();
    }, 500);

Which outputs into the console: 哪些输出到控制台:

"time: 1471462410554  format: [object Object]  url: http://server/file.ext?t=1471462410554" file.php:484:3
Array [ Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, 1 more… ] file.php:491:3
Deleting features... file.php:492:3
Array [  ] file.php:501:3
Loading features from file... file.php:503:3
GET XHR http://server/file.ext [HTTP/1.1 200 OK 34ms]
Array [ Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, 1 more… ]

After several tests with GeoJSON and KML layers I confirmed that this method works!!! 在使用GeoJSON和KML图层进行多次测试后,我确认此方法有效!

However, because the loader makes its request asynchronously, I am left with the problem of how to execute code after the loader function has been called. 但是,因为加载器以异步方式发出请求,所以在调用loader函数之后,我遇到了如何执行代码的问题。 Obviously using setTimeout() is a horrible way to do this and was only implemented for testing purposes. 显然使用setTimeout()是一种可怕的方法,并且仅用于测试目的。 A success/failure callback function would be perfect and when looking at the source of featureloader.js it appears that they are offered as parameters in ol.featureloader.loadFeaturesXhr. 成功/失败回调函数是完美的,当查看featureloader.js的来源时,它们似乎作为ol.featureloader.loadFeaturesXhr中的参数提供。 See below code block from featureloader.js: 请参阅featureloader.js中的以下代码块:

/**
 * @param {string|ol.FeatureUrlFunction} url Feature URL service.
 * @param {ol.format.Feature} format Feature format.
 * @param {function(this:ol.VectorTile, Array.<ol.Feature>, ol.proj.Projection)|function(this:ol.source.Vector, Array.<ol.Feature>)} success
 *     Function called with the loaded features and optionally with the data
 *     projection. Called with the vector tile or source as `this`.
 * @param {function(this:ol.VectorTile)|function(this:ol.source.Vector)} failure
 *     Function called when loading failed. Called with the vector tile or
 *     source as `this`.
 * @return {ol.FeatureLoader} The feature loader.
 */
ol.featureloader.loadFeaturesXhr = function(url, format, success, failure)

I attempted to implement these functions like so when creating the loader: 我在创建加载器时尝试实现这些功能:

loader = ol.featureloader.xhr(url, format, 
        function(){
            console.log(selectedLayerSource.getFeatures());
            map.updateSize();
            console.log("Successful load!");
        },
        function(){
            console.log("Could not load "+selectedLayerName+" layer data from "+url);
        }
    );

but neither function is being called. 但这两种功能都没有被调用。 Any suggestions? 有什么建议么? I feel like I am missing something really simple here... 我觉得我在这里错过了一些非常简单的东西......

Update 7: 更新7:

Using the solution provided by @Jonatas Walker I adapted it to use jQuery: 使用@Jonatas Walker提供的解决方案我改编它使用jQuery:

        var now = Date.now();
        var format = selectedLayerSource.getFormat();
        var url = selectedLayerSource.getUrl();
        url = url + '?t=' + now;

        //make AJAX request to source url
        $.ajax({url: url, success: function(result){

            //manually remove features from the source
            selectedLayerSource.forEachFeature(function(feature){
                selectedLayerSource.removeFeature(feature);
            });

            //create features from AJAX results
            var features = format.readFeatures(result, {
                featureProjection: 'EPSG:3857'
            });

            //add features to the source
            selectedLayerSource.addFeatures(features);

        },
        error: function(err){
            alert("Could not load features from "+selectedLayerName+" at "+url+" error code: "+err.status);
        }
        });

After extensive testing with GeoJSON and KML sources this has proved an extremely reliable refresh method! 经过对GeoJSON和KML源的广泛测试,这已被证明是一种非常可靠的刷新方法!

Try an adaptation of this: 尝试改编:

function refreshSource() {
  var now = Date.now();
  var source = vectorLayer.getSource();
  var format = new ol.format.GeoJSON();
  var url = '//your_server.net/tmp/points.json?t=' + now;
  var loader = ol.featureloader.xhr(url, format);

  source.clear();
  loader.call(source, [], 1, 'EPSG:3857');
}

The trick is to tell the browser this is a new loading by changing the url. 诀窍是通过更改URL告诉浏览器这是一个新的加载。

Well, there are another options! 好吧,还有另外一个选择! Have your own loader. 有自己的装载机。

Load this script - just in case someone is still using old browsers 加载此脚本 - 以防有人仍使用旧版浏览器

<script src="//cdn.polyfill.io/v2/polyfill.min.js?features=fetch"></script>

Then load your JSON file and know when it's ready/loaded: 然后加载您的JSON文件并知道它何时准备好/已加载:

function refreshSelectedLayer(layer) {

  var now = Date.now();
  var source = layer.getSource();
  var format = new ol.format.GeoJSON();
  var url = '//your_server.net/tmp/points.json?t=' + now;


  fetch(url)
    .then(function(response) {
      return response.json();
    }).then(function(json) {
      console.log('parsed json', json);

      source.clear(); // if this is not enough try yours

      var features = format.readFeatures(json, {
        featureProjection: 'EPSG:3857'
      });
      source.addFeatures(features);

    }).catch(function(ex) {
      console.log('parsing failed', ex);
    });
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM