在另一个组件中使用独立的 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。这工作得很好。所以我开始添加更多层以获取更多信息:
- TileLayer + BitmapLayer 用于整合不同底图
- IconLayer 可视化不同的兴趣点
- nebula.gl SelectionLayer 选择地图中的多个元素
这也很好用。所以我想适当地封装这个解决方案,以便它可以很容易地用作子组件。因此,我使用 create-react-app 作为脚手架并开始迁移代码。
生成的代码结构如下所示(仅相关部分):
- public
- index.html
src
- 组件
- Map.js
数据
- atlas.png
- mapping.json
- icons.json
- geodata.json
index.js
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 似乎是此用例的最新版本。
我正在开发一个 React 应用程序,该应用程序应该用于可视化不同的地理信息,主要以 GeoJSON 格式描述。更详细地说,我目前正在开发一个地图组件,稍后应该将其包含在整体前端部分中。
为此,我开始使用他们的 GeoJSON 示例应用程序代码 (https://github.com/uber/deck.gl/tree/master/examples/website/geojson) 来评估 Deck.gl。这工作得很好。所以我开始添加更多层以获取更多信息:
- TileLayer + BitmapLayer 用于整合不同底图
- IconLayer 可视化不同的兴趣点
- nebula.gl SelectionLayer 选择地图中的多个元素
这也很好用。所以我想适当地封装这个解决方案,以便它可以很容易地用作子组件。因此,我使用 create-react-app 作为脚手架并开始迁移代码。
生成的代码结构如下所示(仅相关部分):
- public
- index.html
src
- 组件
- Map.js
数据
- atlas.png
- mapping.json
- icons.json
- geodata.json
index.js
- 组件
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 似乎是此用例的最新版本。