openlayers3 same style for selected features (only one changed property)? - javascript

I have an ol3 layer with a style definition. I would like to use the same style for the select interaction:
style = function(feature, resolution) {
var iconFont = 'FontAwesome';
var iconFontText = '\uf1f8'; // fa-trash
var iconSize = 24;
var col = 'black';
var styles = [];
var styleIcon = new ol.style.Style({
text: new ol.style.Text({
font: 'Normal ' + iconSize + 'px ' + iconFont,
text: iconFontText,
fill: new ol.style.Fill({color: col})
})
});
styles.push(styleIcon);
}
return styles;
};
new ol.layer.Vector({
source: that.source,
style: style
});
new ol.interaction.Select({
features: that.selectedFeatures,
style: style
})
I only want to change one property (iconSize) of the style to highlight the selected features. Is that somehow possible? I do not want to define two seperate styles, but I would be ok if it was somwhow possible to programmatically copy the style and replace the iconsize value.

Now I found a working solution:
You can add an additional argument (e.g. highlight [bool]) to the style function:
style = function(feature, resolution, highlight) {
...
}
and the instead of
new ol.interaction.Select({
features: that.selectedFeatures,
style: style
})
you can use
new ol.interaction.Select({
features: that.selectedFeatures,
style: function(feature,resolution){
return style(feature,resolution,true);
}
})

A possible solution: Tell your function that ol.interaction.Select is active:
var style = function(ft, res, selecting){
return [
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#ffcc33',
width: selecting ? 4 : 2
})
})
];
};
var selecting = false;
selectInteraction.on('select', function(evt) {
selecting = evt.selected.length > 0;
});
http://jsfiddle.net/jonataswalker/eepyne75/

Related

Can we use html tags and css to style the features in openlayers?

I have a polygon feature in OpenLayers 5. It has some random id which I need to show at the center of the polygon slightly right aligned.
I have used the ol.style.Text() style to display the id on polygon. I can manage the alignment using the offsetX and offsetY options of the class but how can I display the text in html elements or imitate it, because ol.style.Text() only accepts text data.
Overlays in openlayers will definitely solve the problem, I was able get middle point of polygon using getInteriorPoint() on geometry but I don't want to use overlays because there could be large number of polygons on the map and adding overlay for each would deteriorate performance and memory utilization.
Here is the expected output / I am trying to achieve :
Here is my code :
Current code
Also check what I have done to toggle the Id ON and OFF and mention if that can be bettered. The ID could be turned ON and OFF based on zoom level.
Instead of using CSS you could draw the background in a canvas element and use it in an icon style. And use a style function to style the interior point of a polygon without needing to create more features:
var canvas = document.createElement("canvas");
canvas.width = ??;
canvas.height = ??;
var ctx = canvas.getContext("2d");
// draw an arraw and rounded box
.....
.....
var iconUrl = canvas.toDataURL();
var offStyle = new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255,255,255,0)'
}),
stroke: new ol.style.Stroke({
color: 'green',
width: 1.5
})
});
var onStyle = new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255,255,255,0)'
}),
stroke: new ol.style.Stroke({
color: 'black',
width: 1.5
})
});
var styleFunction = function (feature, resolution) {
if (off) { // or use (resolution > ????)
return offStyle;
else {
var styles = [onStyle];
if (feature.getGeometry().getType == 'Polygon') {
styles.push( new ol.style.Style({
geometry: feature.getGeometry().getInteriorPoint(),
image: new ol.style.Icon({
src: iconUrl,
// options to anchor the icon
....
}),
text: new ol.style.Text({
scale: 1,
text: feature.get('.....'),
font: 'normal 10px FontAwesome',
fill: new ol.style.Fill({
color: 'black'
}),
}),
zIndex: 100
}));
}
return styles;
}
}

Openlayers feature selection area is offset

I have this very odd problem, where the selection box shifts further and further down the closer the marker is to the bottom of the map.
If the marker is at the very top of the map the selection box is right on top of the marker, but if it's further down the map I have to click further and further under the marker to select it.
Has anyone else experienced this? I'm using Openlayers 4.1.1. Here are some code chunks that could be relevant (I'm more than happy to share more code if that can help!):
var imageStyleFunction = function (feature, resolution) {
if (feature.iconSrc) {
var anchorX = feature.anchorX ? feature.anchorX : 0.5;
var anchorY = feature.anchorY ? feature.anchorY : 0.5;
return [new ol.style.Style({
image: new ol.style.Icon({
src: feature.iconSrc,
anchor: [anchorX, anchorY]
})
})];
}
};
...
markerLayer = new ol.layer.Vector({
source: new ol.source.Vector(),
style: imageStyleFunction
});
...
selectionInteraction = new ol.interaction.Select({
condition: ol.events.condition.click,
//hitTolerance: 20,
style: imageStyleFunction
});
selectionInteraction.on('select', function (e) {
var userLocationFeature = _.find(e.target.getFeatures().getArray(),
function (feature) {
return feature.markerType === "userLocation";
});
if (userLocationFeature) {
store.commit("setSelectedUserLocation", userLocationFeature);
} else {
store.commit("setSelectedUserLocation", null);
selectionInteraction.getFeatures().clear();
}
});
...
function createMarker(location, iconFile, markerType, markerId) {
var markerLayerSource = markerLayer.getSource();
var markerFeature = new ol.Feature({
geometry: new ol.geom.Point(transformLonLat(location.longitude, location.latitude))
});
markerFeature.iconSrc = app.config.map.iconPath + iconFile;
if (markerType) {
markerFeature.markerType = markerType;
}
if (markerId) {
markerFeature.markerId = markerId;
}
markerLayerSource.addFeature(markerFeature);
}
Mick's own solution by calling map.updateSize() solved it for me also. I haven't investigated it more closely but I'm assuming it has to do with sizes changing during init of my ionic app. It's a quick fix to a more difficult problem at least.

minimal size for a rectangle in style

I'm writing an application where users can mark regions on a world map. Now these regions can be very small, so that it's hard to click on them when not zoomed in.
Is there a way how I can define (e.g. in the style function) that a (rectangle) feature should always be rendered with at least e.g. 10px × 10px?
Update: some code I currently use:
on the drawing side:
var draw = new ol.interaction.Draw({
source: vectorSource,
type: 'LineString',
geometryFunction: function(coordinates, geometry) {
if(!geometry) {
geometry = new ol.geom.Polygon(null);
}
var start = coordinates[0];
var end = coordinates[1];
geometry.setCoordinates([[
start,
[start[0], end[1]],
end,
[end[0], start[1]],
start
]]);
return geometry;
},
maxPoints: 2
});
draw.on('drawend', function(e) {
var extent = e.feature.getGeometry().getExtent();
extent = app.map.rlonlate(extent); // own function to convert it from map coordinates into lat/lon
// some code to save the extent to the database
});
and on the displaying side:
vectorSource.addFeature(
new ol.Feature({
geometry: ol.geom.Polygon.fromExtent(app.map.lonlate(extent)),
// … some more custom properties like a display name …
})
);
the style function:
function(feature) {
return [new ol.style.Style({
stroke: new ol.style.Stroke({
color: feature.get('mine') ? '#204a87' : '#729fcf',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, ' + (feature.get('mine') ? '0.5' : '0.2') + ')'
})
})];
}
Using a style function is a good idea. The second argument of the style function is resolution which you can use to check if your feature geometry would be smaller than e.g. 10 px at the current resolution:
var styleFn = function(feature, resolution) {
var minSizePx = 30;
var minSize = minSizePx * resolution;
var extent = feature.getGeometry().getExtent();
if (ol.extent.getWidth(extent) < minSize || ol.extent.getHeight(extent) < minSize) {
// special style for polygons that are too small
var center = new ol.geom.Point(ol.extent.getCenter(extent));
return new ol.style.Style({
geometry: center,
image: ...
} else {
// normal style
return defaultStyle;
}
};
http://jsfiddle.net/ukc0nmy2/1/

DragBox change color while drawing

In order to help users to draw a boxarea with minimum of 100 px at width and height, I've thought to start drawing in red color (fill and border of box) and then change automatically to green when it reaches the 100 px mentioned while user is drawing the feature.
Any idea how to do this?
I got it something like that when user has finished drawing, but in my opinion, that behavior is not enough comfortable.
Thanks in advance
UPDATE:
http://jsfiddle.net/jonataswalker/41j800kv/
Found a better solution. Put these conditions inside a ol.interaction.Draw#StyleFunction:
var draw = new ol.interaction.Draw({
source: vectorSource,
type: 'LineString',
maxPoints: 2,
style: function(feature){
var style;
var geometry = feature.getGeometry();
var extent = geometry.getExtent();
var topLeft =
map.getPixelFromCoordinate(ol.extent.getTopLeft(extent));
var bottomLeft =
map.getPixelFromCoordinate(ol.extent.getBottomLeft(extent));
var topRight =
map.getPixelFromCoordinate(ol.extent.getTopRight(extent));
var width = topRight[0] - topLeft[0];
var height = bottomLeft[1] - topLeft[1];
coords_element.innerHTML =
'width: ' + width + '<br>height: ' + height;
if (width > 100 && height > 100) {
style = new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new ol.style.Stroke({
color: 'red',
width: 2
})
});
} else {
style = new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new ol.style.Stroke({
color: '#ffcc33',
width: 2
})
});
}
return [style];
},
geometryFunction: function(coordinates, geometry) {
if (!geometry) {
geometry = new ol.geom.Polygon(null);
}
var start = coordinates[0];
var end = coordinates[1];
geometry.setCoordinates([
[start, [start[0], end[1]], end, [end[0], start[1]], start]
]);
return geometry;
}
});
Take this piece of code and put some conditions on it:
Using the latest version of ol3. 3.13.1 you may do the following to achieve your goal.
Create a map with a layer and add a dragbox interaction
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var map = new ol.Map({
layers: [raster],
target: 'map',
view: new ol.View({
center: [0, 0],
zoom: 2
})
});
var selectOnBoxInt = new ol.interaction.DragBox({
condition : ol.events.condition.always
});
map.addInteraction(selectOnBoxInt);
//set it active on start up
selectOnBoxInt.setActive(true);
Create two css classes holding styles for your drawbox
//this is the deafult
.ol-dragbox {
background-color: rgba(255,0,0,0.4);
border-color: rgba(2500,0,0,1);
border-width:2;
}
//this is when width,height>100
.ol-mydragbox {
background-color: rgba(0,255,0,0.4);
border-color: rgba(0,255,0,1);
border-width:2;
}
asign a boxdrag event to your drawbox interaction so you can truck down its width, height and make the style changes. For this action, and for the sake of time, I use jquery. You may use your imagination to do it without jquery.
selectOnBoxInt.on('boxdrag',function(e){
var width = Math.abs(e.target.box_.endPixel_[0] - e.target.box_.startPixel_[0]);
var height = Math.abs(e.target.box_.endPixel_[1] - e.target.box_.startPixel_[1]);
if (width>100 && height>100){
$('.ol-dragbox').removeClass('ol-dragbox').addClass('ol-mydragbox');
$('.ol-box').removeClass('ol-box').addClass('ol-mydragbox');
} else {
$('.ol-mydragbox').removeClass('ol-mydragbox').addClass('ol-dragbox');
}
});
And a fiddle to see it in action.

Change style of an object without interactions

I would like to make a stroke on a map when I select an object in OpenLayers3 like this site when you click on a metro station :
http://vis.oobrien.com/tube/#tongues
For that I tried that :
map.on('click', function(evt)
{
// Delete the overlay if it's not null
if (overlay = 'undefined'){map.removeLayer(overlay)};
// Remove popups
$(popup).remove();
popup = null;
var coord = evt.coordinate;
var feature = map.forEachFeatureAtPixel(evt.pixel,
function(feature, layer)
{
return feature;
},null,
function(layer)
{
return layer == vector1;
}
);
spawnPopup(coord,feature);
}
);
function spawnPopup(coord,feature)
{
if (feature) {
var props = feature.getProperties();
console.log(props);
}
var test = null;
popup = $("<div class='tad_popup'><div id='tad_close_popup' onclick='destroyPopup()'></div><div id='tad_text_popup'><p>Arrêt "+ props.num + "</p><p>"+ props.nom + "</p></div></div>");
var test = $(props.geometry);
var overlay = new ol.Overlay({
element:popup
});
var overlay_test = new ol.layer.Vector({source: test,
style: new ol.style.Style
({
image: new ol.style.Circle
({
radius: 5,
fill: new ol.style.Fill({color: 'rgba(255,100,100,0.9)'}),
stroke: new ol.style.Stroke ({color: 'rgba(255, 255, 255, 1)',width: 2})
})})});
overlay.setPosition(coord);
map.addOverlay(overlay);
map.addLayer(overlay_test);
But it's not the solution .
What can I do for that ?
Thank you very much.
Geo-x
You can either use the Select interaction, and then set the style property in the constructor. Or manually add a feature (that you get with forEachFeatureAtPixel in a click event handler) to a feature overlay with a custom style, like in this example: http://openlayers.org/en/master/examples/vector-layer.html

Categories