Mapbox GL JS getBounds()/fitBounds()

62

我正在使用Mapbox GL JS v0.14.2,我已经在文档中搜索了很久,但是很少有关于这个问题的清晰解释。

如果您使用标准JS API,则非常清楚如何使用他们提供的示例来“适应地图到标记”(https://www.mapbox.com/mapbox.js/example/v1.0.0/fit-map-to-markers/); 但是,当使用GL API时,设置方式则有所不同。GL API具有getBounds()https://www.mapbox.com/mapbox-gl-js/api/#Map.getBounds),但由于您没有像标准JS API那样的命名图层,因此我很难弄清楚如何使用getBounds()

我找到了这个(Mapbox GL JS API Set Bounds),但肯定不是正确答案?

这是我的设置大部分内容;不包括JSON设置和其他选项。

mapboxgl.accessToken = '<myaccesstoken>';

var markers = <?php echo $programme_json; ?>;

var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/richgc/cikyo5bse00nqb0lxebkfn2bm',
    center: [-1.470085, 53.381129],
    zoom: 15
});

map.on('style.load', function() {
    map.addSource('markers', {
        'type': 'geojson',
        'data': markers
    });

    map.addLayer({
        "id": "markers",
        "interactive": true,
        "type": "symbol",
        "source": "markers",
        "layout": {
            "icon-image": "venue-map-icon-blue",
            'icon-size': 0.5,
            "icon-allow-overlap": true
        }
    });

    map.scrollZoom.disable();

});

我已经尝试了以下方法:

alert(map.getBounds()); // LngLatBounds(LngLat(-1.4855345239256508, 53.37642500812015), LngLat(-1.4546354760740883, 53.38583247227842))
var bounds = [[-1.4855345239256508, 53.37642500812015],[-1.4546354760740883, 53.38583247227842]]
map.fitBounds(bounds);

我知道如何使用fitBounds,但不确定如何获取它们。map.getBounds()似乎只返回设置的中心位置的经度/纬度。

标记JSON:

var markers = {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"title":"Site Gallery","url":"\/Freelance\/art-sheffield-2016\/programme\/site-gallery\/","summary":"Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Donec id justo. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Suspendisse feugiat. Etiam rhoncus.","image":"\/Freelance\/art-sheffield-2016\/site\/assets\/files\/1032\/site_gallery.jpg","marker-symbol":"venue-map-icon-blue","colour":"blue"},"geometry":{"type":"Point","coordinates":["-1.466439","53.376842"]}},{"type":"Feature","properties":{"title":"Moore Street Substation","url":"\/Freelance\/art-sheffield-2016\/programme\/moore-street-substation\/","summary":"","image":null,"marker-symbol":"venue-map-icon-green","colour":"green"},"geometry":{"type":"Point","coordinates":["-1.477881","53.374798"]}},{"type":"Feature","properties":{"title":"S1 Artspace","url":"\/Freelance\/art-sheffield-2016\/programme\/s1-artspace\/","summary":"","image":null,"marker-symbol":"venue-map-icon-red","colour":"red"},"geometry":{"type":"Point","coordinates":["-1.459620","53.380562"]}}]};

"map.getBounds() 似乎只返回设置中心位置的经纬度。" 它不是返回边界框的左下角和右上角坐标吗? - alphabetasoup
7个回答

155

如果您想将地图适配到标记,可以创建包含所有标记的范围。

var bounds = new mapboxgl.LngLatBounds();

markers.features.forEach(function(feature) {
    bounds.extend(feature.geometry.coordinates);
});

map.fitBounds(bounds);

2
这个可以用!我能在这里添加一些填充吗? - John the Painter
通常在调用fitBounds时,地图会添加填充以防止地图UI元素(如缩放加/减按钮、标志等)与边界重叠。如果您想要添加更多的填充,可以使用map.setZoom(map.getZoom() - 1)将地图缩小一点,但这看起来像是一个小技巧。 - Timur Bilalov
34
已经可以使用填充:map.fitBounds(bounds, { padding: 100 });。该代码将在地图上自适应调整视野范围,并在边缘添加 100 像素的填充。 - John the Painter
5
请注意,fitBounds方法无法使用单个纬度/经度坐标 - 因此,如果您只有一个标记,则您的“bounds”对象将没有用处(这是有道理的)。 - Ben Hull
1
当FeatureCollection中的某些几何图形不是点时,这段代码会出错。使用turf.js envelope()函数,以下代码可以解决问题:map.fitBounds(envelope(myfeaturecollection).bbox) - kgeo
错误:LngLatLike参数必须指定为LngLat实例,或者是一个对象{lng:<lng>,lat:<lat>}。 - mik3fly-4steri5k

22

如果您需要适用于所有GeoJSON对象,而不仅仅是一组标记的解决方案,请查看Mapbox的Turf.js

这段代码对我非常有帮助:https://bl.ocks.org/danswick/83a8ddff7fb9193176a975a02a896792

但是为了防止该链接失效,以下是重复的基础知识:

var bounds = turf.bbox(markers);
map.fitBounds(bounds, {padding: 20});

链接中提到的extent方法已被弃用,推荐使用bbox,但结果相同。


6
Mapbox的geojson-extent插件可以很好地完成这个任务。假设你的markers对象是有效的GeoJSON,你只需将其传递给geojsonExtent()函数即可获得一组边界值,然后将其传递给fitBounds()
一旦加载了geojson-extent.js文件(例如通过在HTML代码中使用<script>标签),您就可以执行此操作以使您的地图适应GeoJSON markers对象的边界:
map.fitBounds(geojsonExtent(markers));

更新

GeoJSONLint 报告说你的 markers 对象不是有效的 GeoJSON,因为每个位置上的元素被解释为字符串而不是数字。如果你从经纬度坐标中移除引号,它应该可以正常工作:

var markers = {
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "title": "Site Gallery",
        "url": "\/Freelance\/art-sheffield-2016\/programme\/site-gallery\/",
        "summary": "Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Donec id justo. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Suspendisse feugiat. Etiam rhoncus.",
        "image": "\/Freelance\/art-sheffield-2016\/site\/assets\/files\/1032\/site_gallery.jpg",
        "marker-symbol": "venue-map-icon-blue",
        "colour": "blue"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [
          -1.466439,
          53.376842
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "title": "Moore Street Substation",
        "url": "\/Freelance\/art-sheffield-2016\/programme\/moore-street-substation\/",
        "summary": "",
        "image": null,
        "marker-symbol": "venue-map-icon-green",
        "colour": "green"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [
          -1.477881,
          53.374798
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "title": "S1 Artspace",
        "url": "\/Freelance\/art-sheffield-2016\/programme\/s1-artspace\/",
        "summary": "",
        "image": null,
        "marker-symbol": "venue-map-icon-red",
        "colour": "red"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [
          -1.459620,
          53.380562
        ]
      }
    }
  ]
};


感谢您的帮助。我在控制台中遇到了这个错误 Uncaught Error: Invalid LngLat object: (NaN, 1) - 我相信它是有效的geoJSON,因为地图正在工作,但我猜markers返回的不仅仅是坐标,所以也许我需要将坐标传递到geojsonExtent中。 - John the Painter
根据这个示例(https://www.mapbox.com/mapbox.js/example/v1.0.0/static-map-from-geojson-with-geo-viewport/),它使用静态地图而不是GL JS地图,看起来我的geoJSON是有效的。 - John the Painter

1
这是我基于Mapbox示例中reduce操作的解决方案。
它适用于包含点要素和多点要素(如线)的geojson。
let bounds = geoJSON.features.reduce(function(bounds, feature) {
  if(!Array.isArray(feature.geometry.coordinates[0])) { // point feature
    return bounds.extend(feature.geometry.coordinates);
  } else {
    return feature.geometry.coordinates.reduce(function(bounds, coord) {
      return bounds.extend(coord);
    }, bounds);
  }
}, new mapboxgl.LngLatBounds());

map.fitBounds(bounds, {
  maxZoom: 12,
  padding: 30, // in px, to make markers on the top edge visible
})

如果我正在构建我的GeoJson,应该如何执行此操作:map.on('load', function() { map.addSource('points', { 'type': 'geojson', 'data': { 'type': 'FeatureCollection', 'features': [ {等。 - Hecatonchires

1

基于@timur的解决方案,改进了处理大数据集的方法,使用while循环和promise

setBounds() {
            return new Promise((resolve, reject) => {
                const bounds = new mapboxgl.LngLatBounds();
                let i = 0;
                while (i < GEO_JSON.length) {
                    bounds.extend(GEO_JSON[i].geometry.coordinates/OR/Markers_ARRAY);
                    i++;
                }
                if (bounds) {
                    resolve(bounds);
                } else {
                    reject(`error:${  bounds}`);
                }
            });
        },

fitBoundsToMarkers() {
            this.setBounds().then((bounds) => {
                    map.fitBounds(bounds, {
                        padding: {
                            top: 100,
                            bottom: 100,
                            left: 100,
                            right: 100,
                        },
                        maxZoom: 14,
                    });
            });
        },

0
为了修复地图边缘切割标记的问题,需要在获取东北坐标时将HighestLng增加0.02,并在获取西南坐标时将lowestLng减去0.02。

1
您的回答可以通过提供更多支持性信息来改进。请编辑以添加进一步的细节,例如引用或文献,以便他人可以确认您的回答的正确性。您可以在帮助中心中找到有关如何撰写好回答的更多信息。 - Community

-1

如果有人在使用React-Map-GL,有超过两个标记,并且想要在加载地图和检索Mapbox实例之前确定边界,可以执行以下操作:

const lat = myCoordinatesArray.map(location => parseFloat(location.lat));
const lng = myCoordinatesArray.map(location => parseFloat(location.lng));

// Note that WebMercatorViewport requires this format [lng, lat]
const minCoords = [Math.min.apply(null, lng), Math.min.apply(null, lat)];
const maxCoords = [Math.max.apply(null, lng), Math.max.apply(null, lat)];
const formattedGeoData = [minCoords, maxCoords];

const vPort = new WebMercatorViewport(this.state.viewport).fitBounds(formattedGeoData, {
  padding: 100
});

const { latitude, longitude, zoom } = vPort;

源代码:https://stackoverflow.com/a/40506115/1259863

你可以在构造函数中完成所有这些操作,然后在渲染函数之前获取插件初始化所需的值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接