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
Related
I'm trying to implement a zoom bar in mapbox gl js but the only thing I found is this code from their documentation which adds an +, - and a reset. Is there anyway I can add a slider zoom level bar ? (A bar like this https://www.w3schools.com/howto/howto_js_rangeslider.asp )
var nav = new mapboxgl.NavigationControl();
map.addControl(nav, 'bottom-left');
Yes, you can, it requires to create a custom control and add manually the event to update the map zoom... but it's only a few lines of code. I didn't work too much in the css styling.
here's a fiddle I have created with an example How to create a custom zoom control
And here's the relevant scripting code
<script>
mapboxgl.accessToken = 'PUT HERE YOUR MAPBOX TOKEN';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11', // stylesheet location
zoom: 3, // starting zoom
center: [-95, 40], // starting position [lng, lat]
});
map.on('load', function () {
let zoomControl = new CustomZoomControl();
map.addControl(zoomControl, 'top-right');
map.on('zoom', function () {
zoomControl.update();
});
});
class CustomZoomControl {
onAdd(map) {
this.map = map;
this.container = document.createElement('div');
this.container.className = " mapboxgl-ctrl mapboxgl-ctrl-group";
this.input = document.createElement('input');
this.input.type = "range"
this.input.min = 1;
this.input.max = 220;
this.createAttribute(this.input , "value", map.getZoom()*10)
this.input.className = "slider";
this.input.id = "myRange";
this.container.appendChild(this.input);
// Update the current slider value (each time you drag the slider handle)
this.input.oninput = function () {
map.setZoom(this.value/10);
}
return this.container;
}
onRemove() {
this.container.parentNode.removeChild(this.container);
this.map = undefined;
}
createAttribute(obj, attrName, attrValue) {
var att = document.createAttribute(attrName);
att.value = attrValue;
obj.setAttributeNode(att);
}
update() {
let zoom = map.getZoom() * 10;
if (this.input.value != zoom) this.input.value = zoom;
}
}
</script>
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.
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/
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/
I use the following code to modify the radius of a Circle marker based on the zoom level:
//add the layer to the map
map.addLayer(vectorLayer);
//add selection interactivity, using the default OL3 style
var select = new ol.interaction.Select();
map.addInteraction(select);
map.getView().on('change:resolution', function(evt) {
var zoom = map.getView().getZoom();
var radius = zoom / 2 + 1;
var newStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: radius,
fill: new ol.style.Fill({color: 'red'}),
stroke: new ol.style.Stroke({color: 'black', width: 1})
})
})
vectorLayer.setStyle(newStyle);
});
But the problem I have is that if I select a feature on the map, the selected/highlighed style does not change when the map zoom changes. How can I also dynamically modify the style of selected features based on zoom/resolution?
Clarification The code above already works for changing radius of all features on the map, but in addition to that I also need the radius of selected features to change. Both selected and unselected features should be changing based on zoom level.
You need to set a style function on interaction constructor like:
var select = new ol.interaction.Select({
style: function(feature, resolution){
var zoom = map.getView().getZoom();
var radius = zoom / 2 + 1;
console.info(radius);
var newStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: radius,
fill: new ol.style.Fill({color: 'red'}),
stroke: new ol.style.Stroke({color: 'black', width: 1})
})
});
return [newStyle];
}
});
A working demo.
Use the scale base for the radius resizing when zoomed.
map.getCurrentScale = function () {
//var map = this.getMap();
var map = this;
var view = map.getView();
var resolution = view.getResolution();
var units = map.getView().getProjection().getUnits();
var dpi = 25.4 / 0.28;
var mpu = ol.proj.METERS_PER_UNIT[units];
var scale = resolution * mpu * 39.37 * dpi;
return scale;
};
map.getView().on('change:resolution', function(evt){
var divScale = 60;// to adjusting
var radius = map.getCurrentScale()/divScale;
feature.getStyle().getGeometry().setRadius(radius);
})
Did you set the radius in that other style (selected/highlighed), too?