I have a leafletmap that only renders into one corner. When I resize the browser window, the whole map renders.
That is how I invoce the map
var map = L.map('map',
{ crs: L.CRS.EPSG3857 }
).setView([105.2, 100], 6);
var tiles = L.tileLayer('http://stamen-tiles-{s}.a.ssl.fastly.net/toner/{z}/{x}/{y}.{ext}', {
attribution: 'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap',
subdomains: 'abcd',
ext: 'png'
}).addTo(map);
And this is my css
.myContainer {
width: 1024px;
height: 800px;
position: relative;
}
.map {
width: 1024px;
height: 800px;
z-index: 0;
}
The map is wrapped in a container. Everything I found online regarding this problem points to relative sizes in the map- or its parent container. However, I have absolute sizes. I load the .css before the .js (where I initialize the map). The map will be displayed not on the startpage, but when you click on a submenue point.
Why is the map not rendered properly?
The map will be displayed not on the startpage, but when you click on a submenue point.
You're initializing the map on a hidden or zero-size container, which makes this question a duplicate of 2nd leaflet map not rendering correctly
The answer is the same: run map.invalidateSize() whenever the map should become visible, or when you change its dimensions or visibility.
As IvanSanchez stated, map.invalidateSize() is indeed the way to go.
A useful code example for map refresh every 2 seconds in Javascript would be:
//Using setInterval can help the reloading or refreshing of the map itself.
setInterval(function() {
map.invalidateSize();
}, 2000);
...Note that setInterval() counts time in milliseconds so for example , 2000 are 2 seconds.
map in map.invalidateSize() is the name of the variable you set your map to when you initialized it:
Map initialization example:
var map = L.map(...
Related
I'm using LeafletJS to display maps in a number of places on my site.
In most cases, it is working fine, but on one specific page I am getting a weird rendering glitch, which looks like this:
As you can see, the map is rendering with vertical grey bars at each tile boundary. Additionally, if I add markers to the map, they also display squashed.
The weird thing is that I'm using the same code (ie the exact same JS file) to render similar maps on other pages without any problems. I'm also using similar code to render two other maps on the same page, again without problems.
The JS code looks like this:
(function(L) {
var element = document.querySelector('.details-map');
if (!element) { return; }
var coords = element.getAttribute('data-coords');
var latlng = coords.split(',');
var location = {lat: parseFloat(latlng[0]), lng: parseFloat(latlng[1])};
var zoom = element.getAttribute('data-zoom');
var map = new L.Map(element, {center: location, zoom: zoom, gestureHandling: true});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
}).addTo(map);
})(L);
And the associated HTML looks like this:
<div class="details-map" data-coords="51.5216,-0.71998" data-zoom="14"></div>
The rendering glitch occurs at all zoom levels and persists after the doing things like map.invalidateSize() to force a redraw.
Can anyone explain why I'm getting this glitch and how to fix it? Thank you.
After much digging, I finally resolved this.
The problem was some CSS that was linked to an element some distance further up the DOM tree, and not really relevant to the map. It looks like this:
.local-branch img {
vertical-align: top;
display: inline-block;
width: 25%;
padding: 0 10px;
}
The intention of this CSS is to lay out a set of images related to the branch. The map is being added later as part of the branch details, within the .local-branch element. The styles are obviously not intended to apply to the map, but are applying anyway as the CSS selector is too broad.
I'll need to find a way to fix it that doesn't break the existing image layout, which still needs some thought, but at least I've worked out what was going on, so the mystery is solved.
So I'm using the latest version of leaflet (v1.0.2), and am trying to dynamically apply text labels to specific lat lng points on a custom (geo aligned) map.
My issue is that I need the text on the map to maintain it's the size (as though the text is actually part of the tile image) when zooming. Using a Marker of any kind results in the text staying at its correct size. If I use something like an image overlay and add an SVG with text in, it scales with the map zoom.
I've noticed that the image overlay has a CSS3 scale added to its transform property when zooming whereas the marker does not.
Can I extend the marker to scale as the image overlay does?
I've already written code that listens to the zoom event and adjusts the font size of markers but this is CPU intensive (especially for mobile browsers) and I don't really want to render the text dynamically within svgs either!
I've provided a demo so that this makes more sense. You can see that example_1 (the marker) maintains it's size however far you zoom in or out. Example_2 (the svg image) scales relative to the map when zooming. This (Example_2) is what I'm trying to get an L.DivIcon with html text content to do!
Any help or suggestions are appreciated!
https://jsfiddle.net/z96L7hdu/
Example Code
HTML
<div id="map" style="width:500px; height:600px;"></div>
JavaScript
var map = L.map('map', {
zoomSnap: 0
}).setView([0, 0], 3);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
var img = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICAgICB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgNTAwIDQwIj4gIDx0ZXh0IHg9IjAiIHk9IjAiIGZvbnQtZmFtaWx5PSJWZXJkYW5hIiBmb250LXNpemU9IjM1Ij4gICAgRXhhbXBsZV8yICA8L3RleHQ+PC9zdmc+";
imageBounds = [[-8.636810901898114, -12.135975261193327], [-18.28136415046407, 17.181122017133486]];
L.imageOverlay(img, imageBounds).addTo(map);
var myIcon = L.divIcon({className: 'my-div-icon', html:"Example_1"});
L.marker({lat: 0.7800052024755708, lng: 0.010986328125}, {icon: myIcon}).addTo(map);
Apologies for the late answer, but I thought it was an interesting question. You can indeed extend the L.Marker class, to create markers that resize the font of their DivIcon to match the zoom level:
L.FixedSizeMarker = L.Marker.extend({
options: {
fontSize: 12, // starting size of icon in pixels
zoomBase: 3 // Zoom level where fontSize is the correct size
},
update: function () {
if (this._icon && this._icon.tagName === 'DIV' && this._map) {
let size = this.options.fontSize * Math.pow(2, (this._map.getZoom() - this.options.zoomBase));
this._icon.style.fontSize = size + 'px';
}
return L.Marker.prototype.update.call(this);
}
});
L.fixedSizeMarker = (latlng, options) => new L.FixedSizeMarker(latlng, options);
The code above defines a new FixedSizeMarker, which behaves just like a normal Marker, but if you add a DivIcon to it, it will resize the font. It takes two options, to specify the font size in pixels, and the zoom level that you want that font size to be correct for. In the example in the OP's JSFiddle, you would use it like this:
var myIcon3 = L.divIcon({className: 'my-div-icon', html:"Example_3"});
L.fixedSizeMarker({lat: 0.7800052024755708, lng: -12.135975261193327},
{icon: myIcon3, fontSize: 24, zoomBase: 3}).addTo(map);
When using these markers, it may look better to set {markerZoomAnimation: false} in the map options. The marker size change is otherwise quite obvious when you zoom the map.
const element = marker.getElement();
element.style[L.DomUtil.TRANSFORM] = `scale(3)`;
You can use this to modify the css of a marker
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
I created a little website for the upcoming football world cup. The website contains a map (provided by mapbox), which is the most important element in my website, so it is the background of the website. The width is set to 100%. Now I would like to resize the map according to the device (tablet, phone), so that the map and the whole website looks similar on each device.
Here is a code snippet from my javascript:
var map = L.mapbox.map('map', 'hashtagwinti.i72di96c', {
zoomControl: false,
maxZoom: 10,
minZoom: 2,
} )
.setView([-8, -50], 4);
L.control.scale({position: 'bottomleft', metric: true, imperial:false}).addTo(map);
map.featureLayer.setGeoJSON(brazil).addTo(map);
Here is my css of my body and the map:
body {
margin:0;
padding:0;
width:auto% !important;
height: auto% !important;
}
#map {
position:absolute;
top:0;
bottom:0;
width:100%;
Now I'd like that on every device the map view is the same. At the moment the zoom level doesn't change (the map view isn't the same) if you go on the website from for example the an iPhone.
any ideas?
(if already looked for a solution on the mapbox API, but I didn't found a solution)
You can tell Mapbox which screen size you want to adjust for with a media query-type call in and to do something different for that size with an if/else statement.
For example, to set a zoom level for a phone screen size less than 420px it'd be something like this:
var mq = window.matchMedia( "(min-width: 420px)" );
if (mq.matches){
var map = new L.mapbox.map('map', 'hashtagwinti.i72di96c').setView([-8, -50], 4); //set map zoom level for desktop size
} else {
var map = new L.mapbox.map('map', 'hashtagwinti.i72di96c').setView([-8, -50], 3); //set map zoom level for mobile size
};
You can set other things too like map center, markers, etc.
I've found using map.invalidateSize(); the simplest solution to adjust the map size based on the size of the browser window or parent container.
Here is some more information about invalidateSize on MapBox's website: https://www.mapbox.com/help/why-map-cropped-hidden-shown/
I am trying to create a presentation running on top of Reveal.js, which would include a Leaflet.js map within one of the slides. I have included all necessary Javascript & CSS files into my Reveal.js presentation and I can make the map appear on the slide.
However, the problem is: map tiles are not displayed correctly. Instead of the actual map tiles, all I am seeing is gray background and some horizontal black lines. I can zoom in/out and pan the map, and the black lines are moving accordingly.
There are no error message in the Javascript console, and the browser seems to be downloading map tiles from server exactly as it should. I believe the problem has something to do with the CSS code of Leaflet map tiles - .leaflet-tile within leaflet.css - being somehow incompatible with Reveal.js.
The question is: Does anyone know how to get around this issue? Or is it a deadend with no possible solution?
I have the following CSS for the <div id="map">:
#map {
height:400px;
width:100%;
}
EDIT: One obvious workaround for this is to use <iframe> tag to embed the map into the presentation. Seems to work just fine, and maybe it is better to keep the frameworks separated. However, the downside is that if there are several maps in the presentation, each within its own <iframe>, a copy of Leaflet.js is loaded to memory for each and every iframe.
EDIT #2: A better solution, it seems, is to use Polymaps instead of Leaflet.js. It seems that several Polymaps maps can be embedded into a reveal.js presentaion. No issues.
I found it easily to do it with a web component, this way, the shadow dom will protect my leaflet map from the evil hands of reveals css
here is a repo with an example
<link rel="import" href="./leaflet-map.html">
...
<div class="reveal">
<div class="slides">
<section data-state="map">
<leaflet-map></leaflet-map>
</section>
</div>
</div>
Here is the web component
<template id="leaflet-map-template">
<link rel="stylesheet" href="./bower_components/leaflet/dist/leaflet.css">
<div id="mapid" style="height: 500px"></div>
<!-- LEAFLET JS -->
</template>
<script src="./bower_components/leaflet/dist/leaflet.js"></script>
<script>
class LeafletMap extends HTMLElement {
constructor () {
super();
let tmpl = document.currentScript.ownerDocument.querySelector('template')
let shadowRoot = this.attachShadow({mode: 'open'})
shadowRoot.appendChild(tmpl.content.cloneNode(true))
let mapDiv = this.shadowRoot.getElementById('mapid')
this.map = L.map(mapDiv).setView([19.39682052576622, -99.13478851318361], 13)
// this.setAttribute('map', map)
// Tiles de open street maps
//L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map)
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'mapbox.streets'
}).addTo(this.map)
let myIcon = L.icon({
iconUrl: './lentes.png',
iconSize: [40, 40], // size of the icon
iconAnchor: [20, 20], // point of the icon which will correspond to marker's location
tooltipAnchor: [20,0]
})
L.marker(
[19.418657758792698, -99.14065182209016],
{icon: myIcon}
).bindTooltip('Ranchito').addTo(this.map)
}
resize() {
this.map.invalidateSize()
}
}
window.customElements.define('leaflet-map', LeafletMap)
</script>
It might be happening because the #map element is hidden (due to the hidden slide) when it is initialized, so it cannot read the dimensions..
Try using map.invalidateSize(false); once your slide becomes visible..
Reveal.addEventListener( 'slidechanged', function( event ) {
// event.previousSlide, event.currentSlide, event.indexh, event.indexv
if (event.indexh == 5){ // assuming your 5th slide is the one with the map
map.invalidateSize(false); // assuming that map holds the the reference to your leaflet instance
}
} );