I am creating an interactive map:
https://jsfiddle.net/71hns9b3/2/
that uses an image (No Google API etc.) to mark points. With a Google API you use lat, long coordinates so you don't have to worry when you resize the map. Obviously this can't be done without Google API.
#termini {
top: 37.5%;
left: 61.8%;
}
These two images indicate what I mean:
http://s10.postimg.org/6m4oufudl/Screen_Shot_2016_02_22_at_12_45_51.png
Just add position property relative to your container div, and also you need to declare width and height of the pin in %
-Best what can be done in this example
Example
Related
It appears to me that Leaflet.ImageOverlay does not have a minZoom and a maxZoom option like Leaflet.TileLayer has. But I have a map image that I only want to be visible at two or three zoom leves. Is there a cunning way to circumvent this issue?
I guess, I can use gdal2tiles.py or gdal_retile.py to create a file for a tile layer, but as I'm using UTM projection (Mercator gives a horrible distortion in Norway), I'm not sure what tile numbers makes my map image fit in with the background map.
Use the zoomend event to conditionally hide/show your L.ImageOverlay.
I have tried this couple of ways but have not been able to get it working. I want to place clocks at the top of the map within multiple timezones. I have a the javascript to create the clock and I place the clock in a div element.
Here is what I tried:
Create a Point with 0,0 coordinates.
From this point get the latitude value for the top of the map using containerPointToLatLng.
Create LatLng using the above lat and long for the timezone.
Converted this LatLng to Point and then positioning the div element with the x,y from this point.
I execute the logic both when the page is first rendered and then on body resize. However, if I change the size of the browser window, the clock does not position correctly.
Any suggestions?
To answer OP's precise issue, i.e. how to re-position the clock when the browser window is resized (hence map container dimensions may have changed), one should probably just re-compute the clock position on map's "resize" event.
However, it is not clear whether OP placed the clock as a child of the map container, or somewhere else on the page DOM tree.
It is probably much easier to place it as a child of the map, so that its position is always relative to the map container.
What OP originally asked?
If I understand correctly the original desired result, the OP would like to overlay a clock (or whatever information) on top of a particular geographical position (Toronto city in that case [UTC -5], according to comments).
However, the information container should not lay at a basemap fixed position, i.e. not at a precise geographic coordinated point (like a marker or a popup would), but at the top of the map container, similarly to a Control (hence iH8's original answer).
Except that it should not be totally fixed within the map container, but horizontally move with the city (or whatever specified geographical coordinates). Hence OP's comment to iH8's answer.
Therefore it sounds like something similar to that site, except with an interactive (navigate-able) map and the "UTC-5" header replaced by a clock (or whatever information, hence an HTML container should do it) and horizontally following Toronto.
Put differently, the clock should sit at a particular vertical line, i.e. longitude / meridian.
Unfortunately, even 2 years and a half after the question is posted, there is still no Leaflet plugin that provides such functionality (at least within Leaflet plugins page).
Extending the use case to highly zoomed-in map…
That being said, and given the fact that the user may be able to zoom highly into the city (OP did not specify the maximum zoom level), it might not be a very good user experience having that clock horizontally follow a precise longitude: for example, it could track Toronto centroid / city hall / whatever particular place, and when user is zoomed-in at another city district, the clock is no longer visible, whereas he/she is still viewing a part of Toronto city…
To extend that use case, the clock should very probably be visible in whatever area it applies, i.e. as soon as the map view port intersects the associated time zone.
Extending the use case to highly zoomed-out map…
Another point not detailed by OP, is what to do when places of different time zones are visible in the map view port? In the above mentioned site, we have one header per visible time zone, which seems the most complete information we can get.
But since Leaflet allows to zoom out down to level 0, where the entire world (i.e. essentially 24 time zones / actually 39 according to Wikipedia, not including potential effect of Daylight Saving Time - DST) is represented with a 256 pixels width, there is little room to fit all these clocks, if each one must be vertically aligned with its associated time zone.
For now let's assume we do not care if clocks overlap.
Even more custom case…
But OP may have wished to display the clock only for particular places, not for the entire world. OP did not even say that clocks would be different (we could have clocks for cities in the same time zone, even though it could be more interesting to have these clocks sit next to their city - even on par with their latitude, so that it is easier to spot which city the clock is associated to, like in the case of 2 cities on the same meridian; but in that case, a marker with L.divIcon would be enough).
Hence a custom case would be not to consider official time zones, but developer's specified areas.
So we forget about the latitude and try to align a clock vertically above the area, as long as it intersects the map view port.
Describing a generic solution
Therefore it sounds like a generic solution would be to enable the application developer to specify an array of HTML Elements, each one associated with a range of longitudes (could also be an area / polygon).
The time zones use case would then be a particular case where the specified areas are simply those from the time zones.
Then, each Element should be visible if and only if its associated area intersects the view port (therefore we introduce a possibility to hide it when the latitude range is out of view).
As for positioning, let's choose:
By the top of the map container (similar to a Control), as mentioned by OP.
Horizontally centered within the intersection of the view port and of the associated area.
HTML:
<div id="map"></div>
<div id="clockToronto" class="clock leaflet-control">Clock here for Toronto</div>
<div id="clockBurlington" class="clock leaflet-control">Clock here for Burlington</div>
CSS:
.clock {
width: 150px;
text-align: center;
position: absolute;
border: 1px solid black;
}
#clockToronto {
background-color: yellow;
}
#clockBurlington {
background-color: orange;
}
JavaScript:
var map = L.map("map").setView([43.7, -79.4], 10);
// Application specific.
var clockTorontoElement = L.DomUtil.get("clockToronto"),
clockBurlingtonElement = L.DomUtil.get("clockBurlington"),
zones = [
{
element: clockTorontoElement, // Using the HTML Element for now.
width: parseInt(window.getComputedStyle(clockTorontoElement, null).getPropertyValue("width"), 10),
area: L.latLngBounds([43.58, -79.64], [43.86, -79.10]) // Using L.latLngBounds for now.
},
{
element: clockBurlingtonElement, // Using the HTML Element for now.
width: parseInt(window.getComputedStyle(clockBurlingtonElement, null).getPropertyValue("width"), 10),
area: L.latLngBounds([43.28, -79.96], [43.48, -79.71]) // Using L.latLngBounds for now.
}
];
// Initialization
var controlsContainer = map._container.getElementsByClassName("leaflet-control-container")[0],
firstCorner = controlsContainer.firstChild,
mapContainerWidth;
map.on("resize", setMapContainerWidth);
setMapContainerWidth();
// Applying the zones.
for (var i = 0; i < zones.length; i += 1) {
setZone(zones[i]);
}
function setZone(zoneData) {
// Visualize the area.
L.rectangle(zoneData.area).addTo(map);
console.log("width: " + zoneData.width);
controlsContainer.insertBefore(zoneData.element, firstCorner);
map.on("move resize", function () {
updateZone(zoneData);
});
updateZone(zoneData);
}
function updateZone(zoneData) {
var mapBounds = map.getBounds(),
zoneArea = zoneData.area,
style = zoneData.element.style;
if (mapBounds.intersects(zoneArea)) {
style.display = "block";
var hcenterLng = getIntersectionHorizontalCenter(mapBounds, zoneArea),
hcenter = isNaN(hcenterLng) ? 0 : map.latLngToContainerPoint([0, hcenterLng]).x;
// Update Element position.
// Could be refined to keep the entire Element visible, rather than cropping it.
style.left = (hcenter - (zoneData.width / 2)) + "px";
} else {
style.display = "none";
}
}
function getIntersectionHorizontalCenter(bounds1, bounds2) {
var west1 = bounds1.getWest(),
west2 = bounds2.getWest(),
westIn = west1 < west2 ? west2 : west1,
east1 = bounds1.getEast(),
east2 = bounds2.getEast(),
eastIn = east1 < east2 ? east1 : east2;
return (westIn + eastIn) / 2;
}
function setMapContainerWidth() {
mapContainerWidth = map.getSize().x;
}
Live demo: http://plnkr.co/edit/V2pvcva5S9OZ2N7LlI8r?p=preview
Usually one would use L.Control to create a custom control which you can then add to the control layer. If you do so, leaflet will take care of positioning when resizing the map. Take a look at the reference for L.Control: http://leafletjs.com/reference.html#control
There is an example of a custom control in the reference: http://leafletjs.com/reference.html#icontrol If you would like to see more examples you could check out one of the many custom control plugins to see how they implemented L.Control: http://leafletjs.com/plugins.html (under Controls and interaction)
The only drawback of L.Control is that you can't position a control vertically or horizontally centered. You may only use topleft, topright, bottomleft & bottomright.
This question is directed to Leaflet users (and those who use the Leaflet.draw plugin)...
I'm using Leaflet and would like to allow my user to draw 1--and only 1--single polygon over any area of the map. I would also like to limit the size of that polygon in some way (such as limiting the length of the side for a square or the area covered it covers--preferably specified in degrees so that the set size limits would translate regardless of the zoom level).
My end goal is simply to extract the coordinates of the 4 square vertices or the coordinates covered by the polygon area.
That said, I found the Leaflet.Draw plugin. It is fantastic, however, I need to limit its functionality to my requirements (only 1 polygon drawn at a time and, in particular, the size cannot be drawn too large). Is this possible to do? If so, how?
Regardless of if it is or is not possible, is there a better way to go about doing this?
Can I propose another solution to this issue?
I would limit the number of polygons to one by doing the following:
map.on('draw:created', function (e) {
var layer = e.layer;
if(drawnItems && drawnItems.getLayers().length!==0){
drawnItems.clearLayers();
}
drawnItems.addLayer(layer);
});
I am listening to the draw:created event and determine if there is already a marker. If there is, I remove that marker and place my new one in the desired location. Therefore, one less click for user as they no longer need to delete the previous and one marker rule is always enforced.
If you wanted to allow more than one marker you could do a FIFO delete of the oldest layer.
If you do not want to automatically delete a layer, you could either prompt the user or ignore the request.
That said, I found the Leaflet.Draw plugin. It is fantastic, however, I need to limit its functionality to my requirements (only 1 polygon drawn at a time and, in particular, the size cannot be drawn too large). Is this possible to do? If so, how?
I think you'll need to code it yourself.
I see two possibities:
hacking the draw plugin (writing your own code inside the plugin)
extending the L.Draw.Polygon class from the draw plugin (see the docs about OOP in Leaflet) to create a costum one
1 is faster, 2 is cleaner. You'll have to choose depending on the size of your project.
I did it without hacking the Leaflet Draw source.
After the controls are added to the map, I place a hidden div inside the controls. Then when a polygon is created I display that div. I used CSS to absolute position it over the controls so the buttons are then "disabled" and CSS to make the buttons look faded. If the polygon is deleted then I hide that div.
Not the best solution, but I works without having to edit the source.
After drawControl is added, I add the hidden div:
$('.leaflet-draw-section:first').append('<div class="leaflet-draw-inner-toolbar" title="Polygon already added"></div>');
Here's the JS to toggle them:
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
// keep the polygon on the map
drawnItems.addLayer(layer);
// disable the create polygon tools
$('.leaflet-draw-inner-toolbar').show();
});
map.on('draw:deleted', function(e) {
// enable the create polygon tools
$('.leaflet-draw-inner-toolbar').hide();
});
Here's the CSS:
.leaflet-draw-inner-toolbar {
background: none repeat scroll 0 0 rgba(255, 255, 255, 0.6);
bottom: 0;
display: none;
left: 0;
position: absolute;
right: 0;
top: 0;
}
I have a very busy Google Maps app that I have built and I'm trying to create a "buffer zone" around the outside edge of the map so that the google map commands won't put things there. My solution was to create invisible divs and add them to the map as controls, one for each of the edges. This seems to work great as all of the google commands see them and adjust accordingly, and the map appears normally. For example, fitBounds ensures my bounds is not under the invisible layers. For the top where I have a control bar it's a perfect solution, but for the other edges where there is nothing, it creates a problem - I can't click on the map or info windows under these controls as they take the click event.
So I'm looking for one of 2 solutions:
1) can I make my invisible controls pass clicks through to the map, or;
2) is there a better way to pad the edge of the map; something that doesn't involve a much of math every time I want to call a fitBounds or panTo would be preferred as I automate a lot of map motion
Cheers
I managed to solve this.
The best way to add padding to your maps is with invisible controls. It creates the padding that all other map functions obey without any additional coding when call them. Here is how to do it for everyone else who needs this.
First.. I create a function to simplify creating the divs.
function createDummyDiv(w, h){
var out = $(document.createElement('div')).addClass('dummy-div').css('width', w).css('height', h);
return out[0];
}
Then I add the controls as needed. In this case I have the normal zoom control in the LEFT_CENTER position, so I had to create 2 for the left side. This creates a 10% padding on the left, right and bottom, and a 55px padding at the top under my own control bar.
map.controls[google.maps.ControlPosition.TOP_CENTER].push(createDummyDiv('100%', '55px'));
map.controls[google.maps.ControlPosition.LEFT_TOP].push(createDummyDiv('10%', '45%'));
map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(createDummyDiv('10%', '45%'));
map.controls[google.maps.ControlPosition.RIGHT_CENTER].push(createDummyDiv('10%', '100%'));
map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(createDummyDiv('100%', '10%'));
The.. the final fix to my problem is to put them behind the map layer with css.
.dummy-div{ z-index: -100 !important; }
I hope this helps someone else
Try to give the invisible DIV a negative z-index, e.g. -10
For me, fitBounds api worked itself with second parameter (padding)
Add padding to google map bounds
I have a map set to 100% of the page width. The map has one marker and is centered on that marker. When I print the browser, I want the map to stay centered on the marker. This is the code I wrote to do so:
var lastPos = map.getCenter();
google.maps.event.addListener(map, "idle", function() {
lastPos = map.getCenter();
console.log(lastPos.toString());
});
google.maps.event.addDomListener(window, "resize", function() {
google.maps.event.trigger(map, "resize");
map.setCenter(lastPos);
console.log("Re-center on " + lastPos.toString());
});
This works when I re-size my browser, but does not work when the browser re-sizes itself before printing. If my browser is above a certain width then the marker is shifted entirely off the page (to the right) when the map is printed.
Here is my test case: http://www-sf.talispoint.com/testmapprint.html
You would need to add a #media print and give the map the size when printing and then you can do what is explained below.
When a map is printed what happens is that the top left corner is kept and the map is adjusted to fit the #media print size.
If you want the center to stay the same you need to manually change the center of the map.
http://jsbin.com/owiwox/33 is an example on how to work around this.
It uses a listener for the print media being applied event and adjusts the center of the map using a ratio on how the map is changed (made smaller)
One thing that you have to take care of is that you might have to make this browser targeted to make it work on all browsers (This solution works well in Chrome)
A good resource for making it work across browsers is:
http://tjvantoll.com/2012/06/15/detecting-print-requests-with-javascript/
The js code from the sample above listens to print requests and shifts the map so that the top left corner has the same center as the big map.
To cut the whole story short, this is how it works
You need to put in the ratio of the map vs printed map (or get the size by checking it from JS)
You assign it to:
var widthRatio = 2;
var heightRatio = 3;
You listen for print media being applied and shift the center so that it does not change
After you finish print , you revert the change.
Still here you have the problem that a part of the map will be cut, but there is not a good solution here, since the zoom level -1 tiles might not be cached so when you zoom out to fit bounds you might get no tiles.
It seems the problem with your 'printing' or 'printer'.
I did a test:
load the test map and make the browser very wide
print preview and saw the problem you described
But: I can change the printing scale from 'Shrint to fit' (default for IE and FF) to say 30% and was able to print the map as seen on the screen.
Another thought is:
You may try to use another CSS for print to limit the map div width, but I am not sure if that will trigger the resize of the map first (you may refer to this post: Javascript Event Handler for Print)