Skip to main content

Use different map projections for web maps

Set the Map object's projection parameter to create a map with a non-Mercator projection. You can also change the projection at runtime using the setProjection method, or set it as a part of your map style in Studio.

Both albers and lambertConformalConic can be configured with center: [lng, lat] and parallels: [lat1, lat2] to make them appropriate for other regions.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Use different map projections for web maps</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.8.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.8.0/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<style>
.map-overlay {
font:
12px/20px 'Helvetica Neue',
Arial,
Helvetica,
sans-serif;
position: absolute;
width: 200px;
top: 0;
left: 0;
padding: 10px;
}

.map-overlay .map-overlay-inner {
background-color: #fff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
border-radius: 3px;
padding: 10px;
margin-bottom: 10px;
}

.map-overlay-inner fieldset {
border: none;
padding: 0;
margin: 0 0 10px;
}

.map-overlay-inner fieldset:last-child {
margin: 0;
}

.map-overlay-inner select,
.map-overlay-inner input {
width: 100%;
}

.map-overlay-inner label {
display: block;
font-weight: bold;
margin: 0 0 5px;
}
</style>

<div id="map"></div>
<div class="map-overlay top">
<div class="map-overlay-inner">
<fieldset>
<label>Select projection</label>
<select id="projection" name="projection">
<option value="mercator">Mercator</option>
<option value="globe">Globe</option>
<option value="albers">Albers</option>
<option value="equalEarth">Equal Earth</option>
<option value="equirectangular">Equirectangular</option>
<option value="lambertConformalConic" selected="">
Lambert Conformal Conic
</option>
<option value="naturalEarth">Natural Earth</option>
<option value="winkelTripel">Winkel Tripel</option>
</select>
</fieldset>
<fieldset class="conic-param-input">
<label>Center Longitude: <span id="lng-value">0</span></label>
<input id="lng" type="range" min="-180" max="180" step="any" value="0">
</fieldset>
<fieldset class="conic-param-input">
<label>Center Latitude: <span id="lat-value">30</span></label>
<input id="lat" type="range" min="-90" max="90" step="any" value="30">
</fieldset>
<fieldset class="conic-param-input">
<label>Southern Parallel Lat: <span id="lat1-value">30</span></label>
<input id="lat1" type="range" min="-90" max="90" step="any" value="30">
</fieldset>
<fieldset class="conic-param-input">
<label>Northern Parallel Lat: <span id="lat2-value">30</span></label>
<input id="lat2" type="range" min="-90" max="90" step="any" value="30">
</fieldset>
</div>
</div>

<script>
// TO MAKE THE MAP APPEAR YOU MUST
// ADD YOUR ACCESS TOKEN FROM
// https://account.mapbox.com
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
const map = new mapboxgl.Map({
container: 'map',
// Choose from Mapbox's core styles, or make your own style with Mapbox Studio
style: 'mapbox://styles/mapbox/satellite-streets-v12',
zoom: 0,
center: [0, 1],
projection: {
name: 'lambertConformalConic',
center: [0, 30],
parallels: [30, 30]
}
});

const projectionInput = document.getElementById('projection');
const conicParamInputs =
document.getElementsByClassName('conic-param-input');
const lngInput = document.getElementById('lng');
const lngValue = document.getElementById('lng-value');
const latInput = document.getElementById('lat');
const latValue = document.getElementById('lat-value');
const lat1Input = document.getElementById('lat1');
const lat1Value = document.getElementById('lat1-value');
const lat2Input = document.getElementById('lat2');
const lat2Value = document.getElementById('lat2-value');
const inputs = [
[lngInput, lngValue],
[latInput, latValue],
[lat1Input, lat1Value],
[lat2Input, lat2Value]
];

projectionInput.addEventListener('change', (e) => {
const isConic = ['albers', 'lambertConformalConic'].includes(
e.target.value
);

// Hide non-conic projection params
for (const input of conicParamInputs) {
input.style.display = isConic ? 'block' : 'none';
}

map.setProjection(e.target.value);

if (isConic) {
const { center, parallels } = map.getProjection();
lngInput.value = center[0];
latInput.value = center[1];
lat1Input.value = parallels[0];
lat2Input.value = parallels[1];
}
for (const [input, value] of inputs) {
value.textContent = input.value;
}
});

for (const [input, value] of inputs) {
input.addEventListener('change', (e) => {
value.textContent = e.target.value;
map.setProjection({
name: projectionInput.value,
center: [Number(lngInput.value), Number(latInput.value)],
parallels: [Number(lat1Input.value), Number(lat2Input.value)]
});
});
}
</script>

</body>
</html>
This code snippet will not work as expected until you replace YOUR_MAPBOX_ACCESS_TOKEN with an access token from your Mapbox account.
Was this example helpful?