【问题标题】:How to zoom to bounds of arcs with deck.gl?如何使用deck.gl缩放到弧的边界?
【发布时间】:2020-08-25 19:08:36
【问题描述】:

我使用deck.gl 创建了一些弧线。当您单击不同的点/多边形时,国家之间会出现不同的弧线。执行此操作时,我希望地图缩放到这些弧的边界。

为清楚起见,这里是一个示例:单击 Glasgow 时,我想放大到显示的弧线(尽可能紧密):

看来,使用 WebMercatorViewport,您可以调用 fitBounds (见:https://deck.gl/docs/api-reference/core/web-mercator-viewport#webmercatorviewport

不过,我不清楚它是如何使用的。我试图找到例子,但没有找到。我怎样才能将它添加到我拥有的东西中?

这是弧的代码:

    fetch('countries.json')
    .then(res => res.json())
    .then(data => {

      console.log('data',data)

      const inFlowColors = [
        [0, 55, 255]
      ];

      const outFlowColors = [
        [255, 200, 0]
      ];

      const countyLayer = new deck.GeoJsonLayer({
        id: 'geojson',
        data: data,
        stroked: true,
        filled: true,
        autoHighlight: true,
        lineWidthScale: 20,
        lineWidthMinPixels: 1,
        pointRadiusMinPixels: 10,
        opacity:.5,
        getFillColor: () => [0, 0, 0],
        getLineColor: () => [0,0,0],
        getLineWidth: 1,
        onClick: info => updateLayers(info.object),
        pickable: true
      });

      const deckgl = new deck.DeckGL({
        mapboxApiAccessToken: 'pk.eyJ1IjoidWJlcmRhdGEiLCJhIjoiY2pudzRtaWloMDAzcTN2bzN1aXdxZHB5bSJ9.2bkj3IiRC8wj3jLThvDGdA',
        mapStyle: 'mapbox://styles/mapbox/light-v9',
        initialViewState: {
          longitude: -19.903283,
          latitude: 36.371449,
          zoom: 1.5,
          maxZoom: 15,
          pitch: 0,
          bearing: 0
        },
        controller: true,
        layers: []
      });

      updateLayers(
        data.features.find(f => f.properties.name == 'United States' )
      );

      function updateLayers(selectedFeature) {
        const {exports, centroid, top_exports, export_value} = selectedFeature.properties;

        const arcs = Object.keys(exports).map(toId => {
          const f = data.features[toId];
          return {
            source: centroid,
            target: f.properties.centroid,
            value: exports[toId],
            top_exports: top_exports[toId],
            export_value: export_value[toId]
          };
        });

        arcs.forEach(a => {
          a.vol = a.value;
        });

        const arcLayer = new deck.ArcLayer({
          id: 'arc',
          data: arcs,
          getSourcePosition: d => d.source,
          getTargetPosition: d => d.target,
          getSourceColor: d => [0, 55, 255],
          getTargetColor: d => [255, 200, 0],
          getHeight: 0,
          getWidth: d => d.vol
        });

        deckgl.setProps({
          layers: [countyLayer, arcLayer]
        });

      }

    });

这是一个 Plunker: https://plnkr.co/edit/4L7HUYuQFM19m9rI

【问题讨论】:

  • 在弧层上开启pickable,为onClick事件添加事件句柄,计算弧的范围,然后FlyToInterpolator?只是一个基本的想法。
  • 我没有在没有 ReactJS 的情况下使用 Deck.gl 的经验,但我可以尝试给你一个想法如何在 ReactJS 中解决它,然后我们可以尝试将它移植到 vanilla。第一个想法:它使用 onClick 处理程序、草皮、WebMercatorViewport 和 LinearInterpolator。基本思路:打开弧层上的可拾取属性,然后使用 Turf 计算对象的范围,创建具有新边界的新视口,并使用 LinearInterpolator(或 FlyToInterpolator)过渡到新视口。
  • 我需要更好地理解“这些弧的边界”的含义。您想要一个从源到目的地的所有弧线(从选定区域开始)都可见的视图吗?
  • @lezan re:“那些弧线的边界”:是的,这正是我的意思。我在帖子中添加了一个示例来进一步解释。
  • @lezan FlyToInterpolator 似乎是正确的方法,但我不确定如何添加边界。看这个例子--codepen.io/vis-gl/pen/pKvrGP-- 我只看到如何添加纬度/经度。感谢您的帮助!

标签: deck.gl


【解决方案1】:

我尝试让它变得简单,从使用 ReactJs 的原始实现开始,然后尝试转换为 vanilla。

在 ReactJS 中我会做类似的事情。

react-map-gl 导入LinearInterpolatorWebMercatorViewport

import {LinearInterpolator, WebMercatorViewport} from 'react-map-gl';

然后我为视口定义一个useEffect:

const [viewport, setViewport] = useState({
    latitude: 37.7577,
    longitude: -122.4376,
    zoom: 11,
    bearing: 0,
    pitch: 0
});

然后我将定义一个层来显示:

const layerGeoJson = new GeoJsonLayer({
    id: 'geojson',
    data: someData,
    ...
    pickable: true,
    onClick: onClickGeoJson,
    });

现在我们需要定义onClickGeoJson

const onClickGeoJson = useCallback((event) => {
    const feature = event.features[0];
    const [minLng, minLat, maxLng, maxLat] = bbox(feature); // Turf.js
    const viewportWebMercator = new WebMercatorViewport(viewport);
    const {longitude, latitude, zoom} = viewport.fitBounds([[minLng, minLat], [maxLng, maxLat]], {
        padding: 20
    });
    viewportWebMercator = {
        ...viewport,
        longitude,
        latitude,
        zoom,
        transitionInterpolator: new LinearInterpolator({
            around: [event.offsetCenter.x, event.offsetCenter.y]
        }),
        transitionDuration: 1500,
    };
    setViewport(viewportWebMercator);
}, []);

第一个问题:通过这种方式,我们在单击的点或多边形上进行拟合,但您想要的是拟合弧。我认为克服此类问题的唯一方法是在多边形内添加关于弧线边界的参考。您可以预先计算每个特征的边界并将它们存储在您的 geojson 中(单击的元素),或者您可以仅在 feature.properties 中存储一个引用以指向另一个对象,您可以在其中定义边界(您也可以即时计算它们)。

const dataWithComputeBounds = { 
   'firstPoint': bounds_arc_computed,
   'secondPoint': bounds_arc_computed,
   ....
}

bounds_arc_computed 需要是一个对象

bounds_arc_computed = {
    minLng, minLat, maxLng, maxLat,
}

然后onClick函数就拿参考

   const { minLng, minLat, maxLng, maxLat} = dataWithComputedBounds[event.features[0].properties.reference];
   const viewportWebMercator = new WebMercatorViewport(viewport);
   ...

现在只需定义我们的主要元素:

return (
    <DeckGL
        layers={[layerGeoJson]}
        initialViewState={INITIAL_VIEW_STATE}
        controller
    >
        <StaticMap
            reuseMaps
            mapStyle={mapStyle}
            preventStyleDiffing
            mapboxApiAccessToken={YOUR_TOKEN}
        />
    </DeckGL>
);

此时我们已经非常接近您已经链接的内容 (https://codepen.io/vis-gl/pen/pKvrGP),但您需要使用 deckgl.setProps() onClick 函数而不是 setViewport 来更改您的视口。

这对你有意义吗?

【讨论】:

  • 谢谢!有道理,也很有帮助。
  • 如果您需要进一步的帮助,请告诉我。
  • fitBounds 返回一个视口对象,基于我们作为参数给出的边界,但是我们如何根据边界控制缩放级别呢?我正在尝试实现相同的目标,但缩放级别不适合所提到的范围。知道为什么会这样吗?
猜你喜欢
  • 2017-04-06
  • 2018-04-26
  • 1970-01-01
  • 2019-10-02
  • 1970-01-01
  • 2018-11-07
  • 2018-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多