My server is statically serving several different PNG images of the same object, each taken with a different spectral filter (for example, just a red channel or just a blue channel). I'd like to show a slippy, false-colored map of that object. I do so by creating three separate images sources like so:
extent = [0, 0, ncols, nrows];
pixelProjection = new ol.proj.Projection({
code: 'some-image',
units: 'pixels',
extent: extent
});
rsource = new ol.source.ImageStatic({
url: "static/imgs/band_1.png",
projection: pixelProjection,
imageExtent: extent
});
gsource = new ol.source.ImageStatic({
url: "static/imgs/band_2.png",
projection: pixelProjection,
imageExtent: extent
});
bsource = new ol.source.ImageStatic({
url: "static/imgs/band_3.png",
projection: pixelProjection,
imageExtent: extent
});
Next, I use these sources as inputs to a raster source which can compose them:
rgbSources = [rsource, gsource, bsource];
raster = new ol.source.Raster({
sources: rgbSources,
operation: function(bands, data) {
var rband = bands[0];
var gband = bands[1];
var bband = bands[2];
var composed = [
rband[0],
gband[0],
bband[0],
255
];
return composed;
}
});
I then create a layer that uses this raster as its source:
colorLayer = new ol.layer.Image({
source: raster
});
Lastly, I can create a map and add my raster layer to the map:
var map = new ol.Map({
target: 'map',
view: new ol.View({
center:ol.extent.getCenter(extent),
projection: pixelProjection,
zoom: 1.5
})
});
map.addLayer(colorLayer);
So far so good! This displays a colorized version of the image as expected. The problem arises when the user triggers a change to a color channel by inputting a new channel index to pull from. I handle a blue channel change like this:
var index = 4; // actually gets passed in from user
bsource = new ol.source.ImageStatic({
url: "static/imgs/band_" + index + ".png",
projection: pixelProjection,
imageExtent: extent
});
rgbSources[2] = bsource; // this was in global scope from before
raster.sources = rgbSources; // as was this
Expected behavior is that the map would immediately change colors, or at least it would change when I zoom in or pan but neither of those things happens. I am unable to get the new colors to appear at all. Am I updating the wrong thing? Perhaps the raster.sources field has an associated setter function that I am unable to find?
Found a solution! It looks like setting a raster's source directly is not allowed, but setting a layer's source is. So unfortunately, I have to create a new raster object (new source entirely), but at least I don't need a new layer:
raster = new ol.source.Raster({
sources: rgbSources,
operation: composeBands
});
colorLayer.setSource(raster);
Accepting my own answer but willing to accept someone else's solution if it means I don't need to create a new source.
Related
Background
Specs
OpenLayers 4.4.1
OSM
I'm fairly new to OpenLayers and have never used vectors before (primarily because I found out that I was using OpenLayers version 1, and had to relearn everything).
My application adds circles to a map relating to a position with a specific radius indicating position accuracy.
In its operation, multiple circles are added to the map at different times.
This is my code for loading the map:
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'mapdiv',
controls: ol.control.defaults({
attributionOptions: /** #type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}),
view: new ol.View({
//center: [0, 0],
zoom: 16
})
});
//this is where all map 'features' (circles) are stored
var vectorSource = new ol.source.Vector({
projection: 'EPSG:4326'
});
As you can see, I load the 'vector source' right after the map as I understood that it holds all 'vectors' which are displayed on the map so long as you specify it as the 'source'.
This is the code I use to generate the circle (source) (I tweaked it at getPointResolution because the OP made a mistake):
//code from https://stackoverflow.com/a/28299599
function addCircle(map, vectorSource, radius) {
var view = map.getView();
var projection = view.getProjection();
var resolutionAtEquator = view.getResolution();
var center = view.getCenter();
var pointResolution = ol.proj.getPointResolution(projection, resolutionAtEquator, center);
var resolutionFactor = resolutionAtEquator/pointResolution;
var radius = (radius / ol.proj.METERS_PER_UNIT.m) * resolutionFactor;
var circle = new ol.geom.Circle(center, radius);
var circleFeature = new ol.Feature(circle);
// vector layer
vectorSource.addFeature(circleFeature);
var vectorLayer = new ol.layer.Vector({
source: vectorSource
});
map.addLayer(vectorLayer);
}
Problem
Loading one circle goes normally, adds a blue stroked, opaque circle at the specified location with specified radius.
Loading a second circle appears more opaque than the last. Moving the map to the previous circle, it is also more opaque.
With each added circle, the apparent opacity increases for all displayed circles.
Running vectorLayer.getOpacity() in every circle generation results in 1, when clearly the circle is translucent, becoming increasingly opaque with every new circle.
Summary
Looking around, it appears that often it is the case that the developer is reloading the same circle over and over until many are stacked on top of one another. It almost seems like this is the case for me too, except I've triple-checked that I'm only running addCircle() once and the circle is in a different position than the last.
Is it possible that OpenLayers is redrawing all previous circles with every new circle?
Maybe this isn't related to getOpacity but has to do with the color as an rgba() combination...
Question
I want every circle to remain the same after drawing new circles. The default opacity and color is fine.
Am I doing something wrong?
Here's a fiddle as an example - https://jsfiddle.net/f5zrLt20/5/
Define the layer when defining the vectorSource:
var layer = null;
//this is where all map 'features' (circles) are stored
var vectorSource = new ol.source.Vector({
projection: 'EPSG:4326'
});
And check if it exists on creating a new circle:
// If layer is not yet set, create new layer and add it to map
if (!layer) {
vectorSource.addFeature(circleFeature);
layer = new ol.layer.Vector({
source: vectorSource
});
map.addLayer(layer);
}
//Otherwise, just add feature to the source
else {
layer.getSource().addFeature(circleFeature);
}
So, i'm pretty new to JS.
I'm creating features styled as markers in the center of my rgb layers and assign them to an array with this code:
for (var i = 0, len = Layers.length; i < len; i++) {
var mExtent = ol.proj.transformExtent(Layers[i].BoundingBox[0].extent, 'EPSG:4326', 'EPSG:3857');
var X = mExtent[0] + (mExtent[2]-mExtent[0])/2;
var Y = mExtent[1] + (mExtent[3]-mExtent[1])/2;
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point([X, Y]),
name: Layers[i].Title,
layername: Layers[i].Name,
description: Layers[i].Abstract
});
var iconStyle = new ol.style.Style({
image: new ol.style.Icon(/** #type {olx.style.IconOptions} */ ({
anchor: [0.5, 46],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: ortho
}))
});
iconFeature.setStyle(iconStyle);
var vectorSource = new ol.source.Vector({
features: [iconFeature]
});
var vectorLayer = new ol.layer.Vector({source: vectorSource, zIndex: 100 });
layers2[i] = vectorLayer
}
when im trying then to call a map with:
var map = new ol.Map({
layers:[BaseLayers, POI, layers2],
target: document.getElementById('map')
});
My layers2 array of features does not show up on the map.
If then i try to add this array of features manually in the console:
map.addLayer(layers2)
I get following error:
TypeError: a.addEventListener is not a function
But if i try to manually call an element from array like such:
map.addLayer(layers2[0])
It works fine.
My array containing base layers(OSM+mapbox) works fine.
Im pretty sure there's something wrong with my type of array.
But don't know what.
Thanks for coming by.
Edit 1
Tried to put all my features, rgb layers and basemaps in a single array "layers".
So the code changed in a first loop from
layers2[i] = vectorLayer;
To:
layers.push(vectorLayer);
Where "layers" already contains all the rest layer objects.
When calling the map - no "vectorLayer" features are on it.
When calling "layers" manually in console with map.addLayer(layers) still get the same error.
When calling specific "vectorLayer" feature with map.addLayer(layers[2]), for example - it show's up.
By looking at the code layers2 is an array of layers. While creating ol.Map object layers attribute should be single dimension array but in your code its a 2 dimensional array like below. Thats the reason its giving error.
[ BaseLayer, POI, [layers[0],layers2[1],.....]]
Solution 1: Add array of layers after creating the map object using loop.
var map = new ol.Map({
layers:[BaseLayers, POI],
target: document.getElementById('map')
});
for ( var i=0; i<layers2.length; i++ ) {
map.addLayer(layers2[i]);
};
Solution 2 : Insert BaseLayers and POI to layers2 then configure ol.Map by declaring only layers2.
I am looking to create simple markers in Openlayers 3 based on a two dimensional array which I pass via PHP containing the Geolocation of the marker and an attribute. Currently the attribute is the colour that I would like the layer to be:
//run external php script to grab all entries from the database; make an array and add to source vector
var latlong_marker = <?php require 'db_multiple_gps.php'; echo $ol_latlong;?>;
var latlong_array_length = latlong_marker.length;
var vectorSource = new ol.source.Vector({
//create empty vector
});
// cycle through all entries in the array
for (var i = 0; i < latlong_array_length; i++){
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform(latlong_marker[i][0], 'EPSG:4326', 'EPSG:3857'))});
vectorSource.addFeature(iconFeature);
var iconStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({color: latlong_marker[i][2]})
})
});
//add the feature vector to the layer vector, and apply a style to whole layer
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: iconStyle
});
}
The vector layers are presented on the map in the correct locations but the issue is that the colour is that of the last entry in the array latlong_marker[i][2].
From my research I think it may be due to closure of functions but I'm not exactly sure how or where the functions are in play here.
Debugging show the entire array being passed through the loop and vectorSource looking good; however iconStyle appears to have many null values.
Am I going about this in the right fashion? I could pass another parameter other than the actual colour required (such as a integer) to then be used to create the different style.
In addition to the code snippet above, I have played around with with closing the loop earlier; this works fro creation of vectorSource but I always fall down with trying to pass an array of iconStyle to the Vector Layer.
Try this
var latlong_marker = <?php require 'db_multiple_gps.php'; echo $ol_latlong;?>;
var latlong_array_length = latlong_marker.length;
var vectorSource = new ol.source.Vector({
//create empty vector
});
// cycle through all entries in the array
for (var i = 0; i < latlong_array_length; i++){
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform(latlong_marker[i][0],
'EPSG:4326','EPSG:3857'))});
var iconStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({color: latlong_marker[i][2]})
})
});
// THIS IS NEW - add each style individually to the feature
iconFeature.setStyle(iconStyle);
// First add the feature when it has got its style
vectorSource.addFeature(iconFeature);
}
//add the feature vector to the layer vector, and apply a style to whole layer
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
// remove this - > style: iconStyle
});
I've just started having a look at OpenLayers 3 and am attempting to zoom to a single feature whose extent is to be a geoJSON object sent by the server (which will change at each refresh, so I can't hard code the zoom/central point). I would like this to happen preferably as soon as the page loads, but I'm struggling to get it to work.
I keep getting an error saying "Uncaught TypeError: undefined is not a function" at the line where I try to read the geometry from a geoJSON object, and I'm not really sure how to fix it. The geoJSON string seems fine (i've also tried parsing it before passing it to readGeometry but that gave the same results).
If there's an easier/faster way to do this than how I am currently, i'd be interested to hear it too!
Any help is greatly appreciated.
var feature = new ol.Feature({
});
var view = new ol.View({
center: [0, 0],
zoom: 1
});
var client = new XMLHttpRequest();
client.open('GET', 'http://localhost:3000/extent');
client.send();
client.onreadystatechange=function(){
if (client.readyState==4 && client.status==200){
var geomstring = client.responseText;
console.log(geomstring)
var geojson = new ol.format.GeoJSON();
var geom = geojson.readGeometry(geomstring);
var size = (map.getSize());
feature.setGeometry(geom);
view.fitGeometry(
geom,
size,
{
padding: [170, 50, 30, 150],
constrainResolution: false
});
}
}
var map = new ol.Map({
layers: [raster, vector, feature],
target: 'map',
});
Reading the geometry should work like this:
var geojson = new ol.format.GeoJSON();
var json = {"st_asgeojson":"{\"type\":\"Polygon\",\"coordinates\":[[[-2416.91312435933,6700304.87650272],[-2416.91312435933,6700339.02584392],[-2255.97853651961,6700339.02584392],[-2255.97853651961,6700304.87650272],[-2416.91312435933,6700304.87650272]]]}"};
var geom = geojson.readGeometry(json['st_asgeojson']);
console.log(geom.getArea());
Do you know how to have a nice clustering in OpenLayers such as this google example ?
You can add label to pointStyle in above example and explain context of this label.
Your code should be something like this:
var pointStyle = new OpenLayers.Style({
// ...
'label': "${label}",
// ...
}, {
context: {
// ...
label: function(feature) {
// clustered features count or blank if feature is not a cluster
return feature.cluster ? feature.cluster.length : "";
}
// ..
}
});
var styleMap = new OpenLayers.StyleMap({
'default': pointStyle,
});
var googleLikeLayer = new OpenLayers.Layer.Vector("GoogleLikeLayer", {
// ...
styleMap : styleMap,
// ...
});
Use OpenLayers.Strategy.Cluster for clustering.
Example Code
Working Example
Custom Styling
In-depth Explanation
I have just implemented a so called AnimatedCluster strategy for OpenLayers.
You can see a bit more about it at: http://www.acuriousanimal.com/2012/08/19/animated-marker-cluster-strategy-for-openlayers.html
It is only a firts version but adds a nice animation to the clusters. There are many things to improve but it is a starting point.
There's a great clustering example available in OpenLayers 3.
I created a jsFiddle from the code so you can play with it.
Basically you have to create an ol.source.Cluster with a grouping distance from an ol.source.Vector formed by an array of ol.Feature. Each ol.Feature created from your source coordinates in the form of ol.geom.Point.
var features = [
new ol.Feature(new ol.geom.Point([lon1, lat1])),
new ol.Feature(new ol.geom.Point([lon2, lat2])),
...
];
var cluster = new ol.source.Cluster({
distance: 50,
source: new ol.source.Vector({ features: features });
});
var map = new ol.Map({
layers: [
new ol.source.MapQuest({layer: 'sat'}), // Map
new ol.layer.Vector({ source: cluster }) // Clusters
],
renderer: 'canvas',
target: 'map'
});
you can do this with as igorti has said. the soltion is using OpenLayers.Strategy.Cluster class and styling your layer with OpenLayers.Style class...
for styling :
var pointStyle = new OpenLayers.Style({
'default': new OpenLayers.Style({
'pointRadius': '${radius}',
'externalGraphic': '${getgraph}'
....
},{
context:{
radius: function(feature){
return Math.min(feature.attributes.count,7)+3;
},{
getgraph : function(feature){
return 'ol/img/googlelike.png';
}}}};
it must helps you, more power to you!
Here is the JSfiddle for clustering based on custom attributes added to the layers. I struggled a bit with this so putting it here; Also shows creating a summary pie graph image when zoomed out with the clustered data http://jsfiddle.net/alexcpn/518p59k4/
Also created a small openlayer tutorial to explain this OpenLayers Advanced Clustering
var getClusterCount = function (feature) {
var clustercount = {};
var planningcount = 0;
var onaircount = 0;
var inerrorcount = 0;
for (var i = 0; i < feature.cluster.length; i++) {
if (feature.cluster[i].attributes.cluster) {
//THE MOST IMPORTANT LINE IS THE ONE BELOW, While clustering open layers removes the orginial feature layer with its own. So to get the attributes of the feature you have added add it to the openlayer created feature layer
feature.attributes.cluster = feature.cluster[i].attributes.cluster;
switch (feature.cluster[i].attributes.cluster) {
......
return clustercount;
};