OpenLayers 3 重新加载图层
OpenLayers 3 Reload Layer(s)
我正在开发一个使用 OL3 的项目,在该项目中,我需要能够手动(通过按下按钮)或自动(基于时间)重新加载矢量图层,如果它们自上次使用 HTTP 条件 GET 加载以来已更新( 304 headers 等)。
我发现这个用于 KML 图层的 post (https://gis.stackexchange.com/questions/333/how-to-dynamically-refresh-reload-a-kml-layer-in-openlayers) 非常旧,但它似乎使用了 OL3 中不再存在的变量,我不确定它是否只允许加载文件自上次加载以来已被修改。乍一看似乎是强制重新加载,即使文件没有被修改。
API 中似乎没有任何类似于 OL3 中地图或图层 objects 的重新加载功能。有办法吗?
更新 1:
我找到了一种可能的方法来解决这个问题:https://gis.stackexchange.com/questions/125074/openlayers3-how-to-reload-a-layer-from-geoserver-when-underlying-data-change 使用代码:
layer.getSource().updateParams({"time": Date.now()});
然而,当我 运行 这段代码时,我得到了错误:
TypeError: selectedLayer.getSource(...).updateParams is not a function
检查 OL3 的 API 参考资料后,似乎不存在此类功能。最接近的是 setProperties() 或 setAttributions()。两者都不起作用。似乎并非所有层类型都实现 getSource()。
更新二:
refresh() 重新加载图块,但似乎并未从服务器请求它们。相反,它们似乎是从缓存中加载的(而不是 HTTP 缓存)。没有请求,没有 HTTP 304 或类似的东西。将尝试 KML 方法的变体,并很快 post 获取结果。
更新 3:
在尝试了很多不同的解决方案后,我无意中发现了一些适用于矢量图层的东西。通过调用图层 source.clear() 函数,然后调用 Map.updateSize(),图层会自动从其源 URL 重新加载。发出 XHR GET 请求,如果源文件已更改,将从文件中重新加载。如果源文件未更改,将发出 304 并从缓存中重新加载源。
下面是一个应该使用此方法重新加载给定图层的函数:
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();
}
}
但是,似乎在发送请求的前几次尝试(取决于浏览器)中,发回了 200 代码,但图层未反映任何更改。经过几次尝试(并重新加载页面几次)后,它起作用了。一旦它开始为一个图层工作,它就会像源文件更改一样频繁地继续工作。有谁知道发生了什么事吗?
更新 4:
使用乔纳塔斯答案的改编版,我得到了更好的结果。重新加载时会立即弹出新功能。但是,不会从地图中删除旧要素,并且许多已移动位置的要素会在地图上显示两次。下面是我的代码:
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();
}
}
请注意,var selectedLayer 是在代码的其他地方设置的。知道为什么会出现这些非常奇怪的结果吗?
更新 5:
我注意到如果我删除除以下代码之外的所有其他代码:
source.clear();
发出 XHR GET 请求并且功能没有消失。为什么清除源代码并没有删除所有功能?
更新 6:
在发现 ol.source.clear() 实际上并未从给定数据中删除特征后 source/layer 我使用以下代码替换了它:
selectedLayerSource.forEachFeature(function(feature){
selectedLayerSource.removeFeature(feature);
});
通过在每一步前后输出图层中的特征,我得到了:
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);
哪个输出到控制台:
"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… ]
在使用 GeoJSON 和 KML 图层进行多次测试后,我确认此方法有效!!!
但是,由于加载器是异步发出请求的,所以我遇到了调用加载器函数后如何执行代码的问题。显然使用 setTimeout() 是一种可怕的方法,并且仅用于测试目的。 success/failure 回调函数将是完美的,当查看 featureloader.js 的源代码时,它们似乎在 ol.featureloader.loadFeaturesXhr 中作为参数提供。请参阅 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)
我在创建加载程序时尝试像这样实现这些功能:
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);
}
);
但这两个函数都没有被调用。有什么建议么?我觉得我在这里遗漏了一些非常简单的东西......
更新 7:
使用@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);
}
});
在对 GeoJSON 和 KML 源进行大量测试后,证明这是一种极其可靠的刷新方法!
尝试对此进行改编:
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');
}
诀窍是通过更改 url 来告诉浏览器这是一个新的加载。
好吧,还有其他选择!拥有自己的装载机。
加载此脚本 - 以防有人仍在使用旧浏览器
<script src="//cdn.polyfill.io/v2/polyfill.min.js?features=fetch"></script>
然后载入你的 JSON 文件并且 知道 什么时候是 ready/loaded:
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);
});
}
我正在开发一个使用 OL3 的项目,在该项目中,我需要能够手动(通过按下按钮)或自动(基于时间)重新加载矢量图层,如果它们自上次使用 HTTP 条件 GET 加载以来已更新( 304 headers 等)。
我发现这个用于 KML 图层的 post (https://gis.stackexchange.com/questions/333/how-to-dynamically-refresh-reload-a-kml-layer-in-openlayers) 非常旧,但它似乎使用了 OL3 中不再存在的变量,我不确定它是否只允许加载文件自上次加载以来已被修改。乍一看似乎是强制重新加载,即使文件没有被修改。
API 中似乎没有任何类似于 OL3 中地图或图层 objects 的重新加载功能。有办法吗?
更新 1:
我找到了一种可能的方法来解决这个问题:https://gis.stackexchange.com/questions/125074/openlayers3-how-to-reload-a-layer-from-geoserver-when-underlying-data-change 使用代码:
layer.getSource().updateParams({"time": Date.now()});
然而,当我 运行 这段代码时,我得到了错误:
TypeError: selectedLayer.getSource(...).updateParams is not a function
检查 OL3 的 API 参考资料后,似乎不存在此类功能。最接近的是 setProperties() 或 setAttributions()。两者都不起作用。似乎并非所有层类型都实现 getSource()。
更新二:
refresh() 重新加载图块,但似乎并未从服务器请求它们。相反,它们似乎是从缓存中加载的(而不是 HTTP 缓存)。没有请求,没有 HTTP 304 或类似的东西。将尝试 KML 方法的变体,并很快 post 获取结果。
更新 3:
在尝试了很多不同的解决方案后,我无意中发现了一些适用于矢量图层的东西。通过调用图层 source.clear() 函数,然后调用 Map.updateSize(),图层会自动从其源 URL 重新加载。发出 XHR GET 请求,如果源文件已更改,将从文件中重新加载。如果源文件未更改,将发出 304 并从缓存中重新加载源。
下面是一个应该使用此方法重新加载给定图层的函数:
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();
}
}
但是,似乎在发送请求的前几次尝试(取决于浏览器)中,发回了 200 代码,但图层未反映任何更改。经过几次尝试(并重新加载页面几次)后,它起作用了。一旦它开始为一个图层工作,它就会像源文件更改一样频繁地继续工作。有谁知道发生了什么事吗?
更新 4:
使用乔纳塔斯答案的改编版,我得到了更好的结果。重新加载时会立即弹出新功能。但是,不会从地图中删除旧要素,并且许多已移动位置的要素会在地图上显示两次。下面是我的代码:
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();
}
}
请注意,var selectedLayer 是在代码的其他地方设置的。知道为什么会出现这些非常奇怪的结果吗?
更新 5:
我注意到如果我删除除以下代码之外的所有其他代码:
source.clear();
发出 XHR GET 请求并且功能没有消失。为什么清除源代码并没有删除所有功能?
更新 6:
在发现 ol.source.clear() 实际上并未从给定数据中删除特征后 source/layer 我使用以下代码替换了它:
selectedLayerSource.forEachFeature(function(feature){
selectedLayerSource.removeFeature(feature);
});
通过在每一步前后输出图层中的特征,我得到了:
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);
哪个输出到控制台:
"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… ]
在使用 GeoJSON 和 KML 图层进行多次测试后,我确认此方法有效!!!
但是,由于加载器是异步发出请求的,所以我遇到了调用加载器函数后如何执行代码的问题。显然使用 setTimeout() 是一种可怕的方法,并且仅用于测试目的。 success/failure 回调函数将是完美的,当查看 featureloader.js 的源代码时,它们似乎在 ol.featureloader.loadFeaturesXhr 中作为参数提供。请参阅 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)
我在创建加载程序时尝试像这样实现这些功能:
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);
}
);
但这两个函数都没有被调用。有什么建议么?我觉得我在这里遗漏了一些非常简单的东西......
更新 7:
使用@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);
}
});
在对 GeoJSON 和 KML 源进行大量测试后,证明这是一种极其可靠的刷新方法!
尝试对此进行改编:
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');
}
诀窍是通过更改 url 来告诉浏览器这是一个新的加载。
好吧,还有其他选择!拥有自己的装载机。
加载此脚本 - 以防有人仍在使用旧浏览器
<script src="//cdn.polyfill.io/v2/polyfill.min.js?features=fetch"></script>
然后载入你的 JSON 文件并且 知道 什么时候是 ready/loaded:
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);
});
}