如何解决来自 PolylineMeasure 插件的坐标数组反应传单 创建自定义 react-leaflet v3 控件改变原来的插件来播种数据将它们捆绑在一起警告
我有 3 个文件:
1.
PolylineMeasure.jsx
import { MapControl,withLeaflet } from "react-leaflet";
import * as L from "leaflet";
class PolylineMeasure extends MapControl {
createLeafletElement() {
return L.control.polylineMeasure({
position: "topleft",unit: "metres",showBearings: true,clearMeasurementsOnStop: false,showClearControl: true,showUnitControl: true,});
}
componentDidMount() {
const { map } = this.props.leaflet;
const polylineMeasure = this.leafletElement;
polylineMeasure.addTo(map);
}
}
export default withLeaflet(PolylineMeasure);
Map.jsx
import { Map,TileLayer } from "react-leaflet";
import PolylineMeasure from "./PolylineMeasure";
import "leaflet/dist/leaflet.css";
import "leaflet/dist/leaflet.css";
import "leaflet.polylinemeasure/Leaflet.PolylineMeasure.css";
import "leaflet.polylinemeasure/Leaflet.PolylineMeasure";
const Leaflet = () => {
return (
<>
<Map
center={[52.11,19.21]}
zoom={6}
scrollWheelZoom={true}
style={{ height: 600,width: "50%" }}
>
<TileLayer
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<PolylineMeasure />
</Map>
</>
);
};
export default Leaflet;
- 我正在使用 nextjs,所以我不得不在没有 SSR 的情况下导入。
home.js
import dynamic from "next/dynamic";
function HomePage() {
const Map = dynamic(() => import("../components/Map"),{
loading: () => <p>A map is loading</p>,ssr: false,});
return <Map />;
}
export default HomePage;
https://github.com/ppete2/Leaflet.PolylineMeasure
使用上面链接中的演示,我能够像这样记录一组坐标:
{ ... }
polylineMeasure.addTo(map);
function debugevent() {
polylineMeasure._arrPolylines[0].arrowMarkers.map((el) => {
console.log(el._latlng);
});
}
map.on("polylinemeasure:toggle",debugevent);
如何在 nextjs(home.js 文件)中访问这些坐标?
如何通过将数组作为道具传递给已经带有坐标的 PolylineMeasure(Map.jsx 文件)?
解决方法
所以这是关于两件事:lifting up state,以及捕获 Leaflet.Polyline 的 internal events。
首先,让我们跟踪 Home.js
中的状态变量,并将其 setter 向下传递到地图组件:
function HomePage() {
const [pointarray,setPointarray] = useState()
const Map = dynamic(() => import("../components/Map"),{...})
return <Map setPointarray={setPointarray} />;
}
现在在 Map 中,我们需要获取对底层传单地图的引用,以便我们可以附加一些事件处理程序。您使用的是 createLeafletElement
和 withLeaflet
,所以我假设您使用的是 reat-leaflet 版本 2。(我建议您尽可能更新到 v3)。
const Leaflet = ({ setPointarray }) => {
const mapRef = React.useRef()
useEffect(() => {
if (mapRef && mapRef.current){
mapRef.current.leafletElement.on(
'polylinemeasure:finish',currentLine => setPointarray(currentLine.getLatLngs())
)
}
},[mapRef])
return (
<>
<Map
ref={mapRef}
...
>
<TileLayer ... />
<PolylineMeasure />
</Map>
</>
);
};
此处发生的情况是,引用附加到您的 Map
组件,该组件引用了底层传单 L.map
实例。当该 ref 准备就绪时, useEffect if 语句中的代码将运行。它从 mapRef.current.leafletElement
获取地图实例,并附加基于 Leaflet.PolylineMeasure's events 的事件处理程序,特别是绘制完成时的事件。发生这种情况时,它会将绘制的线保存到状态变量中,该变量位于 Home
组件中。
这方面有很多变化,这取决于您究竟要做什么。至于将预先存在的折线坐标作为道具提供给 PolylineMeasurer,即使使用香草传单PolylineMeasurer
,我也找不到任何示例。我发现插件作者的评论说“restoring of drawed measurements is not possible”,这基本上就是我们所说的通过将道具传递给该组件来做的事情。我确信它可以通过深入研究源代码并以编程方式绘制多段线来完成,但是我已经没时间了,我会尝试重新审视它稍后。
react-leaflet 版本 3 答案
根据要求,以下是如何使用 react-leaflet v3 执行此操作,同时使用作为道具传递的数据初始化折线测量器。
创建自定义 react-leaflet v3 控件
使用 react-leaflet 创建自定义组件比以往更容易。看看createcontrolcomponent
。如果您不习惯阅读这些文档,可以归结为:要创建自定义控件组件,您需要创建一个函数,该函数返回您要创建的控件的传单实例。您将该函数提供给 createcontrolcomponent
,就是这样:
import { createControlComponent } from "@react-leaflet/core";
const createPolylineMeasurer = (props) => {
return L.control.polylineMeasure({ ...props });
};
const PolylineMeasurer = createControlComponent(createPolylineMeasurer);
export default PolylineMeasurer;
改变原来的插件来播种数据
然而,在我们的例子中,我们想要添加一些额外的逻辑来为 PolylineMeasurer 预先设定一些我们作为道具传递的纬度。我在原始插件中加入了 pull request 以添加 .seed
方法。但是,在 react-leaflet 的情况下,我们需要比使用我放在那里的代码更加小心。许多绘制折线所需的方法只有在将 L.Control.PolylineMeasure
添加到地图后才能使用。在将 polylineMeasure 添加到地图后,我可能花了太多时间试图找出 react/react-leaflet 生命周期中的哪个位置拦截 polylineMeasure 的实例,所以我最终的解决方案是更改 Leaflet.PolylineMeasure 的源代码.
在 onAdd
方法中,在所有代码运行后,我们添加此代码,它表示如果您使用 seedData
选项,一旦添加控件,它将绘制该种子数据到地图:
// inside L.Control.PolylineMeasure.onAdd:
onAdd: function(map) {
// ... all original Leaflet.PolylineMeasure code here ...
if (this.options.seedData) {
const { seedData } = this.options;
seedData.forEach((polyline) => {
// toggle draw state on:
this._toggleMeasure();
// start line with first point of each polyline
this._startLine(polyline[0]);
// add subsequent points:
polyline.forEach((point,ind) => {
const latLng = L.latLng(point);
this._mouseMove({ latLng });
this._currentLine.addPoint(latLng);
// on last point,if (ind === polyline.length - 1) {
this._finishPolylinePath();
this._toggleMeasure();
}
});
});
}
return this._container;
}
此代码以编程方式调用所有相同的事件,如果用户打开控件、单击周围并以这种方式绘制线条,则会调用这些事件。
将它们捆绑在一起
所以现在我们的 <PolylineMeasurer />
组件将提供给 L.control.polylineMeasure
的选项作为它的 props,此外还有一个名为 seedData
的新可选 props,它将导致地图被渲染使用该种子数据:
const Map = () => {
return (
<MapContainer {...mapContainerProps}>
<TileLayer url={url} />
<PolylineMeasurer
position="topleft"
clearMeasurementsOnStop={false}
seedData={seedData}
/>
</MapContainer>
);
};
Working Codesandbox
警告
如果您的应用程序中的某些其他机制使 seedData
发生变化,您不能期望 PolylineMeasurer
组件以与普通 React 组件相同的方式反应。在创建传单中,此控件将使用您提供给它的选项一次添加到地图中,仅此而已。虽然一些 react-leaflet-v3 组件工厂函数带有更新参数,但 createcontrolcomponent
没有(即它的第一个参数是一个创建控件实例的函数,但它不接受潜在的 的第二个参数更新控件实例,例如createlayercomponent所做的)。
话虽如此,您可以将 key
属性应用到 PolylineMeasurer
组件,如果您的 seedData
在应用程序的其他地方发生了更改,也可以更改 key
,并且 PolylineMeasurer
将被迫重新渲染并绘制您的新数据。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。