Add text box on leaflet map with selected overlay layer name - javascript

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.

Related

Leaflet MarkerCluster: Is it possible to open multiple cluster-groups?

I have recently started working with leaflet. I found the great plugin leaflet markercluster. I am currently trying to open several clustergroups at the same time. Unfortunately I can't find anything on google.
I have several cluster groups and draw connections from one marker to another. The user should be able to open both cluster groups to which the drawn line goes:
Therefore my question: Is there a function for this that I have to switch on or is opening several groups at the same time not provided at all?
Okay I have experimented a little bit on it now ;)
In the leaflet.markercluster-src.js I created an array called _spiderMan[] which is filled with the clicked objects in the function spiderfy.
spiderfy: function() {
if (this._group._spiderfied === this || this._group._inZoomAnimation) {
return;
}
var childMarkers = this.getAllChildMarkers(null, true),
group = this._group,
map = group._map,
center = map.latLngToLayerPoint(this._latlng),
positions;
// this._group._unspiderfy(); //deactivated
var markers = markers + childMarkers;
_spiderMan.push(this); //new
if (childMarkers.length >= this._circleSpiralSwitchover) {
positions = this._generatePointsSpiral(childMarkers.length, center);
} else {
center.y += 10;
positions = this._generatePointsCircle(childMarkers.length, center);
}
this._animationSpiderfy(childMarkers, positions);
},
Then I have created a for loop which runs through the array and calls _spiderMan[i].unspiderfy(zoomDetails) every time. I built this loop into the function _unspiderfyZoomAnim for testing. Means every time you zoom in or out, all open groups are summarized.
_unspiderfyZoomAnim: function(zoomDetails) {
if (L.DomUtil.hasClass(this._map._mapPane, 'leaflet-touching')) {
return;
}
this._map.off('zoomanim', this._unspiderfyZoomAnim, this);
var i;
for (i = 0; i < _spiderMan.length; i++) {
_spiderMan[i].unspiderfy(zoomDetails);
}
_spiderMan = [];
},
In addition, the following lines must be deactivated in the unspiderfy function:
unspiderfy: function(zoomDetails) {
/// <param Name="zoomDetails">Argument from zoomanim if being called in a zoom animation or null otherwise</param>
// if (this._group._inZoomAnimation) {
// return;
// }
this._animationUnspiderfy(zoomDetails);
// this._group._spiderfied = null;
},
So it's now possible to open and close mutiple cluster-groups but this is only a workaround and I think it will have some bad effects somewhere because of commenting out or removing code lines.
I think someone with more experience in JS and this plugin should find a better and more comfortable solution ;).
Welcome to SO!
Unfortunately, the spiderfication management in Leaflet.markercluster plugin currently assumes a single cluster can be spiderfied at a time.
See also danzel's comment in Leaflet.markercluster issue #744 (Spiderfy all clusters at a particular view):
Leaflet.MarkerCluster only supports having one cluster spiderfied at the moment, so this would need a bit of work to support.
May be you will get a better answer if you give a use case ...
However, it is safe to say that there is no function you can switch on to open several groups in one click.
From a usability point of view, it does not make much sense as the basic behaviour of MarkerCluster is to click on one icon to zoom in and expand the group you are interested in.
Quoting from https://github.com/Leaflet/Leaflet.markercluster#other-clusters-methods :
spiderfy: Spiderfies the child markers of this cluster
unspiderfy: Unspiderfies a cluster (opposite of spiderfy)
So once you have references to the clusters you want to "open" (spiderify) at the same time, just call their .spiderify() method.
e.g. if the desired clusters are in variables cluster1 and cluster2:
cluster1.spiderify();
cluster2.spiderify();
See also https://github.com/Leaflet/Leaflet.markercluster#getting-the-visible-parent-of-a-marker and https://github.com/Leaflet/Leaflet.markercluster#clusters-methods about how to get references to the clusters.
As far as I can tell you can keep open multiple clusters, but only one for each group. My guess is that your markers all belong to a single group. In which case you can't keep open multiple clusters.
You could opt for a hover approach, which opens a cluster if you hover over it.
const mymap = L.map('mapid').setView([48.550, 8.207], 6);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
id: 'mapbox.streets'
}).addTo(mymap);
const markers = L.markerClusterGroup({zoomToBoundsOnClick: false});
[[47.5617, 7.5504], [47.5255, 7.6163], [47.5691, 7.6355],
[49.4922, 8.3922], [49.5306, 8.5172], [49.4547, 8.5062]]
.map(latLng => L.marker(latLng))
.forEach(marker => markers.addLayer(marker));
mymap.addLayer(markers);
markers.on("clustermouseover", a => a.layer.spiderfy());
markers.on("clustermouseout", a => a.layer.unspiderfy());
html, body, #mapid { margin: auto; height: 100%; }
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.5.1/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet#1.5.1/dist/leaflet.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster#1.4.1/dist/MarkerCluster.Default.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster#1.4.1/dist/MarkerCluster.css" />
<script src="https://unpkg.com/leaflet.markercluster#1.4.1/dist/leaflet.markercluster.js"></script>
<div id="mapid"></div>

How to use Polyline in cesium version 1.52.0 to connect points with Rhumb lines?

I am currently using Cesium version 1.52.0 and cannot upgrade at this time. But my goal is to show the polyline in rhumb format on the map . In version 1.54.0, arcType: Cesium.ArcType.RHUMB is the property to get rhumb lines.
How can I get the same result with version 1.52.0?
var viewer = new Cesium.Viewer("cesiumContainer" );
viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArrayHeights([-112.3268785,29.1673139,1000,139.1652182,9.6235915,1000,11.4423255,50.0235941,1000,-41.2873034,-5.1132736,1000]),
width: 8,
arcType: Cesium.ArcType.RHUMB,
material: new Cesium.PolylineOutlineMaterialProperty({
color: Cesium.Color.YELLOW,
outlineWidth: 4,
outlineColor: Cesium.Color.BLACK
})
}
});
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
font-family: sans-serif;
}
<link href="http://cesiumjs.org/releases/1.54/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"/>
<script src="http://cesiumjs.org/releases/1.54/Build/Cesium/Cesium.js">
</script>
<div id="cesiumContainer"></div>
This would be possible in version 1.52 via Turf.js - specifically the greatCircle function.
You simply would need to call this function for each segment of your polyline - passing in the start and end coordinates.
Each function call would give you the GeoJSON for a particular segment - chain this GeoJSON together (there are some more helpers that Turf provides for this kind of operation) and voila - you're now ready to upload it into Cesium via a Cesium.GeoJsonDataSource.

Dojo/Dijit TooltipDialog.open: How to override offsetHeight and offsetTop values

Currently trying to implement/solve an issue with an ESRI ArcGIS map environment where I have a multi-layered map with custom graphics being rendered in each layer. Some of the graphics are simple shapes such as lines and circles, but a majority of the graphics are icons (.png) files that are being drawn on the layers. (All of this is being done in JavaScript.)
I have been able to get all of the layers generated correctly - the data IS NOT being stored in ArcGIS maps but a custom designed Contact & Location database (SQL) and other forms within the web application maintain this C&L data.
The graphic icons that are rendered on the map need to have a mouse-over tooltip popup appear with information that has been stored with the icon when it is created where the .substitute() command will update the template. The information displayed is HTML formatted in a <div>.
Problem:
When the mouse is moved over an icon, the tooltipDialog appears but 1) it always appears in the lower right corner of the screen - despite "orient:" and specific "x:" and "y:" coordinates being specified. Additionally when the tooltipDialog.open() command is executed, the dialog's offsetHeight is set to 624 and the offsetTop is set to 502. (The offsetWidth is actually set to the correct value.) How do I override either/both the offsetHeight/offsetTop?
I have tried specifying additional parameters to the tooltipDialog.open() command but nothing tried so far has altered the outcome. Even when I change the template content to be as simple as "Hi There!" does not change the outcome.
Note: If I click on an icon the IconWindow dialog will popup with the proper content and formatting being displayed. So it leads me to believe that the issue is within CSS or some other aspect of dojo/dijit as the tooltipDialog.open() command is actually where the offset changes are being made - the values are retained (offsetTop=0 offsetHeight=0) prior to the open() call.
Ideas/Recommendations?
You could try to use the dijit/popup module to open the TooltipDialog, which would allow you to pass in the DOM node around which the tooltip should be opened:
popup.open({
popup: myTooltipDialog,
around: dom.byId('thenode')
});
There a full example of this here (next to "A TooltipDialog may be popped up from any node.")
Well, It seems like you want to show info popup with some offset value whenever you hover on any feature on the map.
Solution-
Well to do so i don't think you need to deal with TooltipDialog because whenever you are loading feature or feature layer on the map you can attach info popup to it. It will take care of entire loading and displaying info popup Dialog along with its positioning.
To pass offset value-
If want to pass some offset value to popup dialog you can use below mentioned properties:-
For more Properties of popup dialog refer this link- https://developers.arcgis.com/javascript/3/jsapi/popup.html
Hovering Dialog sample-
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
<title>Feature Layer - display results as an InfoWindow onHover</title>
<link rel="stylesheet" href="https://js.arcgis.com/3.18/dijit/themes/tundra/tundra.css">
<link rel="stylesheet" href="https://js.arcgis.com/3.18/esri/css/esri.css">
<style>
html, body, #mapDiv {
padding:0;
margin:0;
height:100%;
}
#mapDiv {
position: relative;
}
#info {
background: #fff;
box-shadow: 0 0 5px #888;
left: 1em;
padding: 0.5em;
position: absolute;
top: 1em;
z-index: 40;
}
</style>
<script src="https://js.arcgis.com/3.18/"></script>
<script>
var map, dialog;
require([
"esri/map", "esri/layers/FeatureLayer",
"esri/symbols/SimpleFillSymbol", "esri/symbols/SimpleLineSymbol",
"esri/renderers/SimpleRenderer", "esri/graphic", "esri/lang",
"esri/Color", "dojo/number", "dojo/dom-style",
"dijit/TooltipDialog", "dijit/popup", "dojo/domReady!"
], function(
Map, FeatureLayer,
SimpleFillSymbol, SimpleLineSymbol,
SimpleRenderer, Graphic, esriLang,
Color, number, domStyle,
TooltipDialog, dijitPopup
) {
map = new Map("mapDiv", {
basemap: "streets",
center: [-80.94, 33.646],
zoom: 8,
slider: false
});
var southCarolinaCounties = new FeatureLayer("https://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/3", {
mode: FeatureLayer.MODE_SNAPSHOT,
outFields: ["NAME", "POP2000", "POP2007", "POP00_SQMI", "POP07_SQMI"]
});
southCarolinaCounties.setDefinitionExpression("STATE_NAME = 'South Carolina'");
var symbol = new SimpleFillSymbol(
SimpleFillSymbol.STYLE_SOLID,
new SimpleLineSymbol(
SimpleLineSymbol.STYLE_SOLID,
new Color([255,255,255,0.35]),
1
),
new Color([125,125,125,0.35])
);
southCarolinaCounties.setRenderer(new SimpleRenderer(symbol));
map.addLayer(southCarolinaCounties);
map.infoWindow.resize(245,125);
dialog = new TooltipDialog({
id: "tooltipDialog",
style: "position: absolute; width: 250px; font: normal normal normal 10pt Helvetica;z-index:100"
});
dialog.startup();
var highlightSymbol = new SimpleFillSymbol(
SimpleFillSymbol.STYLE_SOLID,
new SimpleLineSymbol(
SimpleLineSymbol.STYLE_SOLID,
new Color([255,0,0]), 3
),
new Color([125,125,125,0.35])
);
//close the dialog when the mouse leaves the highlight graphic
map.on("load", function(){
map.graphics.enableMouseEvents();
map.graphics.on("mouse-out", closeDialog);
});
//listen for when the onMouseOver event fires on the countiesGraphicsLayer
//when fired, create a new graphic with the geometry from the event.graphic and add it to the maps graphics layer
southCarolinaCounties.on("mouse-over", function(evt){
var t = "<b>${NAME}</b><hr><b>2000 Population: </b>${POP2000:NumberFormat}<br>"
+ "<b>2000 Population per Sq. Mi.: </b>${POP00_SQMI:NumberFormat}<br>"
+ "<b>2007 Population: </b>${POP2007:NumberFormat}<br>"
+ "<b>2007 Population per Sq. Mi.: </b>${POP07_SQMI:NumberFormat}";
var content = esriLang.substitute(evt.graphic.attributes,t);
var highlightGraphic = new Graphic(evt.graphic.geometry,highlightSymbol);
map.graphics.add(highlightGraphic);
dialog.setContent(content);
domStyle.set(dialog.domNode, "opacity", 0.85);
dijitPopup.open({
popup: dialog,
x: evt.pageX,
y: evt.pageY
});
});
function closeDialog() {
map.graphics.clear();
dijitPopup.close(dialog);
}
});
</script>
</head>
<body class="tundra">
<div id="mapDiv">
<div id="info">
Hover over a county in South Carolina to get more information.
</div>
</div>
</body>
</html>
Hoping this will help you :)
However its always recommended to add your code here if you are looking for exact Fix.
Found the answer to my situation. There is what appears to be an unstated requirement that either one of the supplied CSS dijit/themes be utilized OR the user must create their own theme which has some CSS that configures the display location.
The offsetTop issue was resolved by using eliminating any style references to top:.

javascript map in leaflet how to refresh

i have a basic geoJson program in javascript by using leaflet API.
<html>
<head>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.js"></script>
<script src="india.js" type="text/javascript"></script>
</head>
<body>
<div id = "map1" style="width: 1100px; height: 400px"> </div>
<script>
var area = L.map('map1', {center: [27.8800,78.0800], zoom: 4 });
L.tileLayer('http://a.tiles.mapbox.com/v3/raj333.map-gugr5h08/{z}/{x}/{y}.png').addTo(area);
var indiaLayer= L.geoJson(india, {style: {weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.1}});
area.addLayer(indiaLayer);
function clicked(){
this.options.style.fillOpacity = 0.8;
//how to refresh layer in the given map
}
indiaLayer.on('click', clicked);
</script>
</body>
</html>
the problem is how would i automatically refresh the content of Layer on the map.
example here
function clicked(){
indiaLayer.style.fillOpacity = 0.8;
//how to refresh layer in the given map
}
indiaLayer.on('click', clicked);
when user click on indiaLayer , the fillOpacity variable changes but doesn't reflect back on map which is understood since i am not refreshing the map. I don't know how to do it.
please help
P/s: these are the functions available on indiaLayer object (i.e. this object inside clicked function...which one to use for this purpose or there exist none)
You can check the list of methods available of GEOJson in the Leaflef documentation This is the link to v.0.7.7, which is the closest available to used in this example.
Last time I've used
map._onResize();
and that help me refresh map. Maybe a little hack, but, it work.
In your code will be area._onResize()
P.S: Maybe you should try change the way to set new opacity value - try change
function clicked(){
this.options.style.fillOpacity = 0.8;
}
to that
function clicked(){
this.setStyle({fillOpacity: 0.2});
}
map.invalidateSize();
map._onResize() - it's a hack, and there is no guarantee that it won't be removed in future versions.
area.fitBounds(area.getBounds());

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