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
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);
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 have a map with bingmap and I implemented waypoints. I would like when I click in a waypoint it displays an info window.
if (!$scope.directionsManager) { _createDirectionsManager(); }
if($scope.directionsManager.getAllWaypoints().length < 2)
{
$scope.directionsManager.resetDirections();
$scope.waypoints.forEach(function (waypoint) {
var order_waypoint = new Microsoft.Maps.Directions.Waypoint({ location: new Microsoft.Maps.Location(waypoint.lat, waypoint.lng) });
console.log("order_waypoint", order_waypoint)
$scope.directionsManager.addWaypoint(order_waypoint);
})
}
var renderOption = {
itineraryContainer: document.getElementById('directionsItinerary'),
waypointPushpinOptions:{
// icon: "http://vignette3.wikia.nocookie.net/freeciv/images/1/1c/Crystal_128_penguin.png/revision/latest?cb=20071106133132&path-prefix=es",
// hoverIcon: "http://vignette3.wikia.nocookie.net/freeciv/images/1/1c/Crystal_128_penguin.png/revision/latest?cb=20071106133132&path-prefix=es",
// height: 10,
// width: 10,
draggable: false,
textOffset: new Microsoft.Maps.Point(-1, 3)
}
}
$scope.directionsManager.setRenderOptions(renderOption);
$scope.directionsManager.calculateDirections();
Thanks!
I haven't used the Bing maps API, but the docs on the Waypoint class make it look like you might need a custom pushpin in order to intercept its click event.
var location = new Microsoft.Maps.Location(waypoint.lat, waypoint.lng);
var pushpin = new Microsoft.Maps.Pushpin(location);
Microsoft.Maps.Events.addHandler(pushpin, 'click', function() {
// Implementation can go here
});
var order_waypoint = new Microsoft.Maps.Directions.Waypoint(
{location: location, pushpin: pushpin});
$scope.directionsManager.addWaypoint(order_waypoint);
It is possible you can get a reference to the default pushpin on order_waypoint without creating your own custom one, and use addHandler to bind the click event to it. I don't have running code to test that on, and I don't see a way to get a reference to the default pushpin, only the custom one. You could try it anyway (order_waypoint.getPushpin()) and see if it works.
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.
I am working on a Tobacco shop finder Windows 8.1 Store application. The main idea is, that when the user opens the app a map appears zoomed in to his position. This map displays the tobacco shops as pushpins. The data of these shops are stored in a json file with the following structure:
{
addr: Some street 12,
lng: longitude,
lat: lattitude,
open: {10:00-23:00, 20:00-22:00, and 5 more}
}
It contains the address, the longitudinal position, the lattitudinal position and the open hours of the shop.
This is the javascript code I use to put the pushpins in the correct position on the map:
var pins = [];
$.getJSON(dbURL, function (response) {
pins = response;
$.each(pins, function (i, item) {
var _location = new Microsoft.Maps.Location(item.lat, item.lng);
var pin = new Microsoft.Maps.Pushpin(_location, { description: i, icon: markerImgURL, height: markerImgHeight, width: markerImgWidth, draggable: false });
Microsoft.Maps.Events.addHandler(pin, 'click', showDetails);
map.entities.push(pin);
});
});
I've added a click handler for each pin, so that when a user clicks (or touches) a pin, a window slides in with detailed information about it.
This is my showDetails() function:
function showDetails() {
var details = $("#details");
details.animate({
"right": parseInt(details.css("right")) == 0
? -1 * details.innerWidth() - 5
: 0
}, 500)
}
My problem is, that I don't really know how can I access the data of that individual pin from this function. I've added an id as the description of the pin, this would reference to the "pins" array where the full data is saved, but if I call the function like showDetails(i) inside my $.each() loop, the application would freeze at startup.
tl;dr: How can I access the description property of a pushpin from an external function without passing an id to the function?
Thanks!
Your showDetails() event handler will be passed the event object as a parameter. If you update your function signature to accept this event arg, you can access the target object which will be a reference to the pushpin. From there you can access the description and other properties
function showDetails(e) {
var details = $("#details");
var smokeShop = e.target;
WinJS.Utilities.setInnerHTML(details, smokeShop.description);
details.animate({
"right": parseInt(details.css("right")) == 0
? -1 * details.innerWidth() - 5
: 0
}, 500)
}