I'm developing an app with Google Maps and Angularjs. I have this code to control whether the zoom is changed
google.maps.event.addListener(map, 'zoom_changed', function(){
});
The question is, I need to execute this event only when the user changes the zoom. I have in my code a fitBounds() that change the zoom as well.
var latlngbounds = new google.maps.LatLngBounds();
for (var i = 0; i < latlng.length; i++) {
latlngbounds.extend(latlng[i]);
}
map.fitBounds(latlngbounds);
Is there an option to distinguish between user action and not?
The current accepted solution may not properly solve this question in a general case. Go to the interactive demo for map UI events in the Google Maps API docs. Note that if you position your cursor on the map and scroll with your mouse's scroll wheel, none of the mouse or drag events are fired because the mouse remains stationary.
The current solution assumes that no programmatic map updates will occur after the mouseover event, which may be true for the original poster's application but might not be true in a more general application.
I propose the alternate solution below.
First, wherever you programmatically update the map in your code, set a custom property on the map—we'll call ours systemZoomChange—that indicates the change was initiated programmatically.
var latlngbounds = new google.maps.LatLngBounds();
for (var i = 0; i < latlng.length; i++) {
latlngbounds.extend(latlng[i]);
}
map.systemZoomChange = true
map.fitBounds(latlngbounds);
Then on any event listeners, add a check for the custom systemZoomChange property, and reset it if flagged, to avoid acting on system events.
map.addListener('zoom_changed', function () {
if (map.systemZoomChange) {
map.systemZoomChange = false // Reset the flag for a system-initiated event
} else {
// Handle the user-initiated event
}
});
Ok, I found the way
google.maps.event.addListenerOnce(map, 'mousemove', function(){
google.maps.event.addListener(map, 'zoom_changed', function(){
});
});
First, it detects if the mouse moves and then if the zoom if changed and works properly!
Related
I'm trying to write a custom event handler for CTRL + click in Leaflet. My problem is that the click location given by the map is different from the click location in the event handler, e.g. LatLng(51.49174, -0.11639) from the map click becomes LatLng(51.50938, -0.126) in the handler. The click locations match exactly if the map is the only thing on the page. Adding some other div elements above the map (like an <h1> title) makes the clicks not match. Panning the map also makes the click locations not match.
I'm wondering if I attached my L.DomEvent.on() correctly. Following the Leaflet Handlers tutorial, my code looks like
L.CtrlClickHandler = L.Handler.extend({
addHooks: function() {
L.DomEvent.on(document, 'click', this._captureClick, this);
},
removeHooks: function() {
L.DomEvent.off(document, 'click', this._captureClick, this);
},
_captureClick: function(event) {
if (event.ctrlKey) {
console.log('control click registered at layer '
+ map.layerPointToLatLng(new L.point(event.layerX, event.layerY)));
}
}
});
// add this to all maps
L.Map.addInitHook('addHandler', 'ctrlClick', L.CtrlClickHandler);
Here's a live example on JSFiddle.
I'm using Leaflet 0.7.7 due to some other dependencies in my code. Upgrading to Leaflet 1.0.1 makes it match better (e.g., LatLng(51.49868, -0.1018) vs. LatLng(51.4987, -0.1018)) but the two locations still are not exactly the same.
Am I attaching the L.DomEvent to the correct thing? Should that be attached to the map div somehow, as opposed to document?
Edit: Thanks to #AlexParij for the suggestion. I realized that panning the map also makes the clicks not match, with or without div elements above the map. This happens for Leaflet 1.0.1 as well as 0.7.7. I've tried every combination I can think of, combining different event locations (event.layerX, event.pageX, event.clientX, event.offsetX, event.screenX, and event.x) with projection methods layerPointToLatLng and unproject but none of them match the map click. Now I'm really confused... Fiddle with these different options and Leaflet 1.0.1: https://jsfiddle.net/c4tkyewz/
TL; DR: use map.mouseEventToLatLng() in a custom handler.
#AlexParij was correct; I was not using the correct definition of the layer points and container points. Inside the handler, event is different from Leaflet's internal mouse event (where the location is available from e.latlng).
I looked through Leaflet's core to find the answer. Getting the location from event requires taking the Mouse Event -> Container Point -> Layer Point -> latLng. Thankfully, the Leaflet developers already programmed a nice function for this: mouseEventToLatLng().
/*
* This is a custom handler to check if someone has control clicked
* the map and print the location of the click
*/
L.CtrlClickHandler = L.Handler.extend({
addHooks: function() {
L.DomEvent.on(document, 'click', this._captureClick, this);
},
removeHooks: function() {
L.DomEvent.off(document, 'click', this._captureClick, this);
},
_captureClick: function(event) {
if (event.ctrlKey) {
// translate mouse event to lat/lng (note: `mouseEventToLatLng()`
// calls Leaflet's `mouseEventToContainerPoint()` followed by
// `containerPointToLayerPoint()` and finally `layerPointToLatLng()`)
var latlng = map.mouseEventToLatLng(event);
console.log('Handler detected CTRL + click at ' + latlng);
}
}
});
// add this to all maps
L.Map.addInitHook('addHandler', 'ctrlClick', L.CtrlClickHandler);
Live example with Leaflet 1.0.1: https://jsfiddle.net/c4tkyewz/1/
Also tested with Leaflet 0.7.7.
As a bonus, to access the CTRL key directly from Leaflet's native handling of the click event map.on('click', function(e) {});, use e.originalEvent.ctrlKey.
I need to handle a zoom event in Open Layers 3.
The following is my code:
map_object = new ol.Map({
target: 'map',
controls: controls_list,
interactions: interactions_list,
overlays: [overlay],
layers: [OSM_raster, WFS_layer],
view: view
});
map_object.on("Zoom", function() {
console.log('Zooming...');
});
This code runs with no errors and shows a map, but there is no output to the console, suggesting this function isn't firing.
I have also tried:
map_object.on("drag", function() {
console.log('Dragging...');
});
And this too does nothing.
Any help as to how to handle map control events in OL3 would be much appreciated (particularly zooming!). Note I have tried 'zoom' as well as 'Zoom' for the type field of the on method.
Just to add to this, you can check variations of events available with 'propertychange', from what I am seeing, there is no explicit .on ('zoom', ...) but rather you can access 'resolution' and other properties as mentioned in previous comments:
map.getView().on('propertychange', function(e) {
switch (e.key) {
case 'resolution':
console.log(e.oldValue);
break;
}
});
try with moveend event. (see https://openlayers.org/en/latest/apidoc/module-ol_MapEvent-MapEvent.html#event:moveend).
As mentioned by tonio, the way to listen on zoom change, which is called resolution change in openlayers terminology, is with
map.getView().on('change:resolution', (event) => {
console.log(event);
});
I find this is better (more succinct, less cruft) than listening on the general propertychange and verifying manually if the change concerns resolution.
This fires rapidly when using the mouse button so throttling it might be a good idea before launching any computation that waits for it to change.
Documentation for View
You can manage the moveend event...
We will need a global variable to alocate map’s view zoom level. I’ve
named it as currentZoomLevel.
There is available a moveend event. Let’s use it, and add a zoom level
check function..
In case of there’s a new zoom level, we trigger a zoomend event to
DOM’s document.
Finally we will need to add zoomend listener to the document element.
var = currentZoomLevel;
map.on('moveend', checknewzoom);
function checknewzoom(evt)
{
var newZoomLevel = map.getView().getZoom();
if (newZoomLevel != currentZoomLevel)
{
currentZoomLevel = newZoomLevel;
$(document).trigger("zoomend", zoomend_event);
}
}
$(document).on('zoomend', function () {
console.log("Zoom");
//Your code here
});
Source
On this website:
http://www.crunchpanorama.com/
you use the google maps zoom control slider to zoom and recluster markers. I want to capture this event as well and perform actions accordingly.
Problem is when reading google docs, all I could find is the zoom_changed event of map. However, this event is not only called when changing zoom using slider, but also when clicking marker (which zooms into marker). So zoom_changed will not help me:
google.maps.event.addListener(map, 'zoom_changed', function () {
...
I want to be able to target the zoom change on the control slider specifically. How can I go about this?
Maybe there's a workaround similar to this:
var self = this;
google.maps.event.addListener(marker, 'click', function() {
// Set a boolean variable to true to indicate a marker/cluster has been clicked
self._markerClicked = true;
});
google.maps.event.addListener(map, 'zoom_changed', function() {
// Check the boolean variable and run your code if it's false
if ( !self._markerClicked ) {
// Take action here
} else {
// Reset variable back to false
self._markerClicked = false;
}
});
Ran into this recently. This is the best solution I have found if you want to capture when the map default zoom buttons are clicked. This assumes that the zoom buttons are the only ones in the div map container.
$("#id-container-name").on("click", "button", whateverfunction());
I have a google map using V3 of the API. It has one marker on it which is draggable and then a load of other markers that are static. I have set up a dragend listener for the draggable marker which calls a function called clear_markers() like so:
google.maps.event.addListener(marker_0, "dragend", function() {
clear_markers();
});
function clear_markers()
{
if (markers) {
for (var i = 1; i <= markers.length; i++ ) {
if(typeof markers[i] !== "undefined") {
markers[i].setMap(null);
}
}
}
}
The reason I start the for loop at 1 and not 0 is that my draggable marker is the first marker so I want to clear all markers from the map except this one.
Here is the problem:
If I call clear_markers(); in any other way it works fine and the markers are removed from the map, so something like this works:
$('#mybutton').click(function() {
clear_markers();
});
When you drag and drop the green marker though and it's called from the dragend listener it does not work. The markers do get removed but then they immediately get re added again. I know they do get removed because if I put something in the clear_markers() function just after the for loop that kills the script, the markers get removed. But if the script is allowed to continue they are still there meaning they have been removed and then instantly added back on again.
I'm not calling any other code so it seems like a bug with the api to me. Does anyone have any ideas?
Here is a working example showing the problem:
https://tinker.io/64b68/1
Remove the markerClusterer. It is adding the markers back in and you aren't using it.
Update:
Since you need to keep it, if you want the markers to not be displayed, you need to remove them from the markerClusterer:
markerCluster.clearMarkers();
(but you will need to make it global to use it that way)
Your calling the eventListener the right way, just not in the right place in your code. I added a couple of alerts in your eventListener to see what was going on and if you see on the second alert, the markers are actually cleared - but once the alert box is closed the markers reappear. Try it yourself:
google.maps.event.addListener(marker_0, "dragend", function() {
alert("before calling clear_markers()");
clear_markers();
alert("markers should be cleared");
});
This means that once javascript runs into your "dragend" event listener, it will execute the code inside - but then it will go through the rest of the code as well and hence the markers get populated on the map again. You can fix this issue by adding your event listener to the end of your initialize() function after var markerCluster = new MarkerClusterer(map, markers, clusterOptions);.
text in italics is the wrong explanation, refer to the response below for the correct solution
/---------------------------------------------------------------------------------------------/
Disregarding my previous answer, allow me to re-edit. Again, you are calling the eventListener the right way. You will just need to add the following piece of code in the listener function to make sure the markers do not show up.
google.maps.event.addListener(marker_0, "dragend", function() {
clear_markers();
markerCluster.setMap(null);
});
It seems initiating the markerCluster overrides your clear function on the markers. So you will also need to clear the markerCluster from your map as well. Apologies for misguiding you in my previous response, markerCluster is a new concept to me as well.
I want to know when a Google Maps zoom_changed event is fired specifically by a user interaction with the +/- zoom buttons. If I use a general event listener for zoom_changed, I can't tell if it is a user-generated event or a zoom change caused by something like fitBounds(). Looking for the best way to do this.
I've tried the following things, none of which seem to work:
1) Looked for event information on zoom_changed. There appears to be none.
2) Add listeners for mouseover and mouseout that let me set a flag to see if the user is in the map bounds, and check the flag on zoom_changed. This doesn't work because the map does not consider the zoom buttons as part of the map frame (in other words, hovering over zoom buttons triggers the mouseout event).
3) Add a normal (non-gMap) listener to the zoom buttons. However, I can't find a definitive CSS selector that will allow me to grab just the buttons.
4) Looked for a function in the gMaps API that would let me do something like getZoomElements(), and then I could set listeners using that.
The weird thing is I can clearly do what I want if I add a custom control to the map. It seems very odd that they would force me to do that instead of having a hook into the default zoom controls.
I wouldn't just hook in to the +/- buttons (or buttons on your own custom control for that matter). The user can change the zoom on the map with the mouse wheel, or by double-clicking on the map. Plus, you'd be relying on implementation detail rather than documented API, which is a major no-no.
This really means the only reliable, documented way to detect a change in zoom is to listen to the zoom_changed event of the Map.
If your event handler can't determine whether the event came from user action or an API call, there's two approaches:
Set a flag before calling an API function so that you know you can ignore this change.
Can you re-architect your app such that it does not matter whether a zoom change came from code or the user?
I solved this issue by creating Custom Zoom buttons for my Map.
Here is the code from my project:
Edit: Removed unnecessary and self explanatory common code
your zoomControl function:
function zoomControl(map, div) {
var controlDiv = div,
control = this;
// Set styles to your own pa DIV
controlDiv.style.padding = '5px';
// Set CSS for the zoom in div.
var zoomIncrease = document.createElement('div');
zoomIncrease.title = 'Click to zoom in';
// etc.
// Set CSS for the zoom in interior.
var zoomIncreaseText = document.createElement('div');
// Use custom image
zoomIncreaseText.innerHTML = '<strong><img src="./images/plusBut.png" width="30px" height="30px"/></strong>';
zoomIncrease.appendChild(zoomIncreaseText);
// Set CSS for the zoom out div, in asimilar way
var zoomDecreaseText = document.createElement('div');
// .. Code .. Similar to above
// Set CSS for the zoom out interior.
// .. Code ..
// Setup the click event listener for Zoom Increase:
google.maps.event.addDomListener(zoomIncrease, 'click', function() {
zoom = MainMap.getZoom();
MainMap.setZoom(zoom+1);
// Other Code parts
});
// Setup the click event listener for Zoom decrease:
google.maps.event.addDomListener(zoomDecrease, 'click', function() {
zoom = MainMap.getZoom();
MainMap.setZoom(zoom-1);
});
}
your initialize function:
function initializeMap() {
var latlng = new google.maps.LatLng(38.6, -98);
var options = {
zoom : 5,
center : latlng,
mapTypeId : google.maps.MapTypeId.ROADMAP,
// Other Options
};
MainMap = new google.maps.Map(document.getElementById("google-map-canvas"),
options);
// The main part - Create your own Custom Zoom Buttons
// Create the DIV to hold the control and call the zoomControl()
var zoomControlDiv = document.createElement('div'),
zoomLevelControl = new zoomControl(MainMap, zoomControlDiv);
zoomControlDiv.index = 1;
MainMap.controls[google.maps.ControlPosition.RIGHT_TOP].push(zoomControlDiv);
}
Hope it helps
var zoomFlag = "user"; // always assume it's user unless otherwise
// some method changing the zoom through API
zoomFlag = "api";
map.setZoom(map.getZoom() - 1);
zoomFlag = "user";
// google maps event handler
zoom_changed: function() {
if (zoomFlag === "user") {
// user zoom
}
}
I was looking to do the same thing. I was hoping to find that there was a way built into the Google Maps API, but at a minimum, you should be able to store the starting zoom level as a variable when you initialize the map. Then, compare the result of getZoom() to it to know whether it was a zoom in or a zoom out.
For example:
map = new google.maps.Map(document.getElementById('map_canvas'), { zoom: 11 });
var previous_zoom = 11;
google.maps.event.addListener(map,'zoom_changed',function(){
if(map.getZoom() > previous_zoom) {
alert('You just zoomed in.');
}
previous_zoom = map.getZoom();
}
I would suggest using a custom control for zoom in/out and then using event listeners on the custom congrol:
http://goo.gl/u8gKC
You can easily hide the default zoom control:
http://goo.gl/N5HIE
(zoomControl to be specific)