ALD2355
ALD2355

Reputation: 21

MapBox Leaflet Base Layer Map with EPSG27700 Overlay

I have been trying for several days to display a an OSM base map and an overlay in a different CRS (EPSG:27700). Whichever method I try I am getting projection errors. Is there a way to change the CRS 'on the fly' or do you have to use two map divs and synchronise them.

Here is my demo code:

     <!DOCTYPE html>
 <html>
 <head>
<title>Leaflet Map with EPSG:27700 Overlay</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.6.2/proj4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4leaflet/1.0.2/proj4leaflet.min.js"></script>
</head>
<body>
<div id="map" style="width: 100%; height: 600px;"></div>
<script>
    // Initialize the map in EPSG:3857 (Web Mercator)
    var map = L.map('map').setView([51.505, -0.09], 10);

    // Add a base map layer (OpenStreetMap in EPSG:3857)
    var osmLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '&copy; OpenStreetMap contributors'
    }).addTo(map);

    // Define the EPSG:27700 projection using Proj4Leaflet
    proj4.defs("EPSG:27700", "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs");

    // Create CRS for EPSG:27700
    var crs27700 = new L.Proj.CRS('EPSG:27700',
        proj4.defs("EPSG:27700"),
        {
            resolutions: [
                896, 448, 224, 112, 56, 28, 14, 7, 3.5, 1.75
            ],
            origin: [-238375.0, 1376256.0],
            bounds: L.bounds([0, 0], [700000, 1300000])
        }
    );

    // Add EPSG:27700 overlay with correct tile settings
    var osLayer = new L.tileLayer('https://api.os.uk/maps/raster/v1/zxy/Leisure_27700/{z}/{x}/{y}.png?key=API_KEY', {
        attribution: '&copy; Ordnance Survey',
        tms: true,  // Switch tms to true for correct tile alignment
        continuousWorld: true
    });

    // Add control for the base map and overlay
    var baseMaps = {
        "OpenStreetMap (EPSG:3857)": osmLayer
    };

    var overlayMaps = {
        "Ordnance Survey (EPSG:27700)": osLayer
    };

    L.control.layers(baseMaps, overlayMaps).addTo(map);

    // Add event listeners to check tile load status
    osLayer.on('tileload', function() {
        console.log('OS Layer loaded');
    });

    osLayer.on('tileerror', function(error) {
        console.log('Error loading OS Layer: ', error);
    });

    // Add the EPSG:27700 overlay to the map
    osLayer.addTo(map);
   </script>
  </body>
  </html>

Upvotes: 0

Views: 57

Answers (1)

ALD2355
ALD2355

Reputation: 21

Thanks. Including a CRS on the layer does not work and results in tile errors. It seems that Leaflet will not allow two CRSs unlike OpenLayers. I tried a different approach by recreating the map as below which seems to work. The transformation function is not needed since whilst the map is in 27700 Leaflet still works in 3857 for markers, lines etc.

        <!DOCTYPE html>
        <html>
        <head>
        <title>OS Leaflet Demo Leaflet Dual Coordinate Systems</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
        <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.6.2/proj4.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/proj4leaflet/1.0.2/proj4leaflet.min.js"></script>
        <style>
            #toggleButton {
                position: absolute;
                top: 10px;
                right: 10px;
                z-index: 1000;
                background-color: white;
                border: 2px solid black;
                padding: 10px;
                cursor: pointer;
                font-size: 16px;
                box-shadow: 0 0 3px rgba(0,0,0,0.4);
            }
        </style>
        </head>
        <body>
        <div id="map" style="width: 100%; height: 600px;"></div>
        <div id="toggleButton">Switch CRS</div>
        <script>
            var map, currentCRS = 'EPSG:3857';
    
            // Define the EPSG:27700 projection
            proj4.defs("EPSG:27700", "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs");
    
            // Define the EPSG:27700 (British National Grid) projection using proj4
            const crs27700 = new L.Proj.CRS('EPSG:27700', '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs', {
            resolutions: [ 896.0, 448.0, 224.0, 112.0, 56.0, 28.0, 14.0, 7.0, 3.5, 1.75 ],
            origin: [ -238375.0, 1376256.0 ],
             bounds: L.bounds([0, 0], [700000, 1300000])
        });
            
            function createMap(center, zoom) {
                if (currentCRS === 'EPSG:3857') {
                    zoom = zoom + 7;
                    map = L.map('map').setView(center, zoom);
                    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                        maxZoom: 19,
                        attribution: '&copy; OpenStreetMap contributors'
                    }).addTo(map);
                } else {
                    map.options.crs = crs27700; 
                    map = L.map('map', {
                        center: center,
                        zoom: zoom -7,
                        maxZoom: 19,
                        crs: crs27700
                    });
                    L.tileLayer(
                        "https://api.os.uk/maps/raster/v1/zxy/Leisure_27700/{z}/{x}/{y}.png?key=API_KEY",
                        {
                            maxZoom: 10,
                            attribution: '&copy; Ordnance Survey'
                        }
                    ).addTo(map);
                }
            }
    
            function transformCoordinates(latlng, fromCRS, toCRS) {
                console.log('Transforming coordinates:', latlng, 'from', fromCRS, 'to', toCRS);
                var point = proj4(fromCRS, toCRS, [latlng.lng, latlng.lat]);
                console.log('Transformed coordinates:', point);
                if (isFinite(point[0]) && isFinite(point[1])) {
                    return L.latLng(point[1], point[0]);
                } else {
                    console.error('Transformed coordinates are not finite:', point);
                    return null;
                }
            }
    
            document.getElementById('toggleButton').addEventListener('click', function() {
                var center, zoom;
    
                if (currentCRS === 'EPSG:3857') {
                    center = map.getCenter();
                    zoom = map.getZoom();
                    map.remove();
                    currentCRS = 'EPSG:27700';
                    center27700 = center;
                    //var center27700 = transformCoordinates(center, 'EPSG:4326', 'EPSG:27700');
                    //var center27700 = transformCoordinates(center, 'EPSG:4326', 'EPSG:4326');
                    if (center27700) {
                        createMap(center27700, zoom);
                    } else {
                        console.error('Failed to transform coordinates to EPSG:27700');
                    }
                } else {
                    center = map.getCenter();
                    zoom = map.getZoom();
                    map.remove();
                    currentCRS = 'EPSG:3857';
                    center3857 = center;
                    //var center3857 = transformCoordinates(center, 'EPSG:27700', 'EPSG:4326');
                    if (center3857) {
                        createMap(center3857, zoom);
                    } else {
                        console.error('Failed to transform coordinates to EPSG:4326');
                    }
                }
            });
    
            // Initialize the map in EPSG:3857
            createMap([51.505, -0.09], 8);
        </script>
        </body>
        </html>
    
    

Upvotes: 0

Related Questions