Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for getBounds() with conic and thematic projections #12503

Merged
merged 8 commits into from
Jan 10, 2023
Merged
305 changes: 305 additions & 0 deletions debug/getbounds.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
<!DOCTYPE html>
<html>
<head>
<title>Mapbox GL JS debug page</title>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel='stylesheet' href='../dist/mapbox-gl.css' />
<style>
body { margin: 0; padding: 0; }
#map, #map2 { position: absolute; top: 0; bottom: 0; }
#map { left: 0; right: 50%; }
#map2 { left: 50%; right: 0; }
.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: #fff8;
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>
</head>

<body>

<div id='map'></div>
<div id='map2'></div>
<div class="map-overlay top">
<details open>
<summary>Toggle controls</summary>
<div class="map-overlay-inner">
<fieldset>
<label>Select style</label>
<select id="style" name="style">
<option value="streets-v11">Streets</option>
<option value="satellite-v9">Satellite</option>
</select>
</fieldset>
<fieldset>
<label>Select projection</label>
<select id="projection" name="projection">
<option value="albers" selected>Albers</option>
<option value="equalEarth">Equal Earth</option>
<option value="equirectangular">Equirectangular</option>
<option value="lambertConformalConic">
Lambert Conformal Conic
</option>
<option value="mercator">Mercator</option>
<option value="globe">Globe</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">29.5</span></label
>
<input
id="lat1"
type="range"
min="-90"
max="90"
step="any"
value="29.5"
/>
</fieldset>
<fieldset class="conic-param-input">
<label
>Northern Parallel Lat: <span id="lat2-value">45.5</span></label
>
<input
id="lat2"
type="range"
min="-90"
max="90"
step="any"
value="45.5"
/>
</fieldset>
<fieldset>
<label>Show Debug Tiles:</label>
<input id="debug" type="checkbox">
</fieldset>
</div>
</details>
</div>

<script src='../dist/mapbox-gl-dev.js'></script>
<script src='../debug/access_token_generated.js'></script>
<script>

const projectionInput = document.getElementById('projection');
const styleInput = document.getElementById('style');
const debugInput = document.getElementById('debug');
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');

var map = window.map = new mapboxgl.Map({
container: 'map',
zoom: 2,
center: [0, 0],
style: 'mapbox://styles/mapbox/streets-v12',
hash: true,
projection: projectionInput.value
});

var map2 = new mapboxgl.Map({
container: 'map2',
zoom: 0.1,
center: [0, 0],
style: 'mapbox://styles/mapbox/streets-v12',
projection: 'mercator'
});

var lineData = {
type: 'LineString',
coordinates: []
};
var boundsData = {
'type': 'LineString',
coordinates: []
};

map2.on('load', () => {
map2.addSource('bounds', {
'type': 'geojson',
'data': lineData
});
map2.addLayer({
'id': 'bounds',
'type': 'line',
'source': 'bounds',
'paint': {
'line-color': 'blue',
'line-width': 5
}
});

map2.addSource('abounds', {
'type': 'geojson',
'data': boundsData
});
map2.addLayer({
'id': 'abounds',
'type': 'line',
'source': 'abounds',
'paint': {
'line-color': 'red',
'line-width': 5
}
});

updateBounds();
});

map.on('move', () => {
updateBounds();
});

function updateBounds() {
const points = [];
const w = map._containerWidth;
const h = map._containerHeight;
const res = 49;
for (let i = 0; i <= res; i++) points.push([w * i / res, 0]);
for (let i = 0; i <= res; i++) points.push([w, h * i / res]);
for (let i = 0; i <= res; i++) points.push([w * (1 - i / res), h]);
for (let i = 0; i <= res; i++) points.push([0, h * (1 - i / res)]);

lineData.coordinates = points.map(p => map.unproject(p).toArray());

const bounds = map.getBounds();

boundsData.coordinates = [
bounds.getSouthWest().toArray(),
bounds.getSouthEast().toArray(),
bounds.getNorthEast().toArray(),
bounds.getNorthWest().toArray(),
bounds.getSouthWest().toArray(),
];

map2.getSource('bounds').setData(lineData);
map2.getSource('abounds').setData(boundsData);
}

const marker = new mapboxgl.Marker();

map.on('mousemove', (e) => {
marker.setLngLat(e.lngLat).addTo(map2);
});

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';
}

const projection = isConic ? {
name: e.target.value
} : e.target.value;

map.setProjection(projection);

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

styleInput.addEventListener('change', () => {
map.setStyle('mapbox://styles/mapbox/' + styleInput.value);
});
debugInput.addEventListener('change', () => {
map.showTileBoundaries = debugInput.checked;
});

const inputs = [
[lngInput, lngValue],
[latInput, latValue],
[lat1Input, lat1Value],
[lat2Input, lat2Value]
];

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>
Loading