forked from MapComplete/MapComplete
Polishing and translations for the import helper
This commit is contained in:
parent
f7844d8b2b
commit
8e2e227563
7 changed files with 161 additions and 87 deletions
|
@ -4,8 +4,12 @@ import {UIEventSource} from "../../Logic/UIEventSource";
|
|||
import ValidatedTextField from "../Input/ValidatedTextField";
|
||||
import {LocalStorageSource} from "../../Logic/Web/LocalStorageSource";
|
||||
import Title from "../Base/Title";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import Svg from "../../Svg";
|
||||
import {Utils} from "../../Utils";
|
||||
|
||||
export class AskMetadata extends Combine implements FlowStep<{
|
||||
features: any[],
|
||||
|
@ -25,7 +29,7 @@ export class AskMetadata extends Combine implements FlowStep<{
|
|||
public readonly IsValid: UIEventSource<boolean>;
|
||||
|
||||
constructor(params: ({ features: any[], theme: string })) {
|
||||
|
||||
const t = Translations.t.importHelper.askMetadata
|
||||
const introduction = ValidatedTextField.ForType("text").ConstructInputElement({
|
||||
value: LocalStorageSource.Get("import-helper-introduction-text"),
|
||||
inputStyle: "width: 100%"
|
||||
|
@ -42,28 +46,39 @@ export class AskMetadata extends Combine implements FlowStep<{
|
|||
})
|
||||
|
||||
super([
|
||||
new Title("Set metadata"),
|
||||
"Before adding " + params.features.length + " notes, please provide some extra information.",
|
||||
"Please, write an introduction for someone who sees the note",
|
||||
new Title(t.title),
|
||||
t.intro.Subs({count: params.features.length}),
|
||||
t.giveDescription,
|
||||
introduction.SetClass("w-full border border-black"),
|
||||
"What is the source of this data? If 'source' is set in the feature, this value will be ignored",
|
||||
source.SetClass("w-full border border-black"),
|
||||
"On what wikipage can one find more information about this import?",
|
||||
t.giveSource,
|
||||
source.SetClass("w-full border border-black"),
|
||||
t.giveWikilink ,
|
||||
wikilink.SetClass("w-full border border-black"),
|
||||
new VariableUiElement(wikilink.GetValue().map(wikilink => {
|
||||
try{
|
||||
const url = new URL(wikilink)
|
||||
if(url.hostname.toLowerCase() !== "wiki.openstreetmap.org"){
|
||||
return new FixedUiElement("Expected a link to wiki.openstreetmap.org").SetClass("alert");
|
||||
return t.shouldBeOsmWikilink.SetClass("alert");
|
||||
}
|
||||
|
||||
if(url.pathname.toLowerCase() === "/wiki/main_page"){
|
||||
return new FixedUiElement("Nope, the home page isn't allowed either. Enter the URL of a proper wikipage documenting your import").SetClass("alert");
|
||||
return t.shouldNotBeHomepage.SetClass("alert");
|
||||
}
|
||||
}catch(e){
|
||||
return new FixedUiElement("Not a valid URL").SetClass("alert")
|
||||
return t.shouldBeUrl.SetClass("alert")
|
||||
}
|
||||
}))
|
||||
})),
|
||||
t.orDownload,
|
||||
new SubtleButton(Svg.download_svg(), t.downloadGeojson).OnClickWithLoading("Preparing your download",
|
||||
async ( ) => {
|
||||
const geojson = {
|
||||
type:"FeatureCollection",
|
||||
features: params.features
|
||||
}
|
||||
Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson), "prepared_import_"+params.theme+".geojson",{
|
||||
mimetype: "application/vnd.geo+json"
|
||||
})
|
||||
})
|
||||
]);
|
||||
this.SetClass("flex flex-col")
|
||||
|
||||
|
|
|
@ -2,45 +2,30 @@ import Combine from "../Base/Combine";
|
|||
import {FlowStep} from "./FlowStep";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Link from "../Base/Link";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import CheckBoxes from "../Input/Checkboxes";
|
||||
import Title from "../Base/Title";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import Svg from "../../Svg";
|
||||
import {Utils} from "../../Utils";
|
||||
import Translations from "../i18n/Translations";
|
||||
|
||||
export class ConfirmProcess extends Combine implements FlowStep<{ features: any[], theme: string }> {
|
||||
|
||||
public IsValid: UIEventSource<boolean>
|
||||
public Value: UIEventSource<{ features: any[],theme: string }>
|
||||
|
||||
constructor(v: { features: any[], theme: string }) {
|
||||
public Value: UIEventSource<{ features: any[], theme: string }>
|
||||
|
||||
constructor(v: { features: any[], theme: string }) {
|
||||
const t = Translations.t.importHelper.confirmProcess;
|
||||
const toConfirm = [
|
||||
new Combine(["I have read the ", new Link("import guidelines on the OSM wiki", "https://wiki.openstreetmap.org/wiki/Import_guidelines", true)]),
|
||||
new FixedUiElement("I did contact the (local) community about this import"),
|
||||
new FixedUiElement("The license of the data to import allows it to be imported into OSM. They are allowed to be redistributed commercially, with only minimal attribution"),
|
||||
new FixedUiElement("The process is documented on the OSM-wiki (you'll need this link later)")
|
||||
new Link(t.readImportGuidelines, "https://wiki.openstreetmap.org/wiki/Import_guidelines", true),
|
||||
t.contactedCommunity,
|
||||
t.licenseIsCompatible,
|
||||
t.wikipageIsMade
|
||||
];
|
||||
|
||||
const licenseClear = new CheckBoxes(toConfirm)
|
||||
super([
|
||||
new Title("Did you go through the import process?"),
|
||||
licenseClear,
|
||||
new FixedUiElement("Alternatively, you can download the dataset to import directly"),
|
||||
new SubtleButton(Svg.download_svg(), "Download geojson").OnClickWithLoading("Preparing your download",
|
||||
async ( ) => {
|
||||
const geojson = {
|
||||
type:"FeatureCollection",
|
||||
features: v.features
|
||||
}
|
||||
Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson), "prepared_import_"+v.theme+".geojson",{
|
||||
mimetype: "application/vnd.geo+json"
|
||||
})
|
||||
})
|
||||
new Title(t.titleLong),
|
||||
new CheckBoxes(toConfirm),
|
||||
]);
|
||||
this.SetClass("link-underline")
|
||||
this.IsValid = licenseClear.GetValue().map(selected => toConfirm.length == selected.length)
|
||||
this.IsValid = new CheckBoxes(toConfirm).GetValue().map(selected => toConfirm.length == selected.length)
|
||||
this.Value = new UIEventSource<{ features: any[], theme: string }>(v)
|
||||
}
|
||||
}
|
|
@ -8,47 +8,58 @@ import {VariableUiElement} from "../Base/VariableUIElement";
|
|||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import Svg from "../../Svg";
|
||||
import Translations from "../i18n/Translations";
|
||||
|
||||
export class CreateNotes extends Combine {
|
||||
|
||||
public static createNoteContents(feature: {properties: any, geometry: {coordinates: [number,number]}},
|
||||
options: {wikilink: string; intro: string; source: string, theme: string }
|
||||
): string[]{
|
||||
const src = feature.properties["source"] ?? feature.properties["src"] ?? options.source
|
||||
delete feature.properties["source"]
|
||||
delete feature.properties["src"]
|
||||
let extraNote = ""
|
||||
if (feature.properties["note"]) {
|
||||
extraNote = feature.properties["note"] + "\n"
|
||||
delete feature.properties["note"]
|
||||
}
|
||||
|
||||
const tags: string [] = []
|
||||
for (const key in feature.properties) {
|
||||
if (feature.properties[key] === null || feature.properties[key] === undefined) {
|
||||
console.warn("Null or undefined key for ", feature.properties)
|
||||
continue
|
||||
}
|
||||
if (feature.properties[key] === "") {
|
||||
continue
|
||||
}
|
||||
tags.push(key + "=" + (feature.properties[key]+"").replace(/=/, "\\=").replace(/;/g, "\\;").replace(/\n/g, "\\n"))
|
||||
}
|
||||
const lat = feature.geometry.coordinates[1]
|
||||
const lon = feature.geometry.coordinates[0]
|
||||
const note = Translations.t.importHelper.noteParts
|
||||
return [
|
||||
options.intro,
|
||||
extraNote,
|
||||
note.datasource.Subs({source: src}).txt,
|
||||
note.wikilink.Subs(options).txt,
|
||||
'',
|
||||
note.importEasily.txt,
|
||||
`https://mapcomplete.osm.be/${options.theme}.html?z=18&lat=${lat}&lon=${lon}#import`,
|
||||
...tags]
|
||||
}
|
||||
|
||||
constructor(state: { osmConnection: OsmConnection }, v: { features: any[]; wikilink: string; intro: string; source: string, theme: string }) {
|
||||
|
||||
const t = Translations.t.importHelper.createNotes;
|
||||
const createdNotes: UIEventSource<number[]> = new UIEventSource<number[]>([])
|
||||
const failed = new UIEventSource<string[]>([])
|
||||
const currentNote = createdNotes.map(n => n.length)
|
||||
|
||||
for (const f of v.features) {
|
||||
|
||||
const src = f.properties["source"] ?? f.properties["src"] ?? v.source
|
||||
delete f.properties["source"]
|
||||
delete f.properties["src"]
|
||||
let extraNote = ""
|
||||
if (f.properties["note"]) {
|
||||
extraNote = f.properties["note"] + "\n"
|
||||
delete f.properties["note"]
|
||||
}
|
||||
|
||||
const tags: string [] = []
|
||||
for (const key in f.properties) {
|
||||
if (f.properties[key] === null || f.properties[key] === undefined) {
|
||||
console.warn("Null or undefined key for ", f.properties)
|
||||
continue
|
||||
}
|
||||
if (f.properties[key] === "") {
|
||||
continue
|
||||
}
|
||||
tags.push(key + "=" + (f.properties[key]+"").replace(/=/, "\\=").replace(/;/g, "\\;").replace(/\n/g, "\\n"))
|
||||
}
|
||||
|
||||
const lat = f.geometry.coordinates[1]
|
||||
const lon = f.geometry.coordinates[0]
|
||||
const text = [v.intro,
|
||||
extraNote,
|
||||
"Source: " + src,
|
||||
'More information at ' + v.wikilink,
|
||||
'',
|
||||
'Import this point easily with',
|
||||
`https://mapcomplete.osm.be/${v.theme}.html?z=18&lat=${lat}&lon=${lon}#import`,
|
||||
...tags].join("\n")
|
||||
const text = CreateNotes.createNoteContents(f, v).join("\n")
|
||||
|
||||
state.osmConnection.openNote(
|
||||
lat, lon, text)
|
||||
|
@ -62,13 +73,19 @@ export class CreateNotes extends Combine {
|
|||
}
|
||||
|
||||
super([
|
||||
new Title("Creating notes"),
|
||||
"Hang on while we are importing...",
|
||||
new Title(t.title),
|
||||
t.loading ,
|
||||
new Toggle(
|
||||
new Loading(new VariableUiElement(currentNote.map(count => new FixedUiElement("Imported <b>" + count + "</b> out of " + v.features.length + " notes")))),
|
||||
new Loading(new VariableUiElement(currentNote.map(count => t.creating.Subs({
|
||||
count, total: v.features.length
|
||||
}
|
||||
|
||||
)))),
|
||||
new Combine([
|
||||
new FixedUiElement("All done!").SetClass("thanks"),
|
||||
new SubtleButton(Svg.note_svg(), "Inspect the progress of your notes in the 'import_viewer'", {
|
||||
Svg.party_svg().SetClass("w-24"),
|
||||
t.done.Subs(v.features.length).SetClass("thanks"),
|
||||
new SubtleButton(Svg.note_svg(),
|
||||
t.openImportViewer , {
|
||||
url: "import_viewer.html"
|
||||
})
|
||||
]
|
||||
|
|
|
@ -120,11 +120,11 @@ export class FlowPanel<T> extends Toggle {
|
|||
isError.setData(true)
|
||||
}
|
||||
}),
|
||||
"Select a valid value to continue",
|
||||
new SubtleButton(Svg.invalid_svg(), t.notValid),
|
||||
initial.IsValid
|
||||
),
|
||||
new Toggle(
|
||||
new FixedUiElement("Something went wrong...").SetClass("alert"),
|
||||
t.error.SetClass("alert"),
|
||||
undefined,
|
||||
isError),
|
||||
]).SetClass("flex w-full justify-end space-x-2"),
|
||||
|
|
|
@ -37,9 +37,9 @@ export default class ImportHelperGui extends LeftIndex {
|
|||
.then(t.selectTheme, v => new SelectTheme(v))
|
||||
.then(t.compareToAlreadyExistingNotes, v => new CompareToAlreadyExistingNotes(state, v))
|
||||
.then("Compare with existing data", v => new ConflationChecker(state, v))
|
||||
.then("License and community check", v => new ConfirmProcess(v))
|
||||
.then("Metadata", (v) => new AskMetadata(v))
|
||||
.finish("Note creation", v => new CreateNotes(state, v));
|
||||
.then(t.confirmProcess, v => new ConfirmProcess(v))
|
||||
.then(t.askMetadata, (v) => new AskMetadata(v))
|
||||
.finish(t.createNotes.title, v => new CreateNotes(state, v));
|
||||
|
||||
const toc = new List(
|
||||
titles.map((title, i) => new VariableUiElement(furthestStep.map(currentStep => {
|
||||
|
@ -58,11 +58,11 @@ export default class ImportHelperGui extends LeftIndex {
|
|||
, true)
|
||||
|
||||
const leftContents: BaseUIElement[] = [
|
||||
new SubtleButton(undefined, "Inspect your preview imports", {
|
||||
new SubtleButton(undefined, t.gotoImportViewer, {
|
||||
url: "import_viewer.html"
|
||||
}),
|
||||
toc,
|
||||
new Toggle(new FixedUiElement("Testmode - won't actually import notes").SetClass("alert"), undefined, state.featureSwitchIsTesting),
|
||||
new Toggle(t.testMode.SetClass("block alert"), undefined, state.featureSwitchIsTesting),
|
||||
LanguagePicker.CreateLanguagePicker(Translations.t.importHelper.title.SupportedLanguages())?.SetClass("mt-4 self-end flex-col"),
|
||||
].map(el => el?.SetClass("pl-4"))
|
||||
|
||||
|
|
|
@ -3,18 +3,42 @@ import {FlowStep} from "./FlowStep";
|
|||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Title from "../Base/Title";
|
||||
import {CreateNotes} from "./CreateNotes";
|
||||
|
||||
export default class Introdution extends Combine implements FlowStep<void> {
|
||||
readonly IsValid: UIEventSource<boolean> = new UIEventSource<boolean>(true);
|
||||
readonly Value: UIEventSource<void> = new UIEventSource<void>(undefined);
|
||||
readonly IsValid: UIEventSource<boolean>;
|
||||
readonly Value: UIEventSource<void>;
|
||||
|
||||
constructor() {
|
||||
const example = CreateNotes.createNoteContents({
|
||||
properties:{
|
||||
"some_key":"some_value",
|
||||
"note":"a note in the original dataset"
|
||||
},
|
||||
geometry:{
|
||||
coordinates: [3.4,51.2]
|
||||
}
|
||||
}, {
|
||||
wikilink: "https://wiki.openstreetmap.org/wiki/Imports/<documentation of your import>",
|
||||
intro: "There might be an XYZ here",
|
||||
theme: "theme",
|
||||
source: "source of the data"
|
||||
})
|
||||
|
||||
super([
|
||||
new Title(Translations.t.importHelper.introduction.title),
|
||||
Translations.t.importHelper.introduction.description,
|
||||
Translations.t.importHelper.introduction.importFormat,
|
||||
new Combine(
|
||||
[new Combine(
|
||||
example
|
||||
).SetClass("flex flex-col")
|
||||
] ).SetClass("literal-code")
|
||||
]);
|
||||
this.SetClass("flex flex-col")
|
||||
this. IsValid= new UIEventSource<boolean>(true);
|
||||
this. Value = new UIEventSource<void>(undefined);
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue