OpenLayers WFS request on mouseclick to GeoServer doesn't load features into vector layer - openlayers-3

I would appreciate advice on getting an OpenLayers WFS request to GeoServer working in OL3.2 or later, triggered by a mouse singleclick.
By way of background, I have had this working fine for a year at http://maps.nls.uk/geo/find/ using OL 3.1. However, Firefox version 43.0 records the mouse click event at the top left corner of the map canvas, not on the real pixel location clicked on - earlier versions of Firefox, Chrome and IE record the correct pixel location on the canvas. From my basic tests, later versions of OL after 3.1 record the correct pixel location using Firefox 43.0, so I would like to upgrade to a newer version of OL.
My original code in OL3.1 is as follows:
var urlgeoserver = 'http://geoserver2.nls.uk/geoserver/wfs?service=WFS' +
'&version=1.1.0&request=GetFeature&typename=' + TypeName +
'&outputFormat=text/javascript&format_options=callback:loadFeatures' +
'&srsname=EPSG:3857&cql_filter=INTERSECTS(the_geom,POINT('
+ point27700[0] + ' ' + point27700[1] + '))';
}
var vectorSource = new ol.source.ServerVector({
format: new ol.format.GeoJSON(),
loader: function(extent, resolution, projection) {
var url = urlgeoserver
$.ajax({
url: url,
dataType: 'jsonp',
jsonpCallback: 'loadFeatures',
success: handleJson
});
},
strategy: ol.loadingstrategy.createTile(new ol.tilegrid.XYZ({
maxZoom: 19
})),
projection: 'EPSG:3857'
});
function handleJson(data) {
vectorSource.addFeatures(vectorSource.readFeatures(data));
....
I have tried adapting this following the example at http://openlayers.org/en/v3.6.0/examples/vector-wfs.html and OL 3.6 with the code below:
var urlgeoserver = 'http://geoserver2.nls.uk/geoserver/wfs?service=WFS' +
'&version=1.1.0&request=GetFeature&typename=' + TypeName +
'&outputFormat=text/javascript&format_options=callback:loadFeatures' +
'&srsname=EPSG:3857&cql_filter=INTERSECTS(the_geom,POINT('
+ point27700[0] + ' ' + point27700[1] + '))';
}
vectorSource = new ol.source.Vector({
loader: function(extent, resolution, projection) {
var url = urlgeoserver
// use jsonp: false to prevent jQuery from adding the "callback"
// parameter to the URL
$.ajax({url: url, dataType: 'jsonp'});
},
strategy: ol.loadingstrategy.tile(ol.tilegrid.createXYZ({
maxZoom: 19
}))
});
window.loadFeatures(response) {
vectorSource.addFeatures(geojsonFormat.readFeatures(response));
};
However I try things, it fails to retrieve features from GeoServer into vectorSource and I would appreciate any pointers or suggestions.
Many thanks,

Just in case helpful for others, following further investigation, I realised I just needed to add a 'jsonp: false' to the $.ajax query for it to work. The correct vectorSource syntax is therefore:
var geojsonFormat = new ol.format.GeoJSON();
var vectorSource = new ol.source.Vector({
loader: function(extent, resolution, projection) {
var url = urlgeoserver
$.ajax({url: url, dataType: 'jsonp', jsonp: false})
},
strategy: ol.loadingstrategy.tile(ol.tilegrid.createXYZ({
maxZoom: 19
}))
});
window.loadFeatures = function(response) {
vectorSource.addFeatures(geojsonFormat.readFeatures(response));
};
This is implemented at: http://maps.nls.uk/geo/find/

Related

Tile loading progress for vector sources utilizing tile loading strategy

I am loading features into Vector source utilizing the Tile strategy. I'd like implement kind of progress bar, similar to http://openlayers.org/en/master/examples/tile-load-events.html.
However, unlike VectorTile source, the Vector source doesn't trigger tile loading events which could be used for calculating desired ratio 100*(tilesLoaded/tilesToLoad).
So far I can retrieve the total count of tiles to load, but I am unable to count already loaded tiles. The most promising is a custom loader, but it is not clear to me how to modify it without touching the original OL source code.
var vectorSource = new ol.source.Vector({
loader: ol.featureloader.xhrX(url, format),
strategy: ol.loadingstrategy.tile(tileGrid)
});
// forked method, but the inner 'loadFeaturesXhr' method seems to be private and cannot be used
ol.featureloader.xhrX = function(url, format) {
/*
return ol.featureloader.loadFeaturesXhr(url, format,
function(features, dataProjection) {
this.addFeatures(features);
// when tile loading succeeds
tilesLoaded++;
},
function() {
// when tile loading fails
tilesLoaded++;
});
*/
// just returning the original loader
return ol.featureloader.xhr(url, format);
}
var url = function(extent, resolution) {
tilesToLoad++; // when a new tile is needed, this counter is incremented
var tileCoord = tileGrid.getTileCoordForCoordAndResolution(ol.extent.getCenter(extent), resolution);
return 'tiles/' +
tileCoord[0] + '/' +
tileCoord[1] + '/' +
(Math.pow(2, tileCoord[0]) + tileCoord[2]) + '.json';
}
var format = new ol.format.GeoJSON({
defaultDataProjection: 'EPSG:3857'
});
Any idea how to call loadFeaturesXhr() method from my source?
Instead of forking default OL3 loader I created a custom one. It was not as hard as expected. Now I can freely add counters to proper places (in the code below I actually update the progress component itself):
var vectorSource = new ol.source.Vector({
loader: function(extent, resolution, projection) {
var getUrl = function(extent, resolution) {
progress.addLoading();
...
};
var xhr = new XMLHttpRequest();
xhr.open('GET', getUrl(extent, resolution), true);
xhr.onload = function(event) {
...
progress.addLoaded();
}.bind(this);
xhr.onerror = function(event) {
progress.addLoaded();
}
xhr.send();
},
strategy: ol.loadingstrategy.tile(tileGrid)
});

cannot add features to WFS layer

I try to load some features from Geoserver to a vector layer in Openlayers 3.9.0.
var url = 'http://localhost:5550/geoserver/mymap/wfs?service=WFS&'+'version=1.0.0&request=GetFeature&typeName=mymap:layer&'+'outputFormat=application/json&maxFeatures=50';
var projection = ol.proj.get('EPSG:3857');
var extent = [2297128.5, 4618333, 2459120.25, 4763120];
var amir = new ol.source.Vector({
format: new ol.format.GeoJSON(),
loader: function (extent) {
$.ajax(url, {type: 'GET'})
.done(loadFeatures)
.fail(function () {alert("error");});
},
strategy: ol.loadingstrategy.bbox
});
function loadFeatures(response) {
formatWFS = new ol.format.WFS();
var features = formatWFS.readFeatures(response);
amir.addFeatures(features);
//-----------OR---------------------
var features = amir.readFeatures(response);
amir.addFeatures(features);
}
var fill = new ol.style.Fill({
color: 'rgba(0,0,0,0.2)'
});
var stroke = new ol.style.Stroke({
color: 'rgba(0,0,0,0.4)'
});
var circle = new ol.style.Circle({
radius: 6,
fill: fill,
stroke: stroke
});
jake = new ol.layer.Vector({
source: amir,
style: new ol.style.Style({
fill: fill,
stroke: stroke,
image: circle
})
});
In loadFeatures function if I use
formatWFS = new ol.format.WFS();
var features = formatWFS.readFeatures(response);
amir.addFeatures(features);
I get Uncaught AssertionError: Failure: Unknown source type pointing to a openlayers line that throws errors and to this line of my codevar features = formatWFS.readFeatures(response);.
If I use
var features = amir.readFeatures(response);
amir.addFeatures(features);
I get Uncaught TypeError: sourceVector.readFeatures is not a function pointing to var features = amir.readFeatures(response); .
The request to the WFS looks ok, with OK 200 status. If I grab the request's URL sended to Geoserver and open it in a new tab I get raw GeoJSON like {"type":"FeatureCollection","totalFeatures":422,"features":[{"type":"Feature","id":"layer.709","geometry":{"type":"Point","coordinates":[2391735.8907621,4695330.8039257005]},"geometry_name":"l_geom","properties":{"l_name":"Leeron"}},....//next feature
So its a FeatureCollection not just an array. Not that I know how to handle this
I dont get why to set a ol.format.WFS and not just read/add features. I dont know how to debug and add the features to my layer
You are instructing GeoServer to use GeoJSON as output format, so you'll need to use the GeoJSON format in OpenLayers to parse the features. You should be able to simplify your source configuration to something like
var url = 'http://localhost:5550/geoserver/mymap/wfs?service=WFS&' +
'version=1.0.0&request=GetFeature&typeName=mymap:layer&' +
'outputFormat=application/json&maxFeatures=50';
var amir = new ol.source.Vector({
format: new ol.format.GeoJSON(),
url: function(extent, projection) {
return url + '&bbox=' + extent.join(',') +
'&srsName=' + projection.getCode();
},
strategy: ol.loadingstrategy.bbox
});

How do I dynamically load GeoJSON data, based on the map extent, into my OpenLayers 3.5.0 map layer?

I am migrating from OpenLayers 3.2.0 to 3.5.0 and am having trouble loading my GeoJSON data into my vector layer. I have it working, but I'm transforming the geometry of the features from my GeoJSON data source before I add them to my vector source.
Is there a way to make OpenLayers 3.5.0 apply the transformation automatically?
The data from my GeoJSON data source uses the EPSG:4326, I believe that I need to re-project the geometries to EPSG:3857 in order to display them on my map. The GeoJSON data source has the projection information in it's crs attribute and my vector source also has it's projection set. Still, the feature geometries are not transformed on their own.
I need to pass the bounds of the viewable map area via the URL to my GeoJSON data source, I do not want to load all of the data at once. I have a loader function on my vector source that gets the current map extent and builds the URL for the request.
Sample Data from my GeoJSON source is available, it validates through a linter and I believe it to be reasonable.
Below is the current code that I am using.
var vectorFormat = new ol.format.GeoJSON();
var featureStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill(
{color: 'rgba(255, 69, 0, 0.75)'}),
stroke: new ol.style.Stroke(
{color: 'rgba(255, 0, 0, 0.95)', width: 1})
})
});
var vectorSource = new ol.source.Vector({
projection: new ol.proj.Projection({'code':'EPSG:3857'}),
strategy: ol.loadingstrategy.bbox,
loader: function(extent, resolution, projection) {
var coordinate1 = ol.proj.transform([extent[0], extent[1]],
'ESPG:3857', 'EPSG:4326')
var coordinate2 = ol.proj.transform([extent[2], extent[3]],
'ESPG:3857', 'EPSG:4326')
var url = 'api/sites/geo/bounds/4326/' + coordinate1[1]
+ ',' + coordinate1[0] + '/' + coordinate2[1] + ','
+ coordinate2[0] + "/";
$.ajax({
url: url,
dataType: 'json'
}).then(function(response) {
var features = vectorFormat.readFeatures(response);
var transformFn = ol.proj.getTransform(
response.crs.properties.name, projection);
for(index in features) {
var feature = features[index];
feature.getGeometry().applyTransform(transformFn);
}
vectorSource.addFeatures(features);
});
}
});
this.state.map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
new ol.layer.Vector({
source: vectorSource,
style: featureStyle
})
],
view: new ol.View({
center: this.transformToOl(this.state.center),
zoom: this.state.zoom
})
});
Any help or pointers in the right direction would be greatly appreciated. :-D
Yes, OpenLayers can do the reprojection for you. You don't even have to set the projection on the source. The geometries will be automatically reprojected to the view projection.
var vectorSource = new ol.source.Vector({
url: 'file.json',
format: new ol.format.GeoJSON()
});
http://jsfiddle.net/h9zwjf88/
Update
In case you want to you use a custom loader, you can specify the target projection when parsing the features (see also ol.format.GeoJSON.html#readFeatures):
var vectorSource = new ol.source.Vector({
strategy: ol.loadingstrategy.bbox,
loader: function(extent, resolution, projection) {
$.ajax(url).then(function(response) {
var features = format.readFeatures(response,
{featureProjection: 'EPSG:3857'});
vectorSource.addFeatures(features);
});
}
});
http://jsfiddle.net/h9zwjf88/1/

vector layer loading issue in openlayers 3

I have an issue regarding vector loading in ol3.
I am using a geoserver which doesn't handle js callback
In order to have my features loaded, I have to add them in the loader's ajax done function :
var buildLoader = function(vectorSource, $vector) {
return function(extent, resolution, projection) {
extent = ol.extent.applyTransform(extent, ol.proj.getTransform("EPSG:3857", "EPSG:4326"));
var url = 'https://myurl/' + $vector.attr("url") +
'?service=WFS' +
'&version=' + $vector.attr("version") +
'&request=GetFeature' +
'&typename=' + $vector.attr("typename") +
'&srs=' + $vector.attr("projection") +
'&bbox=' + extent +
'&outputFormat=json';
$.ajax({
url: url,
dataType: 'json',
xhrFields: {
withCredentials: true
}
})
.done(function(response) {
vectorSource.addFeatures(vectorSource.readFeatures(response));
});
}
}
I then build an array of vectors to be added on my map.
for (index=0; index < vectors.length; index++) {
var $vector = $(vectors[index]);
var vectorSource = new ol.source.ServerVector({
format: new ol.format.GeoJSON(),
loader: buildLoader(vectorSource, $vector),
projection: 'EPSG:3857'
});
// ... //
datas[index] = new ol.layer.Vector({
source: vectorSource,
visible: 'false',
style: iconStyle
});
}
The problem here is that the vectorSource used by the loader is always [index - 1]
I have found an Hack, by defining the loader function after the instantiation:
var vectorSource = new ol.source.ServerVector({
format: new ol.format.GeoJSON(),
projection: 'EPSG:3857'
});
vectorSource.loader_ = buildLoader(vectorSource, $vector);
I find this ugly enough, but there is no setLoader() available for the ServerVector type. Do you know if there is anoter solution which does not involve using another geoserver?
I save a reference to the VectorSource object instance in the buildLoader function (instead of argument).
vectorSource = this;
Then I use that reference in the done() function:
vectorSource.addFeatures(vectorSource.readFeatures(response));
Full source:
var buildLoader = function($vector) {
return function(extent, resolution, projection) {
vectorSource = this;
extent = ol.extent.applyTransform(extent, ol.proj.getTransform("EPSG:3857", "EPSG:4326"));
var url = 'https://myurl/' + $vector.attr("url") +
'?service=WFS' +
'&version=' + $vector.attr("version") +
'&request=GetFeature' +
'&typename=' + $vector.attr("typename") +
'&srs=' + $vector.attr("projection") +
'&bbox=' + extent +
'&outputFormat=json';
$.ajax({
url: url,
dataType: 'json',
xhrFields: {
withCredentials: true
}
})
.done(function(response) {
vectorSource.addFeatures(vectorSource.readFeatures(response));
});
}
}

Highcharts error #13 unless I run under IE compatibility view?

I am developing an asp applicaiton which uses WCF service (.svc not .asmx) to send JSON data to highcharts. It only seems to work if I use ie compatibility view. If I don't I get the message:
Microsoft JScript runtime error: Highcharts error #13: http://www.highcharts.com/errors/13
Error number 13 along with online searching suggests I don't have the proper renter to DIV, but it is present. Any ideas what I can to to make my application work in non-compatibility view?
Thanks!
As requested here is some code. I have two charts placed side by side one deals with "Low" turnover items the other with high. Here is the low item chart thus it renders to a named containerLow.
var lowchart = new Highcharts.Chart({
chart: {
renderTo: 'containerLow'
},
xAxis: {
},
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function () {
//ChartClicked(this.x, this.y, this, this.series.name, this.series.color, this.index);
ChartClicked(this, 'Low');
}
}
}
}
},
tooltip: {
formatter: function () {
var txt = "$= <b>" + this.point.x + "</b><br/>"
+ "Wait = <b>" + this.point.y + "</b><br/>"
+ "Other DB Info for future Bow Wave, etc. <b><br/>"
+ "Avg Fill Rate = <b>" + this.point.config.averageFillRate + "</b><br/>"
+ "Average On Hand = <b>" + this.point.config.averageOnHand + "</b><br/>"
+ "Average Workload = <b>" + this.point.config.averageWorkload + "</b><br/>"
return txt;
}
}
}); //end chart
My web service get a JSON string with multiple series in it. On page load I call the web service (twice once for hight and low items) and loop through the series array to plot the multiple curves.
function pageLoad() {
JSONGraphing.GetDataDB(getParameterByName("id"), 2, OnGetDataCompleteLow, OnError);
JSONGraphing.GetDataDB(getParameterByName("id"), 1, OnGetDataCompleteHigh, OnError);
}
Here is an example of one of the call back functions for the low graph.
function OnGetDataCompleteLow(result) {
var webServiceData = eval("(" + result + ")");
for (var i = 0; i < webServiceData.series.length; i++) {
lowchart.addSeries({
data: webServiceData.series[i].data
});
}
lowchart.yAxis[0].setTitle({
text: 'Wait time in Days'
});
lowchart.xAxis[0].setTitle({
text: 'Investment in Dollars'
});
lowchart.setTitle({ text: 'Low Frequency Items' });
}
I am in a bit of a time crunch any and all help greatly appreciated.
To make sure that the container exists you have to run your code after it gets rendered, it can be done by the following ways.
document.ready(function() {
// your code
});
or
<div id="container"> </div>
<script type="type="text/javascript"">
// your code here
</script>
or
(function() {
// your code here
})

Resources