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" ;
import State from "../../State" ;
2021-09-21 02:10:42 +02:00
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" ;
2021-07-13 16:11:57 +02:00
import { GeoOperations } from "../../Logic/GeoOperations" ;
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 { OsmObject , OsmWay } from "../../Logic/Osm/OsmObject" ;
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-07-13 11:26:50 +02:00
export default class SplitRoadWizard extends Toggle {
2021-09-21 02:10:42 +02:00
private static splitLayerStyling = new LayerConfig ( {
id : "splitpositions" ,
source : { osmTags : "_cutposition=yes" } ,
icon : "./assets/svg/plus.svg"
} , "(BUILTIN) SplitRoadWizard.ts" , true )
2021-07-15 00:26:25 +02:00
2021-07-13 11:26:50 +02:00
/ * *
* A UI Element used for splitting roads
*
* @param id : The id of the road to remove
* /
constructor ( id : string ) {
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-07-13 16:11:57 +02:00
// Minimap on which you can select the points to be splitted
2021-09-21 02:10:42 +02:00
const miniMap = Minimap . createMiniMap ( { background : State.state.backgroundLayer , allowMoving : false } ) ;
2021-07-15 20:47:28 +02:00
miniMap . SetStyle ( "width: 100%; height: 24rem;" ) ;
2021-07-13 16:11:57 +02:00
// Define how a cut is displayed on the map
// Load the road with given id on the minimap
const roadElement = State . state . allElements . ContainingFeatures . get ( id )
const roadEventSource = new UIEventSource ( [ { feature : roadElement , freshness : new Date ( ) } ] ) ;
// Datalayer displaying the road and the cut points (if any)
2021-09-21 02:10:42 +02:00
new ShowDataMultiLayer ( {
features : new StaticFeatureSource ( roadEventSource , true ) ,
layers : State.state.filteredLayers ,
leafletMap : miniMap.leafletMap ,
enablePopups : false ,
zoomToFeatures : true
} )
new ShowDataLayer ( {
features : new StaticFeatureSource ( splitPoints , true ) ,
leafletMap : miniMap.leafletMap ,
zoomToFeatures : false ,
enablePopups : false ,
layerToShow : SplitRoadWizard.splitLayerStyling
} )
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 ) {
// 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
pointOnRoad . properties . _cutposition = "yes" ;
2021-07-15 00:26:25 +02:00
pointOnRoad [ "_matching_layer_id" ] = "splitpositions" ;
2021-07-13 16:11:57 +02:00
// let the state remember the point, to be able to retrieve it later by id
State . state . allElements . addOrGetElement ( pointOnRoad ) ;
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-07-15 20:47:28 +02:00
const splitButton = new SubtleButton ( Svg . scissors_ui ( ) , t . inviteToSplit . Clone ( ) ) ;
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 ( )
. onClick ( ( ) = > State . state . osmConnection . AttemptLogin ( ) )
. SetClass ( "login-button-friendly" ) ;
const splitToggle = new Toggle ( splitButton , loginBtn , State . 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 )
2021-07-18 14:52:09 +02:00
const way = OsmObject . DownloadObject ( id )
const partOfSrc = OsmObject . DownloadReferencingRelations ( id ) ;
let hasRun = false
way . map ( way = > {
const partOf = partOfSrc . data
2021-09-09 00:05:51 +02:00
if ( way === undefined || partOf === undefined ) {
2021-07-18 14:52:09 +02:00
return ;
}
2021-09-09 00:05:51 +02:00
if ( hasRun ) {
2021-07-18 14:52:09 +02:00
return
2021-07-15 20:47:28 +02:00
}
2021-07-18 14:52:09 +02:00
hasRun = true
const splitAction = new SplitAction (
< OsmWay > way , way . asGeoJson ( ) , partOf , splitPoints . data . map ( ff = > ff . feature )
)
State . state . changes . applyAction ( splitAction )
2021-09-09 00:05:51 +02:00
2021-07-18 14:52:09 +02:00
} , [ partOfSrc ] )
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-07-13 11:26:50 +02:00
}
}