Leaflet bindpop() using lazyload of images - javascript

I'm using leaflet to put some markers on a map. I have set that, clicking on a marker, a popup will be opened showing an image. Here's a brief example:
var map = L.map('map')
.addLayer(tile)
.setView([initLat, initLon], initZoom);
var m = L.marker([lat, lon])
.bindPopup('<img src="1.jpg"/>')
.addTo(map);
My objective is to load those images ("1.jpg" in the example above) using lazy load, so it's only loaded when I click on the marker.
Does anyone knows how to do this?
Thanks!

You could set the content of the popup when the popup is opened.
Let's create a custom popup with a lazyload option and without content :
var m = L.marker([0, 0])
.bindPopup(L.popup({
lazyload: '<img src="1.jpg"/>'
}))
.addTo(map);
You can then set a global handler to fill your popup when needed:
map.on('popupopen', function(e) {
var popen = e.popup;
if (popen.options.lazyload) {
popen.setContent(popen.options.lazyload);
}
});
And a demo:
var map = L.map('map', {
center: [0, 0],
zoom: 1
});
var m = L.marker([-30, 0])
.bindPopup(L.popup({
lazyload: '<img src="https://i.stack.imgur.com/shfxy.jpg?s=32&g=1" />'
}))
.addTo(map);
map.on('popupopen', function(e) {
var popen = e.popup;
if (popen.options.lazyload) {
popen.setContent(popen.options.lazyload);
}
});
html, body {
height: 100%;
margin: 0;
}
#map {
width: 100%;
height: 150px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.js"></script>
<div id='map'></div>

Actually in the case where you fill your popup with a String content (like you did with .bindPopup('<img src="1.jpg"/>'), Leaflet converts it (through innerHTML) to DOM nodes only when the Popup is first opened on a map. Therefore your image will be loaded only at that moment, which is exactly the lazy loading behaviour you are looking for.
So you do not need to do anything extra from what you already have done in your question code:
(make sure you refresh your page / clear your cache to see the image loading pass in the browser network requests)
var map = L.map('map', {
center: [0, 0],
zoom: 1
});
var m = L.marker([-30, 0])
.bindPopup('<img src="https://i.stack.imgur.com/shfxy.jpg?s=32&g=1" />')
.addTo(map);
html, body {
height: 100%;
margin: 0;
}
#map {
width: 100%;
height: 150px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.js"></script>
<div id='map'></div>

Related

Openlayers - trying to create the Overlay feature on the map

I am struggling with creating the overlay on my map. I want to incorporate the example like here:
https://openlayers.org/en/latest/examples/overlay.html
https://openlayers.org/en/latest/apidoc/module-ol_Overlay-Overlay.html
but I cannot use the import statement, because I am getting an error:
Uncaught SyntaxError: Cannot use import statement outside a module
which has an explanation here:
https://github.com/TypeStrong/ts-node#syntaxerror
and here:
Why examples don't work? (a struggle with imports)
"Uncaught SyntaxError: Cannot use import statement outside a module" when importing ECMAScript 6
I tried to do sth like this:
<script type="module" src="./layers/overlay.js" type="text/javascript"></script>
but an error still comes out and now it's related to the CORS policy:
Access to script at 'file:///C:/Users.../layers/overlay.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.
Unfortunately I need this feature offline.
In this thread I found, that there is an alternative to the import feature:
https://gis.stackexchange.com/questions/310482/unexpected-token-identifier-error-import-openlayers/310501#310501
and I tried to adjust my code into it, which looks like this:
var fromLonLat = ol.proj.fromLonLat
var pos = fromLonLat([-0.21005,52.08093]);
var overlay = new ol.Overlay({
element: container,
autoPan: true,
autoPanAnimation: {
duration: 250,
},
});
var popup = new overlay({
element: document.getElementById('popup'),
});
map.addOverlay(popup);
// Vienna marker
var marker = new overlay({
position: pos,
positioning: 'center-center',
element: document.getElementById('marker'),
stopEvent: false,
});
map.addOverlay(marker);
// Vienna label
var vienna = new overlay({
position: pos,
element: document.getElementById('vienna'),
});
map.addOverlay(vienna);
map.on('click', function (evt) {
var element = popup.getElement();
var coordinate = evt.coordinate;
var hdms = toStringHDMS(toLonLat(coordinate));
$(element).popover('dispose');
popup.setPosition(coordinate);
$(element).popover({
container: element,
placement: 'top',
animation: false,
html: true,
content: '<p>The location you clicked was:</p><code>' + hdms + '</code>',
});
$(element).popover('show');
});
and now I am getting an error like this:
Uncaught TypeError: overlay is not a constructor
at overlay.js:15
similar to the issue here:
openlayers3 undefined is not a constructor error on ol.source.StaticVector
Regarding this I found:
https://github.com/Viglino/ol-ext
including all relevant extensions for OpenLayers. Unfortunately after attaching the relevant scripts, the problem is still the same.
My another approaching was to replace everywhere the new overlay with the new ol.Overlay. In this event the console says nothing, but I can't see an overlay at all.
The code might be specicif, because it comes from the QGIS2web plugin. The major script with map as well as the index.html file you can find in this fiddle link below:
https://jsfiddle.net/2adv41bs/
Many sources refers me to the newest ol package
https://openlayers.org/download/
but since I superseded the link in my HTML code it's still doesn't work at all
I am also not familiar with creating the bundle in openlayers
https://openlayers.org/en/latest/doc/tutorials/bundle.html
A similar thread is here:
https://gis.stackexchange.com/questions/380382/incorporating-overlay-for-the-openlayers-map-generated-by-qgis2web-plugin
Is it possible to launch the overlay option for Openlayers map offline?
The good alternative for import can be found here:
https://gis.stackexchange.com/questions/310482/unexpected-token-identifier-error-import-openlayers/310501#310501
which changes the situation completely, because now the final code can look as this:
HTML
<div id="map" class="map">
<div id="popup" class="ol-popup">
<div id="popup-content"></div>
</div>
</div>
<div style="display: none;">
<!-- Clickable label for Vienna -->
<a class="overlay" id="vienna" target="_blank"
href="http://en.wikipedia.org/wiki/Vienna">Vienna</a>
<div id="marker" title="Marker"></div>
<!-- Popup -->
<div id="popup" title="Welcome to OpenLayers"></div>
</div>
Next, our CSS
#marker {
width: 20px;
height: 20px;
border: 1px solid #088;
border-radius: 10px;
background-color: #0FF;
opacity: 0.5;
}
#vienna {
text-decoration: none;
color: white;
font-size: 11pt;
font-weight: bold;
text-shadow: black 0.1em 0.1em 0.2em;
}
.popover-body {
min-width: 276px;
}
and the JS
var fromLonLat = ol.proj.fromLonLat
var pos = fromLonLat([-0.19610,52.07769]);
var popup = new ol.Overlay({
element: document.getElementById('popup'),
});
map.addOverlay(popup);
var marker = new ol.Overlay({
position: pos,
positioning: 'center-center',
element: document.getElementById('marker'),
stopEvent: false,
});
map.addOverlay(marker);
var vienna = new ol.Overlay({
position: pos,
element: document.getElementById('vienna'),
});
map.addOverlay(vienna);
map.on('click', function (evt) {
var element = popup.getElement();
var coordinate = evt.coordinate;
var hdms = toStringHDMS(toLonLat(coordinate));
$(element).popover('dispose');
popup.setPosition(coordinate);
$(element).popover({
container: element,
placement: 'top',
animation: false,
html: true,
content: '<p>The location you clicked was:</p><code>' + hdms + '</code>',
});
$(element).popover('show');
});
Obviously, both CSS and JS files must be attached to the main index.html file if we want it working.

Mapbox Leaflet zoom does not work from the center

Problem
I use Mapbox (Leaflet) on a site https://upplevelsekartan.com/djurparker (click Karta). When zooming in it does not zoom in from the middle, but from the upper left corner.
In isolation
The example below uses the exact same code as my site, but this example works as expected. Too see it, run in Full page. When zooming it zooms from the center like it should.
Question
Why does it work perfectly in isolation, but not on my site? How can I fix it?
window.addEventListener('DOMContentLoaded', async() => {
await import('https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js');
mapboxgl.accessToken = 'pk.eyJ1IjoiamVuc3Rvcm5lbGwiLCJhIjoiY2tjb290djIwMG5kZzJzbG1zNGsxeWZ6OSJ9.85qNaU7Xm5Cs2i2yK5tVCw';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
});
map.fitBounds([ // Sweden
[11.0273686052, 55.3617373725],
[23.9033785336, 69.1062472602]
], {padding: 16});
map.on('load', () => {
});
}, false );
div {
height: 300px;
width: 300px;
}
<div id="map"></div>
If you change
.mapboxgl-canvas {
position: absolute;
left: 0;
top: 0;
}
to
.mapboxgl-canvas {
position: relative;
left: 0;
top: 0;
}
it should fix your issue.

Distorted display of Google Maps

What is wrong with my map?
Please go to gtob.openfile.ch/mitglieder, click on «A-Z», then on «Screenbox Multimedia Ltd.». Finally click on the button «Show map», than the same map is displayed in three div's. In the middle div, the map is distorted, and i can not find out why... (Responsive design is not finished now, please show with a screen-width of at least 1200 pixels)
Ok, here is the JavaScript/jquery for building the map:
var memberGoogleMapsData = new Object;
memberGoogleMapsData.mapContainer = $(this).closest('div').find('div.memberMap');
$(this).closest('div').find('button').on('click', function() {
memberGoogleMapsData.mapsPosLat = parseFloat(memberGoogleMapsData.mapContainer.data('mapsposlat'));
memberGoogleMapsData.mapsPosLng = parseFloat(memberGoogleMapsData.mapContainer.data('mapsposlong'));
memberGoogleMapsData.mapsZoom = parseInt(memberGoogleMapsData.mapContainer.data('mapszoom'));
memberGoogleMapsData.markerPosLat = parseFloat(memberGoogleMapsData.mapContainer.data('markerposlat'));
memberGoogleMapsData.markerPosLng = parseFloat(memberGoogleMapsData.mapContainer.data('markerposlng'));
memberGoogleMapsData.mapId = memberGoogleMapsData.mapContainer.prop('id');
memberGoogleMapsData.mapPos = new google.maps.LatLng(memberGoogleMapsData.mapsPosLat, memberGoogleMapsData.mapsPosLng);
memberGoogleMapsData.options = {
zoom: memberGoogleMapsData.mapsZoom,
center: memberGoogleMapsData.mapPos,
mapTypeId: google.maps.MapTypeId.HYBRID,
tilt: 0,
heading: 0
};
memberGoogleMapsData.myMap = new google.maps.Map(document.getElementById(memberGoogleMapsData.mapId),memberGoogleMapsData.options);
memberGoogleMapsData.myMap2 = new google.maps.Map(document.getElementById('testMap'),memberGoogleMapsData.options);
memberGoogleMapsData.myMap3 = new google.maps.Map(document.getElementById('testMap2'),memberGoogleMapsData.options);
});
In the div.memberMap I have data-Tags for providing the required data for building the google map:
<div class="memberMap" id="memberMap207" data-mapsposlat="47.50467769999999" data-mapsposlong="9.39942510000003" data-mapszoom="13" data-markerposlat="47.50467769999999" data-markerposlng="9.39942510000003">
You have the following style in the customer.css file
#members div.members img {
float: right;
max-width: 300px;
max-height: 150px;
}
Your first map is inside the div.members, so this style breaks all the tile images of the map as well.
The second map that is outside of div.members is not affected by this style.
I hope it helps!

height=100% not rendering the google maps api

I am trying to use the Google Maps API. I have gotten a authorization key and pasted in the sample code they gave us, but I can't make the map render even when they tell me to make sure I put a height of 100% on both my map div and the body.
I have tried and it won't show up. If I put a 500px height on the map div, and give the body a 100% height it works... but I want to know why height of 100% will not will not render. I am copying exactly what the Google documentation (https://developers.google.com/maps/documentation/javascript/examples/map-simple) is giving me and what the video tells me to double check. HELP, i am insanely curious on why 100% height won't render. I want to use percentages, so that it can be compatible on mobile devices!
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>Ice Cream</title>
<!-- Google Docs-->
<!-- external CSS link -->
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/style.css">
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>
<div id='map_canvas'> HELLOooo <!--dimensions of map are set in CSS-->
</div>
<!-- external CSS link -->
<!-- javascript -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDuDCh7Odrr4RUUiAHMyDtfwC2NtySy-64"></script>
<script src="js/index.js"></script>
</body>
</html>
body, #map_canvas{
margin:0;
padding:0;
height: 100%;
}
$(function() {
console.log("hi");
function initialize() {
var mapOptions = {
center: { lat: -34.397, lng: 150.644},
zoom: 8
};
var map = new google.maps.Map(document.getElementById('map_canvas'),
mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
console.log('map');
});
The browser needs to be able to calculate a non-zero height. You need to set the height of the html element to 100% also:
$(function() {
console.log("hi");
function initialize() {
var mapOptions = {
center: { lat: -34.397, lng: 150.644},
zoom: 8
};
var map = new google.maps.Map(document.getElementById('map_canvas'),
mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
console.log('map');
});
html, body, #map_canvas{
margin:0;
padding:0;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id='map_canvas'> HELLOooo <!--dimensions of map are set in CSS-->
</div>
The reason why 100% doesn't work is because the map needs to send you images at a specific ratio (fixed width & height). Now I might be guessing here, but that's what I think happens.
Since I had a similar problem, where my map was hidden inside a tab, I wrote this function to calculate the width & height or a ratio. Then whatever size of the container, the map was in, it would adjust. It uses jQuery so I just thought I'd post it to get the idea.
/**
* Calculate the dimensions
*/
calculateMap: function () {
var self = this,
parent = this.map.parent(), // jQuery obj
w, h, id, el;
// if the container of your map is visible and wrapped it has a certain width and height...
// if it also has 100% height, it could be collapsed (not wrapped)
// so you ask the google maps api to load images at 500px width and 0height?
// if the parent has 0height, the ratio helps to set it properly
// with debug tools (F12) you can see the parent if it lights up
if (parent.is(':visible')) {
w = parent.outerWidth(false);
h = w * this.settings.options.mapRatio; // 4/3 | 16/9
this.map.css({ width: w, height: h });
} else {
// if your map is invisible after load...
id = this.onParent.prop('id'); // this id references my tab
el = this.onParent.prevAll().find('a[href=#' + id + ']'); // trigger of the tab
// add click event once
el.one(this.settings.events.onParent, function() {
setTimeout(function() {
self.activate(); // goes back to calculateMap()
}, 200);
}.bind(this));
}
},
If you trigger calculateMap on the resize event, you'll have a responsive approach.
Also a quick google showed me this approach ... notice the fixed width and height on the iframe.
In short:
fixed width and height in css gives you the cleanest solution. you might be able to use a clearfix, padding in % or min-height in px to gain dimension if that works out.
For dynamic/event loading I suggest JavaScript or jQuery
or with an iframe and a fixed width and height

Openlayers / Openstreetmap background is vertically striped and squished

So the background of my openlayers implementation appears to be squished into vertical stripes. The weird thing is that it wasn't always like this but even when I stash all my changes back to a point where I know it was working, it still is broken. It makes me wonder if perhaps something has changed about the way the tile assets are being delivered. I have tried switching between using osm and wms layers with no change, any help would be appreciated.
Here is the pertinent code:
initMap: function() {
var view = this;
var map = this.map = new OpenLayers.Map();
map.render(this.$map[0]);
var wmsLayer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0?", {layers: 'basic'});
var osmLayer = new OpenLayers.Layer.OSM();
this.layers = {
point: new OpenLayers.Layer.Vector("Point Layer"),
line: new OpenLayers.Layer.Vector("Line Layer"),
polygon: new OpenLayers.Layer.Vector("Polygon Layer")
};
this.setValue(this.value);
map.addLayers([this.layers.point, this.layers.line, this.layers.polygon, osmLayer]);
drawControls = {
point: new OpenLayers.Control.DrawFeature(this.layers.point,
OpenLayers.Handler.Point),
line: new OpenLayers.Control.DrawFeature(this.layers.line,
OpenLayers.Handler.Path),
polygon: new OpenLayers.Control.DrawFeature(this.layers.polygon,
OpenLayers.Handler.Polygon)
};
this.layers[this.layerType].events.on({'sketchcomplete': function(feature) {
if (!view.multiple) {
// deactivate polygon layer once a polygon has been added
drawControls[view.layerType].deactivate();
}
}});
for(var key in drawControls) {
map.addControl(drawControls[key]);
}
if (this.layers[this.layerType].features.length) {
var bounds = this.layers[this.layerType].getDataExtent();
var zoom = this.layers[this.layerType].getZoomForExtent(bounds);
var lon = (bounds.top - bounds.bottom) / 2;
var lat = (bounds.right - bounds.left) / 2;
map.setCenter(new OpenLayers.LonLat(lon,lat), 3);
map.zoomToExtent(bounds);
if (view.multiple) {
drawControls[view.layerType].activate();
}
} else {
map.setCenter(new OpenLayers.LonLat(-11174482.03751,4861394.9982606), 4);
drawControls[view.layerType].activate();
}
this.$('.clear').click(function(e) {
e.preventDefault();
view.layers[view.layerType].destroyFeatures();
drawControls[view.layerType].activate();
});
},
Here is the output:
So I found the problem. Twitter bootstrap has a line in its reset file that sets:
img { max-width: 100% }
This was squishing the images. You can fix it by doing:
img { max-width: none; }
I was having the same problem, are you using bootstrap of twitter?
I found out there was a selector for img elements that was affecting the map. I had the next selector into the bootstrap.css"
img {
max-width: 100%;
vertical-align: middle;
border: 0;
-ms-interpolation-mode: bicubic;
}
I don't know whay the parameter max-width: 100%; makes the vertical stripes in my case. I remove the propertie max-width: 100% and now is working.
Setting img { max-width: 100% } to img { max-width:none; } does resolve the problem.
However this will have an unintended effect on images throughout your website. I am also using Bootstrap Twitter's carousel component for images and when I made the above changed the images did not fit in the carousel. Therefore I changed it so that it only targets the images in the OpenLayers / OpenStreetMap div.
div#outlet_map img { max-width:none; }

Categories