英文:
How to toggle layers in Mapbox GL JS using React
问题
我使用react-map-gl
来显示一个带有多个图层的Mapbox地图。我现在想让用户通过地图上的某种控制面板切换这些图层的可见性。我非常喜欢Mapbox网站上的切换图层示例的外观,但它完全是原生JS,而不是React。
我找到的最好的示例使用复选框来调整图层,但是它是通过使用具有多个内置图层的默认样式,修改定义该样式的JSON,然后将修改后的JSON传递给Map
组件的mapStyle
属性来实现的。目前,我正在使用此属性来指向我在Mapbox Studio中创建的样式:
<Map
initialViewState={{
longitude: -0.239348,
latitude: 51.592045,
zoom: 11.5
}}
style={{ width: "100%", height: 400 }}
mapStyle="mapbox://styles/mapboxuser/xxxxxxxxx"
styleDiffing
mapboxAccessToken={process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN}
onMouseMove={onHover}
interactiveLayerIds={['protectedSegments']}
>
<Source type="geojson" data={boundaries}>
<Layer {...boundaryStyle} />
</Source>
<Source type="geojson" data={majorRoads}>
<Layer {...majorRoadsStyle} />
</Source>
<Source type="geojson" data={protectedSegments}>
<Layer {...protectedSegmentStyle} />
</Source>
</Map>
显然,该URL不可路由,因此我无法例如在其上使用fetch
。
而且,这种方法似乎很奇怪,因为我正在客户端添加到地图上的图层。事实上,Layer
组件的API文档中说:
一旦
<Layer>
被挂载,以下属性不应更改。如果您动态添加/删除多个JSX图层,请确保使用React的key
属性为每个元素提供稳定的标识。
示例代码似乎没有处理这个问题,因为它正在调整的图层没有React的<Layer>
组件。
我觉得我肯定漏掉了一些明显的东西,因为这是构建Web地图的人最常想要做的事情之一。在Leaflet中,甚至有一个内置控件来让您调整各个图层的可见性。在react-map-gl
中该如何实现呢?
英文:
I am using react-map-gl
to display a Mapbox map with several layers. I now want to let the user toggle the visibility of these layers using some kind of control panel on the map. I really like the look of the Toggle Layers example on the Mapbox website, but it's all in vanilla JS not React.
The best example I've found uses checkboxes to adjust layers, but it does so by using a default style with several built-in layers, modifying the JSON which defines that style, and then passing the modified JSON to the mapStyle
property of the Map
component. Presently, I'm using this property to point to a style I have created in Mapbox Studio:
<Map
initialViewState={{
longitude: -0.239348,
latitude: 51.592045,
zoom: 11.5
}}
style={{ width: "100%", height: 400 }}
mapStyle="mapbox://styles/mapboxuser/xxxxxxxxx"
styleDiffing
mapboxAccessToken={process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN}
onMouseMove={onHover}
interactiveLayerIds={['protectedSegments']}
>
<Source type="geojson" data={boundaries}>
<Layer {...boundaryStyle} />
</Source>
<Source type="geojson" data={majorRoads}>
<Layer {...majorRoadsStyle} />
</Source>
<Source type="geojson" data={protectedSegments}>
<Layer {...protectedSegmentStyle} />
</Source>
</Map>
Obviously, that URL isn't routeable, so I couldn't for example use fetch
on it.
And anyway, this approach seems odd, since the layers I'm adjusting have been added to the map on the client side. In fact, the API docs for the Layer
component say:
> Once a <Layer>
is mounted, the following props should not change. If you add/remove multiple JSX layers dynamically, make sure you use React's key prop to give each element a stable identity.
The sample code doesn't seem to deal with this, since the layers that it is adjusting don't have React <Layer>
components.
I feel like I must be missing something obvious, since this is one of the most common things someone building a web map would want to do. In Leaflet, there's even a built-in control to let you adjust the visibility of the various layer. How does one do this in react-map-gl
?
答案1
得分: 0
你可以使用<Layer>
组件的layout
属性来调整单个图层的可见性。不过,需要注意的是,你需要将字符串[visible
或none
] 1 提供给visibility
属性。
Map.js
const [layersVisibility, setLayersVisibility] = React.useReducer(
(state, updates) => ({ ...state, ...updates }
), {});
return (
<Map ...>
<Source key="boundaries" type="geojson" data={boundaries}>
<Layer key="boundaries"
{...boundaryStyle}
layout={{ visibility: layersVisibility["boundaries"] }} />
</Source>
<ControlPanel onChange={setLayersVisibility} />
</Map>
)
ControlPanel.js
function ControlPanel(props) {
const [visibility, setVisibility] = useState({
boundaries: true,
protectedSegments: true,
majorRoads: true
});
useEffect(() => {
// 将true/false转换为"visible"/"none"
const visibilityState = Object.fromEntries(
Object.entries(visibility).map(([k, v]) => [k, v ? "visible" : "none"])
);
props.onChange(visibilityState);
}, [visibility]);
const onVisibilityChange = (name, value) => {
setVisibility({ ...visibility, [name]: value });
};
return (
<div className={styles["control-panel"]}>
<h3>图层</h3>
<label><input type="checkbox"
checked={visibility["boundaries"]}
onChange={evt => onVisibilityChange("boundaries", evt.target.checked)}
/> 边界</label>
</div>
)
}
export default React.memo(ControlPanel);
英文:
You can use the layout
property of the <Layer>
component to adjust visibility of an individual layer. The trick, though, is that you need to provide the strings visible
or none
to the visibility
property.
Map.js
const [layersVisibility, setLayersVisibility] = React.useReducer(
(state, updates) => ({ ...state, ...updates }
), {});
return (
<Map ...>
<Source key="boundaries" type="geojson" data={boundaries}>
<Layer key="boundaries"
{...boundaryStyle}
layout={{ visibility: layersVisibility["boundaries"] }} />
</Source>
<ControlPanel onChange={setLayersVisibility} />
</Map>
)
ControlPanel.js
function ControlPanel(props) {
const [visibility, setVisibility] = useState({
boundaries: true,
protectedSegments: true,
majorRoads: true
});
useEffect(() => {
// Convert true/false to "visible"/"none"
const visibilityState = Object.fromEntries(
Object.entries(visibility).map(([k, v]) => [k, v ? "visible" : "none"])
);
props.onChange(visibilityState);
}, [visibility]);
const onVisibilityChange = (name, value) => {
setVisibility({ ...visibility, [name]: value });
};
return(
<div className={styles["control-panel"]}>
<h3>Layers</h3>
<label><input type="checkbox"
checked={visibility["boundaries"]}
onChange={evt => onVisibilityChange("boundaries", evt.target.checked)}
/> Boundaries</label>
</div>
)
}
export default React.memo(ControlPanel);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论