OpenLayers 3: "movestart" event on map - javascript

OpenLayers3 API has a map.on("moveend") , however I cannot find a movestart. Any one know how I can achieve this? Is there a equivalent event?
OpenLayers 2 had a movestart event on map. I am looking an exact parallel in OpenLayers3
Here's a basic jsFiddle. If someone want's to play around. I did add a movestart event there, to show what I want, but it doesn't actually exist I think.
Use Case! one might ask: I have stops on maps that have nearly fullscreen infowindows. Users can switch to next marker from infowindow. I make the windows translucent to show the map panning underneath, so users get a context of where next location is. This work's great in OpenLayers2 with movestart and moveend events. But in the new OL3 version of the map, I can't get the movestart event.
Update: I did answer the question my self, but I am still offering bounty if anyone would like to propose a better solution.

UPDATE v4.2.0 now supports native movestart and moveend events
map.on('movestart', function(event) {
//To Remove after first use: ol.Observable.unByKey(event);
});
map.on('moveend', function(event) {
//To Remove after first use: ol.Observable.unByKey(event);
});
For OpenLayers 3 versions before release of v4.2.0
Okay so in the mean while without the movestart event, and with the moveend only triggering if there is a actual movement in map, here's how I was able to achive movestart and moveend behavior.
jsFiddle:
var pan = ol.animation.pan({
duration: 700,
source: this.map.getView().getCenter()
});
map.beforeRender(function(map, frameState) {
var stillPanning = pan(map, frameState); // returns false panning is done
if (stillPanning) {
// do movestart stuff here
if (!everDone) {
doSomething();
everDone = true;
}
} else {
// do move end stuff here
undoSomething();
everDone = false;
}
return stillPanning;
});
map.getView().setCenter(geom);
So why this works?
ol.animation.pan returns a ol.PreRenderFunction, which returns false if animation is not complete
Writing custom function and providing it to map.renderBefore can be now used to write a wrapper around pan animation as shown above
The whole business with everDone is because, stillPanning section will get called multiple times. This is okay if what you want to do there can take repeated calls, but if you want to toggle something then you want to do it only once.
behavior of 'moveend'
moveend callback is only triggered if map actually moves. This is fine, but it prevents us from doing pre-animation activities, by just doing them before animation done. If you had a scenario where map doesn't actually move, then what ever you did before animation will never undo because that behavior is in moveend which never gets called!
Hope this helps someone. I had to spend good two hours to get it to work for me, because a movestart callback is absent :(
UPDATE
Upon more discussion on this thread there is another solution as suggested by #ahocevar. That is to use the propertychange event on the view like so:
function onpropertychange() {
map.dispatchEvent('movestart');
var view = map.getView();
view.un('propertychange', onpropertychange);
map.on('moveend', function() {
view.on('propertychange', onpropertychange);
});
};
map.getView().on('propertychange', onpropertychange);
Here's a working example of this approach: jsFiddle

you could use the pointerdrag or pointermove event, but you will want to debounce them. I did so here with a variable called dragStarted
http://jsfiddle.net/sean9999/j2cP4/115/
http://openlayers.org/en/v3.8.2/apidoc/ol.MapBrowserEvent.html

Related

Identify when Leaflet Draw cancel button is clicked

Problem
I'm using leaflet draw for my application, and I have the 'remove' button active. The remove button has three options:
Save
Cancel
Clear All
I want function foo() to be called if the user clicks Save, however, I want function bar() to be called should they click Cancel.
Live demo
Solution
I know this could be achieved by simply giving it an ID, and adding an event listener, but it's not as clean as I think it should be.
Ideal Solution
Leaflet draw its own methods for detecting when the buttons are pressed but it seems to me they only do it for one level higher. For example:
draw:deletestop The type of edit this is. One of: remove Triggered when the user has finished removing shapes (remove mode) and saves.
- Leaflet Docs
This allows me to call foo() after the user has selected any of the three options, rendering that they have simply finished dealing with the remove button interaction.
I cannot find a way in the docs to be able to listen for leaflet draw firing an event on the individual buttons being pressed.
The handler for the cancel/disable function is stored as part of your L.Control.Draw instance. So, you you can modify the handler right after you instantiate your L.Control.Draw object:
var myDrawControl = new L.Control.Draw();
myDrawControl._toolbars.edit.disable = function () {
if (!this.enabled()) {
/* If you need to do something right as the
edit tool is enabled, do it here right
before the return */
return;
}
this._activeMode.handler.revertLayers();
/* If you need to do something when the
cancel button is pressed and the edits
are reverted, do it here. */
L.Toolbar.prototype.disable.call(this);
};
The source for the handler is here and while this works well, you will have to be careful with future versions of Leaflet.Draw that may change the handler's functionality.
The newest version of Leaflet.draw 0.4.14 you can use
map.on('draw:toolbarclosed', function(){ //Add code here});

Leaflet: Add event to marker?

I tried to add a click event to a leaflet marker like this:
L.marker([41.866056100409044, 12.5349304759025576]).on('click', red).bindLabel('kebabbari (Ali Baba)', {noHide: true}).addTo(map).showLabel();
But the result is just a marker without any click event (the "red()" function exists)
Marker in leaflet, click event doesn't work
Just ran multiple tests with different scenarios and they all worked with an onclick or mouseover. I'm using Leaflet 1.03 I think your using the previous version.
var Marker = L.marker([41.866056100409044, 12.5349304759025576]).on('click', onClick).addTo(map)
Add a new function like below :-
function onClick(e) {
alert(this.getLatLng());
}
Try this and it will prove your onclick function can work. I think the leaflet Plugin Label might be interfering with it as it has an Onclick option and you may need to look further into that.
But sometimes you need to prove you can make it work first and then add things back in bit by bit.
Also Leaflet.Label does have a conflict with onclick.
https://github.com/Leaflet/Leaflet.label/issues/132 This issue from the forum will help you out they have answer detailed on here.
You need to specifically bind an event to marker.label , e.g.:
L.DomEvent.addListener(marker.label, 'click', function(e) { this.togglePopup() }, marker);
I had to dig in to the code a bit to figure this out, worth a mention in the README on how to access the label and bind events to it.

How to prevent LeafletJS from issuing multiple click events on overlapping markers?

I have a LeafletJS map with a lot of markers on it, and many of them overlap at high zoom levels. Currently, when I click on a few overlapping markers, multiple click events are being issued, each of which then issues a popupopen event which in turn does a $.getJSON() to populate the popup. Obviously, this isn't ideal, so I'd like to find out how to get Leaflet to just execute one click at a time.
Other answers I find on here talk about e.preventDefault() or e.stopPropagation(). Neither of those appear to be available in Leaflet's click or popupopen events, so I'm thinking there must be a smarter way that I've not heard of.
Here's my code, watered down for the purpose of this question:
var geojson_options = {
onEachFeature: function(feature, layer){
layer.bindPopup('<img src="/spinner.gif" />');
layer.on("popupopen", function(e){
// e.preventDefault and e.stopPropogating don't exist
$.getJSON("some-url.json", function(json){
e.popup.setContent("Some processing of the json");
});
});
},
pointToLayer: function (feature, latlng) {
// Irrelevant stuff
}
};
L.geoJson(geojson_data, geojson_options);
I've tried variations on using layer.on("click", ...) to set a variable called popping_open to true to keep state while the popup is opening, but that didn't work either. Tell me there's a clean way to handle this?
There is a plugin called OverlappingMarkerSpiderfier-Leaflet that spreads overlapping markers on an onClick event so their identification is more easy. You can see a demo here and download it from github. Maybe it is useful for your application.
It sounds like the issue here, if I'm understanding correctly, is that when you try to click on a marker, the click event is being triggered by multiple markers that overlap instead of being triggered by the intended marker. The only way I know to fix this is to separate the markers before the click event can be triggered. You can do this by using a plugin as suggested, or by preventing popups from opening at specific zoom levels.
I recommend using a plugin called Leaflet.markercluster (https://github.com/Leaflet/Leaflet.markercluster). It creates clusters of overlapping markers that separate as you zoom in. Clicking on a cluster will zoom to the geographic bounds of the cluster, showing markers individually as long as they don't overlap. You could use the plugin with this basic format:
var markerClusterLayer = L.markerClusterGroup();
var geojson_options = {
onEachFeature: function(feature, layer){
layer.bindPopup(your popup content goes here);
... other stuff you want to do ...
markerClusterLayer.addLayer(layer);
}
};

flot.navigate drag start/end events missing

im looking for some javascript events that trigger at the start and at the end navigating via dragging on flot plots so that i can do some ajax updates, however, I've been looking around online for some code to help me. I've found a few things, most didn't work worked or had bugs.
The best thing I've found so far is an answer from DNS, however it has unintentional behavior, when you hold the mouse click button down and stop panning the event triggers.
var delay = null;
element.on("plotpan", function() {
if (delay) clearTimeout(delay);
delay = setTimeout(function() {
// do your stuff here
delay = null;
}, 500);
});
The navigate plugin should really pass the event on to you; then you could easily check the state of the mouse buttons in your handler.
Since it doesn't, you'll need to attach your own mousedown/mouseup listeners to the overlay canvas; the child of your plot placeholder with class 'flot-overlay'. You can use these to update a shared variable with the state of the left button, then check that value in your handler above.

moveend event fired many times when page is load with Leaflet

I need to do some operations when the map is paned or zoomed, so I attached a callback to the event moveend.
map.on('moveend', function() {
// code stuff
});
It works fine, but when the page is load the event is fired three times and I don't know why.
Probably because during its creation the map is moved.
To avoid this i tried to wait the load event before subscribing moveend event, but nothing changed. So I tried to attach it within whenReady callaback, but again it is fired three times.
map.whenReady(function() {
map.on('moveend', function() {
// code stuff
});
});
Finally, I discovered that after the resize event it works quite fine: moveend is fired jonly one time. But I really believe there is a best way to fix the problem.
Another solution could be to attach my callback to both events zoomend and dragend, to cover paning and zooming cases.
But I didn't find a way to do it.
Thank you for your help.
The best solution I found is to attach the callback to both events:
map.on('zoomend', function() {
// callback
});
map.on('dragend', function() {
// callback
});
Although this way the code is a bit replicated, this is by far the best solution.
For others looking into this, research the options.debounceMoveend option on the invalidateSize function. It's mentioned in briefly in the documentation, but unfortunately it looks like it's only for that function, rather than generally for the moveend event.
[...] If options.debounceMoveend is true, it will delay moveend event so that it doesn't happen often even if the method is called many times in a row.
Reference to the line in source code (L3541)
You can use mouseenter and mouseleave events.
Example:
block.addEventListener('mouseenter', ()=>{
//some code when hover
})
block.addEventListener('mouseleave', ()=>{
// some code when leaving block
})
link to developer.mozilla.org
The 'zoomend' and 'dragend' option didn't work for me. I searched a lot for a suitable option and realized that the "moveend" event fires several times because this event is created every time you move the map. Therefore it is necessary to stop this event. I got out of the situation in this way.
Immediately after the map was initialized, I wrote:
map.off('moveend');
and for me it worked. Now it works fine.
I will be very happy if this is useful to someone.

Categories