set different style for lines in vector openlayers layer - openlayers-3

So, I use openlayers 3.9.0 and I have a vector layer that contains LineStrings, Polygons and Points.
By default LineStrings look like black lines, with litte stroke. I try to make them a bit thicker and their color have to be visible, so they not blend in, when many features are rendered.
This is my style
var stroke = new ol.style.Stroke({
color: 'rgba(0,0,0,0.4)',
lineCap: "butt",
lineJoin: "bevel",
width:1
});
function styleFunction(feature, resolution) {
var color = feature.get('color');
var name = feature.get('name');
var geom = feature.getGeometry().getType();
var fill = new ol.style.Fill({
color: color
});
//this does not work
if (geom == 'LineString') {
stroke.width=42;
}
var circle = new ol.style.Circle({
radius: 6,
fill: fill,
stroke: stroke,
color:color
});
var text= new ol.style.Text({
font: '20px Verdana',
text: name,
fill: new ol.style.Fill({
color: [64, 64, 64, 0.75]
})
})
var cStyle = new ol.style.Style({
fill: fill,
stroke: stroke,
image : circle,
text : text
});
return [cStyle];
}
I can not make this work. Any tips?
Thanks

OpenLayers 3 styles are immutable, they can not be changed.
Instead of trying to modify the width of the Stroke, create a new one with the options you want.

Related

OpenLayers 3 Resize a feature

I am using OpenLayers 3 and OpenStreetMap for my application.
I've managed to draw circle vectors on the map to draw markers, the problem I have is that the circles have always the same size when zooming in and out.
How can I change the size of the vectors according to the resolution ?
Here is my vector definition:
var dealerSource = new ol.source.Vector();
function dealerStyle(feature) {
var style = new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
stroke: new ol.style.Stroke({
color: 'white',
width: 2
}),
anchor: [1.5, 1.5],
fill: new ol.style.Fill({
color: 'green'
})
})
});
return [style];
}
you can change the size of your circle depending on the zoom level ( if that's what you want to do )
var dealerSource = new ol.source.Vector();
function dealerStyle(feature) {
var style = new ol.style.Style({
image: new ol.style.Circle({
radius: map.getView().getZoom(),
stroke: new ol.style.Stroke({
color: 'white',
width: 2
}),
anchor: [1.5, 1.5],
fill: new ol.style.Fill({
color: 'green'
})
})
});
return [style];
}
getZoom() returns the zoom level of your map as a number you can either use it or do a transformation on it to suit the desired size of your circle.
Note that above the bigger the zoom level is the bigger your circle will be drawn, to do the inverse you can replace the value by:
yourMaxZoomLevel - map.getView().getZoom()
you can also use getResolution() it returns a number too

Highcharts: circle with image background

i'm using Highcharts to create a pie, and I need to put user's profile image in a circle in the middle of it. I have managed to add the image but couldn't get it to be round, and I managed to add a circle but without image background :/
what it the best way to combine the tow?
by the way it must be part of the svg and not an absolute div on top of the pie, because the tooltip needs to be on top of that image with opacity
to add a circle I used this code :
var pixelX = 438;
var pixelY = 276;
var pixelR = 70;
// add my circle
chart.renderer.circle(pixelX, pixelY, pixelR)
.attr({
zIndex: 100,
align: 'center',
// fill: 'url(https://lh6.googleusercontent.com/-gaAgFzRLxQQ/AAAAAAAAAAI/AAAAAAAAAjc/ies0iU4BEqU/photo.jpg)',
color: 'url(https://lh6.googleusercontent.com/-gaAgFzRLxQQ/AAAAAAAAAAI/AAAAAAAAAjc/ies0iU4BEqU/photo.jpg)',
stroke: 'black',
'stroke-width': 2
})
.css({
backgroundImage :'url(https://lh6.googleusercontent.com/-gaAgFzRLxQQ/AAAAAAAAAAI/AAAAAAAAAjc/ies0iU4BEqU/photo.jpg)'
})
.add();
});
this example shows how to do it with pure SVG : http://jsfiddle.net/9zkfodwp/1/
resolved it by defining a pattern with the img, works fine but now need to find a way for the pattern to be no-repeat.
code used to add pattern:
function(chart) { // on complete
var r = chart.renderer,
pattern = r.createElement('pattern')
.attr({
id: 'pattern',
patternUnits: 'userSpaceOnUse',
x: 0,
y: 0,
width: 180,
height: 190,
viewBox: '0 0 135 135'
})
.add(r.defs);
r.rect(0, 0, 135, 135, 0)
.attr('fill', '#ddd')
.add(pattern);
r.image(profileImg,0,0,135,135)
.add(pattern);
});
and when I add the circle it can have a fill of the pattern:
// add my circle
this.circle = chart.renderer.circle(pixelX, pixelY, pixelR).attr({
fill: 'url(#pattern)'
});
this.circle.add();

Openlayers 3 Offset RegularShape

For clustered features I would like to make the following style in OL3:
A square, and on top of it another smaller square at the right-top corner. The bigger square would hold the symbol, and the smaller square would hold the number of the clustered features.
Similar to this
Is it possible to achieve this? In the API I've seen that ol.style.Icon and ol.style.Text has anchor and offset properties, but not RegularShape...
I have eventually overcome on this problem with using a single png image which included the square AND the smaller square, too, and overlayed the dynamic text on it, like this:
var clusterStyle = [new ol.style.Style({
image: new ol.style.Icon({
src: clustericon.png
}),
text: new ol.style.Text({
text: feature.get('features').length.size.toString(),
offsetY: -18,
offsetX: 18,
font: '12px Arial',
fill: new ol.style.Fill({
color: '#fff'
}),
scale: 1
}),
zIndex: 20
}), new ol.style.Style({
image: new ol.style.Icon({
src: 'overlayicon.png'
}),
zIndex: 21
})];
You can also insert an overlay image on it. Hope it helps you anyways

Cannot set a style for clicked features in vector layer

So in my Openlayers 3 I set a default style , that renders features the first time the layer loads
function styleFunction(feature, resolution) {
var color = feature.get('color');
var name = feature.get('name');
var fill = new ol.style.Fill({
color: color
});
var stroke = new ol.style.Stroke({
color: "black",
lineCap: "butt",
lineJoin: "bevel",
width:1
});
var text= new ol.style.Text({
font: '20px Verdana',
text: name,
fill: new ol.style.Fill({
color: [64, 64, 64, 0.5]
})
})
var cStyle = new ol.style.Style({
fill: fill,
stroke: stroke,
text: text
});
return [cStyle];
}
When a feature is clicked I want that feature to change its style and the rest to keep the default style above.
This is my attempt to do this
//add simple click interaction to the map
var select = new ol.interaction.Select();
map.addInteraction(select);
//for every clicked feature
select.on('select', function(e) {
var name = e.selected[0].get('name');
//set the clicked style
var fillClick= new ol.style.Fill({
color: "black"
});
var strokeClick = new ol.style.Stroke({
color: "white",
lineCap: "butt",
lineJoin: "bevel",
width:3
});
var textClick= new ol.style.Text({
font: '10px Verdana',
text: name,
fill: new ol.style.Fill({
color: [64, 64, 64, 1]
})
})
var styleClick = new ol.style.Style({
stroke: strokeClick,
fill : fillClick,
text : textClick
});
//set all to default
for (i in e.selected){
e.selected[i].setStyle(styleFunction);
}
//reset the first selected to the clicked style
e.selected[0].setStyle(styleClick);
I dont get any errors on the console, but this wont work. The clicked feature wont return to the default style when I click another one. All the clicked features keep the styleClick.
So, how do I fix this and also, is there a simpler way to do this? I think its a lot of code for this kind of functionality
Thanks
EDIT
I replaced
for (i in e.selected){
e.selected[i].setStyle(styleFunction);
}
with
var allfe = sourceVector.getFeatures();
for (i in allfe){
allfe[i].setStyle(styleFunction);
}
So now all the features will get the style defined in the styleFunction function.
This does not work. I get Uncaught TypeError: feature.get is not a function referring to the first line of the function, var color = feature.get('color'); this one.
I cannot just set a style, I need it to be a function so I can check the geomerty type in the future.
So my two problems,
I dont know how to debug and fix this error
If I have like 500 features, redrawing all of them for every click, will slow the rendering. Is there another solution to this?
Thanks
Layer and feature style functions are different
When using OpenLayers 3 style functions, you have to note the difference between an ol.FeatureStyleFunction and a ol.style.StyleFunction. They are very similar, since they both should return an array of ol.Styles based on a feature and a resolution. They are, however, not provided with that information in the same way.
When providing a style function to a feature, using it's setStyle method, the function should be an ol.FeatureStyleFunction. An ol.FeatureStyleFunction is called with this referencing the feature, and with the resolution as the only argument.
When providing a style function to a layer, it should be an ol.style.StyleFunction. They are called with two arguments, the feature and the resolution.
This is the cause of your error. You are using an ol.style.StyleFunction as a feature style. Later, when your style function is called, the first argument is the resolution and not the feature that you are expecting.
Solution 1: setStyle(null)
This solution has already been proposed in #jonatas-walker's answer. It works by setting a style on your selected features and removing the style from the unselected ones (instead of setting your style function to the unselected features).
OpenLayers will use the feature's style if it has one, and otherwise use the layer's style. So re-setting the unselected features' style to null will make them use the layer's style function again.
Solution 2: use the style option of your select interaction
ol.interaction.Select accepts a style option (an ol.style.StyleFunction), which is used to style the selected features.
Pros: Having one style function for the layer (normal view) and a style function for the select interaction makes the code more readable than mixing feature and layer styles. Also, you don't have to keep track of the events or modify the features.
It could be done similar to this:
var selectedStyleFunction = function(feature, resolution) {
return [new ol.style.Style({
stroke: new ol.style.Stroke({
color: "white",
lineCap: "butt",
lineJoin: "bevel",
width:3
}),
fill : new ol.style.Fill({
color: "black"
}),
text : new ol.style.Text({
font: '10px Verdana',
text: feature.get('name'),
fill: new ol.style.Fill({
color: [64, 64, 64, 1]
})
})
})];
};
var select = new ol.interaction.Select({
style: selectedStyleFunction
});
map.addInteraction(select);
UPDATE - fiddle styling with ol.interaction.Select
How about - http://jsfiddle.net/jonataswalker/zz6d4z7d/
var select = new ol.interaction.Select();
map.addInteraction(select);
var styleClick = function() {
// `this` is ol.Feature
return [
new ol.style.Style({
image: new ol.style.Circle({
fill: new ol.style.Fill({ color: [245, 121, 0, 0.8] }),
stroke: new ol.style.Stroke({ color: [0,0,0,1] }),
radius: 7
}),
text: new ol.style.Text({
font: '24px Verdana',
text: this.get('name'),
offsetY: 20,
fill: new ol.style.Fill({
color: [255, 255, 255, 0.8]
})
})
})
];
};
select.on('select', function(evt){
var selected = evt.selected;
var deselected = evt.deselected;
selected.forEach(function(feature){
feature.setStyle(styleClick);
});
deselected.forEach(function(feature){
feature.setStyle(null);
});
});

Is it possible to set different colors to each segment of a LineString without meeting a huge performance hit?

My openlayers 3 application draws several LineString features on the map (from a few dozen up to 2000-3000).
When setting different colors for each segment of the LineStrings, I meet a huge performance hit (starting from just a few LineStrings on the map). The zoom and pan become completely unresponsive making my aplication not usable in this form.
Since I don't want to set a new geometry for each segment (but only change its color), I imagine there must be a more performance effective way of achieving this ?
Here's my code :
var styleFunction = function(feature, resolution) {
var i = 0, geometry = feature.getGeometry();
geometry.forEachSegment(function (start, end) {
color = colors[i];
styles.push(new ol.style.Style({
geometry: new ol.geom.LineString([start, end]),
fill: new ol.style.Fill({
color: color
}),
stroke: new ol.style.Stroke({
color: color,
width: 2
})
}));
i++;
});
return styles;
}
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: styleFunction
});
There are a few things that you can try to optimize:
Cache fill and stroke style
var fillStyles = colors.map(function(color, i) {
return new ol.style.Fill({
color: color
})
});
var strokeStyles = colors.map(function(color, i) {
return new ol.style.Stroke({
color: color,
width: 2
})
});
Cache the style for each feature
vectorSource.forEach(function(feature, i) {
var geometry = feature.getGeometry();
var styles = [];
var i = 0;
geometry.forEachSegment(function (start, end) {
styles.push(new ol.style.Style({
geometry: new ol.geom.LineString([start, end]),
fill: fillStyles[i],
stroke: strokeStyles[i]
}));
i++;
});
feature.setStyle(styles);
});
Disable that the rendering is updated during animations and interactions
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
updateWhileAnimating: false,
updateWhileInteracting: false
});
See ol.layer.Vector.
If all this doesn't help, you might also want to take a look at ol.source.ImageVector(example).
Since we could not solve the problem, even with tsauerwein's recommendations, this feature was put to the fridge for a while. Now we got back to it with fresh ideas and actually found a "solution".
Something that I made not clear in the question is that segments of a LineString are colored according to a property of their starting point. Several consecutive segments may thus share the same color if the value of this property doesn't change between them.
The idea is to avoid creating a new style for each and every segment but rather to create a new style only when necessary (when the color changes) :
let i = 0,
color = '#FE2EF7', //pink. should not be displayed
previousColor = '#FE2EF7',
lineString,
lastCoordinate = geometry.getLastCoordinate();
geometry.forEachSegment(((start, end) => {
color = this.getColorFromProperty(LinePoints[i].myProperty);
//First segment
if (lineString == null) {
lineString = new ol.geom.LineString([start, end]);
} else {
//Color changes: push the current segment and start a new one
if (color !== previousColor) {
styles.push(new ol.style.Style({
geometry: lineString,
fill: new ol.style.Fill({
color: previousColor
}),
stroke: new ol.style.Stroke({
color: previousColor,
width: 2
})
}));
lineString = new ol.geom.LineString([start, end]);
} else {
//Color is same: continue the current segment
lineString.appendCoordinate(end);
}
//Last segment
if (end[0] === lastCoordinate[0] && end[1] === lastCoordinate[1]) {
styles.push(new ol.style.Style({
geometry: lineString,
fill: new ol.style.Fill({
color: color
}),
stroke: new ol.style.Stroke({
color: color,
width: 2
})
}));
}
}
previousColor = color;
i++;
}

Resources