I'm trying to force a vector layer in OpenLayers 3 to periodically reload its data from a GeoJSON source. The GeoJSON source changes from time to time.
After many different attempts using different methods, I think this is the closest I've managed to get so far:
$(document).ready(function(){
map.once("postcompose", function(){
//start refreshing each 3 seconds
window.setInterval(function(){
var source = testLayer.getSource();
var params = source.getParams();
params.t = new Date().getMilliseconds();
source.updateParams(params);
}, 3000);
});
});
However this is giving me the following (every 3 seconds of course):
Uncaught TypeError: source.getParams is not a function
Here's all of the other code I'm using to load and display the GeoJSON file. The other layers in the map are loaded in the same way:
var testSource = new ol.source.Vector({
url: '/js/geojson/testfile.geojson',
format: new ol.format.GeoJSON()
});
...
window.testLayer = new ol.layer.Vector({
source: testSource,
style: function(feature, resolution) {
var style = new ol.style.Style({
fill: new ol.style.Fill({color: '#ffffff'}),
stroke: new ol.style.Stroke({color: 'black', width: 1}),
text: new ol.style.Text({
font: '12px Verdana',
text: feature.get('testvalue'),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.1)'
}),
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 1)',
width: 1
})
})
});
return style
}
});
...
var olMapDiv = document.getElementById('olmap');
var map = new ol.Map({
layers: [paddocksLayer,zonesLayer,structuresLayer,roadsLayer,testLayer],
interactions: ol.interaction.defaults({
altShiftDragRotate: false,
dragPan: false,
rotate: false
}).extend([new ol.interaction.DragPan({kinetic: null})]),
target: olMapDiv,
view: view
});
view.setZoom(1);
view.setCenter([0,0]);
I read somewhere else that getParams simply doesn't work on vector layers, so this error wasn't entirely unexpected.
Is there an alternative method which will reload (not just re-render) a vector layer?
Thanks in advance. I must also apologise in advance as I'll need really specific guidance with this - I'm very new to JS and OpenLayers.
Gareth
(fiddle)
You can achieve this with a custom AJAX loader (when you pass url to ol.source.Vector this is AJAX as well). Then you can reload your JSON file as you will.
You can use any AJAX library you want but this is as simple as:
var utils = {
refreshGeoJson: function(url, source) {
this.getJson(url).when({
ready: function(response) {
var format = new ol.format.GeoJSON();
var features = format.readFeatures(response, {
featureProjection: 'EPSG:3857'
});
source.addFeatures(features);
source.refresh();
}
});
},
getJson: function(url) {
var xhr = new XMLHttpRequest(),
when = {},
onload = function() {
if (xhr.status === 200) {
when.ready.call(undefined, JSON.parse(xhr.response));
}
},
onerror = function() {
console.info('Cannot XHR ' + JSON.stringify(url));
};
xhr.open('GET', url, true);
xhr.setRequestHeader('Accept','application/json');
xhr.onload = onload;
xhr.onerror = onerror;
xhr.send(null);
return {
when: function(obj) { when.ready = obj.ready; }
};
}
};
Related
I am trying to load the big GeoJSON file using the OpenLayers. The problem is after loading it then the complete map becomes slow to do any other operation. So my question is there anyway to load a geojson file as a Tile rather ?
Here is my code:
function addGEOJSONLayer() {
console.log("reached");
var url= 'http://172.61.25.51:8080/test/ab.json';
var vectorSource = new ol.source.Vector({
strategy: ol.loadingstrategy.bbox,
projection: 'EPSG:4326',
loader: function(extent, resolution, projection) {
$.ajax(url).then(function(response) {
console.log(response);
var features = new ol.format.GeoJSON().readFeatures(response);
vectorSource.addFeatures(features);
});
}
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: function (feature, resolution) {
return [new ol.style.Style({
stroke: new ol.style.Stroke({
width: 1,
color: feature.get('stroke')
}),
fill: new ol.style.Fill({
color: feature.get('fill'),
})
})];
}
});
var ext = vectorLayer.getSource().getExtent();
map.getView().fit(ext, map.getSize());
map.addLayer(vectorLayer);
}
Any suggestions will be of great help! Thanks
I am using geoserver and openlayers to display popup by clicking I have displayed popup with one layer. But I couldn't display popup when I have multiple layers.
map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Group({
layers: [
new ol.layer.Group({
layers: [
new ol.layer.Tile({
source: new ol.source.Stamen({
layer: 'baselayer'
})
}),
new ol.layer.Image({
title:'Sometitle',
source: new ol.source.ImageWMS(
{
ratio:1,
url:"http://localhost:wp/wms",
params:{
'LAYERS':'layer:layername',
}
})
}),
new ol.layer.Image({
title:'sometitle2',
source: new ol.source.ImageWMS(
{
ratio:1,
url:"http://localhost:wp/wms",
params:{
'LAYERS':'layer:layername',
}
})
}),
Then using the popup codes,
//Scripts for popup
var popup = new ol.Overlay.Popup();
map.addOverlay(popup);
//Displaying popup on click
map.on('singleclick', function(evt) {
console.log("In singleClick");
//Check for visible layers
var data = [];
layer = map.getSource(); //
var url = layer.getGetFeatureInfoUrl(
evt.coordinate, viewResolution, view.getProjection(),
reqwest({
url: url,
type: 'json',
}).then(function (data) {
if (data.features.length == 0) {
popup.hide(); //If user clicks outside
return;
}
for (var i = 0; i < data.features.length; i++)
{
console.log("In for");
var feature = data.features[i]; //Read features of JSON array
var props = feature.properties; //Read properties of feature array
var data1 = [];
var data2 = [];
Finally after all codes for popup(That worked for me to display popup for single layer) I used this line to render my popup.
popup.show(evt.coordinate,popup);
If you find it easier to have separate layers, e.g. in the GIS StackExchange question you only display bedrock geology:
source: new ol.source.ImageWMS({
url: 'http://ogc.bgs.ac.uk/cgi-bin/BGS_Bedrock_and_Superficial_Geology/wms',
params: {
'FORMAT': 'image/png',
'LAYERS': 'GBR_BGS_625k_BLS',
'TRANSPARENT': 'TRUE'
},
attributions: bgsAttrib,
}),
in an info request you can specify other layers to query, so you could get bedrock and superficial geology in one popup:
source.getGetFeatureInfoUrl( evt.coordinate,
view.getResolution,
view.getProjection(),
{ 'QUERY_LAYERS': 'GBR_BGS_625k_BLS,GBR_BGS_625k_SLS',
'INFO_FORMAT': 'text/html',
'FEATURE_COUNT': '10'} );
I'm trying to return the attributes of a vector source in OpenLayers (v.4) using the .getFeatures function some indexing, and the .get('key') method, like so: gdeltVectorSource.getFeatures()[15].get('count'). When I enter this into the developer console in Chrome, the result is as expected and it works just fine. However, when added to the script referenced in the html document, it returns: Uncaught TypeError: Cannot read property 'get' of undefined at pen.js:46. It seems to fail to return an array of features from .getFeatures(). Can someone help me understand why this occurs? I'd like to return attributes of a vector source in order to use the values ('count' in this example) in styling, etc. Is there something about how javascript compiles and runs code that I'm missing?
Here's a link to a codepen that illustrates what I'm talking about. The points don't display (I don't know why, but it's irrelevant), but the source loads just fine. Also note, you'll have to open the chrome console to see error message.
Thank you for stopping to read my question!
The $.ajax call is asynchronous. You need to process/use the features in the callback function when/where they exist:
current code:
$.ajax({
type: 'GET',
url: gdeltURL,
dataType: "json",
success: function(data) {
// console.log(typeof JSON.stringify(data))
// 'canot read property 'readFeatures' of undefined
var geojsonFormat = new ol.format.GeoJSON();
// console.log(typeof geojsonFormat)
var features = geojsonFormat.readFeatures(data, {
});
gdeltVectorSource.addFeatures(features);
}
});
// snip...
console.log(gdeltVectorSource.getFeatures()[15].get('count'))
should be:
$.ajax({
type: 'GET',
url: gdeltURL,
dataType: "json",
success: function(data) {
// console.log(typeof JSON.stringify(data))
// 'canot read property 'readFeatures' of undefined
var geojsonFormat = new ol.format.GeoJSON();
// console.log(typeof geojsonFormat)
var features = geojsonFormat.readFeatures(data, {
});
gdeltVectorSource.addFeatures(features);
console.log(gdeltVectorSource.getFeatures()[15].get('count'))
}
});
proof of concept fiddle
code snippet:
var osm = new ol.layer.Tile({
source: new ol.source.OSM()
});
function styleFn(f) {
var retSytle;
f.set('styledwithcolor', "red");
retSytle = new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
stroke: new ol.style.Stroke({
color: 'white',
width: 2
}),
fill: new ol.style.Fill({
color: "red"
})
})
});
return [retSytle];
}
var gdeltVectorSource = new ol.source.Vector({
// format: new ol.format.GeoJSON()
})
var gdeltLayer = new ol.layer.Vector({
source: gdeltVectorSource,
visible: true,
style: styleFn
});
var gdeltURL = "https://api.gdeltproject.org/api/v2/geo/geo?query=theme:WB_1791_AIR_POLLUTION&format=GeoJSON"
$.ajax({
type: 'GET',
url: gdeltURL,
dataType: "json",
success: function(data) {
// console.log(typeof JSON.stringify(data))
// 'canot read property 'readFeatures' of undefined
var geojsonFormat = new ol.format.GeoJSON();
// console.log(typeof geojsonFormat)
var features = geojsonFormat.readFeatures(data, {
featureProjection: 'EPSG:3857'
});
gdeltVectorSource.addFeatures(features);
console.log(gdeltVectorSource.getFeatures()[15].get('count'))
}
});
var map = new ol.Map({
layers: [osm, gdeltLayer],
target: 'map',
controls: ol.control.defaults({
attributionOptions: {
collapsible: false
}
}),
view: new ol.View({
center: [-11000000, 4600000],
zoom: 4
})
});
html,
body,
#map {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.5/ol-debug.js"></script>
<div id="map" class="map"></div>
The features are loaded in the ajax call, which returns after the remaining code (creating the map and trying to display the count value) has executed.
Getting the desired feature attribute works fine. You can try putting the call within the ajax function.
To solve your issue, either don't use ajax or ensure the features are loaded before accessing them.
I have a ol 3.10.1 map where the goal is to redraw the features of a layer dynamically. On the road to get there, I'm using the source.clear() function. The strange thing is that the source.clear() actually clear the features from the layer at the current zoom level, but while zooming in or out the features are still there. Am I using the source.clear() function the correct way? Please find bellow the code snippet which I'm using for testing purposes.
var image = new ol.style.Circle({
radius: 5,
fill: null,
stroke: new ol.style.Stroke({color: 'red', width: 1})
});
var styles = {
'Point': [new ol.style.Style({
image: image
})]};
var styleFunction = function(feature, resolution) {
return styles[feature.getGeometry().getType()];
};
var CITYClusterSource = new ol.source.Cluster({
source: new ol.source.Vector({
url: 'world_cities.json',
format: new ol.format.GeoJSON(),
projection: 'EPSG:3857'
}),
})
var CITYClusterLayer = new ol.layer.Vector({
source: CITYClusterSource,
style: styleFunction
});
setTimeout(function () { CITYClusterSource.clear(); }, 5000);
var map = new ol.Map({
target: 'map',
renderer: 'canvas',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM(),
}),
CITYClusterLayer
],
view: new ol.View({
center: ol.proj.transform([15.0, 45.0], 'EPSG:4326', 'EPSG:3857'),
zoom:3
})
});
I'm using the setTimout() function to have the features visible for some seconds, before they are supposed to be cleared.
Please advice.
UPDATE: http://jsfiddle.net/jonataswalker/ayewaz87/
I forgot, OL will load your features again and again, for each resolution. So if you want to remove once and for all, use a custom loader, see fiddle.
Your source is asynchronously loaded, so set a timeout when it's ready:
CITYClusterSource.once('change', function(evt){
if (CITYClusterSource.getState() === 'ready') {
// now the source is fully loaded
setTimeout(function () { CITYClusterSource.clear(); }, 5000);
}
});
Note the once method, you can use on instead of it.
I'm trying to make a Polymer module for working with OpenLayers 3 and displaying openstreetmaps. I know there is a great module working with leaflet but I need some specifics functions like map orientation.
Anyway, I code something and it's working except one thing I can't figure out : When the page loads, only the commands are showing (Zoom + / Zoom -) and not the map (and not any thing such as marker, etc). But if I resize my window (my Chrome window I mean) the map appear and all is working fine !! I was thinking maybe something in relation with DOM Loading...
Module code :
<dom-module id="openlayer-map">
<link rel="stylesheet" href="http://openlayers.org/en/v3.7.0/css/ol.css" type="text/css">
<script src="http://openlayers.org/en/v3.7.0/build/ol.js" type="text/javascript"></script>
<style>
:host {
display: block;
}
#map
{
position: absolute;
height: 100%;
}
</style>
<template>
<div id="map" class="map"></div>
<!-- Tests
<input is="iron-input" bind-value="{{latitude}}" placeholder="latitude">
<input is="iron-input" bind-value="{{longitude}}" placeholder="longitude">
-->
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'openlayer-map',
properties:
{
currentCenter: Array,
currentView: ol.View,
olmap: ol.Map,
geolocation: ol.Geolocation,
layer: Object,
longitude:
{
type: Number,
value:12.889101100000062,
notify: true,
observer: '_updateLongitude'
},
latitude:
{
type: Number,
value: 15.7622695,
notify: true,
observer: '_updateLatitude'
},
geotracking:
{
value: false,
type: Boolean,
notify: true,
},
elemReady: Boolean,
},
ready: function()
{
console.log('openlayer-map ready');
this.initialConfig();
this.elemReady = true;
this.setCenter(this.latitude,this.longitude);
},
initialConfig: function()
{
console.log('initial config for the map...');
this.currentView = new ol.View({
zoom: 14
});
var source = new ol.source.OSM();
this.layer = new ol.layer.Tile();
this.layer.setSource(source);
this.olmap = new ol.Map({
layers: [this.layer],
target: this.$.map,
controls: ol.control.defaults({
attributionOptions: /** #type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}),
view: this.currentView
});
// Localisation
this.geolocation = new ol.Geolocation({
projection: this.currentView.getProjection()
});
this.geolocation.setTracking(this.geotracking);
if(this.geolocation)
{
var accuracyFeature = new ol.Feature();
this.geolocation.on('change:accuracyGeometry', function() {
accuracyFeature.setGeometry(this.geolocation.getAccuracyGeometry());
}.bind(this));
var positionFeature = new ol.Feature();
positionFeature.setStyle(new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: '#3399CC'
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 2
})
})
}));
this.geolocation.on('change:position', function() {
var coordinates = this.geolocation.getPosition();
positionFeature.setGeometry(coordinates ?
new ol.geom.Point(coordinates) : null);
}.bind(this));
var featuresOverlay = new ol.layer.Vector({
map: this.olmap,
source: new ol.source.Vector({
features: [accuracyFeature, positionFeature]
})
});
}
},
_updateLatitude: function(newValue, oldValue)
{
if(this.elemReady)
{
console.log('update latitude from '+oldValue+' to '+newValue);
this.setCenter(newValue, this.longitude);
}
else
{
console.log('_updateLatitude: waiting element to be ready');
}
},
_updateLongitude: function(newValue, oldValue)
{
if(this.elemReady)
{
console.log('update longitude from '+oldValue+' to '+newValue);
this.setCenter(this.latitude, newValue);
}
else
{
console.log('_updateLatitude: waiting element to be ready');
}
},
setCenter: function(latitude, longitude)
{
var center = [longitude, latitude];
this.currentCenter = ol.proj.fromLonLat(center);
console.log('update center of the map with latitude = '+latitude+' and longitude = '+longitude);
this.currentView.centerOn(this.currentCenter,[400,400], [0,0]);
},
});
})();
</script>
And the call in Polymer :
<openlayer-map latitude="48.853" longitude="2.35" geotracking></openlayer-map>
Any clue ?
Found it ! Needed to do the map initialization in an asynchronous call
attached: function()
{
this.async(function()
{
this.initialConfig(); // Create your ol.Map here
});
},