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.
Related
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 have some DOM elements that I don't directly have access to, as they are rendered from an API call. What I would like to do is add a class to this element once it's rendered on the DOM.
Using Template.rendered does not work, as the template renders properly before these DOM elements appear from the API.
My current solution is a Meteor.setTimeout—which might be the definition of a hack—and it only works about 90% of the time.
What is the best way to trigger a function when a particular DOM element is rendered?
Here is some of the relevant code from the API call:
Template.map.rendered = function() {
return this.autorun(function() {
var drawControl, drawnItems, mmap;
if (Mapbox.loaded()) {
L.mapbox.accessToken = '<API KEY>';
L.mapbox.config.FORCE_HTTPS = true;
mmap = L.mapbox.map('map', '<TOKEN>');
L.control.scale().addTo(mmap);
var featureGroup = L.featureGroup().addTo(mmap);
drawControl = new L.Control.Draw({
draw: {
polygon: false,
polyline: false,
rectangle: true,
circle: false,
marker: false
}
});
mmap.addControl(drawControl);
mmap.addControl(L.mapbox.geocoderControl('mapbox.places', {
autocomplete: true
}));
function showPolygonAreaEdited(e) {
e.layers.eachLayer(function(layer) {
showPolygonArea({ layer: layer });
});
}
function showPolygonArea(e) {
coords = {
lat1: normalizeLon(e.layer.toGeoJSON().geometry.coordinates[0][1][0]),
lon1: normalizeLat(e.layer.toGeoJSON().geometry.coordinates[0][1][1]),
lat2: normalizeLon(e.layer.toGeoJSON().geometry.coordinates[0][3][0]),
lon2: normalizeLat(e.layer.toGeoJSON().geometry.coordinates[0][3][1])
}
featureGroup.clearLayers();
featureGroup.addLayer(e.layer);
e.layer.openPopup();
}
mmap.on('draw:created', showPolygonArea);
mmap.on('draw:edited', showPolygonAreaEdited);
}
});
};
I've removed a lot of extraneous code, so this might not compile properly... But it has all the relevant bits.
The selector I initially tried to use was this:
Template.map.rendered = function() {
$('.leaflet-draw-section').attr('data-intro', 'Hello step one!')
...
...
But it didn't work, since the API elements hadn't rendered yet.
Looking at the map box api, it appears that L.mapbox.map does the rendering:
<script>
// Provide your access token
L.mapbox.accessToken = 'id1' //
// Create a map in the div #map
L.mapbox.map('map', 'id2');
// add your class to #map right here!!
</script>
If that doesnt work, them maybe L.mapbox.map is doing something asynchronous. They don't give you a callback, so a window.setTimout(func, 0) may be necessary
Make sure your code looks like this
Template.templatename.onRendered(function(){
//yourcode
});
The solution comes from the Mapbox API. There is a callback on "ready":
var layer = L.mapbox.tileLayer('mapbox.streets');
layer.on('ready', function() {
// the layer has been fully loaded now, and you can
// call .getTileJSON and investigate its properties
});
I try to use ContextMenu to run DeleteMaker function, but ContextMenu just get marker current Latlng, not like other leaflet events like "click", "dblclick" ... what can get marker options info.
Example :
var marker = new customMarker([28.63278, 77.21972],{
clickable: true,
name: 'Connaught Place',
type: 'Neighbourhood'
}).on('click', onClick).addTo(map);
function onClick(e) {
$('#content').html("
Name: "+this.options.name+"
Type: "+this.options.type+"
">;")
}
that code above can get Options value with event "click", HOW CAN I GET options value LIKE THIS WITH CONTEXTMENU
If you want to get marker options from outer, simply use variable name to get there.
marker.options.name
marker.options.type
See example in JSFiddle. Is this what you asked?
I'm using the context menu plugin for leaflet (https://github.com/aratcliffe/Leaflet.contextmenu) and I noticed the same situation. What I did was to use the "contextmenu" option for the marker event to create a global variable with the marker.options.id when a right click is detected. Then, using the context menu callback function, use that global variable (that has the marker id), fetch the marker associated to that id and THEN remove the marker from the markers array. Yes, I use a markers array because is easier to work when you have thousand of markers in the map.
It's not elegant but it works.
If you're using the contextmenu pluging, it's pretty straight forward.
You could use a global variable like Alberto said. Or just feed the callback property of the contextmenu item with a function call.
var type = "type";
var name = "name";
var marker = new customMarker(28.63278, 77.21972,
{
name: name,
type: type,
contextmenu: true,
contextmenuWidth: 140,
contextmenuItems: [ { text: 'Show name/type', callback: function () { SetContent(name, type) } }]
});
function SetContent(name, type)
{
$('#content').html("Name: " + name + "Type: " + type);
}
I'm trying to produce a mapping application with Bing Maps with a button that will retrieve a JSON string and places pins on the map based on the center of the map.
That is working fine, but I'm running into two issues that I'm having trouble diagnosing.
The first is that when I move the map after placing the pins, the majority of them disappear from the map except for 1-3. I've figured out that the pins are still being held in map.entities, but just aren't all displaying.
The second issue is that I have a click event on the pins, and sometimes when I click on a pin it will disappear (and sometimes reappear elsewhere on the map).
Here is my code:
function addPin() {
map.entities.clear();
var pinImg = "images/MapPin.jpg";
var latLong = {};
var name;
for (var i = 0; i < factualJson.response.data.length; ++i) {
latLong['latitude'] = factualJson.response.data[i].latitude;
latLong['longitude'] = factualJson.response.data[i].longitude;
name = factualJson.response.data[i].name;
var pin = new Microsoft.Maps.Pushpin(latLong, {
icon: pinImg,
anchor: new Microsoft.Maps.Point(latLong['latitude'], latLong['longitude']),
draggable: true,
width: 48,
height: 48
});
Microsoft.Maps.Events.addHandler(pin, 'click', displayName);
pin.title = name;
pin.id = 'pin' + i;
map.entities.push(pin);
}
document.getElementById("arrayLength").innerHTML = "Number of locations: " + map.entities.getLength();
}
function displayName(e) {
document.getElementById("name").innerHTML = "";
if (this.target.id != -1) {
document.getElementById("name").innerHTML = this.target.title;
}
}
function boot() {
Microsoft.Maps.loadModule('Microsoft.Maps.Overlays.Style', { callback: getMap });
}
function getMap() {
map = new Microsoft.Maps.Map($gel("bingMap"), {
credentials: getKey(),
customizeOverlays: true,
enableClickableLogo: true,
enableSearchLogo: true,
showDashboard: true,
showBreadcrumb: true,
showCopyright: true,
zoom: 10,
labelOverlay: Microsoft.Maps.LabelOverlay.hidden
});
setGeoLocation();
//setTimeout(optimizeMap, 100);
window.onresize = resizeWin;
resizeWin();
}
Currently I make an ajax call from the button, and the callback function calls 'AddPin' which adds the pins to the map. I thought I'd add in the map initialization code in case it was relevant. Currently boot() is called on body load.
For me the solution was similar to yours #canadian coder
Microsoft.Maps.Location() only accepts float values, no strings and Int.
I use MVC architecture and passed a string using a model. Later i converted that string to float and passed to Location.
Problem solved.
var pushpin = new Microsoft.Maps.Pushpin(
center, { icon: '/Content/BingPushpin.png', width: 50, height: 50, draggable: false });
pushpin.setLocation(new Microsoft.Maps.Location
(parseFloat(#Model.Latitude) , parseFloat(#Model.Longitude)));
dataLayer.push(pushpin);
locations.push(new Microsoft.Maps.Location
(parseFloat(#Model.Latitude) , parseFloat(#Model.Longitude)));
EDIT :
Later found out that problem still exist. Another reason can be that you are calling that Map to load twice. So check for any other instance of the map which is being loaded. In my case see below.
$(document).ready(function () {
loadSearchModule(); //calling Map to Load at a div without pushpins
//code to do something
getMapOnLocation();
}
function getMapOnLocation()
{//code to display map with pushpin
}
In the Above example I was telling the control to load my map with PushPins and when the page is fully Loaded load the map without pushpins.
Hope this helps :)
As always I need to ask the question before I figure it out myself.
The issue was that I needed to push a Microsoft location object into the pin and not an object. Like so:
var loc = new Microsoft.Maps.Location(47.592, -122.332);
And NOT my latLong object.
This also seemed to fix the issue of disappearing pins on click event.