forked from MapComplete/MapComplete
Repair import flow
This commit is contained in:
parent
8c7bb92f8a
commit
e1cdb75001
7 changed files with 85 additions and 74 deletions
20
UI/Base/DivContainer.ts
Normal file
20
UI/Base/DivContainer.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { UIElement } from "../UIElement"
|
||||||
|
import BaseUIElement from "../BaseUIElement"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Introduces a new element which has an ID
|
||||||
|
* Mostly a workaround for the import viewer
|
||||||
|
*/
|
||||||
|
export default class DivContainer extends BaseUIElement {
|
||||||
|
private readonly _id: string
|
||||||
|
|
||||||
|
constructor(id: string) {
|
||||||
|
super()
|
||||||
|
this._id = id
|
||||||
|
}
|
||||||
|
protected InnerConstructElement(): HTMLElement {
|
||||||
|
const e = document.createElement("div")
|
||||||
|
e.id = this._id
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,8 @@ import { VariableUiElement } from "../Base/VariableUIElement"
|
||||||
import * as known_layers from "../../assets/generated/known_layers.json"
|
import * as known_layers from "../../assets/generated/known_layers.json"
|
||||||
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
|
import { Feature } from "geojson"
|
||||||
|
import DivContainer from "../Base/DivContainer"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters out points for which the import-note already exists, to prevent duplicates
|
* Filters out points for which the import-note already exists, to prevent duplicates
|
||||||
|
@ -105,7 +107,7 @@ export class CompareToAlreadyExistingNotes
|
||||||
zoomToFeatures: true,
|
zoomToFeatures: true,
|
||||||
leafletMap: comparisonMap.leafletMap,
|
leafletMap: comparisonMap.leafletMap,
|
||||||
features: StaticFeatureSource.fromGeojsonStore(
|
features: StaticFeatureSource.fromGeojsonStore(
|
||||||
partitionedImportPoints.map((p) => p.hasNearby)
|
partitionedImportPoints.map((p) => <Feature[]>p.hasNearby)
|
||||||
),
|
),
|
||||||
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),
|
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),
|
||||||
})
|
})
|
||||||
|
@ -134,7 +136,7 @@ export class CompareToAlreadyExistingNotes
|
||||||
return new Combine([
|
return new Combine([
|
||||||
t.mapExplanation.Subs(params.features),
|
t.mapExplanation.Subs(params.features),
|
||||||
map,
|
map,
|
||||||
|
new DivContainer("fullscreen"),
|
||||||
new VariableUiElement(
|
new VariableUiElement(
|
||||||
partitionedImportPoints.map(({ noNearby, hasNearby }) => {
|
partitionedImportPoints.map(({ noNearby, hasNearby }) => {
|
||||||
if (noNearby.length === 0) {
|
if (noNearby.length === 0) {
|
||||||
|
|
|
@ -27,24 +27,26 @@ import { GeoOperations } from "../../Logic/GeoOperations"
|
||||||
import FeatureInfoBox from "../Popup/FeatureInfoBox"
|
import FeatureInfoBox from "../Popup/FeatureInfoBox"
|
||||||
import { ImportUtils } from "./ImportUtils"
|
import { ImportUtils } from "./ImportUtils"
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations"
|
||||||
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"
|
|
||||||
import FilteredLayer, { FilterState } from "../../Models/FilteredLayer"
|
|
||||||
import { Feature, FeatureCollection } from "@turf/turf"
|
|
||||||
import * as currentview from "../../assets/layers/current_view/current_view.json"
|
import * as currentview from "../../assets/layers/current_view/current_view.json"
|
||||||
import { CheckBox } from "../Input/Checkboxes"
|
import { CheckBox } from "../Input/Checkboxes"
|
||||||
import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch"
|
import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch"
|
||||||
|
import { Feature, FeatureCollection, Point } from "geojson"
|
||||||
|
import DivContainer from "../Base/DivContainer"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given the data to import, the bbox and the layer, will query overpass for similar items
|
* Given the data to import, the bbox and the layer, will query overpass for similar items
|
||||||
*/
|
*/
|
||||||
export default class ConflationChecker
|
export default class ConflationChecker
|
||||||
extends Combine
|
extends Combine
|
||||||
implements FlowStep<{ features: any[]; theme: string }>
|
implements FlowStep<{ features: Feature<Point>[]; theme: string }>
|
||||||
{
|
{
|
||||||
public readonly IsValid
|
public readonly IsValid
|
||||||
public readonly Value: Store<{ features: any[]; theme: string }>
|
public readonly Value: Store<{ features: Feature<Point>[]; theme: string }>
|
||||||
|
|
||||||
constructor(state, params: { bbox: BBox; layer: LayerConfig; theme: string; features: any[] }) {
|
constructor(
|
||||||
|
state,
|
||||||
|
params: { bbox: BBox; layer: LayerConfig; theme: string; features: Feature<Point>[] }
|
||||||
|
) {
|
||||||
const t = Translations.t.importHelper.conflationChecker
|
const t = Translations.t.importHelper.conflationChecker
|
||||||
|
|
||||||
const bbox = params.bbox.padAbsolute(0.0001)
|
const bbox = params.bbox.padAbsolute(0.0001)
|
||||||
|
@ -175,15 +177,8 @@ export default class ConflationChecker
|
||||||
features: StaticFeatureSource.fromGeojson([bbox.asGeoJson({})]),
|
features: StaticFeatureSource.fromGeojson([bbox.asGeoJson({})]),
|
||||||
})
|
})
|
||||||
|
|
||||||
new ShowDataMultiLayer({
|
new ShowDataLayer({
|
||||||
//layerToShow: layer,
|
layerToShow: layer,
|
||||||
layers: new UIEventSource<FilteredLayer[]>([
|
|
||||||
{
|
|
||||||
layerDef: layer,
|
|
||||||
isDisplayed: new UIEventSource<boolean>(true),
|
|
||||||
appliedFilters: new UIEventSource<Map<string, FilterState>>(undefined),
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
state,
|
state,
|
||||||
leafletMap: osmLiveData.leafletMap,
|
leafletMap: osmLiveData.leafletMap,
|
||||||
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state, { setHash: false }),
|
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state, { setHash: false }),
|
||||||
|
@ -239,7 +234,7 @@ export default class ConflationChecker
|
||||||
|
|
||||||
// Featuresource showing OSM-features which are nearby a toImport-feature
|
// Featuresource showing OSM-features which are nearby a toImport-feature
|
||||||
const toImportWithNearby = StaticFeatureSource.fromGeojsonStore(
|
const toImportWithNearby = StaticFeatureSource.fromGeojsonStore(
|
||||||
paritionedImport.map((els) => els?.hasNearby ?? [])
|
paritionedImport.map((els) => <Feature[]>els?.hasNearby ?? [])
|
||||||
)
|
)
|
||||||
toImportWithNearby.features.addCallback((nearby) =>
|
toImportWithNearby.features.addCallback((nearby) =>
|
||||||
console.log("The following features are near an already existing object:", nearby)
|
console.log("The following features are near an already existing object:", nearby)
|
||||||
|
@ -325,6 +320,7 @@ export default class ConflationChecker
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
]).SetClass("flex"),
|
]).SetClass("flex"),
|
||||||
|
new DivContainer("fullscreen"),
|
||||||
new Title(t.titleNearby),
|
new Title(t.titleNearby),
|
||||||
new Combine([t.mapShowingNearbyIntro, nearbyCutoff]).SetClass("flex"),
|
new Combine([t.mapShowingNearbyIntro, nearbyCutoff]).SetClass("flex"),
|
||||||
new VariableUiElement(
|
new VariableUiElement(
|
||||||
|
@ -369,7 +365,7 @@ export default class ConflationChecker
|
||||||
|
|
||||||
this.Value = paritionedImport.map((feats) => ({
|
this.Value = paritionedImport.map((feats) => ({
|
||||||
theme: params.theme,
|
theme: params.theme,
|
||||||
features: feats?.noNearby,
|
features: <any>feats?.noNearby,
|
||||||
layer: params.layer,
|
layer: params.layer,
|
||||||
}))
|
}))
|
||||||
this.IsValid = this.Value.map((v) => v?.features !== undefined && v.features.length > 0)
|
this.IsValid = this.Value.map((v) => v?.features !== undefined && v.features.length > 0)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Store } from "../../Logic/UIEventSource"
|
import { Store } from "../../Logic/UIEventSource"
|
||||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||||
import { Feature, Geometry } from "@turf/turf"
|
import { Feature, Point } from "geojson"
|
||||||
|
|
||||||
export class ImportUtils {
|
export class ImportUtils {
|
||||||
public static partitionFeaturesIfNearby(
|
public static partitionFeaturesIfNearby(
|
||||||
toPartitionFeatureCollection: { features: Feature<Geometry>[] },
|
toPartitionFeatureCollection: { features: Feature[] },
|
||||||
compareWith: Store<{ features: Feature[] }>,
|
compareWith: Store<{ features: Feature[] }>,
|
||||||
cutoffDistanceInMeters: Store<number>
|
cutoffDistanceInMeters: Store<number>
|
||||||
): Store<{ hasNearby: Feature[]; noNearby: Feature[] }> {
|
): Store<{ hasNearby: Feature[]; noNearby: Feature[] }> {
|
||||||
|
@ -25,7 +25,7 @@ export class ImportUtils {
|
||||||
(f) =>
|
(f) =>
|
||||||
maxDist >=
|
maxDist >=
|
||||||
GeoOperations.distanceBetween(
|
GeoOperations.distanceBetween(
|
||||||
<any>toImportElement.geometry.coordinates,
|
<any>(<Point>toImportElement.geometry).coordinates,
|
||||||
GeoOperations.centerpointCoordinates(f)
|
GeoOperations.centerpointCoordinates(f)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,6 +25,9 @@ import Title from "../Base/Title"
|
||||||
import CheckBoxes from "../Input/Checkboxes"
|
import CheckBoxes from "../Input/Checkboxes"
|
||||||
import { AllTagsPanel } from "../AllTagsPanel"
|
import { AllTagsPanel } from "../AllTagsPanel"
|
||||||
import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch"
|
import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch"
|
||||||
|
import { Feature, Point } from "geojson"
|
||||||
|
import DivContainer from "../Base/DivContainer"
|
||||||
|
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"
|
||||||
|
|
||||||
class PreviewPanel extends ScrollableFullScreen {
|
class PreviewPanel extends ScrollableFullScreen {
|
||||||
constructor(tags: UIEventSource<any>) {
|
constructor(tags: UIEventSource<any>) {
|
||||||
|
@ -41,15 +44,12 @@ class PreviewPanel extends ScrollableFullScreen {
|
||||||
*/
|
*/
|
||||||
export class MapPreview
|
export class MapPreview
|
||||||
extends Combine
|
extends Combine
|
||||||
implements FlowStep<{ bbox: BBox; layer: LayerConfig; features: any[] }>
|
implements FlowStep<{ bbox: BBox; layer: LayerConfig; features: Feature<Point>[] }>
|
||||||
{
|
{
|
||||||
public readonly IsValid: Store<boolean>
|
public readonly IsValid: Store<boolean>
|
||||||
public readonly Value: Store<{ bbox: BBox; layer: LayerConfig; features: any[] }>
|
public readonly Value: Store<{ bbox: BBox; layer: LayerConfig; features: any[] }>
|
||||||
|
|
||||||
constructor(
|
constructor(state: UserRelatedState, geojson: { features: Feature[] }) {
|
||||||
state: UserRelatedState,
|
|
||||||
geojson: { features: { properties: any; geometry: { coordinates: [number, number] } }[] }
|
|
||||||
) {
|
|
||||||
const t = Translations.t.importHelper.mapPreview
|
const t = Translations.t.importHelper.mapPreview
|
||||||
|
|
||||||
const propertyKeys = new Set<string>()
|
const propertyKeys = new Set<string>()
|
||||||
|
@ -90,22 +90,23 @@ export class MapPreview
|
||||||
return copy
|
return copy
|
||||||
})
|
})
|
||||||
|
|
||||||
const matching: Store<{ properties: any; geometry: { coordinates: [number, number] } }[]> =
|
// Create a store which has only features matching the selected layer
|
||||||
layerPicker.GetValue().map((layer: LayerConfig) => {
|
const matching: Store<Feature[]> = layerPicker.GetValue().map((layer: LayerConfig) => {
|
||||||
if (layer === undefined) {
|
if (layer === undefined) {
|
||||||
return []
|
console.log("No matching layer found")
|
||||||
}
|
return []
|
||||||
const matching: { properties: any; geometry: { coordinates: [number, number] } }[] =
|
}
|
||||||
[]
|
const matching: Feature[] = []
|
||||||
|
|
||||||
for (const feature of withId) {
|
for (const feature of withId) {
|
||||||
if (layer.source.osmTags.matchesProperties(feature.properties)) {
|
if (layer.source.osmTags.matchesProperties(feature.properties)) {
|
||||||
matching.push(feature)
|
matching.push(feature)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
console.log("Matching features: ", matching)
|
||||||
|
|
||||||
return matching
|
return matching
|
||||||
})
|
})
|
||||||
const background = new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
|
const background = new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
|
||||||
const location = new UIEventSource<Loc>({ lat: 0, lon: 0, zoom: 1 })
|
const location = new UIEventSource<Loc>({ lat: 0, lon: 0, zoom: 1 })
|
||||||
const currentBounds = new UIEventSource<BBox>(undefined)
|
const currentBounds = new UIEventSource<BBox>(undefined)
|
||||||
|
@ -130,25 +131,22 @@ export class MapPreview
|
||||||
)
|
)
|
||||||
map.SetClass("w-full").SetStyle("height: 500px")
|
map.SetClass("w-full").SetStyle("height: 500px")
|
||||||
|
|
||||||
new ShowDataMultiLayer({
|
layerPicker.GetValue().addCallbackAndRunD((layerToShow) => {
|
||||||
layers: new UIEventSource<FilteredLayer[]>(
|
new ShowDataLayer({
|
||||||
AllKnownLayouts.AllPublicLayers()
|
layerToShow,
|
||||||
.filter((l) => l.source.geojsonSource === undefined)
|
zoomToFeatures: true,
|
||||||
.map((l) => ({
|
features: StaticFeatureSource.fromDateless(
|
||||||
layerDef: l,
|
matching.map((features) => features.map((feature) => ({ feature })))
|
||||||
isDisplayed: new UIEventSource<boolean>(true),
|
),
|
||||||
appliedFilters: new UIEventSource<Map<string, FilterState>>(undefined),
|
leafletMap: map.leafletMap,
|
||||||
}))
|
popup: (tag) => new PreviewPanel(tag),
|
||||||
),
|
})
|
||||||
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 bbox = matching.map((feats) =>
|
||||||
|
BBox.bboxAroundAll(
|
||||||
|
feats.map((f) => new BBox([(<Feature<Point>>f).geometry.coordinates]))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const mismatchIndicator = new VariableUiElement(
|
const mismatchIndicator = new VariableUiElement(
|
||||||
|
@ -171,10 +169,11 @@ export class MapPreview
|
||||||
super([
|
super([
|
||||||
new Title(t.title, 1),
|
new Title(t.title, 1),
|
||||||
layerPicker,
|
layerPicker,
|
||||||
new Toggle(t.autodetected.SetClass("thank"), undefined, autodetected),
|
new Toggle(t.autodetected.SetClass("thanks"), undefined, autodetected),
|
||||||
|
|
||||||
mismatchIndicator,
|
mismatchIndicator,
|
||||||
map,
|
map,
|
||||||
|
new DivContainer("fullscreen"),
|
||||||
layerControl,
|
layerControl,
|
||||||
confirm,
|
confirm,
|
||||||
])
|
])
|
||||||
|
@ -182,10 +181,10 @@ export class MapPreview
|
||||||
this.Value = bbox.map(
|
this.Value = bbox.map(
|
||||||
(bbox) => ({
|
(bbox) => ({
|
||||||
bbox,
|
bbox,
|
||||||
features: geojson.features,
|
features: matching.data,
|
||||||
layer: layerPicker.GetValue().data,
|
layer: layerPicker.GetValue().data,
|
||||||
}),
|
}),
|
||||||
[layerPicker.GetValue()]
|
[layerPicker.GetValue(), matching]
|
||||||
)
|
)
|
||||||
|
|
||||||
this.IsValid = matching.map(
|
this.IsValid = matching.map(
|
||||||
|
|
|
@ -10,24 +10,19 @@ import Histogram from "../BigComponents/Histogram"
|
||||||
import Toggleable from "../Base/Toggleable"
|
import Toggleable from "../Base/Toggleable"
|
||||||
import List from "../Base/List"
|
import List from "../Base/List"
|
||||||
import CheckBoxes from "../Input/Checkboxes"
|
import CheckBoxes from "../Input/Checkboxes"
|
||||||
|
import { Feature, Point } from "geojson"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the attributes by value, requests to check them of
|
* Shows the attributes by value, requests to check them of
|
||||||
*/
|
*/
|
||||||
export class PreviewAttributesPanel
|
export class PreviewAttributesPanel
|
||||||
extends Combine
|
extends Combine
|
||||||
implements
|
implements FlowStep<{ features: Feature<Point>[] }>
|
||||||
FlowStep<{ features: { properties: any; geometry: { coordinates: [number, number] } }[] }>
|
|
||||||
{
|
{
|
||||||
public readonly IsValid: Store<boolean>
|
public readonly IsValid: Store<boolean>
|
||||||
public readonly Value: Store<{
|
public readonly Value: Store<{ features: Feature<Point>[] }>
|
||||||
features: { properties: any; geometry: { coordinates: [number, number] } }[]
|
|
||||||
}>
|
|
||||||
|
|
||||||
constructor(
|
constructor(state: UserRelatedState, geojson: { features: Feature<Point>[] }) {
|
||||||
state: UserRelatedState,
|
|
||||||
geojson: { features: { properties: any; geometry: { coordinates: [number, number] } }[] }
|
|
||||||
) {
|
|
||||||
const t = Translations.t.importHelper.previewAttributes
|
const t = Translations.t.importHelper.previewAttributes
|
||||||
|
|
||||||
const propertyKeys = new Set<string>()
|
const propertyKeys = new Set<string>()
|
||||||
|
@ -93,9 +88,7 @@ export class PreviewAttributesPanel
|
||||||
confirm,
|
confirm,
|
||||||
])
|
])
|
||||||
|
|
||||||
this.Value = new UIEventSource<{
|
this.Value = new UIEventSource<{ features: Feature<Point>[] }>(geojson)
|
||||||
features: { properties: any; geometry: { coordinates: [number, number] } }[]
|
|
||||||
}>(geojson)
|
|
||||||
this.IsValid = confirm.GetValue().map((selected) => selected.length == 1)
|
this.IsValid = confirm.GetValue().map((selected) => selected.length == 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { FlowStep } from "./FlowStep"
|
||||||
import { parse } from "papaparse"
|
import { parse } from "papaparse"
|
||||||
import { FixedUiElement } from "../Base/FixedUiElement"
|
import { FixedUiElement } from "../Base/FixedUiElement"
|
||||||
import { TagUtils } from "../../Logic/Tags/TagUtils"
|
import { TagUtils } from "../../Logic/Tags/TagUtils"
|
||||||
|
import { Feature, Point } from "geojson"
|
||||||
|
|
||||||
class FileSelector extends InputElementMap<FileList, { name: string; contents: Promise<string> }> {
|
class FileSelector extends InputElementMap<FileList, { name: string; contents: Promise<string> }> {
|
||||||
constructor(label: BaseUIElement) {
|
constructor(label: BaseUIElement) {
|
||||||
|
@ -40,7 +41,7 @@ export class RequestFile extends Combine implements FlowStep<{ features: any[] }
|
||||||
/**
|
/**
|
||||||
* The loaded GeoJSON
|
* The loaded GeoJSON
|
||||||
*/
|
*/
|
||||||
public readonly Value: Store<{ features: any[] }>
|
public readonly Value: Store<{ features: Feature<Point>[] }>
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const t = Translations.t.importHelper.selectFile
|
const t = Translations.t.importHelper.selectFile
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue