forked from MapComplete/MapComplete
		
	Refactoring: fix rendering of new roads, generated by a split
This commit is contained in:
		
							parent
							
								
									840990c08b
								
							
						
					
					
						commit
						8eb2c68f79
					
				
					 34 changed files with 443 additions and 333 deletions
				
			
		|  | @ -2,33 +2,25 @@ import Toggle from "../Input/Toggle" | |||
| import Svg from "../../Svg" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import { SubtleButton } from "../Base/SubtleButton" | ||||
| import { GeoOperations } from "../../Logic/GeoOperations" | ||||
| import Combine from "../Base/Combine" | ||||
| import { Button } from "../Base/Button" | ||||
| import Translations from "../i18n/Translations" | ||||
| import SplitAction from "../../Logic/Osm/Actions/SplitAction" | ||||
| import Title from "../Base/Title" | ||||
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import { BBox } from "../../Logic/BBox" | ||||
| import split_point from "../../assets/layers/split_point/split_point.json" | ||||
| import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||
| import { Changes } from "../../Logic/Osm/Changes" | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||
| import FilteredLayer from "../../Models/FilteredLayer" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| import ScrollableFullScreen from "../Base/ScrollableFullScreen" | ||||
| import { LoginToggle } from "./LoginButton" | ||||
| import { SpecialVisualizationState } from "../SpecialVisualization" | ||||
| import SvelteUIElement from "../Base/SvelteUIElement" | ||||
| import WaySplitMap from "../BigComponents/WaySplitMap.svelte" | ||||
| import { OsmObject } from "../../Logic/Osm/OsmObject" | ||||
| import { Feature, Point } from "geojson" | ||||
| import { WayId } from "../../Models/OsmFeature" | ||||
| import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||
| import { Changes } from "../../Logic/Osm/Changes" | ||||
| import { IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource" | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||
| 
 | ||||
| export default class SplitRoadWizard extends Combine { | ||||
|     private static splitLayerStyling = new LayerConfig( | ||||
|         split_point, | ||||
|         "(BUILTIN) SplitRoadWizard.ts", | ||||
|         true | ||||
|     ) | ||||
| 
 | ||||
|     public dialogIsOpened: UIEventSource<boolean> | ||||
| 
 | ||||
|     /** | ||||
|  | @ -37,20 +29,34 @@ export default class SplitRoadWizard extends Combine { | |||
|      * @param id: The id of the road to remove | ||||
|      * @param state: the state of the application | ||||
|      */ | ||||
|     constructor(id: string, state: SpecialVisualizationState) { | ||||
|     constructor( | ||||
|         id: WayId, | ||||
|         state: { | ||||
|             layout?: LayoutConfig | ||||
|             osmConnection?: OsmConnection | ||||
|             changes?: Changes | ||||
|             indexedFeatures?: IndexedFeatureSource | ||||
|             selectedElement?: UIEventSource<Feature> | ||||
|         } | ||||
|     ) { | ||||
|         const t = Translations.t.split | ||||
| 
 | ||||
|         // 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
 | ||||
|         const splitPoints = new UIEventSource<{ feature: any; freshness: Date }[]>([]) | ||||
|         const splitPoints = new UIEventSource<Feature<Point>[]>([]) | ||||
| 
 | ||||
|         const hasBeenSplit = new UIEventSource(false) | ||||
| 
 | ||||
|         // Toggle variable between show split button and map
 | ||||
|         const splitClicked = new UIEventSource<boolean>(false) | ||||
| 
 | ||||
|         const leafletMap = new UIEventSource<BaseUIElement>( | ||||
|             SplitRoadWizard.setupMapComponent(id, splitPoints, state) | ||||
|         ) | ||||
|         const leafletMap = new UIEventSource<BaseUIElement>(undefined) | ||||
| 
 | ||||
|         function initMap() { | ||||
|             SplitRoadWizard.setupMapComponent(id, splitPoints).then((mapComponent) => | ||||
|                 leafletMap.setData(mapComponent.SetClass("w-full h-80")) | ||||
|             ) | ||||
|         } | ||||
|         initMap() | ||||
| 
 | ||||
|         // Toggle between splitmap
 | ||||
|         const splitButton = new SubtleButton( | ||||
|  | @ -70,23 +76,19 @@ export default class SplitRoadWizard extends Combine { | |||
|             splitClicked.setData(false) | ||||
|             const splitAction = new SplitAction( | ||||
|                 id, | ||||
|                 splitPoints.data.map((ff) => ff.feature.geometry.coordinates), | ||||
|                 splitPoints.data.map((ff) => <[number, number]>(<Point>ff.geometry).coordinates), | ||||
|                 { | ||||
|                     theme: state?.layoutToUse?.id, | ||||
|                     theme: state?.layout?.id, | ||||
|                 }, | ||||
|                 5, | ||||
|                 (coordinates) => { | ||||
|                     state.allElements.ContainingFeatures.get(id).geometry["coordinates"] = | ||||
|                         coordinates | ||||
|                 } | ||||
|                 5 | ||||
|             ) | ||||
|             await state.changes.applyAction(splitAction) | ||||
|             await state.changes?.applyAction(splitAction) | ||||
|             // We throw away the old map and splitpoints, and create a new map from scratch
 | ||||
|             splitPoints.setData([]) | ||||
|             leafletMap.setData(SplitRoadWizard.setupMapComponent(id, splitPoints, state)) | ||||
|             initMap() | ||||
| 
 | ||||
|             // Close the popup. The contributor has to select a segment again to make sure they continue editing the correct segment; see #1219
 | ||||
|             ScrollableFullScreen.collapse() | ||||
|             state.selectedElement?.setData(undefined) | ||||
|         }) | ||||
| 
 | ||||
|         saveButton.SetClass("btn btn-primary mr-3") | ||||
|  | @ -131,95 +133,14 @@ export default class SplitRoadWizard extends Combine { | |||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private static setupMapComponent( | ||||
|         id: string, | ||||
|         splitPoints: UIEventSource<{ feature: any; freshness: Date }[]>, | ||||
|         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 | ||||
|             allElements: ElementStorage | ||||
|         } | ||||
|     ): BaseUIElement { | ||||
|         // Load the road with given id on the minimap
 | ||||
|         const roadElement = state.allElements.ContainingFeatures.get(id) | ||||
| 
 | ||||
|         // Minimap on which you can select the points to be splitted
 | ||||
|         const miniMap = Minimap.createMiniMap({ | ||||
|             background: state.backgroundLayer, | ||||
|             allowMoving: true, | ||||
|             leafletOptions: { | ||||
|                 minZoom: 14, | ||||
|             }, | ||||
|     private static async setupMapComponent( | ||||
|         id: WayId, | ||||
|         splitPoints: UIEventSource<Feature[]> | ||||
|     ): Promise<BaseUIElement> { | ||||
|         const osmWay = await OsmObject.DownloadObjectAsync(id) | ||||
|         return new SvelteUIElement(WaySplitMap, { | ||||
|             osmWay, | ||||
|             splitPoints, | ||||
|         }) | ||||
|         miniMap.SetStyle("width: 100%; height: 24rem").SetClass("rounded-xl overflow-hidden") | ||||
| 
 | ||||
|         miniMap.installBounds(BBox.get(roadElement).pad(0.25), false) | ||||
| 
 | ||||
|         // Define how a cut is displayed on the map
 | ||||
| 
 | ||||
|         // Datalayer displaying the road and the cut points (if any)
 | ||||
|         new ShowDataMultiLayer({ | ||||
|             features: StaticFeatureSource.fromGeojson([roadElement]), | ||||
|             layers: state.filteredLayers, | ||||
|             leafletMap: miniMap.leafletMap, | ||||
|             zoomToFeatures: true, | ||||
|             state, | ||||
|         }) | ||||
| 
 | ||||
|         new ShowDataLayer({ | ||||
|             features: new StaticFeatureSource(splitPoints), | ||||
|             leafletMap: miniMap.leafletMap, | ||||
|             zoomToFeatures: false, | ||||
|             layerToShow: SplitRoadWizard.splitLayerStyling, | ||||
|             state, | ||||
|         }) | ||||
|         /** | ||||
|          * 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) { | ||||
|             // First, we check if there is another, already existing point nearby
 | ||||
|             const points = splitPoints.data | ||||
|                 .map((f, i) => [f.feature, i]) | ||||
|                 .filter( | ||||
|                     (p) => GeoOperations.distanceBetween(p[0].geometry.coordinates, coordinates) < 5 | ||||
|                 ) | ||||
|                 .map((p) => p[1]) | ||||
|                 .sort((a, b) => a - b) | ||||
|                 .reverse(/*Copy/derived list, inplace reverse is fine*/) | ||||
|             if (points.length > 0) { | ||||
|                 for (const point of points) { | ||||
|                     splitPoints.data.splice(point, 1) | ||||
|                 } | ||||
|                 splitPoints.ping() | ||||
|                 return | ||||
|             } | ||||
| 
 | ||||
|             // Get nearest point on the road
 | ||||
|             const pointOnRoad = GeoOperations.nearestPoint(<any>roadElement, coordinates) // pointOnRoad is a geojson
 | ||||
| 
 | ||||
|             // Update point properties to let it match the layer
 | ||||
|             pointOnRoad.properties["_split_point"] = "yes" | ||||
| 
 | ||||
|             // 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
 | ||||
|         } | ||||
| 
 | ||||
|         // 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]) | ||||
|             }) | ||||
|         ) | ||||
|         return miniMap | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue