React Leaflet Routing Machine:onClick to add Marker after all the waypoints are removed仅在第二次单击后触发
Posted
技术标签:
【中文标题】React Leaflet Routing Machine:onClick to add Marker after all the waypoints are removed仅在第二次单击后触发【英文标题】:React Leaflet Routing Machine: onClick to add Marker after all waypoints are removed fires only after second click 【发布时间】:2021-01-25 18:50:33 【问题描述】:我遵循了 Leaflet Routing Machine regarding interactions 的建议,即 onClicks。
通过我的实现,我将航路点保存在本地存储中——将我从地图点击中获得的纬度和经度 obj 保存到一个名为 markers
的数组中
事件处理程序有一个条件,它将点击分成两个结果——添加(到标记数组)或更新它。
就像我在标题中所说,初始交互很好,只是当我删除任何标记并尝试再次添加时才是问题所在。我还注意到markers
数组完全是空的,并且触发的下一个事件是一个更新,显然它应该是一个添加:
这是路由机中的相关代码:
class Routing extends MapLayer
static contextType = UserContextDispatch;
constructor(props)
super(props);
this.state =
showSpinner: false,
localDispatch: null,
;
this.handleLoader = this.handleLoader.bind(this);
this.handleRemoveWayPoint = this.handleRemoveWayPoint.bind(this);
this.handleSetMarker = this.handleSetMarker.bind(this);
handleRemoveWayPoint()
var waypoints = this.control.getWaypoints();
for (let i = waypoints.length - 1; i >= 0; i--)
console.log('waypoints[i].latLng !== null ', waypoints[i].latLng !== null);
if (waypoints[i].latLng !== null)
waypoints[i].latLng = null;
break;
this.control.setWaypoints(waypoints);
createLeafletElement(props)
const map = this.props.leaflet;
if (map && !this.control)
this.control = L.Routing.control(
collapsible: true,
show: false,
position: 'bottomleft',
lineOptions:
styles: [ color: 'chartreuse', opacity: 1, weight: 5 ]
,
waypoints: [null],
createMarker: function(i, wp, nWps)
if (i === 0)
return L.marker(wp.latLng,
icon: startIcon,
draggable: true,
keyboard: true,
alt: 'current location'
).on('drag', function(e)
e.latlng.alt = 'current location';
console.log('there be dragons start!!', e);
RoutingMachineRef.handleSetMarker(
...e.oldLatLng,
...e.latlng
);
);
if (i === nWps - 1)
return L.marker(wp.latLng,
icon: endIcon,
draggable: true,
alt: 'current destination'
).on('drag', function(e)
e.latlng.alt = 'current destination';
console.log('there be dragons dest!!', e);
RoutingMachineRef.handleSetMarker(
...e.oldLatLng,
...e.latlng
);
);
);
L.Routing.errorControl(this.control).addTo(map);
return this.control.getPlan();
componentDidMount()
const map = this.props.leaflet;
console.log('markers ', markers);
this.setState(prevState =>
localDispatch: prevState.localDispatch = this.context.dispatch;
);
map.addControl(this.control);
updateLeafletElement(fromProps, toProps)
const map = this.props.leaflet;
var self = this;
self;
var markers = this.props;
function createButton(label, container)
var btn = L.DomUtil.create('button', '', container);
btn.setAttribute('type', 'button');
btn.innerhtml = label;
return btn;
var localDispatch = this.state;
var container = L.DomUtil.create('div'),
startBtn = createButton('Start from this location', container),
destBtn = createButton('Go to this location', container);
map.on(
'click',
function(e)
L.popup()
.setContent(container)
.setLatLng(e.latlng)
.openOn(map);
L.DomEvent.on(startBtn, 'click', function()
if (e.latlng)
e.latlng.alt = 'current location';
console.log('adding);
localDispatch(
type: 'addMarker',
payload:
marker: e.latlng
);
if (markers.length === 0)
console.log('updating ');
e.latlng.alt = 'current location';
localDispatch(
type: 'updateMarkers',
payload:
marker: e.latlng
);
self.control.spliceWaypoints(0, 1, e.latlng);
map.closePopup();
);
L.DomEvent.on(
destBtn,
'click',
function()
console.log('e', e);
if (markers[1] === undefined)
e.latlng.alt = 'current destination';
console.log('e.latlng ', e.latlng);
localDispatch(
type: 'addMarker',
payload:
marker: e.latlng
);
if (toProps.markers[1] !== undefined)
console.log('updating ');
e.latlng.alt = 'current destination';
localDispatch(
type: 'updateMarkers',
payload:
marker: e.latlng
);
this.control.spliceWaypoints(1, 1, e.latlng);
map.closePopup();
.bind(this)
);
.bind(this)
);
if (toProps.removeRoutingMachine !== false)
this.control.setWaypoints([]);
componentWillUnmount()
this.destroyRouting();
destroyRouting()
const map = this.props.leaflet;
if (map)
map.removeControl(this.control);
export default withLeaflet(Routing);
提前致谢!
【问题讨论】:
【参考方案1】:如您所见,我在RoutingMachine
本身中有一些与Map
(航点的onClick)相关的代码;经过考虑后,我将其作为handlerFunction
移至Map
组件。现在它可以工作了!
import React, useState, useEffect, useRef from 'react';
import Button, Dimmer, Loader from 'semantic-ui-react';
import L from 'leaflet';
import * as ELG from 'esri-leaflet-geocoder';
import Control from 'react-leaflet-control';
// import MapboxLayer from '../MapboxLayer/MapboxLayer.jsx';
import Routing from '../RoutingMachine/RoutingMachine.jsx';
import parse, stringify from 'flatted';
import userState, userDispatch from '../Context/UserContext.jsx';
import UIContext from '../Context/UIContext.jsx';
function currentMapViewPropsAreEqual(prevProps, nextProps)
console.log('prevProps, nextProps ', prevProps, nextProps);
console.log(
'prevProps.currentMapView === nextProps.currentMapView && prevProps.Map === nextProps.Map && prevProps.TileLayer === nextProps.TileLayer ',
prevProps.currentMapView === nextProps.currentMapView &&
prevProps.Map === nextProps.Map &&
prevProps.TileLayer === nextProps.TileLayer
);
return (
prevProps.currentMapView === nextProps.currentMapView &&
prevProps.Map === nextProps.Map &&
prevProps.TileLayer === nextProps.TileLayer
);
function MyMap( currentMapView, Map, TileLayer )
console.log('currentMapView; ', currentMapView);
var [animate, setAnimate] = useState(false);
var [userLocation, setUserLocation] = useState(null);
const [myState, setMyState] = useState(null);
var handleWaypointsOnMapRef = useRef(handleWaypointsOnMap);
var mapRef = useRef();
var mapRefForRoutingMachine = useRef();
var state = userState();
var dispatch = userDispatch();
var
currentMap,
isRoutingVisible,
removeRoutingMachine,
isLengthOfMarkersLessThanTwo,
markers
= state;
useEffect(() =>
handleWaypointsOnMapRef.current = handleWaypointsOnMap;
); // update after each render
useEffect(() =>
var current = = mapRef;
var leafletElement: map = current;
console.log('foo');
console.log('currentMap ', currentMapView);
map.locate( setView: true );
map.on('locationfound', handleOnLocationFound);
, []);
useEffect(() =>
var searchControl = new ELG.Geosearch(
useMapBounds: false
);
var current = = mapRef;
var leafletElement: map = current;
console.log('mapRef ', mapRef);
searchControl.addTo(map);
var cb = e => handleWaypointsOnMapRef.current(e); // then use most recent cb value
searchControl.on('results', cb);
if (Object.keys(currentMap).length === 0)
dispatch(
type: 'setMap',
payload:
currentMap: stringify(map)
);
return () =>
searchControl.off('results', cb);
;
, []);
function handleOnClickClearOneMarkerAtTime()
dispatch(
type: 'setIsRoutingVisible',
payload:
isRoutingVisible: false
);
mapRefForRoutingMachine.current.handleRemoveWayPoint();
dispatch(
type: 'deleteUserMarkers'
);
function handleOnClickClearAllMarkers()
mapRefForRoutingMachine.current.handleClearWayPoints();
dispatch(
type: 'resetUserMarkers'
);
function handleOnClickMarkerClick(e)
e.originalEvent.view.L.DomEvent.stopPropagation(e);
function handleWaypointsOnMap(e)
var current = = mapRef;
var leafletElement: map = current;
dispatch(
type: 'setIsRoutingVisible',
payload:
isRoutingVisible: true
);
dispatch(
type: 'setRemoveRoutingMachine',
payload:
removeRoutingMachine: false
);
function createButton(label, container)
var btn = L.DomUtil.create('button', '', container);
btn.setAttribute('type', 'button');
btn.innerHTML = label;
return btn;
var container = L.DomUtil.create('div'),
startBtn = createButton('Start from this location', container),
destBtn = createButton('Go to this location', container);
L.popup()
.setContent(container)
.setLatLng(e.latlng)
.openOn(map);
L.DomEvent.on(startBtn, 'click', function()
if (markers.length === 0)
e.latlng.alt = 'current location';
console.log('adding current location', e.latlng);
dispatch(
type: 'addMarker',
payload:
marker: e.latlng
);
if (markers[0] != undefined)
e.latlng.alt = 'current location';
console.log('updating current location', e.latlng);
dispatch(
type: 'updateMarkers',
payload:
marker: e.latlng
);
mapRefForRoutingMachine.current.handleSpliceWaypoints(0, 1, e.latlng);
map.closePopup();
);
L.DomEvent.on(
destBtn,
'click',
function()
console.log('e', e);
if (markers.length === 1)
e.latlng.alt = 'current destination';
console.log('adding destination ', e.latlng);
dispatch(
type: 'addMarker',
payload:
marker: e.latlng
);
if (markers.length === 2 && markers[1] !== undefined)
e.latlng.alt = 'current destination';
console.log('updating destination', e.latlng);
dispatch(
type: 'updateMarkers',
payload:
marker: e.latlng
);
mapRefForRoutingMachine.current.handleSpliceWaypoints(1, 1, e.latlng);
map.closePopup();
.bind(this)
);
function handleOnViewportChanged(e)
console.log('viewport change', e);
console.log('currentMapView ', currentMapView);
var current = = mapRef;
var leafletElement: map = current;
map.on('zoomend', function()
var zoom = map.getZoom();
console.log('zoom ', zoom);
console.log("'dispatch setMapZoom'; ");
dispatch(
type: 'setMapZoom',
payload:
currentMapView: zoom
);
);
function handleOnLocationFound(e)
console.log('e ', e);
var current = = mapRef;
var leafletElement: map = current;
map.setZoom(currentMapView);
var latlng = e.latlng;
var radius = e.accuracy;
var circle = L.circle(latlng, radius);
circle.addTo(map);
return (
<Map
preferCanvas=true
id="myMap"
animate=animate
zoom=currentMapView
ref=mapRef
onViewportChanged=handleOnViewportChanged
onClick=e => handleWaypointsOnMap(e)
>
<TileLayer
url=`https://api.mapbox.com/styles/v1/$process.env.MAPBOX_USERNAME/$
process.env.MAPBOX_STYLE_ID
/tiles/256/z/x/y@2x?access_token=$process.env.MAPBOX_ACCESS_TOKEN`
attribution='Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>'
/>
<Control position="bottomleft">
<Button onClick=handleOnClickClearOneMarkerAtTime color="orange" size="small">
delete one marker!
</Button>
</Control>
<Control position="bottomright">
<Button onClick=handleOnClickClearAllMarkers color="red" size="small">
clear all!
</Button>
</Control>
mapRef && (
<Routing
isRoutingVisible=isRoutingVisible
ref=mapRefForRoutingMachine
markers=markers
stringify=stringify
isLengthOfMarkersLessThanTwo=isLengthOfMarkersLessThanTwo
removeRoutingMachine=removeRoutingMachine
userLocation=userLocation
/>
)
</Map>
);
var MemoizedMyMap = React.memo(MyMap, currentMapViewPropsAreEqual);
export default MemoizedMyMap;
这是路由机:
import MapLayer from 'react-leaflet';
import L from 'leaflet';
import 'leaflet-routing-machine';
import withLeaflet from 'react-leaflet';
import UserContextDispatch from '../Context/UserContext.jsx';
import Dimmer, Loader from 'semantic-ui-react';
import isEqual from 'lodash';
import AwesomeDebouncePromise from 'awesome-debounce-promise';
class Routing extends MapLayer
static contextType = UserContextDispatch;
constructor(props)
super(props);
this.state =
showSpinner: false,
localDispatch: null,
markerIsBeingDragged: false,
currentMarker:
;
this.handleLoader = this.handleLoader.bind(this);
this.handleRemoveWayPoint = this.handleRemoveWayPoint.bind(this);
this.handleClearWayPoints = this.handleClearWayPoints.bind(this);
this.handleSpliceWaypoints = this.handleSpliceWaypoints.bind(this);
this.handleSetMarker = this.handleSetMarker.bind(this);
handleSetMarker(marker)
var markers = this.props;
if (markers[0] !== undefined && markers[0].alt === 'current location')
this.setState(prevState => (
currentMarker: ...prevState.currentMarker, ...marker
));
if (markers[1] !== undefined && markers[1].alt === 'current destination')
this.setState(prevState => (
currentMarker: ...prevState.currentMarker, ...marker
));
console.log('this.state ', this.state);
handleSpliceWaypoints(start, end, obj)
this.control.spliceWaypoints(start, end, obj);
handleLoader()
var showSpinner = this.state;
if (this.state.showSpinner === false)
this.setState(function(prevState)
return showSpinner: !prevState.showSpinner ;
);
return (
<Dimmer active inverted>
<Loader />
</Dimmer>
);
this.setState(function(prevState)
return showSpinner: (prevState.showSpinner = true) ;
);
handleRemoveWayPoint()
var waypoints = this.control.getWaypoints();
for (let i = waypoints.length - 1; i >= 0; i--)
console.log('waypoints[i].latLng !== null ', waypoints[i].latLng !== null);
if (waypoints[i].latLng !== null)
waypoints[i].latLng = null;
break;
console.log('waypoints ', waypoints);
this.control.setWaypoints(waypoints);
handleClearWayPoints()
this.control.setWaypoints([L.latLng(null, null), L.latLng(null, null)]);
componentDidMount()
const map = this.props.leaflet;
var markers = this.props;
this.control.setWaypoints([L.latLng(markers[0]), L.latLng(markers[1])]);
this.setState(prevState =>
localDispatch: prevState.localDispatch = this.context.dispatch;
);
map.addControl(this.control);
createLeafletElement(props)
const map = this.props.leaflet;
var startIcon = new L.Icon(
iconUrl:
'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
shadowUrl:
'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
);
var endIcon = new L.Icon(
iconUrl:
'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
shadowUrl:
'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
);
if (map && !this.control)
var RoutingMachineRef = this;
this.control = L.Routing.control(
collapsible: true,
show: false,
position: 'bottomleft',
lineOptions:
styles: [ color: 'chartreuse', opacity: 1, weight: 5 ]
,
waypoints: [null],
createMarker: function(i, wp, nWps)
if (i === 0)
return L.marker(wp.latLng,
icon: startIcon,
draggable: true,
keyboard: true,
alt: 'current location'
).on('move', function(e)
e.target._latlng.alt = 'current location';
console.log('e.target._latlng', e.target._latlng);
console.log('there be dragons start!!', e);
RoutingMachineRef.setState(prevState => (
markerIsBeingDragged: !prevState.markerIsBeingDragged
));
RoutingMachineRef.handleSetMarker(e.target._latlng);
);
if (i === nWps - 1)
return L.marker(wp.latLng,
icon: endIcon,
draggable: true,
alt: 'current destination'
).on('move', function(e)
e.target._latlng.alt = 'current destination';
console.log(' e.target._latlng', e.target._latlng);
RoutingMachineRef.setState(prevState => (
markerIsBeingDragged: !prevState.markerIsBeingDragged
));
console.log('there be dragons dest!!', e);
RoutingMachineRef.handleSetMarker(e.target._latlng);
);
);
L.Routing.errorControl(this.control).addTo(map);
return this.control.getPlan();
updateLeafletElement(fromProps, toProps)
var currentMarker, localDispatch, markerIsBeingDragged = this.state;
// console.log('fromProps, toProps ', fromProps, toProps);
if (markerIsBeingDragged && currentMarker.hasOwnProperty('alt'))
if (isEqual(toProps.markers[0], currentMarker) === false)
localDispatch(
type: 'updateMarkers',
payload:
marker: currentMarker
);
if (isEqual(toProps.markers[1], currentMarker) === false)
localDispatch(
type: 'updateMarkers',
payload:
marker: currentMarker
);
this.setState(prevState => (
markerIsBeingDragged: !prevState.markerIsBeingDragged
));
if (toProps.removeRoutingMachine !== false)
this.control.setWaypoints([]);
componentWillUnmount()
console.log("'unmount' ", 'unmount');
this.destroyRouting();
destroyRouting()
const map = this.props.leaflet;
if (map)
map.removeControl(this.control);
export default withLeaflet(Routing);
【讨论】:
以上是关于React Leaflet Routing Machine:onClick to add Marker after all the waypoints are removed仅在第二次单击后触发的主要内容,如果未能解决你的问题,请参考以下文章