如何使用deck.gl缩放到弧的边界?

问题描述

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

为清楚起见,下面是一个示例:单击格拉斯哥时,我想缩放到所示的弧线(尽可能紧密):

map zoomed to arc originating from 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],getLineColor: () => [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,255],getTargetColor: d => [255,getHeight: 0,getWidth: d => d.vol
        });

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

      }

    });

这里是柱塞: https://plnkr.co/edit/4L7HUYuQFM19m9rI

解决方法

我试图简化一下,从使用ReactJs的原始实现开始,然后尝试转换为原始版本。

在ReactJS中,我将做类似的事情。

LinearInterpolator导入WebMercatorViewportreact-map-gl

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,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,maxLat,}

然后ononClick函数仅作为参考

   const { minLng,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来更改视口。

这对您有意义吗?