【问题标题】:Set Map bounds based on multiple marker Lng,Lat根据多个标记 Lng,Lat 设置地图边界
【发布时间】:2018-11-16 01:31:03
【问题描述】:

我正在使用 vue 并安装了 vue-mapbox 组件,位于:https://soal.github.io/vue-mapbox/#/quickstart

我已将 js 和 css 更新到最新版本,也添加到 index.html:

<!-- Mapbox GL CSS -->
<link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.css" rel="stylesheet" />
<!-- Mapbox GL JS -->
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.51.0/mapbox-gl.js"></script>

我正在尝试使用此组件来设置地图边界的默认视图,使用 centerboundsfitBounds 到 Lng,Lat 坐标列表。那么,基本上,如何插入 lng、lat 坐标并让地图默认将这些坐标居中在容器内?

这是我创建的一个组件,在 vue 中称为 Map,用于使用上面列出的组件 vue-mapbox 输出地图框:

<template>
  <b-row id="map" class="d-flex justify-content-center align-items-center my-2">
    <b-col cols="24" id="map-holder" v-bind:class="getMapType">
        <mgl-map
          id="map-obj"
          :accessToken="accessToken"
          :mapStyle.sync="mapStyle"
          :zoom="zoom"
          :center="center"
          container="map-holder"
          :interactive="interactive"
          @load="loadMap" 
          ref="mapbox" />
    </b-col>
  </b-row>
</template>

<script>
import { MglMap } from 'vue-mapbox'
export default {
  components: {
    MglMap
  },
  data () {
    return {
      accessToken: 'pk.eyJ1Ijoic29sb2dob3N0IiwiYSI6ImNqb2htbmpwNjA0aG8zcWxjc3IzOGI1ejcifQ.nGL4NwbJYffJpjOiBL-Zpg',
      mapStyle: 'mapbox://styles/mapbox/streets-v9', // options:  basic-v9, streets-v9, bright-v9, light-v9, dark-v9, satellite-v9
      zoom: 9,
      map: {}, // Holds the Map...
      fitBounds: [[-79, 43], [-73, 45]]
    }
  },
  props: {
    interactive: {
      default: true
    },
    resizeMap: {
      default: false
    },
    mapType: {
      default: ''
    },
    center: {
      type: Array,
      default: function () { return [4.899, 52.372] }
    }
  },
  computed: {
    getMapType () {
      let classes = 'inner-map'
      if (this.mapType !== '') {
        classes += ' map-' + this.mapType
      }
      return classes
    }
  },
  watch: {
    resizeMap (val) {
      if (val) {
        this.$nextTick(() => this.$refs.mapbox.resize())
      }
    },
    fitBounds (val) {
      if (this.fitBounds.length) {
        this.MoveMapCoords()
      }
    }
  },
  methods: {
    loadMap () {
      if (this.map === null) {
        this.map = event.map // store the map object in here...
      }
    },
    MoveMapCoords () {
      this.$refs.mapbox.fitBounds(this.fitBounds)
    }
  }
}
</script>

<style lang="scss" scoped>
  @import '../../styles/custom.scss';

  #map {
    #map-obj {
      text-align: justify;
      width: 100%;
    }
    #map-holder {
      &.map-modal {
        #map-obj {
          height: 340px;
        }
      }
      &.map-large {
        #map-obj {
          height: 500px;
        }
      }
    }
    .mapboxgl-map {
      border: 2px solid lightgray;
    }
  }
</style>

所以,我在这里尝试使用fitBounds 方法来让地图以 2 Lng 为中心进行初始化,这里的纬度坐标:[[-79, 43], [-73, 45]]

如何做到这一点?好的,我想我的代码可能有点错误,所以我认为 fitBounds 应该看起来像这样:

fitBounds: () => {
  return { bounds: [[-79, 43], [-73, 45]] }
}

无论如何,将地图框的初始位置设置为以 2 个或更多坐标为中心是最困难的。有人成功了吗?

好的,所以我最终创建了一个过滤器来为 bbox 添加空间,如下所示:

Vue.filter('addSpaceToBBoxBounds', function (value) {
  if (value && value.length) {
    var boxArea = []
    for (var b = 0, len = value.length; b < len; b++) {
      boxArea.push(b > 1 ? value[b] + 2 : value[b] - 2)
    }
    return boxArea
  }
  return value
})

目前看来这已经足够了。而不是像这样使用它:

let line = turf.lineString(this.markers)
mapOptions['bounds'] = this.$options.filters.addSpaceToBBoxBounds(turf.bbox(line))
return mapOptions

【问题讨论】:

    标签: javascript vuejs2 mapbox-gl-js


    【解决方案1】:

    将地图的初始位置设置为以 2 为中心或 更多坐标

    您可以使用Turf.js 计算所有点特征的边界框,并使用bounds 地图选项使用此bbox 初始化地图:

    http://turfjs.org/docs#bbox

    https://www.mapbox.com/mapbox-gl-js/api/#map

    【讨论】:

    • 谢谢,我采纳了你的建议并让它工作了,只需要弄清楚如何在此处为边界添加一些填充,因为一些标记在边界线上并且没有有空间展示它们。
    • 有一个open GL JS issue 涉及向bounds 映射选项添加填充(以及其他选项)。那里描述的解决方法:... 将不带附加 fitBounds 选项的边界设置为 Map 选项,以使其在初始化时足够接近,然后立即调用具有相同边界但具有附加选项的 map.fitBounds。 @ 987654325@
    • map.fitBounds(bbox, {padding: {top: 20, bottom: 20, left: 20, right: 20} });
    • 或简写为map.fitBounds(bbox, { padding: 20 });
    • 我实际上最终创建了一个过滤器,将 +2 和 -2 缩放级别添加到边界,如下所示:Vue.filter('addSpaceToBBoxBounds', function (value) { if (value &amp;&amp; value.length) { var boxArea = [] for (var b = 0, len = value.length; b &lt; len; b++) { boxArea.push(b &gt; 1 ? value[b] + 2 : value[b] - 2) } return boxArea } return value })
    【解决方案2】:

    如果你不想为这个任务使用另一个库,我想出了一个简单的方法来获取边界框,这里是一个简化的 vue 组件。

    在 vue 组件上存储地图对象时也要小心,你不应该让它反应,因为它会破坏 mapboxgl 这样做

    import mapboxgl from "mapbox-gl";
    
    export default {
        data() {
            return {
                points: [
                    {
                        lat: 43.775433,
                        lng: -0.434319
                    },
                    {
                        lat: 44.775433,
                        lng: 0.564319
                    },
                    // Etc...
                ]
            }
        },
        computed: {
            boundingBox() {
                if (!Array.isArray(this.points) || !this.points.length) {
                    return undefined;
                }
    
                let w, s, e, n;
    
                // Calculate the bounding box with a simple min, max of all latitudes and longitudes
                this.points.forEach((point) => {
                    if (w === undefined) {
                        n = s = point.lat;
                        w = e = point.lng;
                    }
    
                    if (point.lat > n) {
                        n = point.lat;
                    } else if (point.lat < s) {
                        s = point.lat;
                    }
                    if (point.lng > e) {
                        e = point.lng;
                    } else if (point.lng < w) {
                        w = point.lng;
                    }
                });
                return [
                    [w, s],
                    [e, n]
                ]
            },
        },
        watch: {
            // Automatically fit to bounding box when it changes
            boundingBox(bb) {
                if (bb !== undefined) {
                    const cb = () => {
                        this.$options.map.fitBounds(bb, {padding: 20});
                    };
                    if (!this.$options.map) {
                        this.$once('map-loaded', cb);
                    } else {
                        cb();
                    }
                }
            },
            // Watch the points to add the markers
            points: {
                immediate: true, // Run handler on mount (not needed if you fetch the array of points after it's mounted)
                handler(points, prevPoints) {
                    // Remove the previous markers
                    if (Array.isArray(prevPoints)) {
                        prevPoints.forEach((point) => {
                            point.marker.remove();
                        });
                    }
    
                    //Add the new markers
                    const cb = () => {
                        points.forEach((point) => {
    
                            // create a HTML element for each feature
                            const el = document.createElement('div');
                            el.className = 'marker';
                            el.addEventListener('click', () => {
                                // Marker clicked
                            });
                            el.addEventListener('mouseenter', () => {
                                point.hover = true;
                            });
                            el.addEventListener('mouseleave', () => {
                                point.hover = false;
                            });
    
                            // make a marker for each point and add to the map
                            point.marker = new mapboxgl.Marker(el)
                                .setLngLat([point.lng, point.lat])
                                .addTo(this.$options.map);
                        });
                    };
                    if (!this.$options.map) {
                        this.$once('map-loaded', cb);
                    } else {
                        cb();
                    }
                }
            }
        },
        map: null, // This is important to store the map without reactivity
        methods: {
            mapLoaded(map) {
                this.$options.map = map;
                this.$emit('map-loaded');
            },
        },
    }
    

    只要您的点不在太平洋中部,在经度 180° 和 -180° 之间,它应该可以正常工作,如果是,只需添加一个检查以在返回时反转东西边界框应该可以解决问题

    【讨论】:

      【解决方案3】:

      我创建了一些简单的函数来计算一个边界框,其中包含给定[lng, lat] 对(标记)的最西南角和最东北角。然后,您可以使用 Mapbox GL JS map.fitBounds(bounds, options?) 函数将地图缩放到标记集。

      始终牢记:
      lng (lon):经度(伦敦 = 0,伯尔尼 = 7.45,纽约 = -74)
      → 越低越西化

      lat:纬度(赤道 = 0,伯尔尼 = 46.95,开普敦 = -33.9)
      → 越低越南

      getSWCoordinates(coordinatesCollection) {
        const lowestLng = Math.min(
          ...coordinatesCollection.map((coordinates) => coordinates[0])
        );
        const lowestLat = Math.min(
          ...coordinatesCollection.map((coordinates) => coordinates[1])
        );
      
        return [lowestLng, lowestLat];
      }
      
      getNECoordinates(coordinatesCollection) {
        const highestLng = Math.max(
          ...coordinatesCollection.map((coordinates) => coordinates[0])
        );
        const highestLat = Math.max(
          ...coordinatesCollection.map((coordinates) => coordinates[1])
        );
      
        return [highestLng, highestLat];
      }
      
      calcBoundsFromCoordinates(coordinatesCollection) {
        return [
          getSWCoordinates(coordinatesCollection),
          getNECoordinates(coordinatesCollection),
        ];
      }
      

      要使用该功能,您只需调用calcBoundsFromCoordinates 并输入一个包含所有标记坐标的数组:

      calcBoundsFromCoordinates([
        [8.03287, 46.62789],
        [7.53077, 46.63439],
        [7.57724, 46.63914],
        [7.76408, 46.55193],
        [7.74324, 46.7384]
      ])
      
      // returns [[7.53077, 46.55193], [8.03287, 46.7384]]
      

      总的来说,使用 Mapbox 的mapboxgl.LngLatBounds() 函数可能会更容易。

      正如jscastroScale MapBox GL map to fit set of markers 的回答中提到的那样,您可以像这样使用它:

      const bounds = mapMarkers.reduce(function (bounds, coord) {
        return bounds.extend(coord);
      }, new mapboxgl.LngLatBounds(mapMarkers[0], mapMarkers[0]));
      

      然后只需调用

      map.fitBounds(bounds, {
       padding: { top: 75, bottom: 30, left: 90, right: 90 },
      });
      

      【讨论】:

        猜你喜欢
        • 2020-08-13
        • 2011-07-23
        • 2016-05-10
        • 1970-01-01
        • 1970-01-01
        • 2011-11-19
        • 2011-02-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多