forked from MapComplete/MapComplete
More work on splitting roads, WIP; refactoring tests
This commit is contained in:
parent
e374bb355c
commit
1f93923820
62 changed files with 1163 additions and 823 deletions
|
@ -16,6 +16,11 @@ export interface MinimapOptions {
|
|||
lastClickLocation?: UIEventSource<{ lat: number, lon: number }>
|
||||
}
|
||||
|
||||
export interface MinimapObj {
|
||||
readonly leafletMap: UIEventSource<any>,
|
||||
installBounds(factor: number | BBox, showRange?: boolean) : void
|
||||
}
|
||||
|
||||
export default class Minimap {
|
||||
/**
|
||||
* A stub implementation. The actual implementation is injected later on, but only in the browser.
|
||||
|
@ -25,6 +30,6 @@ export default class Minimap {
|
|||
/**
|
||||
* Construct a minimap
|
||||
*/
|
||||
public static createMiniMap: (options: MinimapOptions) => BaseUIElement & { readonly leafletMap: UIEventSource<any> }
|
||||
public static createMiniMap: (options: MinimapOptions) => (BaseUIElement & MinimapObj)
|
||||
|
||||
}
|
|
@ -7,9 +7,9 @@ import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
|
|||
import {BBox} from "../../Logic/GeoOperations";
|
||||
import * as L from "leaflet";
|
||||
import {Map} from "leaflet";
|
||||
import Minimap, {MinimapOptions} from "./Minimap";
|
||||
import Minimap, {MinimapObj, MinimapOptions} from "./Minimap";
|
||||
|
||||
export default class MinimapImplementation extends BaseUIElement {
|
||||
export default class MinimapImplementation extends BaseUIElement implements MinimapObj {
|
||||
private static _nextId = 0;
|
||||
public readonly leafletMap: UIEventSource<Map>
|
||||
private readonly _id: string;
|
||||
|
@ -44,6 +44,65 @@ export default class MinimapImplementation extends BaseUIElement {
|
|||
Minimap.createMiniMap = options => new MinimapImplementation(options)
|
||||
}
|
||||
|
||||
public installBounds(factor: number | BBox, showRange?: boolean) {
|
||||
this.leafletMap.addCallbackD(leaflet => {
|
||||
console.log("Installing max bounds")
|
||||
|
||||
let bounds;
|
||||
if (typeof factor === "number") {
|
||||
bounds = leaflet.getBounds()
|
||||
leaflet.setMaxBounds(bounds.pad(factor))
|
||||
}else{
|
||||
// @ts-ignore
|
||||
leaflet.setMaxBounds(factor.toLeaflet())
|
||||
bounds = leaflet.getBounds()
|
||||
}
|
||||
|
||||
if (showRange) {
|
||||
const data = {
|
||||
type: "FeatureCollection",
|
||||
features: [{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[
|
||||
bounds.getEast(),
|
||||
bounds.getNorth()
|
||||
],
|
||||
[
|
||||
bounds.getWest(),
|
||||
bounds.getNorth()
|
||||
],
|
||||
[
|
||||
bounds.getWest(),
|
||||
bounds.getSouth()
|
||||
],
|
||||
|
||||
[
|
||||
bounds.getEast(),
|
||||
bounds.getSouth()
|
||||
],
|
||||
[
|
||||
bounds.getEast(),
|
||||
bounds.getNorth()
|
||||
]
|
||||
]
|
||||
}
|
||||
}]
|
||||
}
|
||||
// @ts-ignore
|
||||
L.geoJSON(data, {
|
||||
style: {
|
||||
color: "#f00",
|
||||
weight: 2,
|
||||
opacity: 0.4
|
||||
}
|
||||
}).addTo(leaflet)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
const div = document.createElement("div")
|
||||
div.id = this._id;
|
||||
|
|
|
@ -65,7 +65,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
|||
const tabsWithAboutMc = [...FullWelcomePaneWithTabs.ConstructBaseTabs(layoutToUse, isShown)]
|
||||
|
||||
const now = new Date()
|
||||
const date = now.getFullYear()+"-"+Utils.TwoDigits(now.getMonth()+1)+"-"+Utils.TwoDigits(now.getDate())
|
||||
const lastWeek = new Date(now.getDate() - 7 * 24 * 60 * 60 * 1000)
|
||||
const date = lastWeek.getFullYear()+"-"+Utils.TwoDigits(lastWeek.getMonth()+1)+"-"+Utils.TwoDigits(lastWeek.getDate())
|
||||
const osmcha_link = `https://osmcha.org/?filters=%7B%22date__gte%22%3A%5B%7B%22label%22%3A%22${date}%22%2C%22value%22%3A%222021-01-01%22%7D%5D%2C%22editor%22%3A%5B%7B%22label%22%3A%22mapcomplete%22%2C%22value%22%3A%22mapcomplete%22%7D%5D%7D`
|
||||
|
||||
tabsWithAboutMc.push({
|
||||
|
|
|
@ -20,6 +20,7 @@ import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject";
|
|||
import PresetConfig from "../../Models/ThemeConfig/PresetConfig";
|
||||
import FilteredLayer from "../../Models/FilteredLayer";
|
||||
import {And} from "../../Logic/Tags/And";
|
||||
import {BBox} from "../../Logic/GeoOperations";
|
||||
|
||||
/*
|
||||
* The SimpleAddUI is a single panel, which can have multiple states:
|
||||
|
@ -39,8 +40,6 @@ interface PresetInfo extends PresetConfig {
|
|||
export default class SimpleAddUI extends Toggle {
|
||||
|
||||
constructor(isShown: UIEventSource<boolean>) {
|
||||
|
||||
|
||||
const loginButton = new SubtleButton(Svg.osm_logo_ui(), Translations.t.general.add.pleaseLogin.Clone())
|
||||
.onClick(() => State.state.osmConnection.AttemptLogin());
|
||||
const readYourMessages = new Combine([
|
||||
|
@ -52,7 +51,8 @@ export default class SimpleAddUI extends Toggle {
|
|||
|
||||
const selectedPreset = new UIEventSource<PresetInfo>(undefined);
|
||||
isShown.addCallback(_ => selectedPreset.setData(undefined)) // Clear preset selection when the UI is closed/opened
|
||||
|
||||
State.state.LastClickLocation.addCallback( _ => selectedPreset.setData(undefined))
|
||||
|
||||
const presetsOverview = SimpleAddUI.CreateAllPresetsPanel(selectedPreset)
|
||||
|
||||
|
||||
|
@ -82,11 +82,7 @@ export default class SimpleAddUI extends Toggle {
|
|||
return true;
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
() => {
|
||||
selectedPreset.setData(undefined)
|
||||
})
|
||||
|
@ -98,9 +94,9 @@ export default class SimpleAddUI extends Toggle {
|
|||
new Toggle(
|
||||
new Toggle(
|
||||
new Toggle(
|
||||
Translations.t.general.add.stillLoading.Clone().SetClass("alert"),
|
||||
addUi,
|
||||
State.state.featurePipeline.runningQuery
|
||||
Translations.t.general.add.stillLoading.Clone().SetClass("alert"),
|
||||
State.state.featurePipeline.somethingLoaded
|
||||
),
|
||||
Translations.t.general.add.zoomInFurther.Clone().SetClass("alert"),
|
||||
State.state.locationControl.map(loc => loc.zoom >= Constants.userJourney.minZoomLevelToAddNewPoints)
|
||||
|
@ -126,6 +122,7 @@ export default class SimpleAddUI extends Toggle {
|
|||
let location = State.state.LastClickLocation;
|
||||
let preciseInput: LocationInput = undefined
|
||||
if (preset.preciseInput !== undefined) {
|
||||
// We uncouple the event source
|
||||
const locationSrc = new UIEventSource({
|
||||
lat: location.data.lat,
|
||||
lon: location.data.lon,
|
||||
|
@ -137,24 +134,48 @@ export default class SimpleAddUI extends Toggle {
|
|||
backgroundLayer = AvailableBaseLayers.SelectBestLayerAccordingTo(locationSrc, new UIEventSource<string | string[]>(preset.preciseInput.preferredBackground))
|
||||
}
|
||||
|
||||
let features: UIEventSource<{ feature: any }[]> = undefined
|
||||
let snapToFeatures: UIEventSource<{ feature: any }[]> = undefined
|
||||
let mapBounds: UIEventSource<BBox> = undefined
|
||||
if (preset.preciseInput.snapToLayers) {
|
||||
// We have to snap to certain layers.
|
||||
// Lets fetch tehm
|
||||
const asSet = new Set(preset.preciseInput.snapToLayers)
|
||||
features = State.state.featurePipeline.features.map(f => f.filter(feat => asSet.has(feat.feature._matching_layer_id)))
|
||||
snapToFeatures = new UIEventSource<{ feature: any }[]>([])
|
||||
mapBounds = new UIEventSource<BBox>(undefined)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const tags = TagUtils.KVtoProperties(preset.tags ?? []);
|
||||
preciseInput = new LocationInput({
|
||||
mapBackground: backgroundLayer,
|
||||
centerLocation: locationSrc,
|
||||
snapTo: features,
|
||||
snapTo: snapToFeatures,
|
||||
snappedPointTags: tags,
|
||||
maxSnapDistance: preset.preciseInput.maxSnapDistance
|
||||
|
||||
maxSnapDistance: preset.preciseInput.maxSnapDistance,
|
||||
bounds: mapBounds
|
||||
})
|
||||
preciseInput.SetClass("h-32 rounded-xl overflow-hidden border border-gray").SetStyle("height: 12rem;")
|
||||
|
||||
|
||||
if (preset.preciseInput.snapToLayers) {
|
||||
// We have to snap to certain layers.
|
||||
// Lets fetch them
|
||||
|
||||
let loadedBbox : BBox= undefined
|
||||
mapBounds?.addCallbackAndRunD(bbox => {
|
||||
if(loadedBbox !== undefined && bbox.isContainedIn(loadedBbox)){
|
||||
// All is already there
|
||||
// return;
|
||||
}
|
||||
|
||||
bbox = bbox.pad(2);
|
||||
loadedBbox = bbox;
|
||||
const allFeatures: {feature: any}[] = []
|
||||
preset.preciseInput.snapToLayers.forEach(layerId => {
|
||||
State.state.featurePipeline.GetFeaturesWithin(layerId, bbox).forEach(feats => allFeatures.push(...feats.map(f => ({feature :f}))))
|
||||
})
|
||||
snapToFeatures.setData(allFeatures)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import Combine from "../Base/Combine";
|
|||
import Svg from "../../Svg";
|
||||
import State from "../../State";
|
||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
|
||||
import {GeoOperations} from "../../Logic/GeoOperations";
|
||||
import {BBox, GeoOperations} from "../../Logic/GeoOperations";
|
||||
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
|
||||
import * as L from "leaflet";
|
||||
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer";
|
||||
|
@ -38,6 +38,8 @@ export default class LocationInput extends InputElement<Loc> {
|
|||
private readonly _snappedPoint: UIEventSource<any>
|
||||
private readonly _maxSnapDistance: number
|
||||
private readonly _snappedPointTags: any;
|
||||
private readonly _bounds: UIEventSource<BBox>;
|
||||
public readonly _matching_layer: UIEventSource<LayerConfig>;
|
||||
|
||||
constructor(options: {
|
||||
mapBackground?: UIEventSource<BaseLayer>,
|
||||
|
@ -46,32 +48,33 @@ export default class LocationInput extends InputElement<Loc> {
|
|||
snappedPointTags?: any,
|
||||
requiresSnapping?: boolean,
|
||||
centerLocation: UIEventSource<Loc>,
|
||||
bounds?: UIEventSource<BBox>
|
||||
}) {
|
||||
super();
|
||||
this._snapTo = options.snapTo?.map(features => features?.filter(feat => feat.feature.geometry.type !== "Point"))
|
||||
this._maxSnapDistance = options.maxSnapDistance
|
||||
this._centerLocation = options.centerLocation;
|
||||
this._snappedPointTags = options.snappedPointTags
|
||||
this._bounds = options.bounds;
|
||||
if (this._snapTo === undefined) {
|
||||
this._value = this._centerLocation;
|
||||
} else {
|
||||
const self = this;
|
||||
|
||||
let matching_layer: UIEventSource<string>
|
||||
|
||||
if (self._snappedPointTags !== undefined) {
|
||||
matching_layer = State.state.layoutToUse.map(layout => {
|
||||
this._matching_layer = State.state.layoutToUse.map(layout => {
|
||||
|
||||
for (const layer of layout.layers) {
|
||||
if (layer.source.osmTags.matchesProperties(self._snappedPointTags)) {
|
||||
return layer.id
|
||||
return layer
|
||||
}
|
||||
}
|
||||
console.error("No matching layer found for tags ", self._snappedPointTags)
|
||||
return "matchpoint"
|
||||
return LocationInput.matchLayer
|
||||
})
|
||||
} else {
|
||||
matching_layer = new UIEventSource<string>("matchpoint")
|
||||
this._matching_layer = new UIEventSource<LayerConfig>(LocationInput.matchLayer)
|
||||
}
|
||||
|
||||
this._snappedPoint = options.centerLocation.map(loc => {
|
||||
|
@ -83,7 +86,7 @@ export default class LocationInput extends InputElement<Loc> {
|
|||
|
||||
let min = undefined;
|
||||
let matchedWay = undefined;
|
||||
for (const feature of self._snapTo.data) {
|
||||
for (const feature of self._snapTo.data ?? []) {
|
||||
const nearestPointOnLine = GeoOperations.nearestPoint(feature.feature, [loc.lon, loc.lat])
|
||||
if (min === undefined) {
|
||||
min = nearestPointOnLine
|
||||
|
@ -98,19 +101,17 @@ export default class LocationInput extends InputElement<Loc> {
|
|||
}
|
||||
}
|
||||
|
||||
if (min.properties.dist * 1000 > self._maxSnapDistance) {
|
||||
if (min === undefined || min.properties.dist * 1000 > self._maxSnapDistance) {
|
||||
if (options.requiresSnapping) {
|
||||
return undefined
|
||||
} else {
|
||||
return {
|
||||
"type": "Feature",
|
||||
"_matching_layer_id": matching_layer.data,
|
||||
"properties": options.snappedPointTags ?? min.properties,
|
||||
"geometry": {"type": "Point", "coordinates": [loc.lon, loc.lat]}
|
||||
}
|
||||
}
|
||||
}
|
||||
min._matching_layer_id = matching_layer?.data ?? "matchpoint"
|
||||
min.properties = options.snappedPointTags ?? min.properties
|
||||
self.snappedOnto.setData(matchedWay)
|
||||
return min
|
||||
|
@ -144,84 +145,40 @@ export default class LocationInput extends InputElement<Loc> {
|
|||
location: this._centerLocation,
|
||||
background: this.mapBackground,
|
||||
attribution: this.mapBackground !== State.state.backgroundLayer,
|
||||
lastClickLocation: clickLocation
|
||||
lastClickLocation: clickLocation,
|
||||
bounds: this._bounds
|
||||
}
|
||||
)
|
||||
clickLocation.addCallbackAndRunD(location => this._centerLocation.setData(location))
|
||||
map.leafletMap.addCallbackAndRunD(leaflet => {
|
||||
const bounds = leaflet.getBounds()
|
||||
leaflet.setMaxBounds(bounds.pad(0.15))
|
||||
const data = {
|
||||
type: "FeatureCollection",
|
||||
features: [{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[
|
||||
bounds.getEast(),
|
||||
bounds.getNorth()
|
||||
],
|
||||
[
|
||||
bounds.getWest(),
|
||||
bounds.getNorth()
|
||||
],
|
||||
[
|
||||
bounds.getWest(),
|
||||
bounds.getSouth()
|
||||
],
|
||||
|
||||
[
|
||||
bounds.getEast(),
|
||||
bounds.getSouth()
|
||||
],
|
||||
[
|
||||
bounds.getEast(),
|
||||
bounds.getNorth()
|
||||
]
|
||||
]
|
||||
}
|
||||
}]
|
||||
}
|
||||
// @ts-ignore
|
||||
L.geoJSON(data, {
|
||||
style: {
|
||||
color: "#f00",
|
||||
weight: 2,
|
||||
opacity: 0.4
|
||||
}
|
||||
}).addTo(leaflet)
|
||||
})
|
||||
map.installBounds(0.15, true);
|
||||
|
||||
if (this._snapTo !== undefined) {
|
||||
|
||||
|
||||
// Show the lines to snap to
|
||||
new ShowDataMultiLayer({
|
||||
features: new StaticFeatureSource(this._snapTo, true),
|
||||
enablePopups: false,
|
||||
zoomToFeatures: false,
|
||||
leafletMap: map.leafletMap,
|
||||
layers: State.state.filteredLayers
|
||||
}
|
||||
)
|
||||
// Show the central point
|
||||
const matchPoint = this._snappedPoint.map(loc => {
|
||||
if (loc === undefined) {
|
||||
return []
|
||||
}
|
||||
return [{feature: loc}];
|
||||
})
|
||||
if (this._snapTo) {
|
||||
if (this._snappedPointTags === undefined) {
|
||||
// No special tags - we show a default crosshair
|
||||
new ShowDataLayer({
|
||||
features: new StaticFeatureSource(matchPoint),
|
||||
enablePopups: false,
|
||||
zoomToFeatures: false,
|
||||
leafletMap: map.leafletMap,
|
||||
layerToShow: LocationInput.matchLayer
|
||||
})
|
||||
}else{
|
||||
new ShowDataMultiLayer({
|
||||
features: new StaticFeatureSource(matchPoint),
|
||||
enablePopups: false,
|
||||
zoomToFeatures: false,
|
||||
leafletMap: map.leafletMap,
|
||||
layers: State.state.filteredLayers
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
new ShowDataLayer({
|
||||
features: new StaticFeatureSource(matchPoint, true),
|
||||
enablePopups: false,
|
||||
zoomToFeatures: false,
|
||||
leafletMap: map.leafletMap,
|
||||
layerToShow: this._matching_layer.data
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
this.mapBackground.map(layer => {
|
||||
|
|
|
@ -130,7 +130,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
if (!userbadge) {
|
||||
return undefined
|
||||
}
|
||||
return new Combine(editElements)
|
||||
return new Combine(editElements).SetClass("flex flex-col")
|
||||
}
|
||||
))
|
||||
renderings.push(editors)
|
||||
|
|
|
@ -5,13 +5,12 @@ import {SubtleButton} from "../Base/SubtleButton";
|
|||
import Minimap from "../Base/Minimap";
|
||||
import State from "../../State";
|
||||
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
|
||||
import {GeoOperations} from "../../Logic/GeoOperations";
|
||||
import {BBox, GeoOperations} from "../../Logic/GeoOperations";
|
||||
import {LeafletMouseEvent} from "leaflet";
|
||||
import Combine from "../Base/Combine";
|
||||
import {Button} from "../Base/Button";
|
||||
import Translations from "../i18n/Translations";
|
||||
import SplitAction from "../../Logic/Osm/Actions/SplitAction";
|
||||
import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject";
|
||||
import Title from "../Base/Title";
|
||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource";
|
||||
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer";
|
||||
|
@ -21,9 +20,12 @@ export default class SplitRoadWizard extends Toggle {
|
|||
private static splitLayerStyling = new LayerConfig({
|
||||
id: "splitpositions",
|
||||
source: {osmTags: "_cutposition=yes"},
|
||||
icon: "./assets/svg/plus.svg"
|
||||
icon: {render: "circle:white;./assets/svg/scissors.svg"},
|
||||
iconSize: {render: "30,30,center"},
|
||||
}, "(BUILTIN) SplitRoadWizard.ts", true)
|
||||
|
||||
public dialogIsOpened: UIEventSource<boolean>
|
||||
|
||||
/**
|
||||
* A UI Element used for splitting roads
|
||||
*
|
||||
|
@ -40,30 +42,40 @@ export default class SplitRoadWizard extends Toggle {
|
|||
|
||||
// Toggle variable between show split button and map
|
||||
const splitClicked = new UIEventSource<boolean>(false);
|
||||
// Load the road with given id on the minimap
|
||||
const roadElement = State.state.allElements.ContainingFeatures.get(id)
|
||||
|
||||
// Minimap on which you can select the points to be splitted
|
||||
const miniMap = Minimap.createMiniMap({background: State.state.backgroundLayer, allowMoving: false});
|
||||
miniMap.SetStyle("width: 100%; height: 24rem;");
|
||||
const miniMap = Minimap.createMiniMap(
|
||||
{
|
||||
background: State.state.backgroundLayer,
|
||||
allowMoving: true,
|
||||
leafletOptions: {
|
||||
minZoom: 14
|
||||
}
|
||||
});
|
||||
miniMap.SetStyle("width: 100%; height: 24rem")
|
||||
.SetClass("rounded-xl overflow-hidden");
|
||||
|
||||
miniMap.installBounds(BBox.get(roadElement))
|
||||
|
||||
// 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)
|
||||
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
|
||||
layerToShow: SplitRoadWizard.splitLayerStyling
|
||||
})
|
||||
|
||||
new ShowDataMultiLayer({
|
||||
features: new StaticFeatureSource([roadElement]),
|
||||
layers: State.state.filteredLayers,
|
||||
leafletMap: miniMap.leafletMap,
|
||||
enablePopups: false,
|
||||
zoomToFeatures: true
|
||||
})
|
||||
|
||||
/**
|
||||
|
@ -72,12 +84,25 @@ export default class SplitRoadWizard extends Toggle {
|
|||
* @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) * 1000 < 5)
|
||||
.map(p => p[1])
|
||||
.sort()
|
||||
.reverse()
|
||||
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(roadElement, coordinates); // pointOnRoad is a geojson
|
||||
|
||||
// Update point properties to let it match the layer
|
||||
pointOnRoad.properties._cutposition = "yes";
|
||||
pointOnRoad["_matching_layer_id"] = "splitpositions";
|
||||
|
||||
// let the state remember the point, to be able to retrieve it later by id
|
||||
State.state.allElements.addOrGetElement(pointOnRoad);
|
||||
|
@ -94,7 +119,7 @@ export default class SplitRoadWizard extends Toggle {
|
|||
}))
|
||||
|
||||
// Toggle between splitmap
|
||||
const splitButton = new SubtleButton(Svg.scissors_ui(), t.inviteToSplit.Clone());
|
||||
const splitButton = new SubtleButton(Svg.scissors_ui(), t.inviteToSplit.Clone().SetClass("text-lg font-bold"));
|
||||
splitButton.onClick(
|
||||
() => {
|
||||
splitClicked.setData(true)
|
||||
|
@ -110,27 +135,9 @@ export default class SplitRoadWizard extends Toggle {
|
|||
// Save button
|
||||
const saveButton = new Button(t.split.Clone(), () => {
|
||||
hasBeenSplit.setData(true)
|
||||
const way = OsmObject.DownloadObject(id)
|
||||
const partOfSrc = OsmObject.DownloadReferencingRelations(id);
|
||||
let hasRun = false
|
||||
way.map(way => {
|
||||
const partOf = partOfSrc.data
|
||||
if (way === undefined || partOf === undefined) {
|
||||
return;
|
||||
}
|
||||
if (hasRun) {
|
||||
return
|
||||
}
|
||||
hasRun = true
|
||||
const splitAction = new SplitAction(
|
||||
<OsmWay>way, way.asGeoJson(), partOf, splitPoints.data.map(ff => ff.feature)
|
||||
)
|
||||
State.state.changes.applyAction(splitAction)
|
||||
State.state.changes.applyAction(new SplitAction(id, splitPoints.data.map(ff => ff.feature.geometry.coordinates)))
|
||||
})
|
||||
|
||||
}, [partOfSrc])
|
||||
|
||||
|
||||
});
|
||||
saveButton.SetClass("btn btn-primary mr-3");
|
||||
const disabledSaveButton = new Button("Split", undefined);
|
||||
disabledSaveButton.SetClass("btn btn-disabled mr-3");
|
||||
|
@ -152,5 +159,6 @@ export default class SplitRoadWizard extends Toggle {
|
|||
mapView.SetClass("question")
|
||||
const confirm = new Toggle(mapView, splitToggle, splitClicked);
|
||||
super(t.hasBeenSplit.Clone(), confirm, hasBeenSplit)
|
||||
this.dialogIsOpened = splitClicked
|
||||
}
|
||||
}
|
|
@ -37,8 +37,8 @@ export default class ShowDataLayer {
|
|||
this._layerToShow = options.layerToShow;
|
||||
const self = this;
|
||||
|
||||
features.addCallback(() => self.update(options));
|
||||
options.leafletMap.addCallback(() => self.update(options));
|
||||
features.addCallback(_ => self.update(options));
|
||||
options.leafletMap.addCallback(_ => self.update(options));
|
||||
this.update(options);
|
||||
|
||||
|
||||
|
@ -83,13 +83,17 @@ export default class ShowDataLayer {
|
|||
mp.removeLayer(this.geoLayer);
|
||||
}
|
||||
|
||||
this.geoLayer= this.CreateGeojsonLayer()
|
||||
const allFeats = this._features.data;
|
||||
this.geoLayer = this.CreateGeojsonLayer();
|
||||
for (const feat of allFeats) {
|
||||
if (feat === undefined) {
|
||||
continue
|
||||
}
|
||||
this.geoLayer.addData(feat);
|
||||
try{
|
||||
this.geoLayer.addData(feat);
|
||||
}catch(e){
|
||||
console.error("Could not add ", feat, "to the geojson layer in leaflet")
|
||||
}
|
||||
}
|
||||
|
||||
mp.addLayer(this.geoLayer)
|
||||
|
@ -122,7 +126,8 @@ export default class ShowDataLayer {
|
|||
}
|
||||
|
||||
const tagSource = feature.properties.id === undefined ? new UIEventSource<any>(feature.properties) : State.state.allElements.getEventSourceById(feature.properties.id)
|
||||
const style = layer.GenerateLeafletStyle(tagSource, !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0));
|
||||
const clickable = !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0)
|
||||
const style = layer.GenerateLeafletStyle(tagSource, clickable);
|
||||
const baseElement = style.icon.html;
|
||||
if (!this._enablePopups) {
|
||||
baseElement.SetStyle("cursor: initial !important")
|
||||
|
@ -132,7 +137,7 @@ export default class ShowDataLayer {
|
|||
html: baseElement.ConstructElement(),
|
||||
className: style.icon.className,
|
||||
iconAnchor: style.icon.iconAnchor,
|
||||
iconUrl: style.icon.iconUrl,
|
||||
iconUrl: style.icon.iconUrl ?? "./assets/svg/bug.svg",
|
||||
popupAnchor: style.icon.popupAnchor,
|
||||
iconSize: style.icon.iconSize
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue