1+ import { LatLngBounds } from 'leaflet' ;
12import { union } from 'lodash' ;
23import { reaction , IReactionDisposer } from 'mobx' ;
34import { inject , observer } from 'mobx-react' ;
45import React , { Component } from 'react' ;
56import { Polyline } from 'react-leaflet' ;
7+ import ClusterNodeMarker from '~/components/map/layers/markers/ClusterNodeMarker' ;
8+ import { ISelectRoutePathNeighborPopupData } from '~/components/map/layers/popups/SelectRoutePathNeighborPopup' ;
69import NodeSize from '~/enums/nodeSize' ;
710import NodeType from '~/enums/nodeType' ;
11+ import NodeFactory from '~/factories/nodeFactory' ;
812import EventListener , { IEditRoutePathNeighborLinkClickParams } from '~/helpers/EventListener' ;
913import { IRoutePath } from '~/models' ;
1014import INeighborLink from '~/models/INeighborLink' ;
@@ -35,6 +39,7 @@ interface PolylineRefs {
3539@observer
3640class RoutePathNeighborLinkLayer extends Component < IRoutePathLayerProps , IRoutePathLayerState > {
3741 private linkListener : IReactionDisposer ;
42+ private highlightedNeighborLinkListener : IReactionDisposer ;
3843 constructor ( props : IRoutePathLayerProps ) {
3944 super ( props ) ;
4045 this . state = {
@@ -44,10 +49,15 @@ class RoutePathNeighborLinkLayer extends Component<IRoutePathLayerProps, IRouteP
4449 ( ) => this . props . routePathLayerStore ! . neighborLinks ,
4550 ( ) => this . initializePolylineRefs ( )
4651 ) ;
52+ this . highlightedNeighborLinkListener = reaction (
53+ ( ) => this . props . routePathLayerStore ! . highlightedNeighborLinkId ,
54+ ( ) => this . bringNeighborLinkTofront ( )
55+ ) ;
4756 }
4857
4958 public componentWillUnmount ( ) {
5059 this . linkListener ( ) ;
60+ this . highlightedNeighborLinkListener ( ) ;
5161 }
5262
5363 private initializePolylineRefs = ( ) => {
@@ -60,6 +70,13 @@ class RoutePathNeighborLinkLayer extends Component<IRoutePathLayerProps, IRouteP
6070 } ) ;
6171 } ;
6272
73+ private bringNeighborLinkTofront = ( ) => {
74+ const id = this . props . routePathLayerStore ! . highlightedNeighborLinkId ;
75+ if ( id ) {
76+ this . state . polylineRefs [ id ] ! . current . leafletElement . bringToFront ( ) ;
77+ }
78+ } ;
79+
6380 private showNodePopup = ( node : INode , routePaths : IRoutePath [ ] ) => {
6481 const popupData : INodeUsagePopupData = {
6582 routePaths,
@@ -148,6 +165,10 @@ class RoutePathNeighborLinkLayer extends Component<IRoutePathLayerProps, IRouteP
148165 }
149166 } ;
150167
168+ private renderNeighborLinks = ( neighborLinks : INeighborLink [ ] ) => {
169+ return neighborLinks . map ( ( neighborLink ) => this . renderNeighborLink ( neighborLink ) ) ;
170+ } ;
171+
151172 private renderNeighborLink = ( neighborLink : INeighborLink ) => {
152173 const onNeighborLinkClick = ( ) => {
153174 const clickParams : IEditRoutePathNeighborLinkClickParams = {
@@ -193,7 +214,6 @@ class RoutePathNeighborLinkLayer extends Component<IRoutePathLayerProps, IRouteP
193214 if ( isHighlighted ) {
194215 if ( this . props . routePathLayerStore ! . highlightedNeighborLinkId !== id ) {
195216 this . props . routePathLayerStore ! . setHighlightedNeighborLinkId ( id ) ;
196- this . state . polylineRefs [ id ] ! . current . leafletElement . bringToFront ( ) ;
197217 }
198218 } else {
199219 if ( this . props . routePathLayerStore ! . highlightedNeighborLinkId === id ) {
@@ -203,21 +223,114 @@ class RoutePathNeighborLinkLayer extends Component<IRoutePathLayerProps, IRouteP
203223 } ;
204224
205225 render ( ) {
206- const neighborLinks = this . props . routePathLayerStore ! . neighborLinks ;
207- return neighborLinks . map ( ( neighborLink , index ) => {
208- const neighborToAddType = this . props . routePathLayerStore ! . neighborToAddType ;
209- const nodeToRender =
210- neighborToAddType === NeighborToAddType . AfterNode
211- ? neighborLink . routePathLink . endNode
212- : neighborLink . routePathLink . startNode ;
213- return [
214- this . renderNeighborNode ( nodeToRender , neighborLink , index ) ,
215- this . renderNeighborLink ( neighborLink ) ,
216- ] ;
217- } ) ;
226+ const neighborLinks : INeighborLink [ ] = this . props . routePathLayerStore ! . neighborLinks ;
227+ const clusteredNeighborLinksMap : Map <
228+ LatLngBounds ,
229+ INeighborLink [ ]
230+ > = _getClusteredNeighborLinksMap (
231+ neighborLinks ,
232+ this . props . routePathLayerStore ! . neighborToAddType
233+ ) ;
234+
235+ const clusteredNeighborLinkMapEntries = Array . from ( clusteredNeighborLinksMap . entries ( ) ) ;
236+
237+ return (
238+ < >
239+ { clusteredNeighborLinkMapEntries . map ( ( [ bounds , neighborLinkCluster ] , index ) => {
240+ if ( neighborLinkCluster . length === 1 ) {
241+ const neighborLink = neighborLinkCluster [ 0 ] ;
242+ const nodeToRender =
243+ this . props . routePathLayerStore ! . neighborToAddType ===
244+ NeighborToAddType . AfterNode
245+ ? neighborLink . routePathLink . endNode
246+ : neighborLink . routePathLink . startNode ;
247+ return [
248+ this . renderNeighborNode ( nodeToRender , neighborLink , index ) ,
249+ this . renderNeighborLink ( neighborLink ) ,
250+ ] ;
251+ }
252+ if ( neighborLinkCluster . length > 1 ) {
253+ const searchNodes = neighborLinkCluster . map (
254+ ( neighborLink : INeighborLink ) => {
255+ const nodeToRender =
256+ this . props . routePathLayerStore ! . neighborToAddType ===
257+ NeighborToAddType . AfterNode
258+ ? neighborLink . routePathLink . endNode
259+ : neighborLink . routePathLink . startNode ;
260+ return NodeFactory . createSearchNodeFromNode ( nodeToRender ) ;
261+ }
262+ ) ;
263+
264+ const popupData : ISelectRoutePathNeighborPopupData = {
265+ neighborNodes : neighborLinkCluster . map (
266+ ( neighborLink : INeighborLink ) => {
267+ const nodeToRender =
268+ this . props . routePathLayerStore ! . neighborToAddType ===
269+ NeighborToAddType . AfterNode
270+ ? neighborLink . routePathLink . endNode
271+ : neighborLink . routePathLink . startNode ;
272+ return {
273+ neighborLink,
274+ node : nodeToRender ,
275+ } ;
276+ }
277+ ) ,
278+ } ;
279+ return [
280+ < ClusterNodeMarker
281+ key = { `clusterMarker-${ index } ` }
282+ coordinates = { bounds . getCenter ( ) }
283+ nodes = { searchNodes }
284+ iconSize = { 'large' }
285+ popupType = { 'selectRoutePathNeighborPopup' }
286+ popupData = { popupData }
287+ /> ,
288+ this . renderNeighborLinks ( neighborLinkCluster ) ,
289+ ] ;
290+ }
291+ return null ;
292+ } ) }
293+ </ >
294+ ) ;
218295 }
219296}
220297
298+ const _getClusteredNeighborLinksMap = (
299+ neighborLinks : INeighborLink [ ] ,
300+ neighborToAddType : NeighborToAddType
301+ ) : Map < LatLngBounds , INeighborLink [ ] > => {
302+ if ( neighborLinks . length === 0 ) {
303+ return new Map ( ) ;
304+ }
305+ const clusteredNeighborLinksMap = new Map < LatLngBounds , INeighborLink [ ] > ( ) ;
306+
307+ for ( const neighborLink of neighborLinks ) {
308+ let areaBounds ;
309+ const node =
310+ neighborToAddType === NeighborToAddType . AfterNode
311+ ? neighborLink . routePathLink . endNode
312+ : neighborLink . routePathLink . startNode ;
313+ if ( clusteredNeighborLinksMap . size !== 0 ) {
314+ const areaEntries = clusteredNeighborLinksMap . entries ( ) ;
315+ for ( const [ area ] of areaEntries ) {
316+ if ( area . contains ( node . coordinates ) ) {
317+ areaBounds = area ;
318+ break ;
319+ }
320+ }
321+ }
322+
323+ if ( ! areaBounds ) {
324+ areaBounds = node . coordinates . toBounds ( 3 ) ;
325+ }
326+
327+ const neighborLinkGroup = clusteredNeighborLinksMap . get ( areaBounds ) || [ ] ;
328+ neighborLinkGroup . push ( neighborLink ) ;
329+ clusteredNeighborLinksMap . set ( areaBounds , neighborLinkGroup ) ;
330+ }
331+ return clusteredNeighborLinksMap ;
332+ } ;
333+
221334const getNeighborLinkColor = ( neighborLink : INeighborLink , isNeighborLinkHighlighted : boolean ) => {
222335 const isNeighborLinkUsed = neighborLink . nodeUsageRoutePaths . length > 0 ;
223336 if ( isNeighborLinkHighlighted ) {
0 commit comments