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.
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 want to add moving arrows or overlay animation in the Flights Animation example in OpenLayers 6.
I tried doing the overlay moving animation with JavaScript setInterval(), but so far I have only succeeded in animating a single LineString, that too after the line is finished drawing. I wanted to add the moving animation as the line is being drawn, kind of like tracing the LineString's path.
Can someone please help me with this?
Following is the code snippet where I have tried to add the moving animation:
var markerEl = document.getElementById('geo-marker');
var marker = new Overlay({
positioning: 'center-center',
offset: [0, 0],
element: markerEl,
stopEvent: false
});
map.addOverlay(marker);
function animateFlights(event) {
var coords;
var vectorContext = getVectorContext(event);
var frameState = event.frameState;
var features = flightSource.getFeatures();
for (var i = 0; i < features.length; i++) {
var feature = features[i];
if (!feature.get('finished')) {
coords = feature.getGeometry().getCoordinates();
var elapsedTime = frameState.time - feature.get('start');
var elapsedPoints = elapsedTime * pointsPerMs;
if (elapsedPoints >= coords.length) {
feature.set('finished', true);
}
var maxIndex = Math.min(elapsedPoints, coords.length);
var currentLine = new LineString(coords.slice(0, maxIndex));
vectorContext.setStyle(strokeStyle1);
vectorContext.drawGeometry(currentLine);
if (feature.get('finished')) {
var interval = setInterval(
function () { return animatePath(coords, interval) }, 10);
}
}
}
map.render();
}
function animatePath(path, clearInterval) {
if (i == path.length) {
stopAnimatePath(clearInterval);
}
marker.setPosition(path[i]);
i = i + 1;
}
function stopAnimatePath(clearInterval) {
clearInterval(clearInterval);
}
Here is a link to a snapshot of how my app looks right now
Trace your LineString
It should be enough to set your map center to the last point of your LineString if you update often enough
map.getView().setCenter(lastPoint)
If it gets laggy use
var pan = ol.animation.pan({
source: map.getView().getCenter()
});
map.beforeRender(pan);
map.getView().setCenter(lastPoint);
Draw arrows
To draw arrows on your LineString you can use the following style
var styleFunction = function (feature) {
var geometry = feature.getGeometry();
var styles = [
// linestring
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#000',
width: 2
})
})
];
geometry.forEachSegment(function (start, end) {
var dx = end[0] - start[0];
var dy = end[1] - start[1];
var rotation = Math.atan2(dy, dx);
styles.push(new ol.style.Style({
geometry: new ol.geom.Point(end),
image: new ol.style.RegularShape({
fill: new ol.style.Fill({color: '#000'}),
points: 3,
radius: 8,
rotation: -rotation,
angle: Math.PI / 2 // rotate 90°
})
}));
});
return styles;
};
more details: https://stackoverflow.com/a/58237497/546526
I am able to draw box and I can also move/drag box correctly.
But, how can I resize the box?
what exactly i need :
OpenLayers 2 Example,
https://harrywood.co.uk/maps/examples/openlayers/bbox-selector.view.html
Here is my code:
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var source = new ol.source.Vector({wrapX: false});
var vector = new ol.layer.Vector({
source: source
});
var map = new ol.Map({
layers: [raster, vector],
target: 'map',
view: new ol.View({
center: [-11000000, 4600000],
zoom: 4
})
});
var geometryFunction = ol.interaction.Draw.createBox();
box = new ol.interaction.Draw({
source: source,
type: 'Circle',
geometryFunction: geometryFunction
});
box.on('drawend', function (e) {
var bounds = e.feature.getGeometry().getExtent();
console.log(bounds);
});
map.addInteraction(box);
Code for select and drag/move box:
var select = new ol.interaction.Select();
var translate = new ol.interaction.Translate({
features: select.getFeatures()
});
translate.on('translateend', function (e) {
var bounds = e.features.getArray()[0].getGeometry().getExtent()
console.log(bounds);
});
map.addInteraction(select);
map.addInteraction(translate);
Elaboration to my comment:
You need to update/change the geometry of the "box" (polygon I suppose) to make it appear "resized", at the end of any operation that shows something on the map it uses extents that tell OL where to place things (essentially).
I have made a little example demonstrating how to use the .scale method on the Geometry object of a feature.
CodePen
Explanation:
draw.on("drawend", function(e){
var iterations = 0;
var interval = setInterval(function(){
if(iterations == 10){
clearInterval(interval);
}
iterations++;
var feature = e.feature;
var coords = feature.getGeometry();
coords.scale(0.9, 0.9);
}, 300)
This is the code I use to scale the drawn polygon when the polygon has been drawn on the map. I always scale it by 0.9 (that makes it smaller (Basic scale factoring)).
1 = The same size
0.* = Smaller
1.* = Bigger
You need to use similar logic to this above to resize your polygons. You need feature object, then extract the Geometry object, and use the .scale method.
The scale(sx, yx) method arguments are as follows:
sx = The scaling factor in the x-direction.
yx = The scaling factor in the y-direction (defaults to sx).
For more info Geometry Class in the OL docs
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 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