在另一个组件中使用独立的 GeoJson 示例代码是否有特定要求?

Are there specific requirements to use the standalone GeoJson example code in another component?

我正在开发一个 React 应用程序,该应用程序应该用于可视化不同的地理信息,主要以 GeoJSON 格式描述。更详细地说,我目前正在开发一个地图组件,稍后应该将其包含在整体前端部分中。

为此,我开始使用他们的 GeoJSON 示例应用程序代码 (https://github.com/uber/deck.gl/tree/master/examples/website/geojson) 来评估 Deck.gl。这工作得很好。所以我开始添加更多层以获取更多信息:

这也很好用。所以我想适当地封装这个解决方案,以便它可以很容易地用作子组件。因此,我使用 create-react-app 作为脚手架并开始迁移代码。

生成的代码结构如下所示(仅相关部分):

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>deck.gl Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      body {
        margin: 0;
        font-family: sans-serif;
        width: 100vw;
        height: 100vh;
        overflow: hidden;
      }
      .tooltip {
        pointer-events: none;
        position: absolute;
        z-index: 9;
        font-size: 12px;
        padding: 8px;
        background: #000;
        color: #fff;
        min-width: 160px;
        max-height: 240px;
        overflow-y: hidden;
      }
    </style>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

index.js:

import React from "react";
import ReactDOM from "react-dom";

import Map from "./components/Map";

ReactDOM.render(<Map />, document.querySelector("#app"));

Map.js:

import React, { Component } from "react";
import DeckGL, {
  GeoJsonLayer,
  TileLayer,
  BitmapLayer,
  IconLayer
} from "deck.gl";
import myJson from "../data/geodata.json";
import iconLocations from "../data/icons.json";
import { SelectionLayer, SELECTION_TYPE } from "nebula.gl";

const INITIAL_VIEW_STATE = {
  latitude: SOME_LAT,
  longitude: SOME_LONG,
  zoom: 14,
  maxZoom: 19,
  pitch: 0,
  bearing: 0
};

export default class Map extends Component {
  state = {
    hoveredObject: null
  };

  _onHover = ({ x, y, object }) => {
    this.setState({ x, y, hoveredObject: object });
  };

  _renderLayers() {
    const {
      iconMapping = "../data/mapping.json",
      iconAtlas = "../data/atlas.png",
      viewState
    } = this.props;

    let data = myJson;
    data.features = data.features.filter(
      feature => feature.geometry.coordinates.length > 0
    );
    let size = viewState ? Math.min(Math.pow(1.5, viewState.zoom - 10), 1) : 1;

    return [
      new TileLayer({
        opacity: 1,
        minZoom: 0,
        maxZoom: 30,

        renderSubLayers: props => {
          const {x, y, z, bbox} = props.tile;
          const {west, south, east, north} = bbox;
          const base = 1 + ((x + y) % 4);
          return new BitmapLayer(props, {
            image: `http://${base}.base.maps.cit.api.here.com/maptile/2.1/maptile/newest/normal.day/${z}/${x}/${y}/512/png`,
            bounds: [west, south, east, north]
          });
        }
      }),
      new GeoJsonLayer({
        id: "geojson",
        data,
        opacity: 1,
        stroked: false,
        lineWidthMinPixels: 1,
        getLineColor: [255, 0, 0],
        pickable: true,
        autoHighlight: true,
        highlightColor: [0, 100, 255, 80],
        onHover: this._onHover
      }),
      new SelectionLayer({
        id: "selection",
        selectionType: SELECTION_TYPE.RECTANGLE,
        onSelect: ({ pickingInfos }) => {
          this.setState({
            selectedFeatureIndexes: pickingInfos.map(pi => pi.index)
          });
          console.log(pickingInfos);
        },
        layerIds: ["geojson"],

        getTentativeFillColor: () => [255, 0, 255, 100],
        getTentativeLineColor: () => [0, 0, 255, 255],
        getTentativeLineDashArray: () => [0, 0],
        lineWidthMinPixels: 3
      }),
      new IconLayer({
        id: "icon",
        data: iconLocations,
        wrapLongitude: true,
        getPosition: d => d.coordinates,
        iconAtlas,
        iconMapping,
        getIcon: d => d.type,
        getSize: size,
        sizeScale: 50
      })
    ];
  }

  _renderTooltip = () => {
    const { x, y, hoveredObject } = this.state;
    return (
      hoveredObject && (
        // some JSX for tooltip ...
      )
    );
  };

  render() {
    const { viewState, controller = true } = this.props;

    return (
      <DeckGL
        layers={this._renderLayers()}
        initialViewState={INITIAL_VIEW_STATE}
        viewState={viewState}
        controller={controller}
      >
        {this._renderTooltip}
      </DeckGL>
    );
  }
}

Map.js 中使用的代码实际上与扩展示例代码时使用的代码(按预期工作)完全相同,只是渲染方式略有不同。我希望它以相同的方式工作,但我得到以下输出:https://imgur.com/dyat3jc

如果我删除 TileLayer + BitmapLayer,第一个错误将消失并且至少 GeoJSON 数据将正确显示,只是没有底图。 IconLayer 也不起作用,而 SelectionLayer 没有问题,就像 GeojsonLayer 一样。

我对 React 和 Deck.gl 还很陌生,所以有什么我忘记正确迁移示例代码的地方吗?

更新:

我稍微重构了代码并使图标正常工作。我还通过删除道具到 BitmapLayer 的传播来摆脱使用 TileLayer 时的错误消息(props.data 为空,在示例代码中使用时似乎没有问题,但不知何故在这里导致问题),但是位图根本不显示,即使图像 link 和边界是正确的。

import React from "react";
import DeckGL from "@deck.gl/react";
import { GeoJsonLayer, IconLayer, BitmapLayer } from "@deck.gl/layers";
import { TileLayer } from "@deck.gl/geo-layers";
import { SelectionLayer, SELECTION_TYPE } from "nebula.gl";

// test data
import jsonTestFile from "../data/testJson.json";
import signLocations from "../data/signs.json";
import iconAtlas from "../data/trafficSignAtlas.png";
import iconMapping from "../data/trafficSignMapping.json";

// Initial viewport settings
const initialViewState = {
  latitude: 48.872578,
  longitude: 11.431032,
  zoom: 14,
  pitch: 0,
  bearing: 0
};

const LayerEnum = Object.freeze({
  GEOJSON: 1,
  TILE: 2,
  SELECTION: 3,
  ICON: 4
});

export default class Map extends React.Component {
  state = {
    selectedFeatureIndexes: []
  };

  renderLayer = ({ layerType, options }) => {
    switch (layerType) {
      case LayerEnum.GEOJSON:
        return new GeoJsonLayer({
          id: "geojson",
          opacity: 1,
          stroked: false,
          lineWidthMinPixels: 1,
          getLineColor: [255, 0, 0],
          pickable: true,
          autoHighlight: true,
          highlightColor: [0, 100, 255, 80],
          ...options
        });
      case LayerEnum.TILE:
        return new TileLayer({
          opacity: 1,
          // https://wiki.openstreetmap.org/wiki/Zoom_levels
          minZoom: 0,
          maxZoom: 30,

          renderSubLayers: ({ id, tile }) => {
            const { x, y, z, bbox } = tile;
            const { west, south, east, north } = bbox;
            const base = 1 + ((x + y) % 4);
            console.log(tile);
            return new BitmapLayer({
              id,
              image: `http://${base}.base.maps.cit.api.here.com/maptile/2.1/maptile/newest/normal.day/${z}/${x}/${y}/512/png`,
              bounds: [west, south, east, north]
            });
          }
        });
      case LayerEnum.SELECTION:
        return new SelectionLayer({
          id: "selection",
          selectionType: SELECTION_TYPE.RECTANGLE,
          onSelect: ({ pickingInfos }) => {
            this.setState({
              selectedFeatureIndexes: pickingInfos.map(pi => pi.index)
            });
            console.log(pickingInfos);
          },
          layerIds: ["geojson"],

          getTentativeFillColor: () => [255, 0, 255, 100],
          getTentativeLineColor: () => [0, 0, 255, 255],
          getTentativeLineDashArray: () => [0, 0],
          lineWidthMinPixels: 3,
          ...options
        });
      case LayerEnum.ICON:
        return new IconLayer({
          id: "icon",
          wrapLongitude: true,
          getPosition: d => d.coordinates,
          getIcon: d => d.type,
          getSize: 1,
          sizeScale: 50,
          ...options
        });
      default:
        console.error("Unknown errer type detected!");
        return null;
    }
  };

  renderLayers = layers => {
    return layers.map(this.renderLayer);
  };

  render() {
    // preprocess test data
    let data = jsonTestFile;
    data.features = data.features.filter(
      feature => feature.geometry.coordinates.length > 0
    );

    const layers = this.renderLayers([
      {
        layerType: LayerEnum.GEOJSON,
        options: { data }
      },
      {
        layerType: LayerEnum.SELECTION,
        options: {}
      },
      {
        layerType: LayerEnum.ICON,
        options: {
          data: signLocations,
          iconAtlas,
          iconMapping
        }
      },
      {
        layerType: LayerEnum.TILE,
        options: {}
      }
    ]);

    const { viewState, controller = true } = this.props;
    return (
      <DeckGL
        initialViewState={initialViewState}
        viewState={viewState}
        controller={controller}
        layers={layers}
      />
    );
  }
}

看来是依赖版本的问题。示例代码带有 deck.gl@7.0.0-beta,而我使用的是 deck.gl@7.2.2。从版本 7.2.0 开始,此代码不再有效,7.1.11 似乎是此用例的最新版本。