leafletjs overlay layer got hidden - javascript

I have 3 layers in my map. All layers are imageOverlay type in order to show a big image instead of tile style.
The first 2 layers are the base image which i can switch between (radio) and the third layer is the overlay that should be displayed on top of them if checked (checkbox).
My problem is that when i choose one of the base layers the overlay layer (third layer) got hidden instead of showing on top of it.
this is how i initiate the layers:
var image1Layer = L.imageOverlay(image1Url, imageBounds);
var image2Layer = L.imageOverlay(image2Url, imageBounds);
var image3Layer = L.imageOverlay(image3Url, imageBounds);
L.control.layers({
'New': image2Layer,
'Old': image1Layer
},
{
'Changes': image3Layer
},
{collapsed:false}).addTo(map);

You can check to see if the overlayLayer is present on map, and if it is, bring it to front whenever a base layer is changed.
map.on('baselayerchange', function() {
if (map.hasLayer(image3Layer)) {
image3Layer.bringToFront();
}
});

Related

leaflet layer interrupting interactivity of base layer

I am working on a project based on the Leaflet Choropleth Tutorial.
In my map, I have three base layers that the user switches between
var basemaps= {
"layer1": layer1,
"layer2": layer 2,
"layer3": layer 3
};
Each of these layers highlights and displays popup information just like in the Choropleth template.
I also have layers that display labels(values) for each region, depending which one is active.
var activelabel = L.layerGroup();
var showlabels = {
"labels": activelabel
};
map.on('baselayerchange', function (event) {
activelabel.clearLayers();
switch (event.layer) {
case layer1:
label1.addTo(activelabel);
break;
case layer2:
label2.addTo(activelabel);
break;
case layer3:
label3.addTo(activelabel);
break;
}
});
My problem, is that when the labels are active, if the user hovers over the part of the region where the label is, the highlight feature and info box do not display any information.
I am trying to find a way to make the activelabel layer invisible, so that the hover still works.
I found the tutorial on Map Panes, which seems like the solution to my problem, but it doesn't seem to do anything.
I'm not sure if i'm putting the pane information in the wrong spot, or if this doesn't actually work like I think it should? I've tried changing the xIndex everywhere from 0 to 700 and it doesn't seem to make a difference?
map.createPane('labels');
map.getPane('labels').style.zIndex = 700;
map.getPane('labels').style.pointerevents = 'none';
I've tried putting the pane: 'labels' code here, where I format my labels
var createLabelIcon = function(labelClass,labelText){
return L.divIcon({
className: labelClass,
html: labelText
})
};
as well as here, where I create the layer group.
var label1 = new L.layerGroup();
makelabel1();
Any help would be appreciated. Thank you.
I figured out a solution to this. I have three separate .js files where I add all the layers containing labels to my layergroups.
If I add {interactive: false} to each instance of .addLayer in each file, the labels to not interrupt the mouse hover.
I'm not sure why this doesnt work when I add interactive:false to the layer group as opposed to each addLayer line, but it seems to be working.

Leaflet map with popup above legend

I have a situation where I have a map with a custom legend formatted as either an SVG or a PNG. The legend is always placed in the bottom left corner but can be quite large (user can turn it off and on).
The map also has many markers. Each marker will have a tooltip, which can also be large-ish. Tooltips show when the mouse is hovering over the marker. The problem arises when a user hovers over a marker close to the legend - the tooltip appears behind the legends. I'd like to make it so the popups appear above the legend. So, from bottom to top: marker, legend, marker popup.
Here is a JSFiddle https://jsfiddle.net/e51mydwa/9/ to describe what I mean. I add the legends in the same way, although the < div id="legend"> tag contains a < img> or < svg> in reality.
<div id="map">
<div id="legend">
I am Legend
</div>
</div>
I've had a look at http://leafletjs.com/examples/choropleth/ , but as you can see by inspecting the DOM, this will suffer the same problem, as the legend is added into the same div as the leaflet controls, which is always above the map layers (as it should be, controls should always be at the top).
I've also tried inserting the legend into a div which is on a sibling layer to the popup containing layer. This fixes the z-index issue, however the parent div of both of these contains a transform which changes as the user drags the map around - meaning the legends change places and aren't static.
Any and all suggestions appreciated.
This requires some heavy hacking, due to the architecture of the Leaflet layers and controls.
One possible approach is to make a custom layer class which stays in a static position, by repositioning its pixel offset at every change of the map's view.
I heartily recommend reading the Leaflet tutorials, in particular the one about map panes and the one about custom layers, to understand how this works.
// Create a 'static' map pane
L.Map.addInitHook(function(){
this.createPane('static');
this.getPane('static').style.zIndex = 675;
});
// Define a custom layer class
L.Layer.StaticOverlay = L.Layer.extend({
onAdd: function(map) {
this._map = map;
var pane = map.getPane('static');
this._container = L.DomUtil.create('div');
pane.appendChild(this._container);
// styling, content, etc
this._container.style.background = 'white';
this._container.style.width = '100px';
this._container.style.height = '50px';
this._container.innerHTML = 'Hi!'
map.on('move zoom viewreset zoomend moveend', this._update, this);
this._update();
},
onRemove: function(map) {
L.DomUtil.remove(this._container);
map.off('move zoom viewreset zoomend moveend', this._update, this);
},
_update: function() {
// Calculate the offset of the top-left corner of the map, relative to
// the [0,0] coordinate of the DOM container for the map's main pane
var offset = map.containerPointToLayerPoint([0, 0]);
// Add some offset so our overlay appears more or less in the middle of the map
offset = offset.add([340, 220]);
L.DomUtil.setPosition(this._container, offset);
}
});
When that's defined, you can simply
var static = new L.Layer.StaticOverlay().addTo(map);
Obviously there are some bits missing, such as how to position the overlay properly (get the map pixel size with getSize(), do the proper arithmetic), and how to set the contents of the overlay with some custom options in the layer constructor.
These are left as an exercise to the reader :-)
See a working example here.

How to change polygon pane in Leaflet (custom pane)

I figured out how to create a polygon in a custom pane using the Working with Map Panes tutorial and this Stack Overflow question with its associated Fiddle.
Can I switch the polygon to a different (custom) pane after it has been created?
I create a polygon in a custom pane like so:
// create custom pane
mymap.createPane('polygonView');
var polygonViewPane = mymap.getPane('polygonView');
polygonViewPane.style.zIndex = 300;
// with custom renderer
var myrenderer = L.svg({
pane: polygonViewPane
});
// create polygon in a custom pane (note the `renderer:myrenderer` or `pane:polygonViewPane` both work)
var myPoly = L.polygon([
[51.509, -0.08],
[51.503, -0.06],
[51.51, -0.047]
], {
fillOpacity: 1,
pane: polygonViewPane
}).addTo(mymap);
It would seem like I should be able to switch the pane with something like:
// changes pane in options.pane, but appearance on the map is same
myPoly.setStyle({pane: polygonViewPaneTop});
// also does not work (command fails without output in console?)
myPoly.setStyle({renderer: myrenderer2});
but neither of these are working. I can switch the zIndex of the whole pane with polygonViewPane.style.zIndex = 800;, but I will usually have multiple polygons on that main pane, and I only want to bring one to the front of all of my layers.
https://jsfiddle.net/kbkxf220/
EDIT:
Updated Fiddle incorporating IvanSanchez's answer:
https://jsfiddle.net/kbkxf220/3/
// THIS WORKS; polygon switches pane and `myPoly.options.pane` shows new pane.
myPoly.removeFrom(mymap);
myPoly.setStyle({pane: polygonViewPaneTop, renderer: myrenderer2});
myPoly.addTo(mymap);
Note that you need to switch renderer for the polygon to switch panes on the map view (see inspect element) and switch pane for myPoly.options.pane to properly display the new pane.
No, Leaflet doesn't allow to change panes on the fly.
Remove the layer from the map, change its options, then re-add it.
If your problem is bringing lines/polygons on top of each other, remember that you can call bringToFront() on them.

How to use Leaflet to create a map out of an image

OK, I have found an example of exactly what I think I require. I cannot work out how to set it up for my image and I'm not sure if this is the best approach to achieve my goal.
Intended Result :
http://maps.mixedbredie.net/leaflet/image.html
Example Code:
var map = L.map('map', {maxZoom: 17}).setView([51.495, -0.075], 14);
/* Instead of a tile layer, use a bitmap image */
var imageUrl = 'data/TM_pano.jpg';
var imageBounds = [[51.490, -0.122], [51.510, -0.028]];
L.imageOverlay(imageUrl, imageBounds).addTo(map);
L.marker([51.495, -0.075]).addTo(map)
.bindPopup("This is Table Mountain.");
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("You clicked the map at " + e.latlng.toString())
.openOn(map);
}
map.on('click', onMapClick);
map.setMaxBounds(imageBounds);
Questions:
1.) How to choose the longitude/latitude, image bounds?
i.e. .setView([51.495, -0.075], 14)
var imageBounds = [[51.490, -0.122], [51.510, -0.028]];
2.) Should zoom 0 show my whole image? What are reasonable limits?
3.) Can I use real coordinates with this approach? i.e. Mapping a water pipe underground but using a custom .jpg of that pipe schematic? Then I could put markers on the pipe for certain long/lats.
See http://omarriott.com/aux/leaflet-js-non-geographical-imagery/ for question 1 and 2
For the third one, you could georeference (or warp) the image, and then display it onto a map, setting the transparency for example.
The georeferencing could be done with Qgis or a tool like Mapwarper https://github.com/timwaters/mapwarper
(old question, but I think an answer could be useful)

How to find lat/lon location that is closest to mouse cursor on the google map

I have many markers.
On the left I have list of marker-abstractions of actual markers on the google map.
I decided to use polylines
var flightPlanCoordinates = [
new google.maps.LatLng(near_cursor_lat, near_cursor_lon),
new google.maps.LatLng(marker_lat, marker_lon)
];
var flightPath = new google.maps.Polyline({
path: flightPlanCoordinates,
strokeColor: "#FF0000",
strokeOpacity: 1.0,
strokeWeight: 2
});
flightPath.setMap(map);
the idea is that user points his mouse on the marker-abstraction box on the left, and then he shown the line which goes from marker-abstraction to actual marker on the map. I already have everything except of the STARTING POINT(red dots on the image)
How do I find starting lat/long which is located next to the mouse pointer, when the mouse is pointed at the box on the left
Some details:
When the mouse is pointed to the box that sais PERSON1, I want the coordinates of the first red dot. When the mouse is pointed to the box that sais PERSON2, I want the coordinates of the second red dot. And so on. There is a trick part- left boxes are located outside the google maps div; in addition, if there are many boxes with PERSONS, the left div will allow to scroll those persons up and down, so the vertical correlation between the persons box, and the red dot is dynamic.
In theory, I think, I need an event that is triggered when I point to one of the boxes. When even is fired, I need to measure the vertical distance in pixels from the top to the mouse pointer. Then, when I have the vertical distance, I need to perform some action that would measure same vertical distance on the google map, and would get me that point on the map in lat/lon coordinates.
This should be the answer to your question :
You should be using markers to represent (persons) and then add a listener onMouseOver like in the below post :
var overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(map); // 'map' is new google.maps.Map(...)
Use overlay in the listener to get the projection and the pixel coordinates:
google.maps.event.addListener(marker, 'mouseover', function() {
var projection = overlay.getProjection();
var pixel = projection.fromLatLngToContainerPixel(marker.getPosition());
// use pixel.x, pixel.y ... (after some rounding)
});
copied from :
Get Position of Mouse Cursor on Mouseover of Google Maps V3 API Marker
#MehdiKaramosly answer is pretty spot on.
Only slightly furthering the above, to get what would be the lat lng of an element that is not part of the visible map (if the map was visible there) you can pass the events page X/Y to the line:
projection.fromLatLngToContainerPixel(marker.getPosition());
like so:
var pixelLatLng = overlay.getProjection().fromContainerPixelToLatLng(new google.maps.Point(e.pageX,e.pageY));
I have put a very basic example at: http://tinkerbin.com/p30yEXqH
If you click anywhere outside of the map (which equates to the same as clicking on a div that overlays it), you will see in the console log that the lat/lng for wherever you have clicked (even though it is not in the visible map space), is logged.
Cheers,
C.
Update with Working Example of this code
Second Update with JsFiddle Example of both ways click and open map window or mouseover map to highlight listing
As i understand the problem you are having is as following:
Persons are listed in the div separate from map
Persons have latitude and longitude associated to them on the map
Map displays location of person with markers on it using lat long (might be stored in db or something)
You want it so people can highlight a person on map as well as list with mouse over.
If you have lat/long available on map or from list you need to relate them together.
Solution is something like this but there are many ways to achieve this mapping
In your div where person is listed. Insert a data-mapid attribute to each person element when a person hovers over it you highlight it and get the data-mapid from there.
On your map when you render a marker you can additionally pass a parameter data-mapid or something else with same value and have a highlight function on map as well.
`jQuery("#gmap3ul li[data-gb='plist']").each(function(){
elemtopush = {
lat:jQuery(this).attr("data-lat"),
lng:jQuery(this).attr("data-long"),
data:{
"ht":jQuery(this).html(),
"id":jQuery(this).attr("data-mapid")
}
};
ulmarkerspgb.push(elemtopush);
});`
In above code i have html to show on map as well as id as data-mapid now this mapid can be person1 person2 and so on so you can relate back to div with lists. i am using ul and li to list them in the div.
On mouse over your markers events you can do something like this
mouseover: function(marker, event, data)
{
clearalllist();
var listelement = jQuery("td[data-mapid='"+data.id+"']");
jQuery(listelement).attr('style','background-color:#ccc');
var map = jQuery(this).gmap3('get'),
infowindow = jQuery(this).gmap3({action:'get', name:'infowindow'});
if (infowindow){
infowindow.open(map, marker);
infowindow.setContent(data.ht);
google.maps.event.addListener(infowindow, 'closeclick', function(event) {
jQuery(listelement).attr('style','');
});
google.maps.event.addListener(infowindow, 'close', function(event) {
jQuery(listelement).attr('style','');
});
} else {
jQuery(this).gmap3({action:'addinfowindow', anchor:marker, options:{content: data.ht}});
infowindow = jQuery(this).gmap3({action:'get', name:'infowindow'});
google.maps.event.addListener(infowindow, 'closeclick', function(event) {
jQuery(listelement).attr('style','');
});
google.maps.event.addListener(infowindow, 'close', function(event) {
jQuery(listelement).attr('style','');
});
}
}
There is a lot of redundant code i have copied pasted from 3 locations so you might just be able to get what you want out of it.
This can make both map linked to list and list linked to map. See my second update on top or click this JSFiddle example. When you click on list on top it opens the map window and when you mouse over the map icons it highlights the listing.
This also populates the map via list rather than hard coding lat longs in js.

Categories

Resources