如何 refresh/re-render OpenLayers 6 地图

How to refresh/re-render an OpenLayers 6 Map

我有一个 React 组件,我在其中渲染 Open Layers 6 地图。我将 lat/long 坐标作为道具 (props.userEnteredLocation) 传递给地图,该地图被转换为开放图层所需的格式,以便能够呈现正确的位置。

我的状态如下:

const transformCentre = (centre) => {
        return transform(centre, "EPSG:4326", "EPSG:3857");
    };

    const [map, setMap] = useState();
    const [centre, setCentre] = useState([74.31071359697442, 31.588167443954312]);
    const [featuresLayer, setFeaturesLayer] = useState();
    const [point, setPoint] = useState();
    const [feature, setFeature] = useState();
    const [transformedCentre, setTransformedCentre] = useState(
        transformCentre(centre)
    );

    // create state ref that can be accessed in OpenLayers onclick callback function
    //  
    const mapRef = useRef();
    mapRef.current = map;

我有两个 React 钩子

  1. 第一个使用红色标记渲染初始地图并设置状态。
// initialize map on first render - logic formerly put into componentDidMount
    useEffect(() => {
        console.log("Initialised for the first time");

        // Create a point
        var point = new Point(transformedCentre);
        setPoint(point);

        // points/lines/polygons are features that can be used in the vector layer
        var feature = new Feature({
            geometry: point,
            name: "Your address is shown here",
        });

        var iconStyle = new Style({
            image: new Icon({
                src: markerImg,
            }),
        });

        feature.setStyle(iconStyle);
        setFeature(feature);

        // create vector source itself for the marker on the map
        var vectorSource = new VectorSource({
            features: [feature],
        });

        // create and add vector source layer
        const initalFeaturesLayer = new VectorLayer({
            source: vectorSource,
        });

        // create map
        const locationMap = new Map({
            target: mapRef.current,
            layers: [
                new TileLayer({
                    source: new OSM(),
                }),

                initalFeaturesLayer,
            ],
            view: new View({
                center: transformedCentre,
                zoom: 17,
            }),
            controls: [],
        });
        setMap(locationMap);
        setFeaturesLayer(initalFeaturesLayer);
    }, []);

这有效,地图在正确的位置正确呈现

  1. 每当表示 lat/long 坐标的道具发生变化时,第二个钩子应该重新渲染地图,我可以在控制台中看到,每当新坐标传递到道具时,状态确实会发生变化,但是地图本身似乎没有更新
// update map if user changes geo location
    useEffect(() => {
        console.log("Detected change in User geolocation");
        if (props.userEnteredLocation) {
            var array = props.userEnteredLocation.split(",");
            var newCentre = [parseFloat(array[1]), parseFloat(array[0])];
            setCentre(newCentre);
            setTransformedCentre(transformCentre(centre));
        }

        if (map != null) {
            console.log("Changing Centre to: " + transformedCentre);
            map.getView().setCenter(transformedCentre);
            console.log("CENTRE NOW: " + map.getView().getCenter());
            point.setCoordinates(transformedCentre);
            console.log("POINT CENTRE NOW: " + point.getCoordinates());
            feature.setGeometry(point);
            console.log("Feature geometry now CENTRE NOW: " + feature.getGeometry());
            // set features to map
            featuresLayer.setSource(
                new VectorSource({
                    features: [feature],
                })
            );
            featuresLayer.getSource().changed();
        }
    }, [props.userEnteredLocation]);

//render component
return <div ref={mapRef} className="map-container"></div>;

我已经按照常见问题解答的建议尝试了 map.render() 和 map.renderSync() 无济于事。我还在 VectorSource 本身上尝试了 .changed() 和 .refresh(),.changed() 什么都不做,.refresh() 完全删除了我的红色标记。

有谁知道我哪里出错了?我想让地图重新渲染并显示来自道具的新位置,并在该位置显示红色地图标记。

我很想创建一个新地图,即将我在初始渲染时所做的一切粘贴到随着道具变化而变化的挂钩中,但这似乎不是最佳选择。

对于这可能有所帮助的任何人,我意识到问题实际上只是 useEffect() 在组件的下一次渲染之前没有使用更新的状态。为了让它使用最新的坐标,我在将其传递给 Openlayers 视图和布局之前将所需的地图中心分配给了一个 var。

这是有效的代码:

function BusinessLocationMap(props) {
    // Transform the centre into something openlayers understands
    const transformCentre = (centre) => {
        if (centre != null) {
            return transform(centre, "EPSG:4326", "EPSG:3857");
        }
    };

    const [map, setMap] = useState();
    const [centre] = useState([74.31071359697442, 31.588167443954312]);
    const [featuresLayer, setFeaturesLayer] = useState();
    const [point, setPoint] = useState();
    const [feature, setFeature] = useState();
    const [transformedCentre] = useState(transformCentre(centre));

    // create state ref that can be accessed in OpenLayers onclick callback function
    //  
    const mapRef = useRef();
    mapRef.current = map;

    // initialize map on first render - logic formerly put into componentDidMount
    useEffect(() => {
        console.log("Initialised for the first time");
        console.log("Initial Centre used: " + transformedCentre);

        // Create a point
        var point = new Point(transformedCentre);
        setPoint(point);

        // points/lines/polygons are features that can be used in the vector layer
        var feature = new Feature({
            geometry: point,
            name: "Your address is shown here",
        });

        var iconStyle = new Style({
            image: new Icon({
                src: markerImg,
            }),
        });

        feature.setStyle(iconStyle);
        setFeature(feature);

        // create vector source itself for the marker on the map
        var vectorSource = new VectorSource({
            features: [feature],
        });

        // create and add vector source layer
        const initalFeaturesLayer = new VectorLayer({
            source: vectorSource,
        });

        // create the initial view
        const initialView = new View({
            center: transformedCentre,
            zoom: 17,
        });

        // create map
        const locationMap = new Map({
            target: mapRef.current,
            layers: [
                new TileLayer({
                    source: new OSM(),
                }),

                initalFeaturesLayer,
            ],
            view: initialView,
            controls: [],
        });
        setMap(locationMap);
        setFeaturesLayer(initalFeaturesLayer);
    }, []);

    // update map if user changes geo location
    useEffect(() => {
        console.log("Detected change in User geolocation");

        if (props.userEnteredLocation) {
            var array = props.userEnteredLocation.split(",");
            var newCentre = [parseFloat(array[1]), parseFloat(array[0])];
            var newTransformedCentre = transformCentre(newCentre);
        }

        if (map != null) {
            point.setCoordinates(newTransformedCentre);
            feature.setGeometry(point);

            map.setView(
                new View({
                    center: newTransformedCentre,
                    zoom: 17,
                })
            );

            // set features to map
            featuresLayer.setSource(
                new VectorSource({
                    features: [feature],
                })
            );
        }
    }, [props.userEnteredLocation]);

    //render component
    return <div ref={mapRef} className="map-container"></div>;
}

export default BusinessLocationMap;