reactjs - 在 React 中使用 OpenLayers
问题描述
我是一名学生,正在创建一个尝试使用 Openlayers 和 React 在地图上显示数据和信息的大型项目。至此,我创建了一个基本的两页项目,其中地图是应用程序的核心部分。对于每个页面,我都有一个包含侧边栏的主要组件、包含地图本身的查看器组件以及特定于该页面的一些附加组件。
查看器类是我为每个页面编写的特殊组件。该组件处理与页面的所有用户交互。它也发送和接收来自主组件的信息。我选择为每个页面创建一个单独的查看器类,因为每个页面的工作方式不同。它们可能会加载不同的东西或处理完全不同的交互。如果我有一个每个页面都可以使用的查看器类,那么它会变得很大并且必须有很多 if 语句来检查在处理每个单独的页面时要运行什么代码。
这是主页查看器的示例。在构造函数中,我创建了地图并为弹出窗口添加了一些引用,当用户单击地图上的某个功能时会显示该弹出窗口。此弹出窗口显示有关该功能的一些基本信息,让用户将其添加到他的功能列表中。
在 ComponentDidMount 中,我首先让 Map 类添加 boundrieslayer 并提供一个函数,以便在单击该层的一个特征时调用。我还为地图的弹出窗口创建了覆盖窗口。这将在单击功能时显示。
resetMapLayers 是一个将在每个 Render 上调用的函数。在这里,我让地图检查是否使用什么背景 Tilelayer 以及是否应该显示顶层。
featureSelected() 是处理
顶层特征的点击事件的函数。这将创建一个包含
功能基本信息的弹出窗口。closePopup 将在弹出窗口关闭时调用。这将使
地图取消选择单击的功能。否则,当弹出窗口关闭时,它将保持选中状态。它还将从屏幕上删除覆盖(弹出)。addFeature() 是当用户选择将此功能添加到他的功能列表时将调用的函数。这可以由用户在该功能的弹出窗口中完成
.
import React, { Component } from "react";
import { connect } from "react-redux";
import Map from "../Map/Map";
import "ol/ol.css";
import styles from "./Viewer.module.scss";
import Overlay from "ol/Overlay";
import Button from "../UI/Button/Button";
class MainViewer extends Component {
constructor(props) {
super(props);
Map.createNewMap();
this.popup = React.createRef();
this.popupContent = React.createRef();
}
componentDidMount() {
Map.addBoundriesLayer(this.featureSelected);
Map.map.setTarget("map");
let container = this.popup.current;
let overlay = new Overlay({
element: container,
autoPan: true,
autoPanAnimation: {
duration: 250,
},
});
this.overlay = overlay;
Map.map.addOverlay(overlay);
}
resetMapLayers() {
Map.setBackgroundTileLayer(this.props.type);
Map.togglePlotBoundriesLayers(this.props.plotBoundriesState);
}
featureSelected = (event, select) => {
if (event.selected[0]) {
this.selectedFeature = event.selected[0];
let selectedFeature = {
id: event.selected[0].id_,
gewasgroepnaam: event.selected[0].getProperties().GEWASGROEP,
gewasnaam: event.selected[0].getProperties().LBLHFDTLT,
oppervlak: (event.selected[0].getProperties().OPPERVL / 10000).toFixed(
2
),
coords: event.selected[0].getProperties().geometry.extent_,
};
let content = this.popupContent.current;
content.innerHTML =
"<p><strong>Name: </strong>" +
selectedFeature.name +
"</p>" +
"<p><strong>Exact Name: </strong>" +
selectedFeature.exactName +
"</p>" +
"<p><strong>Area: </strong>" +
selectedFeature.area +
" ha</p>";
this.overlay.setPosition(event.mapBrowserEvent.coordinate);
}
};
closePopup() {
this.overlay.setPosition(undefined);
Map.clearSelect();
return false;
}
addFeature() {
this.overlay.setPosition(undefined);
Map.clearSelect();
this.props.featureAddedHandler(this.selectedFeature);
}
render() {
this.resetMapLayers();
return (
<div>
<div id="map" className={styles.Map}></div>
<div ref={this.popup} className={styles.OlPopup}>
<div className={styles.OlPopupButtonsDiv}>
<Button
btnType="Danger"
className={[styles.PopupButton, styles.ClosePopupButton].join(
" "
)}
clicked={() => this.closePopup()}
>
Annuleer
</Button>
<Button
btnType="Success"
className={[styles.PopupButton, styles.AddPopupButton].join(" ")}
clicked={() => this.addFeature()}
>
Voeg Toe
</Button>
</div>
<div ref={this.popupContent}></div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
type: state.mapDetails.type,
plotBoundriesState: state.mapDetails.state,
};
};
export default connect(mapStateToProps)(MainViewer);
Map 类包含实际的地图。它处理所有特定的地图问题。它允许向地图添加图层,创建初始地图,添加地图交互,...
在构造函数中,我调用方法来初始创建具有 TileLayer 背景的基本地图。
createMap 和 createNewMap 方法让我自己创建地图对象。
createBackgroundLayerGroups() 创建作为背景层的 Tilelayer。它可以是 OSM 或 Bing 地图。可见性属性让我处理要显示的内容。
clearAllBoundriesLayers() 删除所有可以放在 Tilelayer 顶部的边界层。它会删除每一层并清除已添加到这些层的交互的选择。我这样做是因为当我更改页面时,图层将被删除。
addBoundriesLayer 让我设置并添加边界层。这是将放置在 TileLayer 之上的矢量图层。
addUsersPlotBoundriesLayer 的作用与“addBoundriesLayer”相同,但对用户拥有的所有层都执行此操作。该函数将在另一个仅显示用户特征的页面中调用。
setExtentOfMapByUserFeaters 让我通过给定范围或用户的特征来设置地图的范围。
setInteractionForPlotBoundriesLayer 为 PlotBoundriesLayer 添加交互。
setHoverInteractionForUserPlotBoundries 为 plotUserBoundriesLayer 添加悬停交互。
clearSelect 清除所有选定的特征。因此,它们将不再突出显示。
setBackgroundTileLayer 让我展示一个特定的背景 TileLayer。
togglePlotBoundriesLayers 让我隐藏或显示然后显示的向量层。
import Map from "ol/Map";
import TileWMS from "ol/source/TileWMS";
import TileLayer from "ol/layer/Tile";
import View from "ol/View";
import OSM from "ol/source/OSM";
import BingMaps from "ol/source/BingMaps";
import VectorSource from "ol/source/Vector";
import { bbox as bboxStrategy } from "ol/loadingstrategy";
import GeoJSON from "ol/format/GeoJSON";
import { Vector, Group, Tile } from "ol/layer";
import Select from "ol/interaction/Select";
import { Feature } from "ol";
import { Polygon } from "ol/geom";
import { Fill, Stroke, Style } from "ol/style";
class OlMap {
constructor() {
this.createNewMap();
this.createBackgroundLayerGroups();
}
createNewMap() {
this.map = this.createMap();
}
createMap() {
return new Map({
target: null,
layers: [],
view: new View({
center: [594668.0262129545, 6602083.305674396],
maxZoom: 19,
zoom: 14,
}),
});
}
createBackgroundLayerGroups() {
this.layersOSM = new Group({
layers: [
new Tile({
source: new OSM(),
}),
new Tile({
source: new BingMaps({
imagerySet: "Aerial",
key: process.env.REACT_APP_BING_MAPS,
}),
visible: false,
}),
],
});
}
clearAllBoundriesLayers() {
this.map.getLayers().forEach((layer) => {
if (
layer.get("name") === "plotBoundriesLayer" ||
layer.get("name") === "plotUserBoundriesLayer"
) {
layer.getSource().clear();
this.map.removeLayer(layer);
}
});
if (this.select) {
this.select.getFeatures().clear();
}
}
addBoundriesLayer(featureSelected) {
this.clearAllBoundriesLayers();
let vectorSource = new VectorSource({
format: new GeoJSON(),
minScale: 15000000,
loader: function (extent, resolution, projection) {
/*
Link for the DLV
let url = process.env.REACT_APP_MAP_API +
extent.join(",") +
",EPSG:3857";
*/ let url = process.env.REACT_APP_MAP_API +
extent.join(",") +
",EPSG:3857";
// */
let xhr = new XMLHttpRequest();
xhr.open("GET", url);
let onError = function () {
vectorSource.removeLoadedExtent(extent);
};
xhr.onerror = onError;
xhr.onload = function () {
if (xhr.status === 200) {
let features = vectorSource
.getFormat()
.readFeatures(xhr.responseText);
features.forEach(function (feature) {
//ID for the DLV
//feature.setId(feature.get("OBJ_ID"));
feature.setId(feature.get("OIDN"));
});
vectorSource.addFeatures(features);
} else {
onError();
}
};
xhr.send();
},
strategy: bboxStrategy,
});
let vector = new Vector({
//minZoom: 13,
source: vectorSource,
});
this.setInteractionForPlotBoundriesLayer(vector, featureSelected);
vector.set("name", "plotBoundriesLayer");
this.map.addLayer(vector);
}
addUsersPlotBoundriesLayer(featureSelected, featureHovered, newFeatures) {
this.clearAllBoundriesLayers();
if (newFeatures.length > 0) {
let vectorSource = new VectorSource({
format: new GeoJSON(),
minScale: 15000000,
strategy: bboxStrategy,
});
newFeatures.forEach((newFeature) => {
let feature = new Feature({
geometry: new Polygon([newFeature.geometry]),
});
feature.setId(newFeature.plotId);
vectorSource.addFeature(feature);
});
let vector = new Vector({
//minZoom: 13,
source: vectorSource,
});
this.setInteractionForPlotBoundriesLayer(vector, featureSelected);
this.setHoverInteractionForUserPlotBoundries(vector, featureHovered);
vector.set("name", "plotUserBoundriesLayer");
this.plotsExtent = vectorSource.getExtent();
this.map.addLayer(vector);
}
}
setExtentOfMapByUserFeaters(extent) {
if (extent === undefined) {
if (this.plotsExtent !== undefined && this.plotsExtent[0] !== Infinity) {
this.map.getView().fit(this.plotsExtent);
}
} else {
this.map.getView().fit(extent);
}
}
setInteractionForPlotBoundriesLayer(layer, featureSelected) {
this.select = new Select({
layers: [layer],
});
this.select.on("select", (event) => featureSelected(event, this.select));
this.map.addInteraction(this.select);
}
setHoverInteractionForUserPlotBoundries(layer, featureHovered) {
this.hoveredFeature = null;
let defaultStyle = new Style({
stroke: new Stroke({
width: 2,
color: "#9c1616",
}),
fill: new Fill({ color: "#c04e4e" }),
});
let hoveredStyle = new Style({
stroke: new Stroke({
width: 2,
color: "#9c1616",
}),
fill: new Fill({ color: "#9c1616" }),
});
this.map.on("pointermove", (e) => {
layer
.getSource()
.getFeatures()
.forEach((feature) => {
feature.setStyle(defaultStyle);
});
let newFeature = null;
this.map.forEachFeatureAtPixel(e.pixel, (f) => {
newFeature = f;
newFeature.setStyle(hoveredStyle);
return true;
});
if (newFeature) {
if (
this.hoveredFeature === null ||
this.hoveredFeature !== newFeature
) {
this.hoveredFeature = newFeature;
featureHovered(this.hoveredFeature.id_);
}
} else {
if (this.hoveredFeature !== null) {
this.hoveredFeature = null;
featureHovered(null);
}
}
});
}
hoveredSideBarFeatureHandler(hoveredFeatureId) {
let defaultStyle = new Style({
stroke: new Stroke({
width: 2,
color: "#9c1616",
}),
fill: new Fill({ color: "#c04e4e" }),
});
let hoveredStyle = new Style({
stroke: new Stroke({
width: 2,
color: "#9c1616",
}),
fill: new Fill({ color: "#9c1616" }),
});
this.map.getLayers().forEach((layer) => {
if (layer.get("name") === "plotUserBoundriesLayer") {
layer
.getSource()
.getFeatures()
.forEach((feature) => {
if (feature.id_ === hoveredFeatureId) {
feature.setStyle(hoveredStyle);
} else {
feature.setStyle(defaultStyle);
}
});
}
});
}
clearSelect() {
this.select.getFeatures().clear();
}
setBackgroundTileLayer(type) {
if (this.backgroundTileType === null) {
this.backgroundTileType = "OPENSTREETMAP";
}
if (this.map.getLayers().getArray().length === 0) {
this.map.setLayerGroup(this.layersOSM);
} else {
if (this.backgroundTileType !== type) {
this.backgroundTileType = type;
console.log(this.map.getLayers());
this.map.getLayers().getArray()[0].setVisible(false);
this.map.getLayers().getArray()[1].setVisible(false);
if (type === "OPENSTREETMAP") {
this.map.getLayers().getArray()[0].setVisible(true);
} else if (type === "BING MAPS") {
this.map.getLayers().getArray()[1].setVisible(true);
}
}
}
}
togglePlotBoundriesLayers(state) {
if (this.plotBoundriesState === null) {
this.plotBoundriesState = true;
}
if (this.plotBoundriesState !== state) {
this.plotBoundriesState = state;
this.map.getLayers().forEach((layer) => {
if (layer.get("name") === "plotBoundriesLayer") {
layer.setVisible(state);
}
if (layer.get("name") === "plotUserBoundriesLayer") {
console.log(state);
layer.setVisible(state);
}
});
}
}
addTileLayer(url) {
const wmsLayer = new TileLayer({
source: new TileWMS({
url,
params: {
TILED: true,
},
crossOrigin: "Anonymous",
}),
});
this.map.addLayer(wmsLayer);
}
}
export default new OlMap();
在这一点上,我想知道我是否做得很好,或者可以做些什么不同或更好的来优化我的代码。从长远来看,这将帮助我不会被我在项目开始时创建的 bade 代码所困扰。
提前谢谢了!
解决方案
推荐阅读
- snmp - Grafana 中使用 SNMP 的每日下载计数器
- python - 遍历 JSON - 列表索引上的 typeError
- ios - 我的应用程序的 iOS 版本未显示在 Fabric 仪表板上
- c# - 如何使用 c# 代码在一个网格中插入 2 个边框
- html - 如何在另一个 div 中将具有背景图像的 div 居中
- javascript - Electron 应用程序无法解析 Steamworks 集成中的“greenworks-linux32”
- java - Spring-Data-Jpa在持久化后清除链接实体的所有参数
- perl - 无法从 perl 中的命令行为标量赋值
- java - Spring Boot + Jackson:根据调用的 REST API 以不同的方式序列化对象
- mysql - 将 MySQL 查询转换为 Laravel