我正在使用开放层构建这个 React 应用程序,但我遇到了 setState 问题。
我将解释这两个相关组件的工作原理:
MapWrapper:包裹开放图层地图的组件。其中有一个侦听器,每次鼠标光标移动时都会检查并将在该屏幕像素中找到的功能发送到父组件 (OlMap.js),如果那里没有任何功能,则将其发送到 null。
这是听众:
initialMap.on('pointermove', function (e) {
let newSelectedFeature = null
initialMap.forEachFeatureAtPixel(e.pixel, function (f) {
newSelectedFeature = f;
console.log("HOVER", newSelectedFeature)
return true;
});
props.onHoverFeature(newSelectedFeature)
});
这是完整的组件:
// react
import React, { useState, useEffect, useRef } from 'react';
// openlayers
import Map from 'ol/Map'
import View from 'ol/View'
import TileLayer from 'ol/layer/Tile'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import XYZ from 'ol/source/XYZ'
import {transform} from 'ol/proj'
import {toStringXY} from 'ol/coordinate';
import OSM, {ATTRIBUTION} from 'ol/source/OSM.js';
//css
import "./MapStyle.css"
import { Feature } from 'ol';
import { Point } from 'ol/geom';
import Style from 'ol/style/Style';
import Icon from 'ol/style/Icon';
function MapWrapper(props) {
// set intial state
const [ map, setMap ] = useState()
const [ featuresLayer, setFeaturesLayer ] = useState()
const [ selectedCoord , setSelectedCoord ] = useState()
const [ selectedFeature, setSelectedFeature ] = useState(null)
// pull refs
const mapElement = useRef()
// create state ref that can be accessed in OpenLayers onclick callback function
// https://stackoverflow.com/a/60643670
const mapRef = useRef()
mapRef.current = map
const highlightStyle = new Style({
image: new Icon({
src: "https://i.imgur.com/gbfpoiQ.png",
//src: "file:\\\\assets\\icons\\badMarker.png",
anchor: [0.5, 1]
})
})
const markerStandardStyle = new Style({
image: new Icon({
src: "https://i.imgur.com/GJ8XUQ7.png",
//src: "file:\\\\assets\\icons\\badMarker.png",
anchor: [0.5, 1]
})
})
const changeSelectedFeature = (f) => {
setSelectedFeature(f)
}
// initialize map on first render - logic formerly put into componentDidMount
useEffect( () => {
// create and add vector source layer
const initalFeaturesLayer = new VectorLayer({
source: new VectorSource(),
style: markerStandardStyle
})
// create map
const initialMap = new Map({
target: mapElement.current,
layers: [
new TileLayer({
source: new OSM(),
}),
initalFeaturesLayer
],
view: new View({
projection: 'EPSG:3857',
center: [1877145.5647310177, 5030935.7155071795],
zoom: 14
}),
controls: []
})
// set map onclick handler
initialMap.on('click', handleMapClick)
initialMap.on('pointermove', function (e) {
let newSelectedFeature = null
initialMap.forEachFeatureAtPixel(e.pixel, function (f) {
newSelectedFeature = f;
console.log("HOVER", newSelectedFeature)
return true;
});
props.onHoverFeature(newSelectedFeature)
});
// save map and vector layer references to state
setMap(initialMap)
setFeaturesLayer(initalFeaturesLayer)
},[])
// update map if features prop changes - logic formerly put into componentDidUpdate
useEffect( () => {
if (props.features.length) { // may be null on first render
// set features to map
featuresLayer.setSource(
new VectorSource({
features: props.features // make sure features is an array
})
)
// // fit map to feature extent (with 100px of padding)
// map.getView().fit(featuresLayer.getSource().getExtent(), {
// padding: [100,100,100,100]
// })
}
},[props.features])
// map click handler
const handleMapClick = (event) => {
// get clicked coordinate using mapRef to access current React state inside OpenLayers callback
// https://stackoverflow.com/a/60643670
const clickedCoord = mapRef.current.getCoordinateFromPixel(event.pixel);
console.log(clickedCoord)
props.onClickMap(clickedCoord)
// transform coord to EPSG 4326 standard Lat Long
const transormedCoord = transform(clickedCoord, 'EPSG:3857', 'EPSG:4326')
// set React state
setSelectedCoord( transormedCoord )
console.log(transormedCoord)
}
// render component
return (
<div ref={mapElement} className="map-container"></div>
)
}
export default MapWrapper
OlMap:这是父组件。它在函数 onHoverFeature 中接收找到的特征;那么如果它与之前找到的功能不同,则应该将其设置为状态,但 setSelectedFeature 被调用但不起作用
import React, { useEffect, useState } from 'react'
import MapWrapper from './MapWrapper'
import MapSearchBar from './MapSearchBar'
import SideSlider from './components/SideSlider'
import { Feature } from 'ol';
import { Point } from 'ol/geom';
import Style from 'ol/style/Style';
import Icon from 'ol/style/Icon';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
export default function OlMap(props) {
const [showRightSlider, setShowRightSlider] = useState(true)
const [coordsToShow, setCoordsToShow] = useState([0,0])
const [features, setFeatures] = useState([])
const [selectedFeature, setSelectedFeature] = useState(null)
const highlightStyle = new Style({
image: new Icon({
src: "https://i.imgur.com/gbfpoiQ.png",
//src: "file:\\\\assets\\icons\\badMarker.png",
anchor: [0.5, 1]
})
})
const onClickMap = (coord) => {
setShowRightSlider(true);
setCoordsToShow(coord)
createMarker("TEST", coord)
}
const onHoverFeature = (f) => {
if(f?.ol_uid === selectedFeature?.ol_uid) {
return
}
if(selectedFeature) {
selectedFeature.setStyle(undefined)
}
if(f){
f.setStyle(highlightStyle)
}
setSelectedFeature(f)
}
let idFeature = 0
//create marker
const createMarker = (name, coord) => {
console.log(name, coord)
const marker = new Feature({
geometry: new Point(coord),
name: name,
id: idFeature++
})
setFeatures(oldFeature => [...oldFeature, marker])
}
return(
<div style={{height: "90%", width: "100%", backgroundColor: "yellow", position: "relative"}}>
<SideSlider show={showRightSlider} coord={coordsToShow} onClose={() => {setShowRightSlider(false)}}></SideSlider>
<MapSearchBar></MapSearchBar>
<MapWrapper features={features} onClickMap={onClickMap} onHoverFeature={onHoverFeature} /*selectedFeature={selectedFeature}*/ />
</div>
)
}
我还在 React 项目中实现了功能选择。而且我在您的代码中没有看到添加“选择”交互。
const selectRef = useRef<Select>(
new Select({
style: null,
multi: false,
})
);
map!.addInteraction(selectRef.current);