7. Leaflet Map tutorial - how to create a 360 degree panorama with pan and zoom

A 360 degree panorama is single rectangular image created by stitching together many separate images taken as the camera is turned through 360 degrees. This is best done with a tripod. I didn't! I was on uneven ground in a blustery wind.

When you stich together the images it is important to duplicate the images on the ends so that both ends of the rectangle have the same view at the lowest zoom. Then when the user pans the image to the end, you can seamlessly switch the view to the same image at the other end and continue the rotation.

This frame shows a live view of the code we are creating:

The HTML code

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <link rel="stylesheet" type="text/css" href="leaflet.css" />
    <title>A 360 degree rotation panoramic image using leaflet</title>
    <script type='text/javascript' src='leaflet.js'></script>
    <h1>A 360 degree rotation panoramic image using leaflet</h1>
    <p>This isn't the best example of a 360 panorama! The stiching software has picked the foreground to match rather than the horizon, but it serves to illustrate the technique with leaflet..</p>
    <div id="image-map" style="width: 1000px; height: 1000px; border: 1px solid #AAA;"></div>
    // Using leaflet.js to pan and zoom a big image.
    var map L.map('image-map', {
            center: [00],
    // dimensions of the image
    // 21109 px * 1914 px at zoom 4 - full size
    // 10554  *  907 at zoom 3
    // 5277 * 453 at zoom 2
    // 2638 * 226 at zoom 1
    var diagramLayerGroup new L.LayerGroup();
    var midimageL.imageOverlay("pano360dartmoor2.jpg", [[0,0], [226,2683]]); // I think that should have been 2638, but I am not going to change it and reposition all the icons!
     var markerGroup diagramLayerGroup.addTo(map);
    // tell leaflet that the map is exactly as big as the image
     map.setMaxBounds(new L.LatLngBounds([0,0], [226,2683])); //size at zoom 1 - get the sizes right before you add markers!
     L.tileLayer('', {
    attribution'&copy; <a href="https://peter-thomson.com">Peter Thomson 2018</a>'

/* The code above is standard code for any image display, with the image placed in a layer so that it can be manipulated.

We need two flags to record whether we have just panned the image past the end of the rectangle on left or right, to prevent leaflet making recursive calls as movend is also fired by setView.

'moveend' is fired when the user finishes a pan, and we check to see if they have reached either end of the rectangle. If the centre of the visible part of the image has moved past our end coordinates we move the display to the other end of the rectangle and reset the flags.

When setView changes the visible image it doesn't trigger a new setView because the flags now prevent this.

Once the view of the image has moved away from the end we reset the flags so that the user panning the image past the end can again trigger the 360 degree pan.


    var rightFlag true;
    var leftFlag true;
    var map.getZoom();
    var mid map.getCenter()
    lng mid.lng
    lat mid.lat
    console.log(mid'zoom = ' +'  left '+leftFlag+' right ' rightFlag);
    if (lng >370 && lng 2380){
        rightFlag true;
        leftFlag true;
     if(lng2390 && rightFlag){
        leftFlag true;
         map.setView([lat,270], z);      
         diagramLayerGroup.addLayer(midimage);// I hoped that removing the image while the view is changed would add to the smoothness of the change, but I cannot see much difference
      if(lng260 && leftFlag){ 
           rightFlag true;
          leftFlag false
         map.setView([lat,2300], z);