Reputation: 21
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: '© 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: '© 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
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: '© 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: '© 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