How to dynamically style Cluster layer without style function - openlayers-3

How can I define the style of a cluster layer as an ol.style.Style object, and not as a function, in Openlayers 3?
I am using a library (ol3-google-maps) which only accepts ol.style.Style objects for styling. The official cluster example uses a style function to dynamically add the number of features in each cluster to it's icon:
style: function(feature, resolution) {
console.log(feature);
var size = feature.get('features').length;
var style = styleCache[size];
if (!style) {
style = [
new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
stroke: new ol.style.Stroke({
color: '#fff'
}),
fill: new ol.style.Fill({
color: '#3399CC'
})
}),
text: new ol.style.Text({
text: size.toString(),
fill: new ol.style.Fill({
color: '#fff'
})
})
})
];
styleCache[size] = style;
}
return style;
}

ol3 style functions are great when the style depends on some property of the feature, such as the number of subfeatures in a cluster. There is no other way to use dynamic properties in the style.
You could use a common style for the cluster layer that does not depend on the cluster size (that does not show the number of features), such as this example.
However, you could also set a non-dynamic style for each feature, instead of each layer. That style could be calculated based on it's properties, giving you some of the possibilities of a style function.
This example is a modification of the official example that does not use a normal style function. Instead, it listens to the addfeature and changefeature events of the cluster source, and sets a style based on it's properties to the feature itself (see code below).
Not that this is not a general solution or replacement for style functions, although it should work fine for cluster sources. Notably, you lose the possibility to generate styles based on the resolution. Setting a style to a feature might not be desirable if the feature is used by other layers. You also have to consider performance issues.
var styleCache = {};
var styleFunctionIsChangingFeature = false
var styleFunction = function (evt) {
if (styleFunctionIsChangingFeature) {
return;
}
var feature = evt.feature;
var size = feature.get('features').length;
var style = styleCache[size];
if (!style) {
style = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
stroke: new ol.style.Stroke({
color: '#fff'
}),
fill: new ol.style.Fill({
color: '#3399CC'
})
}),
text: new ol.style.Text({
text: size.toString(),
fill: new ol.style.Fill({
color: '#fff'
})
})
});
styleCache[size] = style;
}
styleFunctionIsChangingFeature = true
feature.setStyle(style);
styleFunctionIsChangingFeature = false
};
clusterSource.on('addfeature', styleFunction);
clusterSource.on('changefeature', styleFunction);

Related

Openlayers 3 : How to change Geojson Icon src so it won't override other geojson types?

I'm able to change src of an icon when loading Point/MultiPoint Geojson, in this way:
that.geojsonLayers[index] = new that.openlayers.ol.layer.Vector({
source: new that.openlayers.ol.source.Vector({
format: new that.openlayers.ol.format.GeoJSON(),
url: url
}),
style: new that.openlayers.ol.style.Style({
image: new that.openlayers.ol.style.Icon({
src: 'http://mapmip.webiks.com/assets/Markers/marker-icon-blue.png'
})
})
but then I can't load other types of Geojson - Polygons are not being loaded at all, and Geometry Collection (which composed from icon and lines) is load only the icon.
What is the way to change the icon src so it won't override the other geojson type ?
You may use a style function to verify the geometry type you need to style. Setting an icon for styling a polygon is not correct.
Check this
1.Declare your style
var myMultiStyle = {
//here replace with your icon style
'Point': new ol.style.Style({
image: new ol.style.Circle({
fill: new ol.style.Fill({
color: 'rgba(255,255,0,0.4)'
}),
radius: 5,
stroke: new ol.style.Stroke({
color: '#ff0',
width: 1
})
})
}),
'LineString': new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#f00',
width: 3
})
}),
'Polygon': new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(0,255,255,0.5)'
}),
stroke: new ol.style.Stroke({
color: '#0ff',
width: 1
})
})
};
Create a style function
function myStyleFunction(feature,resolution){
return myMultiStyle[feature.getGeometry().getType()];
}
Asign the style function to your vector source
that.geojsonLayers[index] = new that.openlayers.ol.layer.Vector({
source: new that.openlayers.ol.source.Vector({
format: new that.openlayers.ol.format.GeoJSON(),
url: url
}),
style: myStyleFunction
})
Check this official example to see the result.

OpenLayers 3 select style

In OL2 I was able to specify a "select" style in the style definition. In OL3 this doesn't seem to exist. If I understand it correctly, I can set a style for the select interaction. However, this likely won't work in my case since every layer has a unique "selected" style. Am I mistaken in my assessment of the capability? Is there another/optimal way to do this in OL3?
Let's assume that you have a style parameter stored in each ol.Feature, you can add a ol.style.StyleFunction to your ol.interaction.Select and return a style based on this parameter. Like so:
var styles = {
'route': new ol.style.Style({
stroke: new ol.style.Stroke({
width: 6,
color: [237, 212, 0, 0.8]
})
}),
'icon': new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 1],
src: 'pin.png'
})
}),
'geoMarker': new ol.style.Style({
image: new ol.style.Circle({
radius: 7,
snapToPixel: false,
fill: new ol.style.Fill({color: 'black'}),
stroke: new ol.style.Stroke({
color: 'white',
width: 2
})
})
})
};
var select = new ol.interaction.Select({
style: function(feature, resolution) {
return [styles[feature.get('style_parameter')]];
}
});
And your feature would be like:
var geoMarker = new ol.Feature({
style_parameter: 'geoMarker',
geometry: new ol.geom.Point([0,0])
});
I know this a very old thread, but since I haven't been able to find a clear solution to this particular problem yet, I still deem it fit to post mine. Not sure how it holds up with a large number of layers and features, but this is the most elegant and concise solution I could come up with.
BTW: I'm using the latest version of OpenLayers, which at the moment is 6.3.1.
var map = new ol.Map({
...
layers: [
new ol.layer.Vector({
...
// Default style for layer1
style: default1,
// Hover style for layer1 (custom property)
hoverStyle: hover1,
// Selected style for layer1 (custom property)
selectedStyle: selected1
}),
new ol.layer.Vector({
...
// Default style for layer2
style: default2,
// Hover style for layer2 (custom property)
hoverStyle: hover2,
// Selected style for layer2 (custom property)
selectedStyle: selected2
})
],
...
});
var hoverInteraction = new ol.interaction.Select({
condition: ol.events.condition.pointerMove,
style: function(feature) {
var layer = hoverInteraction.getLayer(feature);
return layer.values_.hoverStyle;
}
});
map.addInteraction(hoverInteraction);
var selectInteraction = new ol.interaction.Select({
condition: ol.events.condition.click,
style: function(feature) {
var layer = selectInteraction.getLayer(feature);
return layer.values_.selectedStyle;
}
});

GeoJSON data not displayed on a vector layer

I'm trying to display a vector layer to show a set of geojson features.
When I try and add the layer though I get an error in the ol.js library "k.xd is not a function"
var geoData = {"type":"FeatureCollection",
"features":
[
{"type":"Feature","properties":{"Name":"","Description":""},"geometry":{"type":"Point","coordinates":[0.0,0.0]}},
{"type":"Feature","properties":{"Name":"1","Description":""},"geometry":{"type":"Point","coordinates":[11.50728,3.87471,0.0]}},
]
};
// vector layer
var vector = new ol.layer.Vector({
source: new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(geoData)
}),
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'red',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(255,0,0,0.2)'
})
})
});
I've hacked together an example here http://jsfiddle.net/dxt95yt6/1/ that shows it not working but I can't figure out where this differs from the original tutorials.
Given style object is not correct for points and therefore features just don't show up. Try:
style: new ol.style.Style({
image: new ol.style.Circle({
radius: 8,
stroke: new ol.style.Stroke({
color: 'red',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(255,0,0,0.2)'
})
})
})
Please note, in original code, there's another problem, too. Coordinates have to be transformed to EPSG:3857:
features: (new ol.format.GeoJSON()).readFeatures(
geoData,
{featureProjection: ol.proj.get('EPSG:3857')}
)
http://jsfiddle.net/zqx6644q/8/
Its always helpful to validate your geojson before using it. I can recommend geojsonlint which has alos an api to make sure your using a correct geojson.

Resizing OL3 features programmatically

I have map with user drawn features. After the user custom enters a map scale, I need to be able to resize all the existing map features based on this scale.
What is the way to do this in OL 3? I've seen that in OL 2, there is a geometry.resize function.
Thanks.
You can accomplish this using a StyleFunction.
Look at the following example: http://openlayers.org/en/v3.4.0/examples/vector-layer.html
If you zoom the map, you'll see the labels appear at a certain resolution. This is controlled inside the style function. Here's a snippet:
var featureOverlay = new ol.FeatureOverlay({
map: map,
style: function(feature, resolution) {
// === SEE THIS NEXT LINE ===
var text = resolution < 5000 ? feature.get('name') : '';
if (!highlightStyleCache[text]) {
highlightStyleCache[text] = [new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#f00',
width: 1
}),
fill: new ol.style.Fill({
color: 'rgba(255,0,0,0.1)'
}),
text: new ol.style.Text({
font: '12px Calibri,sans-serif',
text: text,
fill: new ol.style.Fill({
color: '#000'
}),
stroke: new ol.style.Stroke({
color: '#f00',
width: 3
})
})
})];
}
return highlightStyleCache[text];
}
});
So you could define the size you want the features to have for any resolution range. Note that the style property also applies to a ol.layer.Vector layer.
you can use API: map.updateSize().
Reference: http://openlayers.org/en/latest/apidoc/ol.Map.html#updateSize

How to indicate polygon vertices with small circles using OpenLayers?

I am using OpenLayers 3 and have more or less implemented everything in my requirements list, except one thing: I am asked to somehow make the polygon rendering indicate the polygon vertices with small circles.
In plain words, the desired polygon outline is not just a line - it is a line "adorned" with small circles in all the places where there is a vertex.
How can I do that in OL3? I searched in the ol.style.Style docs (that is, the style I pass via setStyle to the ol.layer.Vector containing my polygons), but didn't find anything relevant.
For reference, there is now an example showing how to display the vertices of a polygon with a custom style geometry:
http://openlayers.org/en/master/examples/polygon-styles.html
var styles = [
// style for the polygon
new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'blue',
width: 3
}),
fill: new ol.style.Fill({
color: 'rgba(0, 0, 255, 0.1)'
})
}),
// style for the vertices
new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: 'orange'
})
}),
geometry: function(feature) {
// return the coordinates of the first ring of the polygon
var coordinates = feature.getGeometry().getCoordinates()[0];
return new ol.geom.MultiPoint(coordinates);
}
})
];
The kind OL3 devs have provided the answer on the GitHub issue list. You basically use a style's geometry function, that transforms the geometry before projecting it - and in that function, you add your vertices to a MultiPoint geometry. #tsauerwein went as far as creating a working fiddle - many thanks to him for his work.
var styleFunction = function() {
var image = new ol.style.Circle({
radius: 5,
fill: null,
stroke: new ol.style.Stroke({color: 'orange', width: 2})
});
return [
new ol.style.Style({
image: image,
geometry: function(feature) {
var coordinates = feature.getGeometry().getCoordinates()[0];
return new ol.geom.MultiPoint(coordinates);
}
}),
new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'blue',
width: 3
}),
fill: new ol.style.Fill({
color: 'rgba(0, 0, 255, 0.1)'
})
})
];
};

Resources