2021-07-13 11:26:50 +02:00
import Toggle from "../Input/Toggle" ;
import Svg from "../../Svg" ;
import { UIEventSource } from "../../Logic/UIEventSource" ;
import { SubtleButton } from "../Base/SubtleButton" ;
import Minimap from "../Base/Minimap" ;
2021-09-21 02:10:42 +02:00
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" ;
2021-09-28 17:30:48 +02:00
import { GeoOperations } from "../../Logic/GeoOperations" ;
2021-07-13 16:11:57 +02:00
import { LeafletMouseEvent } from "leaflet" ;
import Combine from "../Base/Combine" ;
import { Button } from "../Base/Button" ;
import Translations from "../i18n/Translations" ;
2021-07-15 20:47:28 +02:00
import SplitAction from "../../Logic/Osm/Actions/SplitAction" ;
import Title from "../Base/Title" ;
2021-09-21 02:10:42 +02:00
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" ;
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer" ;
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" ;
2021-09-28 17:30:48 +02:00
import { BBox } from "../../Logic/BBox" ;
2021-12-03 01:29:09 +01:00
import * as split_point from "../../assets/layers/split_point/split_point.json"
2022-01-19 20:34:04 +01:00
import { OsmConnection } from "../../Logic/Osm/OsmConnection" ;
import { Changes } from "../../Logic/Osm/Changes" ;
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" ;
import { ElementStorage } from "../../Logic/ElementStorage" ;
import BaseLayer from "../../Models/BaseLayer" ;
import FilteredLayer from "../../Models/FilteredLayer" ;
2022-01-18 18:52:42 +01:00
2021-07-13 11:26:50 +02:00
export default class SplitRoadWizard extends Toggle {
2021-12-03 01:29:09 +01:00
// @ts-ignore
private static splitLayerStyling = new LayerConfig ( split_point , "(BUILTIN) SplitRoadWizard.ts" , true )
2021-07-15 00:26:25 +02:00
2021-09-22 05:02:09 +02:00
public dialogIsOpened : UIEventSource < boolean >
2021-07-13 11:26:50 +02:00
/ * *
* A UI Element used for splitting roads
*
* @param id : The id of the road to remove
2022-01-19 20:34:04 +01:00
* @param state : the state of the application
2021-07-13 11:26:50 +02:00
* /
2022-01-19 20:34:04 +01:00
constructor ( id : string , state : {
filteredLayers : UIEventSource < FilteredLayer [ ] > ;
backgroundLayer : UIEventSource < BaseLayer > ;
featureSwitchIsTesting : UIEventSource < boolean > ;
featureSwitchIsDebugging : UIEventSource < boolean > ;
featureSwitchShowAllQuestions : UIEventSource < boolean > ;
osmConnection : OsmConnection ,
featureSwitchUserbadge : UIEventSource < boolean > ,
changes : Changes ,
layoutToUse : LayoutConfig ,
2022-01-26 21:40:38 +01:00
allElements : ElementStorage
} ) {
2021-07-13 11:26:50 +02:00
2021-07-13 16:11:57 +02:00
const t = Translations . t . split ;
2021-07-13 11:26:50 +02:00
2021-07-15 00:26:25 +02:00
// Contains the points on the road that are selected to split on - contains geojson points with extra properties such as 'location' with the distance along the linestring
2021-07-15 20:47:28 +02:00
const splitPoints = new UIEventSource < { feature : any , freshness : Date } [ ] > ( [ ] ) ;
const hasBeenSplit = new UIEventSource ( false )
2021-07-13 16:11:57 +02:00
// Toggle variable between show split button and map
2021-07-15 00:26:25 +02:00
const splitClicked = new UIEventSource < boolean > ( false ) ;
2021-09-22 05:02:09 +02:00
// Load the road with given id on the minimap
2022-01-19 20:34:04 +01:00
const roadElement = state . allElements . ContainingFeatures . get ( id )
2021-07-13 16:11:57 +02:00
// Minimap on which you can select the points to be splitted
2021-09-22 05:02:09 +02:00
const miniMap = Minimap . createMiniMap (
{
2022-01-19 20:34:04 +01:00
background : state.backgroundLayer ,
2021-09-22 05:02:09 +02:00
allowMoving : true ,
leafletOptions : {
minZoom : 14
}
} ) ;
miniMap . SetStyle ( "width: 100%; height: 24rem" )
. SetClass ( "rounded-xl overflow-hidden" ) ;
2021-10-16 02:54:22 +02:00
miniMap . installBounds ( BBox . get ( roadElement ) . pad ( 0.25 ) , false )
2021-07-13 16:11:57 +02:00
// Define how a cut is displayed on the map
2021-10-19 03:00:57 +02:00
2021-07-13 16:11:57 +02:00
// Datalayer displaying the road and the cut points (if any)
2021-09-22 05:02:09 +02:00
new ShowDataMultiLayer ( {
2021-09-26 17:36:39 +02:00
features : new StaticFeatureSource ( [ roadElement ] , false ) ,
2022-01-19 20:34:04 +01:00
layers : state.filteredLayers ,
2021-09-22 05:02:09 +02:00
leafletMap : miniMap.leafletMap ,
2021-10-15 05:20:02 +02:00
zoomToFeatures : true ,
2022-01-19 20:34:04 +01:00
state
2021-09-21 02:10:42 +02:00
} )
2022-01-26 21:40:38 +01:00
2021-12-03 01:29:09 +01:00
new ShowDataLayer ( {
features : new StaticFeatureSource ( splitPoints , true ) ,
leafletMap : miniMap.leafletMap ,
zoomToFeatures : false ,
layerToShow : SplitRoadWizard.splitLayerStyling ,
2022-01-19 20:34:04 +01:00
state
2021-12-03 01:29:09 +01:00
} )
2021-07-13 16:11:57 +02:00
/ * *
* Handles a click on the overleaf map .
* Finds the closest intersection with the road and adds a point there , ready to confirm the cut .
* @param coordinates Clicked location , [ lon , lat ]
* /
function onMapClick ( coordinates ) {
2021-09-22 05:02:09 +02:00
// First, we check if there is another, already existing point nearby
const points = splitPoints . data . map ( ( f , i ) = > [ f . feature , i ] )
2021-11-12 18:39:38 +01:00
. filter ( p = > GeoOperations . distanceBetween ( p [ 0 ] . geometry . coordinates , coordinates ) < 5 )
2021-09-22 05:02:09 +02:00
. map ( p = > p [ 1 ] )
2021-11-09 01:49:07 +01:00
. sort ( ( a , b ) = > a - b )
2022-02-22 14:13:41 +01:00
. reverse ( /*Copy/derived list, inplace reverse is fine*/ )
2021-09-22 05:02:09 +02:00
if ( points . length > 0 ) {
for ( const point of points ) {
splitPoints . data . splice ( point , 1 )
}
splitPoints . ping ( )
return ;
}
2021-07-13 16:11:57 +02:00
// Get nearest point on the road
const pointOnRoad = GeoOperations . nearestPoint ( roadElement , coordinates ) ; // pointOnRoad is a geojson
// Update point properties to let it match the layer
2021-12-03 01:29:09 +01:00
pointOnRoad . properties [ "_split_point" ] = "yes" ;
2021-07-13 16:11:57 +02:00
2021-07-15 20:47:28 +02:00
2021-07-15 00:26:25 +02:00
// Add it to the list of all points and notify observers
splitPoints . data . push ( { feature : pointOnRoad , freshness : new Date ( ) } ) ; // show the point on the data layer
splitPoints . ping ( ) ; // not updated using .setData, so manually ping observers
2021-07-13 16:11:57 +02:00
}
2021-07-13 11:26:50 +02:00
2021-07-13 16:11:57 +02:00
// When clicked, pass clicked location coordinates to onMapClick function
miniMap . leafletMap . addCallbackAndRunD (
( leafletMap ) = > leafletMap . on ( "click" , ( mouseEvent : LeafletMouseEvent ) = > {
onMapClick ( [ mouseEvent . latlng . lng , mouseEvent . latlng . lat ] )
} ) )
// Toggle between splitmap
2021-10-14 17:38:00 +02:00
const splitButton = new SubtleButton ( Svg . scissors_ui ( ) . SetStyle ( "height: 1.5rem; width: auto" ) , t . inviteToSplit . Clone ( ) . SetClass ( "text-lg font-bold" ) ) ;
2021-07-13 11:26:50 +02:00
splitButton . onClick (
( ) = > {
splitClicked . setData ( true )
}
)
2021-07-13 16:11:57 +02:00
// Only show the splitButton if logged in, else show login prompt
2021-07-19 11:47:17 +02:00
const loginBtn = t . loginToSplit . Clone ( )
2022-01-19 20:34:04 +01:00
. onClick ( ( ) = > state . osmConnection . AttemptLogin ( ) )
2021-07-19 11:47:17 +02:00
. SetClass ( "login-button-friendly" ) ;
2022-01-19 20:34:04 +01:00
const splitToggle = new Toggle ( splitButton , loginBtn , state . osmConnection . isLoggedIn )
2021-07-13 11:26:50 +02:00
2021-07-13 16:11:57 +02:00
// Save button
2021-07-15 20:47:28 +02:00
const saveButton = new Button ( t . split . Clone ( ) , ( ) = > {
hasBeenSplit . setData ( true )
2022-01-19 20:34:04 +01:00
state . changes . applyAction ( new SplitAction ( id , splitPoints . data . map ( ff = > ff . feature . geometry . coordinates ) , {
theme : state?.layoutToUse?.id
2021-10-04 03:12:42 +02:00
} ) )
2021-09-22 05:02:09 +02:00
} )
2021-07-15 20:47:28 +02:00
2021-07-19 11:47:17 +02:00
saveButton . SetClass ( "btn btn-primary mr-3" ) ;
const disabledSaveButton = new Button ( "Split" , undefined ) ;
disabledSaveButton . SetClass ( "btn btn-disabled mr-3" ) ;
2021-07-13 16:11:57 +02:00
// Only show the save button if there are split points defined
2021-07-15 00:26:25 +02:00
const saveToggle = new Toggle ( disabledSaveButton , saveButton , splitPoints . map ( ( data ) = > data . length === 0 ) )
2021-07-13 11:26:50 +02:00
2021-07-19 11:47:17 +02:00
const cancelButton = Translations . t . general . cancel . Clone ( ) // Not using Button() element to prevent full width button
. SetClass ( "btn btn-secondary mr-3" )
. onClick ( ( ) = > {
splitPoints . setData ( [ ] ) ;
splitClicked . setData ( false ) ;
} ) ;
2021-07-13 11:26:50 +02:00
2021-07-19 11:47:17 +02:00
cancelButton . SetClass ( "btn btn-secondary block" ) ;
2021-07-13 11:26:50 +02:00
2021-07-15 20:47:28 +02:00
const splitTitle = new Title ( t . splitTitle ) ;
2021-07-13 11:26:50 +02:00
2021-07-18 14:52:09 +02:00
const mapView = new Combine ( [ splitTitle , miniMap , new Combine ( [ cancelButton , saveToggle ] ) . SetClass ( "flex flex-row" ) ] ) ;
2021-07-15 20:47:28 +02:00
mapView . SetClass ( "question" )
const confirm = new Toggle ( mapView , splitToggle , splitClicked ) ;
super ( t . hasBeenSplit . Clone ( ) , confirm , hasBeenSplit )
2021-09-22 05:02:09 +02:00
this . dialogIsOpened = splitClicked
2021-07-13 11:26:50 +02:00
}
}