Reputation: 10874
I'm setting up a Mapbox GL JS map like this:
mapboxgl.accessToken = 'pk.my_token';
var cityBoundaries = new mapboxgl.GeoJSONSource({ data: 'http://domain.com/city_name.geojson' } );
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v8',
center: [cityLongitude,cityLatitude],
zoom: 13
});
Then I'm loading that GeoJSON data onto the map after it loads like this:
map.on('style.load', function(){
map.addSource('city', cityBoundaries);
map.addLayer({
'id': 'city',
'type': 'line',
'source': 'city',
'paint': {
'line-color': 'blue',
'line-width': 3
}
});
});
At this point, I have a map that's centered at the location I specified in new mapboxgl.Map
, and it's at zoom level 13. So, only a piece of the GeoJSON data is visible on the map. I'd like to re-center and re-zoom the map so that the entire GeoJSON data is visible.
In Mapbox JS, I would do this by loading the GeoJSON data into a featureLayer
and then fitting the map to its bounds with:
map.fitBounds(featureLayer.getBounds());
The fitBounds documentation for Mapbox GL JS indicates that it wants the bounds in the format of [[minLng, minLat], [maxLng, maxLat]]
.
Is there a way to determine the mix/max latitude & longitude values of this GeoJSON layer?
Upvotes: 12
Views: 23698
Reputation: 41
Based on James Chevalier's AND tobias47n9e answer.
I have extended the answers with a recursive approach to be able to process all types of polygons and multipolygons. With the answer of tobias47n9e I got partly no results, because the different structures of the polygons were not addressed.
function getPolygonBoundingBox(arr, bounds = [[], []]) {
for (var i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i][0][0])){
bounds = getPolygonBoundingBox(arr[i], bounds);
} else {
polygon = arr[i];
for (var j = 0; j < polygon.length; j++) {
longitude = polygon[j][0];
latitude = polygon[j][1];
bounds[0][0] = bounds[0][0] < longitude ? bounds[0][0] : longitude;
bounds[1][0] = bounds[1][0] > longitude ? bounds[1][0] : longitude;
bounds[0][1] = bounds[0][1] < latitude ? bounds[0][1] : latitude;
bounds[1][1] = bounds[1][1] > latitude ? bounds[1][1] : latitude;
}
}
}
return bounds;
}
getPolygonBoundingBox(feature.geometry.coordinates);
Upvotes: 1
Reputation: 5758
first use the @turf/turf
package:
import * as turf from '@turf/turf'
then construct a bbox via your JSON source feature (in this case a line string):
const locationCoords = locations.map(({ latitude, longitude }) => [
longitude,
latitude,
])
const line = turf.lineString(locationCoords)
const bbox = turf.bbox(line) as LngLatBoundsLike
newMap.fitBounds(bbox)
Upvotes: 0
Reputation: 12385
You can use the turf.js
library. It has a bbox
function:
const bbox = turf.bbox(foo);
Upvotes: 6
Reputation: 2231
Based on James Chevalier's answer. For polygon/multipolygon tilesets that are assigend to a map in Mapbox Studio I am using this to get the bounding box:
getPolygonBoundingBox: function(feature) {
// bounds [xMin, yMin][xMax, yMax]
var bounds = [[], []];
var polygon;
var latitude;
var longitude;
for (var i = 0; i < feature.geometry.coordinates.length; i++) {
if (feature.geometry.coordinates.length === 1) {
// Polygon coordinates[0][nodes]
polygon = feature.geometry.coordinates[0];
} else {
// Polygon coordinates[poly][0][nodes]
polygon = feature.geometry.coordinates[i][0];
}
for (var j = 0; j < polygon.length; j++) {
longitude = polygon[j][0];
latitude = polygon[j][1];
bounds[0][0] = bounds[0][0] < longitude ? bounds[0][0] : longitude;
bounds[1][0] = bounds[1][0] > longitude ? bounds[1][0] : longitude;
bounds[0][1] = bounds[0][1] < latitude ? bounds[0][1] : latitude;
bounds[1][1] = bounds[1][1] > latitude ? bounds[1][1] : latitude;
}
}
return bounds;
}
Upvotes: 2
Reputation: 555
I use the turf-extent library, which is maintained by the Mapbox bunch anyhow. https://www.npmjs.com/package/turf-extent is the node module link. In your code you simply import(ES6) or require as so:
ES6/Webpack: import extent from 'turf-extent';
Via script tag: `<script src='https://api.mapbox.com/mapbox.js/plugins/turf/v2.0.2/turf.min.js'></script>`
Then feed your response to the function, for example:
ES6/Webpack: let orgBbox = extent(response);
Normal: var orgBbox = turf.extent(geojson);
Then you can use the array values to set your map center:
center: [orgBbox[0], orgBbox[1]]
Or as you want, to fit bounds:
map.fitBounds(orgBbox, {padding: 20});
Here is an example using the turf.min.js in a regular html tag in case you are not using webpack or browser: https://bl.ocks.org/danswick/83a8ddff7fb9193176a975a02a896792
Happy coding and mapping!
Upvotes: 2
Reputation: 10874
Based on the 'Obtaining a bounding box' section of this post, I've come up with this process...
map.on('style.load', function(){
$.getJSON('http://citystrides.dev/city_name.geojson', function(response){
var boundingBox = getBoundingBox(response);
var cityBoundary = new mapboxgl.GeoJSONSource({ data: response } );
map.addSource('city', cityBoundary);
map.addLayer({
'id': 'city',
'type': 'line',
'source': 'city',
'paint': {
'line-color': 'blue',
'line-width': 3
}
});
map.fitBounds([[boundingBox.xMin, boundingBox.yMin], [boundingBox.xMax, boundingBox.yMax]]);
})
});
function getBoundingBox(data) {
var bounds = {}, coords, point, latitude, longitude;
for (var i = 0; i < data.features.length; i++) {
coords = data.features[i].geometry.coordinates;
for (var j = 0; j < coords.length; j++) {
longitude = coords[j][0];
latitude = coords[j][1];
bounds.xMin = bounds.xMin < longitude ? bounds.xMin : longitude;
bounds.xMax = bounds.xMax > longitude ? bounds.xMax : longitude;
bounds.yMin = bounds.yMin < latitude ? bounds.yMin : latitude;
bounds.yMax = bounds.yMax > latitude ? bounds.yMax : latitude;
}
}
return bounds;
}
Here's a walkthrough of what the code is doing, for anyone out there who needs a detailed explanation:
map.on('style.load', function(){
$.getJSON('http://citystrides.dev/city_name.geojson', function(response){
response
) inside this function.var boundingBox = getBoundingBox(response);
, function(){
that appears after the 'map on style load' block.var cityBoundary = new mapboxgl.GeoJSONSource({ data: response } );
map.addSource('city', cityBoundary);
map.addLayer({
map.fitBounds([[boundingBox.xMin, boundingBox.yMin], [boundingBox.xMax, boundingBox.yMax]]);
function getBoundingBox(data) {
One thing to note in the getBoundingBox
function is this line:
coords = data.features[i].geometry.coordinates;
In the original post, linked above, this line was written as coords = data.features[i].geometry.coordinates[0];
because their data for the list of coordinates was an array of arrays. My data isn't formatted that way, so I had to drop the [0]
. If you try this code & it blows up, that might be the reason.
Upvotes: 10