Moving a Marker after Locate function is called - javascript

I trying to move the created movable marker so that it points to the results of the Locate() function.
This would allow the recalculation of the other closest markers in the datasource.
Presently, this works only when I manually drag the movable matker.
I can't seem to be able to get the coordinates of the location found and move the marker there.
Thanks for any help!
Here's my code:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Reorder marker list based on proximity</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox.js/v2.1.9/mapbox.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox.js/v2.1.9/mapbox.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<style>
.info {
background:#fff;
position: fixed;
width:90%;
top:70%;
right:5%;
left:5%;
bottom: 5%;
border-radius:2px;
max-height:30%;
overflow:auto;
}
.info .item {
display:block;
border-bottom:1px solid #eee;
padding:5px;
text-decoration:none;
}
.info .item small { color:#888; }
.info .item:hover,
.info .item.active { background:#f8f8f8; }
.info .item:last-child { border-bottom:none; }
</style>
<script src='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-omnivore/v0.2.0/leaflet-omnivore.min.js'></script>
<script src='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-locatecontrol/v0.42.0/L.Control.Locate.min.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-locatecontrol/v0.42.0/L.Control.Locate.mapbox.css' rel='stylesheet' />
<!--[if lt IE 9]>
<link href='https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-locatecontrol/v0.42.0/L.Control.Locate.ie.css' rel='stylesheet' />
<![endif]-->
<div id='map' class='map'></div>
<div id='info' class='info'></div>
<script>
L.mapbox.accessToken = 'pk.eyJ1IjoiamZnaWFyZCIsImEiOiJ6S09PVU5vIn0.kn_-BVWarxfNjT1hak0kyA';
var map = L.mapbox.map('map', 'jfgiard.9dee89eb')
.setView([51.953608, 36.776667], 4);
var info = document.getElementById('info');
// create control and add to map
var lc = L.control.locate({
position: 'topleft', // set the location of the control
drawCircle: true, // controls whether a circle is drawn that shows the uncertainty about the location
follow: false, // follow the user's location
setView: true, // automatically sets the map view to the user's location, enabled if `follow` is true
keepCurrentZoomLevel: true, // keep the current map zoom level when displaying the user's location. (if `false`, use maxZoom)
stopFollowingOnDrag: false, // stop following when the map is dragged if `follow` is true (deprecated, see below)
remainActive: false, // if true locate control remains active on click even if the user's location is in view.
markerClass: L.circleMarker, // L.circleMarker or L.marker
circleStyle: {}, // change the style of the circle around the user's location
markerStyle: {},
followCircleStyle: {}, // set difference for the style of the circle around the user's location while following
followMarkerStyle: {},
icon: 'fa fa-map-marker', // class for icon, fa-location-arrow or fa-map-marker
iconLoading: 'fa fa-spinner fa-spin', // class for loading icon
circlePadding: [0, 0], // padding around accuracy circle, value is passed to setBounds
metric: true, // use metric or imperial units
onLocationError: function(err) {alert(err.message)}, // define an error callback function
onLocationOutsideMapBounds: function(context) { // called when outside map boundaries
alert(context.options.strings.outsideMapBoundsMsg);
},
showPopup: true, // display a popup when the user click on the inner marker
strings: {
title: "Show me where I am", // title of the locate control
popup: "You are within {distance} {unit} from this point", // text to appear if user clicks on circle
outsideMapBoundsMsg: "You seem located outside the boundaries of the map" // default message for onLocationOutsideMapBounds
},
locateOptions: {} // define location options e.g enableHighAccuracy: true or maxZoom: 10
}).addTo(map);
// Creates a single, draggable marker on the page.
var m = L.marker(new L.LatLng(51.953608, 36.776667), {
icon: L.mapbox.marker.icon({
'marker-color': '#000000',
'marker-size': 'large'
}),
draggable: true
}).bindPopup('Drag me around the map to simulate GeoLocalization!').addTo(map);
// Repopulate the features layer in the menu listing based on the dragged markers proximity to them.
// console.log(marker.getLatLng());
m.on('dragend', function() {
populateListing();
});
// Load the features from the CSV files.
var features = omnivore.csv('NMOandHTC.csv')
.on('ready', function(layer) {
// Customizing marker styles based on an attribute.
this.eachLayer(function(marker) {
if (marker.toGeoJSON().properties.type === 'National Member Organization') {
// The argument to L.mapbox.marker.icon is based on the simplestyle-spec: see that specification for a full description of options.
marker.setIcon(L.mapbox.marker.icon({
'marker-color': '#e31837',
'marker-size': 'medium'
}));
} else {
marker.setIcon(L.mapbox.marker.icon({
'marker-color': '#616265',
'marker-size': 'small'
}));
}
// Bind a popup to each icon based on the same properties
marker.bindPopup(marker.toGeoJSON().properties.name + '<br>' + marker.toGeoJSON().properties.country);
});
})
.addTo(map);
map.on('ready', function() {
// Display the tooltip after the marker has been added to the map.
m.openPopup();
});
// When the features layer is ready (added to the map), run populateListing.
features.on('ready', populateListing);
function populateListing() {
// Clear out the listing container first.
info.innerHTML = '';
var listings = [];
// Build a listing of markers
features.eachLayer(function(marker) {
// Draggable marker coordinates.
var home = m.getLatLng();
var metresToMiles = 0.000621371192;
var distance = (metresToMiles * home.distanceTo(marker.getLatLng())).toFixed(1);
var link = document.createElement('a');
link.className = 'item';
link.href = '#';
link.setAttribute('data-distance', distance);
// Populate content from each markers object.
link.innerHTML = marker.feature.properties.type + '<br />' + marker.feature.properties.name + '<br />' +
'<small>' + distance + ' mi. away</small>';
link.onclick = function() {
if (/active/.test(this.className)) {
this.className = this.className.replace(/active/, '').replace(/\s\s*$/, '');
} else {
var siblings = info.getElementsByTagName('a');
for (var i = 0; i < siblings.length; i++) {
siblings[i].className = siblings[i].className
.replace(/active/, '').replace(/\s\s*$/, '');
};
this.className += ' active';
// When a menu item is clicked, animate the map to center
// its associated marker and open its popup.
map.panTo(marker.getLatLng());
marker.openPopup();
}
return false;
};
listings.push(link);
});
// Sort the listing based on the
// assigned attribute, 'data-listing'
listings.sort(function(a, b) {
return a.getAttribute('data-distance') - b.getAttribute('data-distance');
});
listings.forEach(function(listing) {
info.appendChild(listing);
});
}
</script>
</body>
</html>

Is L.control.locate really needed here? Why not just call map.locate() after binding an callback to locationfound with map.on('locationfound, dostuff) Your function receives the results of the geolocation and can proceed from there.

Related

Add text box on leaflet map with selected overlay layer name

Relatively new JavaScript user here, first question.
So I have a choropleth leaflet map that uses a jQuery slider (via https://github.com/dwilhelm89/LeafletSlider) to shift between years. The map contains about 50 years of global data, with each overlay layer corresponding to a layergroup containing each country's data for the appropriate year.
The purpose of the slider is to allow the user to quickly shift between years. However, I would like a visual cue to let the user know what year is being displayed at any moment. Is it possible to display something like a text box on the map that displays the name of the current overlay layer and automatically updates whenever the overlay layer switches? (the name of each layergroup is its respective year)
I know the textbox part is certainly possible
(Overlaying a text box on a leaflet.js map), but I'm not sure how to dynamically update it with the necessary info.
Thanks! Let me know if you need my code and I'll post it.
Okay, I thought a bit and here's a quick solution.
var sliderControl = null;
var map = L.map("map").setView([51.95, 7.6], 9);
L.tileLayer("//{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution:
'© OpenStreetMap contributors',
}).addTo(map);
//Fetch some data from a GeoJSON file
$.getJSON(
"https://dwilhelm89.github.io/LeafletSlider/points.json",
function (json) {
var testlayer = L.geoJson(json);
var sliderControl = L.control.sliderControl({
position: "topright",
layer: testlayer,
range: true,
});
//Make sure to add the slider to the map ;-)
map.addControl(sliderControl);
//An initialize the slider
sliderControl.startSlider();
}
);
map.on("layeradd", function () {
map.eachLayer(function (layer) {
if (layer instanceof L.Marker) {
let desc = document.querySelector(".description");
// desc.textContent = JSON.stringify(layer.getLatLng());
desc.textContent = layer.feature.properties.time;
}
});
});
// create legend
const legend = L.control({ position: "bottomleft" });
legend.onAdd = function () {
let div = L.DomUtil.create("div", "description");
div.className = "description";
return div;
};
legend.addTo(map);
*,
:after,
:before {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html {
height: 100%;
}
body,
html,
#map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.description {
border: 1px solid black;
background: #fff;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.6.0/dist/leaflet.css" />
<link rel="stylesheet" href="https://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" type="text/css">
<script src="https://unpkg.com/leaflet#1.6.0/dist/leaflet.js"></script>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.2/jquery.ui.touch-punch.min.js"></script>
<script src="https://rawgit.com/dwilhelm89/LeafletSlider/master/SliderControl.js" type="text/javascript"></script>
<div id="map"></div>
And I recommend using the newer version of this plugin ;)
There you have the method event
And the easier way to download the data about the marker.
I've used Leaflet, Mapbox, and Google Maps on some personal and commercial projects, and whenever I wanted to overlay some information, I'd just use simple HTML elements. All you need to do is render whatever elements you want on the screen the same way you would normally, just ensure that you use correct position and applicable positioning units and ensure you have a higher z-index on your element that you want to show, i.e. your year indicator, than you do on your map element. Treat it just like you would any other HTML!
Edit:
Here is an example screenshot: https://imgur.com/a/2fXf5CI. Also, if you aren't already using a position property on your Leaflet map, you should go ahead and add a position: relative; property to the selector for the map so that you can also assign it a z-index. And then, in your year indicator's styles, give it a higher z-index value than the one you gave to your Leaflet map.

Showing a content Div on hover of a marker in HERE Maps

I am new to here maps and need to show a div on marker hover. I have been able to put markers with icons but now need to show a div with some extra information. Does HERE maps API provide this functionality?
Any doc URL or piece of code will be appreciated.
NOTE: I am using HERE maps JS API for web.
You can create a tooltip effect by adding various event listeners to the map to check if the mouse pointer is over an object.
(function (ctx) {
// ensure CSS is injected
var tooltipStyleNode = ctx.createElement('style'),
css = '#nm_tooltip{' +
' color:white;' +
' background:black;' +
' border: 1px solid grey;' +
' padding-left: 1em; ' +
' padding-right: 1em; ' +
' display: none; ' +
' min-width: 120px; ' +
'}';
tooltipStyleNode.type = 'text/css';
if (tooltipStyleNode.styleSheet) { // IE
tooltipStyleNode.styleSheet.cssText = css;
} else {
tooltipStyleNode.appendChild(ctx.createTextNode(css));
}
if (ctx.body) {
ctx.body.appendChild(tooltipStyleNode);
} else if (ctx.addEventListener) {
ctx.addEventListener('DOMContentLoaded', function () {
ctx.body.appendChild(tooltipStyleNode);
}, false);
} else {
ctx.attachEvent('DOMContentLoaded', function () {
ctx.body.appendChild(tooltipStyleNode);
});
}
})(document);
Object.defineProperty(Tooltip.prototype, 'visible', {
get: function() {
return this._visible;
},
set: function(visible) {
this._visible = visible;
this.tooltip.style.display = visible ? 'block' : 'none';
}
});
function Tooltip(map) {
var that = this;
that.map = map;
that.tooltip = document.createElement('div');
that.tooltip.id = 'nm_tooltip';
that.tooltip.style.position = 'absolute';
obj = null,
showTooltip = function () {
var point = that.map.geoToScreen(obj.getPosition()),
left = point.x - (that.tooltip.offsetWidth / 2),
top = point.y + 1; // Slight offset to avoid flicker.
that.tooltip.style.left = left + 'px';
that.tooltip.style.top = top + 'px';
that.visible = true;
that.tooltip.innerHTML = obj.title;
};
map.getElement().appendChild(that.tooltip);
map.addEventListener('pointermove', function (evt) {
obj = that.map.getObjectAt(evt.currentPointer.viewportX,
evt.currentPointer.viewportY);
if(obj && obj.title){
showTooltip();
} else {
that.visible = false;
}
});
map.addEventListener('tap', function (evt){
that.tooltip.visible = false;
});
map.addEventListener('drag', function (evt){
if (that.visible) {
showTooltip();
}
});
};
This is initialised by passing the map object as shown:
function addTooltipControlToMap(map) {
tooltip = new Tooltip(map);
}
The code as written is looking for a .title attribute to be added to the map objects - this could be updated to use .getData() if preferred. Tooltips can be initialised as shown below, taking either text or html:
function addMarkersWithTooltips(map) {
// Simple Marker with tooltip
var brandenburgerTorMarker = new H.map.Marker(
{lat:52.516237, lng: 13.35}),
fernsehturmMarker = new H.map.Marker(
{lat:52.520816, lng:13.409417});
brandenburgerTorMarker.title = 'Brandenburger Tor';
// Marker with HTML Tooltip
fernsehturmMarker.title ='<div>' +
'<h2>Tooltip with HTML content<\/h2>' +
'<img width=\'120\' height=90 src=' +
'\'http://upload.wikimedia.org/wikipedia/commons/' +
'8/84/Berlin-fernsehturm.JPG\' ' +
'alt=\'\'/><br/><b>Fernsehturm, Berlin<\/b>' +
'<\/div>';
// Add the markers onto the map
map.addObjects([brandenburgerTorMarker, fernsehturmMarker]);
}
I have been able to find proper mouse over events for HERE map's markers which are pointerenter and pointerleave and sample code to use these events is:
// After Initializing map with your own credentials.
var map = new H.Map(document.getElementById('map'),
defaultLayers.normal.map,{
center: {lat: LAT_VAL, lng: LNG_VAL},
zoom: 12
});
var domMarker = new H.map.DomMarker(coords, {
icon: domIcon
});
var bubble;
domMarker.addEventListener('pointerenter', function(evt) {
bubble = new H.ui.InfoBubble({lat:"SOME_VALUE",lng:"SOME_VALUE"}, {
content: "Your content come here"
});
ui.addBubble(bubble);
}, false);
domMarker.addEventListener('pointerleave', function(evt) {
bubble.close();
}, false);
map.addObject(domMarker);
Depending on the api version you are using, you may find what you are looking for inside the documentation pdf (or at least start from there).
Supposing you need do make some HTML styled marker, you may need:
DomMarker (instead of a Marker, because it allows you to use ->2)
DomIcon (which can embed html)
An example can be found here https://developer.here.com/apiexplorer-v2-sample-data/template-web-default/examples/map-with-dom-marker/index.html
Anyway, if you need to show informations about the marker, I would suggest to use InfoBubbles, which have been developed for this purpose.
From the 3.0.5 docs:
// Create an info bubble object at a specific geographic location:
ui = H.ui.UI.createDefault(self.map, defaultLayers);
var bubble = new H.ui.InfoBubble({ lng: 13.4, lat: 52.51 }, {
content: '<b>Hello World!</b>'
});
// Add info bubble to the UI:
ui.addBubble(bubble);
To show them, you should attach an event to the marker tap event:
marker.addEventListener('tap', function (evt) {
//create and add the bubble
}
In any case, you can find the documentation of your api version here: https://developer.here.com/documentation/versions
You do not have "hover" listener for marker,
but you can show infoBubble on click
http://heremaps.github.io/examples/explorer.html#infobubble-on-marker-click
If this doesn't work for you, you will have to use jquery and to bind "hover" on HTML marker element. (This is not very easy task)

Getting click on Mapbox markers

I have been able to integrate markers to the mapbox we are using, but still wonder if we can get a click on them. If so how?
Following is my code:
<style>
/*
* Unlike other icons, you can style `L.divIcon` with CSS.
* These styles make each marker a circle with a border and centered text.
*/
.count-icon1 {
background:url(images/redpin.png);
color:#000;
font-weight:600;
text-align:center;
padding:19px 0 0 0px; font-size:180%;
}
.count-icon2 {
background:url(images/greenpin.png);
color:#000;
font-weight:600;
text-align:center;
padding:19px 0 0 0px; font-size:180%;
}
</style>
js code:
var defaultLat = 39.12367;
var defaultLon = -76.81229;
if($scope.currentLocDetails != null){
if($scope.currentLocDetails.Lat != null && $scope.currentLocDetails.Lon != null){
defaultLat = $scope.currentLocDetails.Lat;
defaultLon = $scope.currentLocDetails.Lon;
}
}
var x = 0;
if(map != null)
map.remove();
map = L.mapbox.map('map_view', 'your key here').setView([defaultLat, defaultLon], 9);
for (var i = 0; i < responseData.JobLocation.length; i++) {
var eachObj = responseData.JobLocation[i];
if(eachObj.Lat != null && eachObj.Lon != null){
x++;
// Use a little math to position markers.
// Replace this with your own code.
L.marker([
eachObj.Lat,
eachObj.Lon
], {
icon: L.divIcon({
// Specify a class name we can refer to in CSS.
className: ((currentSelectedIndex + 1) == i + 1)?'count-icon1':'count-icon2',
// Define what HTML goes in each marker.
html: i + 1,
// Set a markers width and height.
iconSize: [65, 94]
})
}).addTo(map);
}
}
I tried doing a bit R & D, but get to no where:
We need to use featureLayer, but dunno how.
For the click feature we need to follow this code, but how?
// Listen for individual marker clicks.
myLayer.on('click',function(e) {
// Force the popup closed.
e.layer.closePopup();
var feature = e.layer.feature;
var content = '<div><strong>' + feature.properties.title + '</strong>' +
'<p>' + feature.properties.description + '</p></div>';
info.innerHTML = content;
});
Any help with this is really appreciated.
Thanks
I believe there are various ways of doing this with Mapbox, unfortunately I don't have access to my project where I use it right now so I'm just going off the Mapbox documentation.
Following your example this looks the simplest - if you have added marker, for example:
var marker = L.marker([43.6475, -79.3838], {
icon: L.mapbox.marker.icon({
'marker-color': '#9c89cc'
})
})
.bindPopup('<p>Your html code here</p>')
.addTo(map);
You can pass whatever HTML you want in the bindPopup argument.
https://www.mapbox.com/mapbox.js/example/v1.0.0/clicks-in-popups/
Then it should be be pretty simple via 'addEventListener('click', function)' on that marker variable.
Or alternatively -
myLayer.on('click', function(e) {
resetColors();
e.layer.feature.properties['old-color'] = e.layer.feature.properties['marker-color'];
e.layer.feature.properties['marker-color'] = '#ff8888';
myLayer.setGeoJSON(geoJson);
});
map.on('click', resetColors);
Effectively add an event listener on the map variable - and then listen to what you've clicked on via the event argument passed to the event listener.
This may be useful: https://www.mapbox.com/mapbox.js/example/v1.0.0/change-marker-color-click/
Good luck!

google maps adding overlay layer above UI and markers

I have a website with a google map filled with multiple markers. Each marker stands for an event on a festival. Since the information is too big to fit in the infowindow, i have decided to use a custom overlay. So far, with partially success. Here below is my image. As you can see, the overlay is below the marker. My goal is that the overlay comes above the markers and UI.
Here below is my code sample,
TxtOverlay.prototype.draw = function() {
// create info window
var div = document.createElement("div");
div.id = "mapOverlay";
div.innerHTML = this.txt;
div.style.cssText = 'background-color : #000; color: #FFF; border-radius: 15px; border-style : solid; border-width : 1px;padding: 10px; z-index: 9999; display: block; width: 460px; height: 460px; opacity: 0.4;';
// get projection
var map = this.map;
var overlayProjection = this.getProjection();
var anchor = overlayProjection.fromLatLngToDivPixel(this.pos);
div.style.position = "absolute";
div.style.left = (anchor.x - 240) + 'px';
div.style.top = (anchor.y - 240)+ 'px';
// bind created div to object var
this.div = div;
// add to map
var panes = this.getPanes();
panes.overlayLayer.appendChild(div);
console.log("prototype draw");
}
I have used z-index to 9999 ( there are 4000 markers atm ) but it doesn't work. Maybe it's worth to note that each marker id has its own z-index. (fe marker id = 1 has z-index = 1, marker id = 2 has z-index = 2, marker id = 3 has z-index = 3, ect )
If the overlay is on the top of both markers and UI, then it's easier to catch the click event to remove the overlay instead of manipulating the map and the map-objects.
thank you.
For others whom are also interested to solve this issue, i have found a simple workaround, i am using the floatPane layer, which is above the markers. Then i removed the UI/controls to have a working overlay without map interaction.
In the draw method,
// add to map
var panes = this.getPanes();
panes.floatPane.appendChild(div);
// add event
document.getElementById("mapOverlay").addEventListener('click', this.onRemove, false);
// disable map controls
toggleControls(false);
then when the div got clicked, simply create the next
// remove overlay
TxtOverlay.prototype.onRemove = function(e) {
// remove listener
var element = document.getElementById("mapOverlay");
element.removeEventListener('click', this.onRemove, false);
// and div
var parent = element.parentNode;
parent.removeChild(element);
this._div = null;
// return controls
toggleControls(true);
}
and the toggle method,
// toggles control ; false = no interaction, true = interaction
var toggleControls = function(bool) {
_map.setOptions({draggable: bool, zoomControl: bool, scrollwheel: bool, disableDoubleClickZoom: !bool, scaleControl : bool, zoomControl : bool, panControl : bool});
}
that's all to place and remove an overlay. :-)

Google Map V3 context menu

I am looking for a Google Map V3 context menu library. I have found some code examples here
Gizzmo's blog
Google API tips
GMap3
How I got ..
Stack overflow question Google maps v3 - Contextual menu available? of April also just came up with the above examples. So did Gmap3 adding a simple context menu .
But maybe somebody has encapsulated the examples in a reusable library or found something in the meantime. Obviously there was something for V2.
-- Updated 2012-05-31 --
I have found another one http://googlemapsmania.blogspot.de/2012/04/create-google-maps-context-menu.html , but did not have the time to test it yet.
I don't think you need a library for this. I'd start by trying:
var contextMenu = google.maps.event.addListener(
map,
"rightclick",
function( event ) {
// use JS Dom methods to create the menu
// use event.pixel.x and event.pixel.y
// to position menu at mouse position
console.log( event );
}
);
This assumes your map was created with:
var map = new google.maps.map( { [map options] } );
The event object inside the callback has 4 properties
latLng
ma
pixel
where pixel.x and pixel.y are the offset where your click event triggered - counted from the upper left corner of the canvas holding the map object.
I have created a working JS Fiddle for showing context menu as well as the ability to have clickable items on this context menu.
It shows a clickable Context Menu when a marker is right clicked on Google map.
Basically it makes use of an OverlayView on map. BTW its just a demo.
var loc, map, marker, contextMenu;
ContextMenu.prototype = new google.maps.OverlayView();
/**
* onAdd is called when the map's panes are ready and the overlay has been
* added to the map.
*/
ContextMenu.prototype.onAdd = function() {
$("<div id='cMenu' class='context-menu-marker'></div>").appendTo(document.body);
var divOuter = $("#cMenu").get(0);
for(var i=0;i < this.menuItems.length;i++) {
var mItem = this.menuItems[i];
$('<div id="' + mItem.id + '" class="options-marker">' +
mItem.label + '</div>').appendTo(divOuter);
}
this.div_ = divOuter;
// Add the element to the "overlayLayer" pane.
var panes = this.getPanes();
//panes.overlayLayer.appendChild();
panes.overlayMouseTarget.appendChild(this.div_);
var me = this;
for(var i=0;i < this.menuItems.length;i++) {
var mItem = this.menuItems[i];
var func = function() {
me.clickedItem = this.id;
google.maps.event.trigger(me, 'click');
};
google.maps.event.addDomListener($("#" + mItem.id).get(0), 'click', $.proxy(func, mItem));
}
google.maps.event.addListener(me, 'click', function() {
alert(me.clickedItem);
});
};
ContextMenu.prototype.draw = function() {
var div = this.div_;
div.style.left = '0px';
div.style.top = '0px';
div.style.width = '100px';
div.style.height = '50px';
};
// The onRemove() method will be called automatically from the API if
// we ever set the overlay's map property to 'null'.
ContextMenu.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};
// Set the visibility to 'hidden' or 'visible'.
ContextMenu.prototype.hide = function() {
if (this.div_) {
// The visibility property must be a string enclosed in quotes.
this.div_.style.visibility = 'hidden';
}
};
ContextMenu.prototype.show = function(cpx) {
if (this.div_) {
var div = this.div_;
div.style.left = cpx.x + 'px';
div.style.top = cpx.y + 'px';
this.div_.style.visibility = 'visible';
}
};
function ContextMenu(map,options) {
options = options || {}; //in case no options are passed to the constructor
this.setMap(map); //tells the overlay which map it needs to draw on
this.mapDiv = map.getDiv(); //Div container that the map exists in
this.menuItems = options.menuItems || {}; //specific to context menus
this.isVisible = false; //used to hide or show the context menu
}
function initialize() {
loc = new google.maps.LatLng(62.323907, -150.109291);
var options = {};
var menuItems=[];
menuItems.push({id:"zoomIn", className:'context_menu_item', eventName:'zoom_in_click', label:'Zoom in'});
menuItems.push({id:"zoomOut", className:'context_menu_item', eventName:'zoom_out_click', label:'Zoom out'});
options.menuItems = menuItems;
//=========================================
map = new google.maps.Map(document.getElementById("map"), {
zoom: 12,
center: loc,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
marker = new google.maps.Marker({
map: map,
position: loc,
visible: true
});
contextMenu = new ContextMenu(map, options);
google.maps.event.addListener(marker, 'rightclick', function(mouseEvent){
contextMenu.hide();
this.clickedMarker_ = this;
var overlayProjection = contextMenu.getProjection();
var cpx = overlayProjection.fromLatLngToContainerPixel(mouseEvent.latLng);
contextMenu.show(cpx);
map.setOptions({ draggableCursor: 'pointer' });
});
// Hide context menu on several events
google.maps.event.addListener(map,'click', function(){
map.setOptions({ draggableCursor: 'grab' });
contextMenu.hide();
});
}
google.maps.event.addDomListener(window, 'load', initialize);
Fiddle link:
http://jsfiddle.net/jEhJ3/3409/
You can add context menu very easily in google map by following these steps:
Add a custom control of google maps, hide that control on page load.
Add a right click event handler on map.
Show that custom control on right click at correct position using pixel property of right click event parameter.
Moreover, Following is working snippet, open it in full page (use you own key to avoid that google billing error):
var map;
var karachi = {
lat: 24.8567575,
lng: 66.9701725
};
$(document).ready(function() {
initMap();
});
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
zoom: 13.5,
center: karachi
});
let contextMenu = document.getElementById('contextMenu');
map.controls[google.maps.ControlPosition.TOP_CENTER].push(contextMenu);
hideContextMenu();
google.maps.event.addListener(map, "rightclick", function(event) {
showContextMenu(event);
});
google.maps.event.addListener(map, "click", function(event) {
hideContextMenu();
});
}
function showContextMenu(event) {
$('#contextMenu').css("display", "block");
$('#contextMenu').css({
left: event.pixel.x,
top: event.pixel.y
})
}
function hideContextMenu() {
$('#contextMenu').css("display", "none");
}
#map {
height: 100%;
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
.contextMenu {
background-color: rgb(255, 255, 255);
border: 2px solid rgb(255, 255, 255);
border-radius: 3px;
box-shadow: rgba(0, 0, 0, 0.3) 0px 2px 6px;
cursor: pointer;
font-size: 1rem;
text-align: center;
color: #0d1f49;
width: 20vw;
margin: 1px;/*Please note that this margin is necessary otherwise browser will open its own context menu*/
}
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAGlM3LLIL2j4Wm-WQ9qUz7I7ZpBsUx1X8">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="map"></div>
<div id="contextMenu" class="contextMenu">
<div onclick="alert('On click of item 1 is called')">
Item 1
</div>
</div>
Go to this demo-purpose website: http://easysublease.org/mapcoverjs/
For context menu, I do not suggest implementing one subclass of the overlayView Class provided by Google Maps API. First, one instance of subclass of overlayView should be added to the five panes provided by Google. More possibly one should add this instance to pane overlayMouseTarget .
But, this instance is "shadowed" by other dom over it. So normal original browser event such mouseover, mouseout cannot reach this instance.
One must use Google Maps API method: addDomListener to handle it(why?). It requires lots of JavaScript code to implement different event handlers, do lots of css class adding and deleting just to realize some visual effects, which could be done using several lines of CSS code if this instance is outside the map container.
So actually converting one external dom outside google map container into one context menu has merit that it can receive original DOM events from browser. Also using some external library can make the target behave better. As context menu, it should not only be able to handle original events, but also those events from Map.
-----------see implementations below------------------------
At the map part HTML, this is the code:
<div id="mapcover">
<div id="mapcover-map"></div> <!-- this is map container-->
<div id="demoControlPanel" class="mc-static2mapcontainer panel">I am map UI control button's container, I think one can use jQuery UI to make me look better<br><br>
<div id="zoom-in-control" class="text-center">zoomIn</div>
<div id="zoom-out-control" class="text-center">zoomOut</div>
</div>
<div id="demoContextPanel" class="mc-ascontextmenu panel">
I am map context menu container, you can sytle me and add logic to me, just as normal DOM nodes.
since I am not in Map Container at all!
<div class="text-center">
<div role="group" aria-label="..." class="btn-group">
<button id="place-marker1" type="button" class="btn btn-default">Marker1</button>
<button id="place-marker2" type="button" class="btn btn-default">Marker2</button>
</div>
</div>
<div class="form-group">
<label for="content-marker1">Content of next Marker1</label>
<input id="content-marker1" type="text" placeholder="New of Marker1!" class="form-control">
</div>
</div>
</div>
It shows how one developer can convert one external DOM (id=demoContextPanel) into one map context menu by just adding one css class ".mc-ascontextmenu"!
That pages uses mapcover.js, which helps developer to manage some key components of Map such as Map control UIs, context menu, and customized markers. Then Developers have full freedom to style its map UIs.
If you need more, you can go to its Github see readme.md: https://github.com/bovetliu/mapcover

Categories

Resources