I attached multiple events (click, dragstart, dragend) events on pushpin but the problem is, when I click pushpin on bing map it calls both drag event first.
I want to restirct drag events when click on pushpin and it should be fired when we drag pushpin on map.
I tried attach multiple event handler on same pin but it's not working.
Sample code:
<html>
<head>
<script charset="UTF-8" type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
<script>
var pinInfoBox; //the pop up info box
var infoboxLayer = new Microsoft.Maps.EntityCollection();
var pinLayer = new Microsoft.Maps.EntityCollection();
var apiKey = "Key";
function GetMap() {
map = new Microsoft.Maps.Map(document.getElementById("map"), { credentials: apiKey });
// Create the info box for the pushpin
pinInfobox = new Microsoft.Maps.Infobox(new Microsoft.Maps.Location(0, 0), { visible: false });
infoboxLayer.push(pinInfobox);
for (var i = 0 ; i < 10; i++) {
//add pushpins
var latLon = new Microsoft.Maps.Location(Math.random() * 180 - 90, Math.random() * 360 - 180);
var pin = new Microsoft.Maps.Pushpin(latLon, { draggable: true });
pin.Title = name;//usually title of the infobox
pin.Description = "blahblahblah, " + i; //information you want to display in the infobox
pinLayer.push(pin); //add pushpin to pinLayer
Microsoft.Maps.Events.addHandler(pin, 'click', displayInfobox);
Microsoft.Maps.Events.addHandler(pin, 'dragstart', startDragDetails);
Microsoft.Maps.Events.addHandler(pin, 'dragend', endDragDetails);
}
map.entities.push(pinLayer);
map.entities.push(infoboxLayer);
}
startDragDetails = function (e) {
console.log("Start Latitude/Longitude: " + e.entity.getLocation());
};
endDragDetails = function (e) {
console.log("End Latitude/Longitude: " + e.entity.getLocation());
};
function displayInfobox(e) {
console.log("click");
pinInfobox.setOptions({ title: e.target.Title, description: e.target.Description, visible: true, offset: new Microsoft.Maps.Point(0, 25) });
pinInfobox.setLocation(e.target.getLocation());
}
function hideInfobox(e) {
pinInfobox.setOptions({ visible: false });
}
</script>
<style>
#map {
position: absolute;
top: 20px;
left: 10px;
width: 700px;
height: 500px;
border: #555555 2px solid;
}
</style>
</head>
<body onload="GetMap()">
<div id="map">
</div>
</body>
</html>
I've tested using Both IE and Chrome and both work as expected when I use the mouse or touch to drag a pushpin. When I drag the start drag event fires, when I stop dragging the drag end event fires, finally the click event fires. The drag end event will always fire before the click or mouseup event. If you want to handle the situation where the user doesn't actually move the pin what you can do if capture the pixel coordinates of the pushpin when the drag start event fires, then compare them to the drag end event pixel coordinates of the pushpin. If they are not the same then the pushpin was dragged and you can do what you wanted when a pushpin is dragged, otherwise ignore the event and wait for the click event. If you want you can also do a quick calculation to find out how far the pushpin was moved in pixel distance and if it is small you may also want to ignore the drag event.
I stored the flag value "true" in global variable when it comes to drag method and checking the same flag value on end drag method to execute the code that is needed when pins are dragged.
So if we click pins, it calls the dragging methods but won't execute code that we don't need on click event.
I've only just noticed this in Chrome, so now on dragstart I capture the pin location and then compare this to the pin location at dragend. I needed to round the values though.
Related
I am trying to work around the issue where a map is just part of the page, and on a mobile if you scroll down to the map, it fills the screen and you can't scroll the page anymore - you scroll the map instead.
so I changed the draggable option to have two finger map scrolls. This works.
var mymap = L.map('mapid', {
center: [latitude,longitude],
zoom: 17,
layers: [streetmap], //default layer
dragging: !L.Browser.mobile, tap: !L.Browser.mobile //twofinger map controls, one finger page scrolling
});
But I want to still have the map auto-follow the player, unless they have deliberately dragged the map away (and then a button to snap back to automove). The code of relevance for that stuff is here:
var panbtn = L.easyButton({
states: [{
stateName: 'pauseAutoMove',
icon: 'fa-sign-in fa-lg',
title: 'Centre display at current Player', //Tooltip
onClick: function(btn, map) { //if you click the button whilst it is in pauseAutoMove, recentre map and unpause
currentAutoMove = true; //Set flag, that currently map is being moved to recentre
mymap.panTo([latitude,longitude]);
currentAutoMove = false; //Remove flag again
pauseAutoMove = false; //set flag to stop Auto moving map
panbtn.state('AutoMove');
}
},{
stateName: 'AutoMove', //clicking the button once it is doing AutoMove does nothing
icon: 'fa-crosshairs fa-lg',
}]
}).addTo(mymap);
mymap.on("zoomstart", function (e) { currentAutoMove = true }); //Set flag, that currently map is moved by a zoom command
mymap.on("zoomend", function (e) { currentAutoMove = false }); //Remove flag again
mymap.on('movestart',(e)=>{ //Check if map is being moved
if(!currentAutoMove){ //ignore if it was a natural PlayerLoc or programmatic update
pauseAutoMove = true; //set flag to stop Auto moving map
panbtn.state('pauseAutoMove'); //change button style to remove crosshairs and have a arrow-in icon
}
});
and elsewhere
function updatemap() { // Update the current player location on map
playerLoc.setLatLng([latitude,longitude]); //update current player marker instead of creating new ones
//other stuff goes here to update too
if(!pauseAutoMove){ //pan the map to follow the player unless it is on pause
currentAutoMove = true; //Set flag, that currently map is moved by a normal PlayerLoc Auto update
mymap.panTo([latitude,longitude]);
currentAutoMove = false; //Remove flag again
};
mymap.invalidateSize(); //reset map view
}; // end updatemap
Now that I have two-finger map-dragging, on the mobile, it does NOT register the change of state to panbtn. The map snaps back again and I can't stop it now. (Still works as intended on a PC screen).
So, my question: It seems mymap.on('movestart',(e)=>{... is NOT being triggered by a two-finger drag? Is there a way to re-enable that?
Leaflet events for touch interaction, including touch zoom (which is what happens when you use 2 fingers, even just for panning) are "touchstart", "touchmove" and "touchend".
However, these events will likely fire even with 1 finger touch, so you may need to add a check similar to Leaflet TouchZoom hamdler: https://github.com/Leaflet/Leaflet/blob/v1.7.1/src/map/handler/Map.TouchZoom.js#L68
So you could combine with what you have with something like:
function mayDisableAutomove() { //Check if map is being moved
if(!currentAutoMove){ //ignore if it was a natural PlayerLoc or programmatic update
pauseAutoMove = true; //set flag to stop Auto moving map
panbtn.state('pauseAutoMove'); //change button style to remove crosshairs and have a arrow-in icon
}
});
mymap.on('touchmove', (e) => {
if (!e.touches || e.touches.length !== 2) { return; }
mayDisableAutomove();
});
mymap.on('movestart', mayDisableAutomove);
Im working on an App which retrieves the Users Location via Geolocation-API.
When the user clicks on a button a "HERE-API" Map gets initialized and the user has the option to drag a marker to the exact postition if it isn't already.
When he clicks on the Abort-Button i want to "destroy" or remove the entire Map since i dont want it to be displayed on the screen all the time. Just when the user clicks the Button which retrieves the Location and displays the Map.
I did alot of research on StackOverflow and the Official Documentation but i haven't found any Information on how to approach this.
Can someone please help me.
$("#addLocation").on('click', function()
{
$("#mapContainer").fadeIn();
function addDraggableMarker(map, behavior)
{
var svgMarkup = `<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg"><g><title>background</title><rect fill="none" id="canvas_background" height="42" width="42" y="-1" x="-1"/></g><g><title>Layer 1</title><path stroke="null" fill="#046f47" id="svg_1" d="m20,0.701143a15.19595,15.19595 0 0 0 -15.19595,15.19595c0,10.257267 13.296457,21.844179 13.96128,22.338047l1.234671,1.063717l1.234671,-1.063717c0.664823,-0.493868 13.96128,-12.080781 13.96128,-22.338047a15.19595,15.19595 0 0 0 -15.19595,-15.19595zm0,22.793926a7.597975,7.597975 0 1 1 7.597975,-7.597975a7.597975,7.597975 0 0 1 -7.597975,7.597975z"/><circle stroke="null" fill="#046f47" id="svg_2" r="3.938806" cy="16.03728" cx="19.999999"/></g></svg>`;
var icon = new H.map.Icon(svgMarkup);
var coords = {
lat: lat,
lng: lng
};
var marker = new H.map.Marker(
coords, {
icon: icon
}, {
volatility: true
}
);
// Ensure that the marker can receive drag events
marker.draggable = true;
map.addObject(marker);
// disable the default draggability of the underlying map and calculate the offset between mouse and target's position when starting to drag a marker object:
map.addEventListener('dragstart', function(ev) {
var target = ev.target,
pointer = ev.currentPointer;
if (target instanceof H.map.Marker) {
var targetPosition = map.geoToScreen(target.getGeometry());
target['offset'] = new H.math.Point(pointer.viewportX - targetPosition.x, pointer.viewportY - targetPosition.y);
behavior.disable();
}
}, false);
// re-enable the default draggability of the underlying map when dragging has completed
map.addEventListener('dragend', function(ev) {
var target = ev.target;
if (target instanceof H.map.Marker) {
behavior.enable();
}
}, false);
// Listen to the drag event and move the position of the marker as necessary
map.addEventListener('drag', function(ev) {
var target = ev.target,
pointer = ev.currentPointer;
if (target instanceof H.map.Marker) {
target.setGeometry(map.screenToGeo(pointer.viewportX - target['offset'].x, pointer.viewportY - target['offset'].y));
}
}, false);
}
//Step 1: initialize communication with the platform
var platform = new H.service.Platform({
'apikey': 'MY-API-KEY'
});
var defaultLayers = platform.createDefaultLayers();
//Step 2: initialize a map
var map = new H.Map(document.getElementById('map'),
defaultLayers.vector.normal.map, {
center: {
lat: lat,
lng: lng
},
zoom: 16,
pixelRatio: window.devicePixelRatio || 1
});
// add a resize listener to make sure that the map occupies the whole container
window.addEventListener('resize', () => map.getViewPort().resize());
//Step 3: make the map interactive - MapEvents enables the event system - Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Step 4: Create the default UI:
var ui = H.ui.UI.createDefault(map, defaultLayers, "de-DE");
// Add the click event listener.
addDraggableMarker(map, behavior);
});
$('#mapButtonAbort').on('click', function() {
// Something like map.destroy()
});
Summary:
On the beginning of the page is was a "mapInit" variable defined which is set to false. Now when the user closes the map by either clicking "Save Position" or "Abort" it simply fadeOut the Map and set the "mapInit" variable to true. As a workaround: when the 'addLocation' Button is clicked it simply check if the "mapInit" Variable is set to false. If so a new map gets initialized. Otherwise i simply fadeIn the map again.
Another possibility is to remove the DOM element when the user click on the "Hide" button and vice-versa, onAttach, Detach callbacks can also be created, please reference for that.. developer.here.com/documentation/maps/topics/best-practices.html
I am using a measure plugin tool in leaflet but when I try to measure between markers the pop up window interfers is there a way to fix this? I read something about oddclicks, I tried using that to no avail.
$(".leaflet-control-measure").click(function() {
var oddClick = $(this).data("oddClick");
$(this).data("oddClick", !oddClick);
if (!oddClick) {
map.off('click', popup);
} else {
map.on('click', popup);
}
});
popup logic- I am reading from a database, the popup is called from python in a for loop, and rendered using the jinja2 template
var markers= L.markerClusterGroup({
disableClusteringAtZoom: 15,
minZoom : 2
});
{% for item in markers %}
var resortIcon = L.icon({
iconUrl: '{{ item[3] }}',
iconSize: [25, 25],
popupAnchor: [0,-15]
});
var marker{{ item[0] }} = L.marker({{ item[5:] }}, {icon: resortIcon});
var popup = "<table height='90'><tr><td>{{ item[1] }}</td></tr><tr><td
align='center'><b><i>{{ item[4] }}</b></i></td></tr><tr><td align='center'>
<a href='www.google.com' onmouseover='More info'><img src='../icon/contract.svg' height=30 width=30 /></a></td></tr></table>";
marker{{ item[0] }}.bindPopup(popup);
markers.addLayer(marker{{ item[0] }});
map.addLayer(markers)
{% endfor %}
If I understand you correctly, you are wanting to prevent the popups from appearing if you are measuring. I am by no means an expert in leaflet, as I have never used it before, but from digging through some of the documentation, it seems like this might be your best option.
Essentially, you can bind an event handler to the popupopen event on each marker, and inside that handler, you can immediately close the popup if certain conditions are met (i.e. "measure mode" is enabled).
This is how it would work:
var startingCoords = [40.07573, -105.401047];
var markerCoords = [
startingCoords,
[40.512318, -105.665104],
[39.825169, -104.994123],
];
var map = L.map('map').setView(startingCoords, 8);
var enablePopups = document.getElementById("enablePopups");
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
function onPopupOpen(e) {
// check if you it is okay for the popup to open - if not, close it.
if (!enablePopups.checked) {
// "this" refers to the marker object of the marker that was clicked on, whereas
// e.target will refer to the DOM element itself
this.closePopup();
}
}
// loop to create each marker
markerCoords.forEach(function (coords) {
var marker = L.marker(coords).addTo(map).bindPopup('Location at [' + coords + ']');
// use .bind(marker) so we can access the marker itself via "this" inside the onPopupOpen
// handler. That way, we don't have to keep an array of markers as a reference - each one
// is self-referencing.
marker.on("popupopen", onPopupOpen.bind(marker));
});
#map {
width: 500px;
height: 300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.js"></script>
Enable Popups ? <input type="checkbox" checked id="enablePopups" />
<p>Click on a marker with "enable popups" checked vs unchecked - you can manually disable the popups based on a condition</p>
<div id="map"> </div>
It seems that once we add popup to the layer/polygon, the click on it will be captured first and stops further event propagation, thus makes the measure layer cannot get the click event to draw line.
How ever, we can bypass that though some tricky methods. For example, when start measuring, loop through all layers and remove their click event handlers to prevent them from responding to click; and then, after finish measuring, we add back their click event handlers to keep its original behavior.
Here's the tricky:
// the cached clicks for other layers
_cachedClicks: [],
// try to remove click event handlers of other layers
_removeOtherLayersClicks: function (layer) {
if (layer === this._layerPaint) {
return;
}
var loop = function (childLayer) {
if (childLayer._events && childLayer._events.click) {
this._cachedClicks.push({
layer: childLayer,
click: childLayer._events.click,
});
childLayer._events.click = undefined;
}
if (childLayer.eachLayer) {
this._removeOtherLayersClicks(childLayer);
}
}.bind(this);
layer.eachLayer(loop);
},
// try to bring back the click event handlers
_addOtherLayersClicks: function () {
this._cachedClicks.forEach(function (cached) {
var layer = cached.layer;
var click = cached.click;
layer._events.click = click;
});
},
You can check more details in https://github.com/aprilandjan/leaflet.measure/issues/8
I'm working with Google Maps API V3, and I'd like to display a clickable image near to a drawed polygon when the mouse hovers it.
Until now, I'm able to create this event, but I have no idea how to display this image near to my polygon. Ideally, I'd like this image appears where the mouse entered in the polygon.
Here is a piece of my code, but it's just a try and the image is not displayed, so it is very incomplete (and maybe wrong). You can suggest me to do otherwise, Javascript is not my preferred language...
google.maps.event.addListener(polygon, 'mouseover', function(e) {
this.setOptions( {fillOpacity: 0.1} );
polygon["btnMyButtonClickHandler"] = {};
polygon["btnMyButtonImageUrl"] = MyImage;
displayMyButton(polygon);
});
function displayMyButton(polygon) {
var path = polygon.getPath();
var myButton = getMyButton(path.btnMyButtonImageUrl);
if(myButton.length === 0)
{
console.log("IN"); //Is displayed in the console
var myImg= $("img[src$='http://linkToMyImage.png']");
myImg.parent().css('height', '21px !important');
myImg.parent().parent().append('<div style="overflow-x: hidden; overflow-y: hidden; position: absolute; width: 30px; height: 27px;top:21px;"><img src="' + path.btnMyButtonImageUrl+ '" class="myButtonClass" style="height:auto; width:auto; position: absolute; left:0;"/></div>');
// now get that button back again!
myButton = getMyButton(path.btnMyButtonImageUrl);
myButton.hover(function() {
$(this).css('left', '-30px'); return false; },
function() { $(this).css('left', '0px'); return false; });
myButton.mousedown(function() { $(this).css('left', '-60px'); return false;});
}
// if we've already attached a handler, remove it
if(path.btnDeleteClickHandler)
myButton.unbind('click', path.btnMyButtonClickHandler);
myButton.click(path.btnMyButtonClickHandler);
}
function getMyButton(imageUrl) {
return $("img[src$='" + imageUrl + "']");
}
Thanks for your suggestions !
EDIT
#MrUpsidown, unfortunately no, click event can't be a solution, I really need your Something here div appears at mouseover.
I modified your code like this :
google.maps.event.addListener(polygonPath, 'mouseover', function (event) {
if( $("#map_overlay").css('display') == "none")
{
$("#map_overlay").css({
'position': 'absolute',
'display': 'block',
'left': event.Sa.pageX,
'top': event.Sa.pageY
});
}
});
The div appears when my mouse enter the polygon and don't move except if my mouse hovers the div (which hovers the polygon). On this case, the event seems called continuously. How can we avoid this and let the div at its inital position once the mouse enter the polygon ?
Here is your modified : fiddle
You need to create an element to hold your clickable image. Make it position:absolute; with a bigger z-index than your map container. To place it at a specific place, check the mouse position on your polygon mouseover event and set the element position accordingly. Hope this helps.
Edit: Yes, wrap it in a DIV is a good idea. Here is a simple fiddle to show the concepts. And sorry, of course it was mouseover and not mouseenter like I first wrote.
http://jsfiddle.net/upsidown/zrC2D/
I have reached out to the developer on this one and he indicated I'd be on my own modifying the code to support this method as the code as currently written was not very hack friendly...I have attempted to modify without 100% success.
Currently JCrop is using jquery to track .mousedown to start the selection and .mouseup to accept/stop the selection. What happens on the blackberry device(with trackpad) when you click the trackpad using jcrop it starts to draw the selection as you move the cursor(you cannot click(hold) and drag on the trackpad its more of a click event). The issue is when you click again it removes the selection and starts to redraw it from the current cursor position. It seems to me that JCrop is using the mousedown to track the click and drag selection process and then mouseup to release the selection and keep the crop box.
I thought about assigning a variable like clickCount track the clicks and a function to fireoff the events. So everytime the user clicks it would run a function to track the clickCount and fireoff either the start selection or finish selection events.
Below are all the references to .mousedown and .mouseup:
var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({
position: 'absolute',
top: px(-bound),
left: px(-bound),
zIndex: 290
}).mousedown(newSelection);
function dragDiv(ord, zi) //{{{
{
var jq = $('<div />').mousedown(createDragger(ord)).css({
cursor: ord + '-resize',
position: 'absolute',
zIndex: zi
});
if (Touch.support) {
jq.bind('touchstart', Touch.createDragger(ord));
}
$hdl_holder.append(jq);
return jq;
}
var $track = newTracker().mousedown(createDragger('move')).css({
cursor: 'move',
position: 'absolute',
zIndex: 360
});
function toFront() //{{{
{
$trk.css({
zIndex: 450
});
if (trackDoc) {
$(document)
.bind('mousemove',trackMove)
.bind('mouseup',trackUp);
}
}
//}}}
function toBack() //{{{
{
$trk.css({
zIndex: 290
});
if (trackDoc) {
$(document)
.unbind('mousemove', trackMove)
.unbind('mouseup', trackUp);
}
}
if (!trackDoc) {
$trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);
}
Help / ideas would be greatly appreciated. Thanks
What about something along the lines of this:
var isDragging = false;
function onMouseDown(event) {
isDragging = !isDragging;
}
function onMouseUp(event) {
if (isDragging) {
event.preventDefault();
}
}
window.addEventListener('mousedown', onMouseDown, false);
window.addEventListener('mouseup', onMouseUp, false);
This way, onMouseUp should technically only fire every other time, potentially preventing the release trigger from stopping selection. This would require some deeper integration with your code of course.