205 lines
7.6 KiB
TypeScript
205 lines
7.6 KiB
TypeScript
import Combine from "../Base/Combine"
|
|
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
|
import { BBox } from "../../Logic/BBox"
|
|
import UserRelatedState from "../../Logic/State/UserRelatedState"
|
|
import Translations from "../i18n/Translations"
|
|
import { AllKnownLayouts } from "../../Customizations/AllKnownLayouts"
|
|
import Constants from "../../Models/Constants"
|
|
import { DropDown } from "../Input/DropDown"
|
|
import { Utils } from "../../Utils"
|
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
|
import BaseLayer from "../../Models/BaseLayer"
|
|
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
|
|
import Loc from "../../Models/Loc"
|
|
import Minimap from "../Base/Minimap"
|
|
import Attribution from "../BigComponents/Attribution"
|
|
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"
|
|
import FilteredLayer, { FilterState } from "../../Models/FilteredLayer"
|
|
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
|
import Toggle from "../Input/Toggle"
|
|
import { VariableUiElement } from "../Base/VariableUIElement"
|
|
import { FixedUiElement } from "../Base/FixedUiElement"
|
|
import { FlowStep } from "./FlowStep"
|
|
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
|
|
import Title from "../Base/Title"
|
|
import CheckBoxes from "../Input/Checkboxes"
|
|
import { AllTagsPanel } from "../AllTagsPanel"
|
|
import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch"
|
|
|
|
class PreviewPanel extends ScrollableFullScreen {
|
|
constructor(tags: UIEventSource<any>) {
|
|
super(
|
|
(_) => new FixedUiElement("Element to import"),
|
|
(_) => new Combine(["The tags are:", new AllTagsPanel(tags)]).SetClass("flex flex-col"),
|
|
"element"
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shows the data to import on a map, asks for the correct layer to be selected
|
|
*/
|
|
export class MapPreview
|
|
extends Combine
|
|
implements FlowStep<{ bbox: BBox; layer: LayerConfig; features: any[] }>
|
|
{
|
|
public readonly IsValid: Store<boolean>
|
|
public readonly Value: Store<{ bbox: BBox; layer: LayerConfig; features: any[] }>
|
|
|
|
constructor(
|
|
state: UserRelatedState,
|
|
geojson: { features: { properties: any; geometry: { coordinates: [number, number] } }[] }
|
|
) {
|
|
const t = Translations.t.importHelper.mapPreview
|
|
|
|
const propertyKeys = new Set<string>()
|
|
for (const f of geojson.features) {
|
|
Object.keys(f.properties).forEach((key) => propertyKeys.add(key))
|
|
}
|
|
|
|
const availableLayers = AllKnownLayouts.AllPublicLayers().filter(
|
|
(l) => l.name !== undefined && Constants.priviliged_layers.indexOf(l.id) < 0
|
|
)
|
|
const layerPicker = new DropDown(
|
|
t.selectLayer,
|
|
[{ shown: t.selectLayer, value: undefined }].concat(
|
|
availableLayers.map((l) => ({
|
|
shown: l.name,
|
|
value: l,
|
|
}))
|
|
)
|
|
)
|
|
|
|
let autodetected = new UIEventSource(false)
|
|
for (const layer of availableLayers) {
|
|
const mismatched = geojson.features.some(
|
|
(f) => !layer.source.osmTags.matchesProperties(f.properties)
|
|
)
|
|
if (!mismatched) {
|
|
console.log("Autodected layer", layer.id)
|
|
layerPicker.GetValue().setData(layer)
|
|
layerPicker.GetValue().addCallback((_) => autodetected.setData(false))
|
|
autodetected.setData(true)
|
|
break
|
|
}
|
|
}
|
|
|
|
const withId = geojson.features.map((f, i) => {
|
|
const copy = Utils.Clone(f)
|
|
copy.properties.id = "to-import/" + i
|
|
return copy
|
|
})
|
|
|
|
const matching: Store<{ properties: any; geometry: { coordinates: [number, number] } }[]> =
|
|
layerPicker.GetValue().map((layer: LayerConfig) => {
|
|
if (layer === undefined) {
|
|
return []
|
|
}
|
|
const matching: { properties: any; geometry: { coordinates: [number, number] } }[] =
|
|
[]
|
|
|
|
for (const feature of withId) {
|
|
if (layer.source.osmTags.matchesProperties(feature.properties)) {
|
|
matching.push(feature)
|
|
}
|
|
}
|
|
|
|
return matching
|
|
})
|
|
const background = new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
|
|
const location = new UIEventSource<Loc>({ lat: 0, lon: 0, zoom: 1 })
|
|
const currentBounds = new UIEventSource<BBox>(undefined)
|
|
const map = Minimap.createMiniMap({
|
|
allowMoving: true,
|
|
location,
|
|
background,
|
|
bounds: currentBounds,
|
|
attribution: new Attribution(
|
|
location,
|
|
state.osmConnection.userDetails,
|
|
undefined,
|
|
currentBounds
|
|
),
|
|
})
|
|
const layerControl = new BackgroundMapSwitch(
|
|
{
|
|
backgroundLayer: background,
|
|
locationControl: location,
|
|
},
|
|
background
|
|
)
|
|
map.SetClass("w-full").SetStyle("height: 500px")
|
|
|
|
new ShowDataMultiLayer({
|
|
layers: new UIEventSource<FilteredLayer[]>(
|
|
AllKnownLayouts.AllPublicLayers()
|
|
.filter((l) => l.source.geojsonSource === undefined)
|
|
.map((l) => ({
|
|
layerDef: l,
|
|
isDisplayed: new UIEventSource<boolean>(true),
|
|
appliedFilters: new UIEventSource<Map<string, FilterState>>(undefined),
|
|
}))
|
|
),
|
|
zoomToFeatures: true,
|
|
features: StaticFeatureSource.fromDateless(
|
|
matching.map((features) => features.map((feature) => ({ feature })))
|
|
),
|
|
leafletMap: map.leafletMap,
|
|
popup: (tag) => new PreviewPanel(tag).SetClass("font-lg"),
|
|
})
|
|
var bbox = matching.map((feats) =>
|
|
BBox.bboxAroundAll(feats.map((f) => new BBox([f.geometry.coordinates])))
|
|
)
|
|
|
|
const mismatchIndicator = new VariableUiElement(
|
|
matching.map((matching) => {
|
|
if (matching === undefined) {
|
|
return undefined
|
|
}
|
|
const diff = geojson.features.length - matching.length
|
|
if (diff === 0) {
|
|
return undefined
|
|
}
|
|
const obligatory = layerPicker
|
|
.GetValue()
|
|
.data?.source?.osmTags?.asHumanString(false, false, {})
|
|
return t.mismatch.Subs({ count: diff, tags: obligatory }).SetClass("alert")
|
|
})
|
|
)
|
|
|
|
const confirm = new CheckBoxes([t.confirm])
|
|
super([
|
|
new Title(t.title, 1),
|
|
layerPicker,
|
|
new Toggle(t.autodetected.SetClass("thank"), undefined, autodetected),
|
|
|
|
mismatchIndicator,
|
|
map,
|
|
layerControl,
|
|
confirm,
|
|
])
|
|
|
|
this.Value = bbox.map(
|
|
(bbox) => ({
|
|
bbox,
|
|
features: geojson.features,
|
|
layer: layerPicker.GetValue().data,
|
|
}),
|
|
[layerPicker.GetValue()]
|
|
)
|
|
|
|
this.IsValid = matching.map(
|
|
(matching) => {
|
|
if (matching === undefined) {
|
|
return false
|
|
}
|
|
if (confirm.GetValue().data.length !== 1) {
|
|
return false
|
|
}
|
|
const diff = geojson.features.length - matching.length
|
|
return diff === 0
|
|
},
|
|
[confirm.GetValue()]
|
|
)
|
|
}
|
|
}
|