Merge master

This commit is contained in:
pietervdvn 2022-04-15 00:11:52 +02:00
commit 49d99930e8
18 changed files with 413 additions and 209 deletions

View file

@ -39,6 +39,39 @@ export class AddContextToTranslations<T> extends DesugaringStep<T> {
* }
* rewritten // => expected
*
* // should use the ID if one is present instead of the index
* const theme = {
* layers: [
* {
* tagRenderings:[
*
* {id: "some-tr",
* question:{
* en:"Question?"
* }
* }
* ]
* }
* ]
* }
* const rewritten = new AddContextToTranslations<any>("prefix:").convert(theme, "context").result
* const expected = {
* layers: [
* {
* tagRenderings:[
*
* {id: "some-tr",
* question:{
* _context: "prefix:context.layers.0.tagRenderings.some-tr.question"
* en:"Question?"
* }
* }
* ]
* }
* ]
* }
* rewritten // => expected
*
* // should preserve nulls
* const theme = {
* layers: [
@ -70,6 +103,17 @@ export class AddContextToTranslations<T> extends DesugaringStep<T> {
return leaf
}
if (typeof leaf === "object") {
// follow the path. If we encounter a number, check that there is no ID we can use instead
let breadcrumb = json;
for (let i = 0; i < path.length; i++) {
const pointer = path[i]
breadcrumb = breadcrumb[pointer]
if(pointer.match("[0-9]+") && breadcrumb["id"] !== undefined){
path[i] = breadcrumb["id"]
}
}
return {...leaf, _context: this._prefix + context + "." + path.join(".")}
} else {
return leaf

View file

@ -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",
t.giveSource,
source.SetClass("w-full border border-black"),
"On what wikipage can one find more information about this import?",
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")

View file

@ -21,6 +21,7 @@ import {FixedUiElement} from "../Base/FixedUiElement";
import {VariableUiElement} from "../Base/VariableUIElement";
import * as known_layers from "../../assets/generated/known_layers.json"
import {LayerConfigJson} from "../../Models/ThemeConfig/Json/LayerConfigJson";
import Translations from "../i18n/Translations";
/**
* Filters out points for which the import-note already exists, to prevent duplicates
@ -32,7 +33,7 @@ export class CompareToAlreadyExistingNotes extends Combine implements FlowStep<{
constructor(state, params: { bbox: BBox, layer: LayerConfig, features: any[], theme: string }) {
const t = Translations.t.importHelper.compareToAlreadyExistingNotes
const layerConfig = known_layers.layers.filter(l => l.id === params.layer.id)[0]
if (layerConfig === undefined) {
console.error("WEIRD: layer not found in the builtin layer overview")
@ -63,7 +64,6 @@ export class CompareToAlreadyExistingNotes extends Combine implements FlowStep<{
)
)
const alreadyOpenImportNotes = new FilteringFeatureSource(state, undefined, allNotesWithinBbox)
alreadyOpenImportNotes.features.addCallbackD(features => console.log("Loaded and filtered features are", features))
const map = Minimap.createMiniMap()
map.SetClass("w-full").SetStyle("height: 500px")
@ -99,38 +99,41 @@ export class CompareToAlreadyExistingNotes extends Combine implements FlowStep<{
})
super([
new Title("Compare with already existing 'to-import'-notes"),
new Title(t.titleLong),
new VariableUiElement(
alreadyOpenImportNotes.features.map(notesWithImport => {
if (allNotesWithinBbox.state.data !== undefined && allNotesWithinBbox.state.data["error"] !== undefined) {
return new FixedUiElement("Loading notes failed: "+allNotesWithinBbox.state.data["error"] )
t.loadingFailed.Subs(allNotesWithinBbox.state.data)
}
if (allNotesWithinBbox.features.data === undefined || allNotesWithinBbox.features.data.length === 0) {
return new Loading("Fetching notes from OSM")
return new Loading(t.loading)
}
if (notesWithImport.length === 0) {
return new FixedUiElement("No previous import notes found").SetClass("thanks")
return t.noPreviousNotesFound.SetClass("thanks")
}
return new Combine([
"The red elements on the next map are all the data points from your dataset. There are <b>"+params.features.length+"</b> elements in your dataset.",
t.mapExplanation.Subs(params.features),
map,
new VariableUiElement(partitionedImportPoints.map(({noNearby, hasNearby}) => {
if (noNearby.length === 0) {
// Nothing can be imported
return new FixedUiElement("All of the proposed points have (or had) an import note already").SetClass("alert w-full block").SetStyle("padding: 0.5rem")
return t.completelyImported.SetClass("alert w-full block").SetStyle("padding: 0.5rem")
}
if (hasNearby.length === 0) {
// All points can be imported
return new FixedUiElement("All of the proposed points have don't have a previous import note nearby").SetClass("thanks w-full block").SetStyle("padding: 0.5rem")
return t.nothingNearby.SetClass("thanks w-full block").SetStyle("padding: 0.5rem")
}
return new Combine([
new FixedUiElement(hasNearby.length+" points do have an already existing import note within "+maxDistance.data+" meter.").SetClass("alert"),
"These data points will <i>not</i> be imported and are shown as red dots on the map below",
t.someNearby.Subs({
hasNearby: hasNearby.length,
distance: maxDistance.data
}).SetClass("alert"),
t.wontBeImported,
comparisonMap.SetClass("w-full")
]).SetClass("w-full")
}))

View file

@ -2,12 +2,9 @@ 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 }> {
@ -15,32 +12,20 @@ export class ConfirmProcess extends Combine implements FlowStep<{ features: any[
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)
}
}

View file

@ -7,7 +7,6 @@ import {UIEventSource} from "../../Logic/UIEventSource";
import Constants from "../../Models/Constants";
import RelationsTracker from "../../Logic/Osm/RelationsTracker";
import {VariableUiElement} from "../Base/VariableUIElement";
import {FixedUiElement} from "../Base/FixedUiElement";
import {FlowStep} from "./FlowStep";
import Loading from "../Base/Loading";
import {SubtleButton} from "../Base/SubtleButton";
@ -28,6 +27,7 @@ import * as import_candidate from "../../assets/layers/import_candidate/import_c
import {GeoOperations} from "../../Logic/GeoOperations";
import FeatureInfoBox from "../Popup/FeatureInfoBox";
import {ImportUtils} from "./ImportUtils";
import Translations from "../i18n/Translations";
/**
* Given the data to import, the bbox and the layer, will query overpass for similar items
@ -190,6 +190,7 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
features: toImportWithNearby
})
const t = Translations.t.importHelper.conflationChecker
const conflationMaps = new Combine([
new VariableUiElement(
@ -197,7 +198,7 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
if (geojson === undefined) {
return undefined;
}
return new SubtleButton(Svg.download_svg(), "Download the loaded geojson from overpass").onClick(() => {
return new SubtleButton(Svg.download_svg(), t.downloadOverpassData).onClick(() => {
Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson, null, " "), "mapcomplete-" + layer.id + ".geojson", {
mimetype: "application/json+geo"
})
@ -208,43 +209,53 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
return undefined;
}
if (age < 0) {
return new FixedUiElement("Cache was expired")
return t.cacheExpired
}
return new FixedUiElement("Loaded data is from the cache and is " + Utils.toHumanTime(age) + " old")
return t.loadedDataAge.Subs({age: Utils.toHumanTime(age)})
})),
new Title("Live data on OSM"),
"The "+toImport.features.length+" red elements on the following map are all your import candidates.",
new VariableUiElement(geojson.map(geojson => new FixedUiElement((geojson?.features?.length ?? "No") + " elements are loaded from OpenStreetMap which match the layer "+layer.id+". Zooming in might be needed to show them"))),
osmLiveData,
new Combine(["The live data is shown if the zoomlevel is at least ", zoomLevel, ". The current zoom level is ", new VariableUiElement(osmLiveData.location.map(l => "" + l.zoom))]).SetClass("flex"),
new Title(t.titleLive),
t.importCandidatesCount.Subs({count:toImport.features.length }),
new VariableUiElement(geojson.map(geojson => {
if(geojson?.features?.length === undefined && geojson?.features?.length === 0){
return t.nothingLoaded.Subs(layer).SetClass("alert")
}
return new Combine([
t.osmLoaded.Subs({count: geojson.features.length, name: layer.name}),
new Title("Nearby features"),
new Combine(["The following map shows features to import which have an OSM-feature within ", nearbyCutoff, "meter"]).SetClass("flex"),
])
})),
osmLiveData,
new VariableUiElement(osmLiveData.location.map(location => {
return t.zoomIn.Subs({needed:zoomLevel, current: location.zoom })
} )),
new Title(t.titleNearby),
new Combine([t.mapShowingNearbyIntro, nearbyCutoff]).SetClass("flex"),
new VariableUiElement(toImportWithNearby.features.map(feats =>
new FixedUiElement("The "+ feats.length +" red elements on the following map will <b>not</b> be imported!").SetClass("alert"))),
"Set the range to 0 or 1 if you want to import them all",
t.nearbyWarn.Subs({count: feats.length}).SetClass("alert"))),
,t.setRangeToZero,
matchedFeaturesMap]).SetClass("flex flex-col")
super([
new Title("Comparison with existing data"),
new Title(t.title),
new VariableUiElement(overpassStatus.map(d => {
if (d === "idle") {
return new Loading("Checking local storage...")
}
if (d["error"] !== undefined) {
return new FixedUiElement("Could not load latest data from overpass: " + d["error"]).SetClass("alert")
return new Loading(t.states.idle)
}
if (d === "running") {
return new Loading("Querying overpass...")
return new Loading(t.states.running)
}
if (d["error"] !== undefined) {
return t.states.error.Subs(d).SetClass("alert")
}
if (d === "cached") {
return conflationMaps
}
if (d === "success") {
return conflationMaps
}
return new FixedUiElement("Unexpected state " + d).SetClass("alert")
return t.states.unexpected.Subs({state: d}).SetClass("alert")
}))
])

View file

@ -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 {
constructor(state: { osmConnection: OsmConnection }, v: { features: any[]; wikilink: string; intro: string; source: string, theme: string }) {
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"
})
]

View file

@ -25,15 +25,15 @@ export class FlowPanelFactory<T> {
this._stepNames = stepNames;
}
public static start<TOut>(name: string | BaseUIElement, step: FlowStep<TOut>): FlowPanelFactory<TOut> {
return new FlowPanelFactory(step, [], [name])
public static start<TOut>(name:{title: BaseUIElement}, step: FlowStep<TOut>): FlowPanelFactory<TOut> {
return new FlowPanelFactory(step, [], [name.title])
}
public then<TOut>(name: string | BaseUIElement, construct: ((t: T) => FlowStep<TOut>)): FlowPanelFactory<TOut> {
public then<TOut>(name: string | {title: BaseUIElement}, construct: ((t: T) => FlowStep<TOut>)): FlowPanelFactory<TOut> {
return new FlowPanelFactory<TOut>(
this._initial,
this._steps.concat([construct]),
this._stepNames.concat([name])
this._stepNames.concat([name["title"] ?? name])
)
}
@ -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"),

View file

@ -7,7 +7,7 @@ import MinimapImplementation from "../Base/MinimapImplementation";
import Translations from "../i18n/Translations";
import {FlowPanelFactory} from "./FlowStep";
import {RequestFile} from "./RequestFile";
import {PreviewPanel} from "./PreviewPanel";
import {PreviewAttributesPanel} from "./PreviewPanel";
import ConflationChecker from "./ConflationChecker";
import {AskMetadata} from "./AskMetadata";
import {ConfirmProcess} from "./ConfirmProcess";
@ -26,20 +26,20 @@ import SelectTheme from "./SelectTheme";
export default class ImportHelperGui extends LeftIndex {
constructor() {
const state = new UserRelatedState(undefined)
const t = Translations.t.importHelper;
const {flow, furthestStep, titles} =
FlowPanelFactory
.start("Introduction", new Introdution())
.then("Login", _ => new LoginToImport(state))
.then("Select file", _ => new RequestFile())
.then("Inspect attributes", geojson => new PreviewPanel(state, geojson))
.then("Inspect data", geojson => new MapPreview(state, geojson))
.then("Select theme", v => new SelectTheme(v))
.then("Compare with open notes", 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));
.start(t.introduction, new Introdution())
.then(t.login, _ => new LoginToImport(state))
.then(t.selectFile, _ => new RequestFile())
.then(t.previewAttributes, geojson => new PreviewAttributesPanel(state, geojson))
.then(t.mapPreview, geojson => new MapPreview(state, geojson))
.then(t.selectTheme, v => new SelectTheme(v))
.then(t.compareToAlreadyExistingNotes, v => new CompareToAlreadyExistingNotes(state, v))
.then(t.conflationChecker, v => new ConflationChecker(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"))

View file

@ -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.title),
Translations.t.importHelper.description,
Translations.t.importHelper.importFormat,
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);
}
}

View file

@ -21,7 +21,7 @@ export default class LoginToImport extends Combine implements FlowStep<UserRelat
private static readonly whitelist = [15015689];
constructor(state: UserRelatedState) {
const t = Translations.t.importHelper
const t = Translations.t.importHelper.login
const check = new CheckBoxes([new VariableUiElement(state.osmConnection.userDetails.map(ud => t.loginIsCorrect.Subs(ud)))])
const isValid = state.osmConnection.userDetails.map(ud =>
LoginToImport.whitelist.indexOf(ud.uid) >= 0 || ud.csCount >= Constants.userJourney.importHelperUnlock)

View file

@ -12,16 +12,16 @@ import List from "../Base/List";
import CheckBoxes from "../Input/Checkboxes";
/**
* Shows the data to import on a map, asks for the correct layer to be selected
* Shows the attributes by value, requests to check them of
*/
export class PreviewPanel extends Combine implements FlowStep<{ features: { properties: any, geometry: { coordinates: [number, number] } }[] }> {
export class PreviewAttributesPanel extends Combine implements FlowStep<{ features: { properties: any, geometry: { coordinates: [number, number] } }[] }> {
public readonly IsValid: UIEventSource<boolean>;
public readonly Value: UIEventSource<{ features: { properties: any, geometry: { coordinates: [number, number] } }[] }>
constructor(
state: UserRelatedState,
geojson: { features: { properties: any, geometry: { coordinates: [number, number] } }[] }) {
const t = Translations.t.importHelper;
const t = Translations.t.importHelper.previewAttributes;
const propertyKeys = new Set<string>()
for (const f of geojson.features) {

View file

@ -10,12 +10,12 @@ import Title from "../Base/Title";
import {RadioButton} from "../Input/RadioButton";
import {And} from "../../Logic/Tags/And";
import {VariableUiElement} from "../Base/VariableUIElement";
import {FixedUiElement} from "../Base/FixedUiElement";
import Toggleable from "../Base/Toggleable";
import {BBox} from "../../Logic/BBox";
import BaseUIElement from "../BaseUIElement";
import PresetConfig from "../../Models/ThemeConfig/PresetConfig";
import List from "../Base/List";
import Translations from "../i18n/Translations";
export default class SelectTheme extends Combine implements FlowStep<{
features: any[],
@ -33,7 +33,7 @@ export default class SelectTheme extends Combine implements FlowStep<{
public readonly IsValid: UIEventSource<boolean>;
constructor(params: ({ features: any[], layer: LayerConfig, bbox: BBox, })) {
const t = Translations.t.importHelper.selectTheme
let options: InputElement<string>[] = AllKnownLayouts.layoutsList
.filter(th => th.layers.some(l => l.id === params.layer.id))
.filter(th => th.id !== "personal")
@ -69,15 +69,15 @@ export default class SelectTheme extends Combine implements FlowStep<{
})
super([
new Title("Select a theme"),
"All of the following themes will show the import notes. However, the note on OpenStreetMap can link to only one single theme. Choose which theme that the created notes will link to",
new Title(t.title),
t.intro,
themeRadios,
new VariableUiElement(applicablePresets.map(applicablePresets => {
if (themeRadios.GetValue().data === undefined) {
return undefined
}
if (applicablePresets === undefined || applicablePresets.length === 0) {
return new FixedUiElement("This theme has no presets loaded. As a result, imports won't work here").SetClass("alert")
return t.noMatchingPresets.SetClass("alert")
}
}, [themeRadios.GetValue()])),
@ -115,11 +115,14 @@ export default class SelectTheme extends Combine implements FlowStep<{
if (unmatched === undefined || unmatched.length === 0) {
return
}
const t = Translations.t.importHelper.selectTheme
const applicablePresetsOverview = applicablePresets.map(preset => new Combine([
preset.title.txt, "needs tags",
new FixedUiElement(preset.tags.map(t => t.asHumanString()).join(" & ")).SetClass("thanks")
]))
const applicablePresetsOverview = applicablePresets.map(preset =>
t.needsTags.Subs(
{title: preset.title,
tags:preset.tags.map(t => t.asHumanString()).join(" & ") })
.SetClass("thanks")
);
const unmatchedPanels: BaseUIElement[] = []
for (const feat of unmatched) {
@ -133,20 +136,16 @@ export default class SelectTheme extends Combine implements FlowStep<{
const missing = []
for (const {k, v} of tags) {
if (preset[k] === undefined) {
missing.push(
`Expected ${k}=${v}, but it is completely missing`
)
missing.push(t.missing.Subs({k,v}))
} else if (feat.properties[k] !== v) {
missing.push(
`Property with key ${k} does not have expected value ${v}; instead it is ${feat.properties}`
)
missing.push(t.misMatch.Subs({k, v, properties: feat.properties}))
}
}
if (missing.length > 0) {
parts.push(
new Combine([
new FixedUiElement(`Preset ${preset.title.txt} is not applicable:`),
t.notApplicable.Subs(preset),
new List(missing)
]).SetClass("flex flex-col alert")
)
@ -158,9 +157,9 @@ export default class SelectTheme extends Combine implements FlowStep<{
}
return new Combine([
new FixedUiElement(unmatched.length + " objects dont match any presets").SetClass("alert"),
t.displayNonMatchingCount.Subs(unmatched).SetClass("alert"),
...applicablePresetsOverview,
new Toggleable(new Title("The following elements don't match any of the presets"),
new Toggleable(new Title(t.unmatchedTitle),
new Combine(unmatchedPanels))
]).SetClass("flex flex-col")

View file

@ -9,7 +9,8 @@ export default class InputElementWrapper<T> extends InputElement<T> {
private readonly _inputElement: InputElement<T>;
private readonly _renderElement: BaseUIElement
constructor(inputElement: InputElement<T>, translation: Translation, key: string, tags: UIEventSource<any>, state: FeaturePipelineState) {
constructor(inputElement: InputElement<T>, translation: Translation, key: string,
tags: UIEventSource<any>, state: FeaturePipelineState) {
super()
this._inputElement = inputElement;
const mapping = new Map<string, BaseUIElement>()

View file

@ -1,4 +1,15 @@
[
{
"start_date": "2022-05-30",
"end_date":"2022-06-05",
"message": "The 3rd of June is <b><a href='https://en.wikipedia.org/wiki/World_Bicycle_Day'>World Bicycle Day</a></b>X. Go find a bike shop or bike pump nearby",
"featured_theme": "cyclofix"
},
{
"start_date": "2022-04-24",
"end_date": "2022-05-30",
"message": "Help translating MapComplete! If you have some free time, please translate MapComplete to your favourite language. <a href='https://www.openstreetmap.org/user/Pieter%20Vander%20Vennet/diary/398959'>Read the instructions here</a>"
},
{
"start_date": "2022-04-18",
"end_date": "2022-04-24",

View file

@ -39,7 +39,7 @@
},
"general": {
"about": "OpenStreetMap für ein bestimmtes Thema einfach bearbeiten und hinzufügen",
"aboutMapcomplete": "<h3>Über MapComplete</h3><p>Nutzen Sie es, um OpenStreetMap-Informationen zu einem <b>einzigen Thema</b> hinzuzufügen. Beantworten Sie Fragen, und innerhalb weniger Minuten sind Ihre Beiträge überall verfügbar. Der <b>Theme-Maintainer</b> definiert Elemente, Fragen und Sprachen dafür.</p><h3>Mehr erfahren</h3><p>MapComplete bietet immer <b>den nächsten Schritt</b>, um mehr über OpenStreetMap zu erfahren.</p><ul><li>Wenn es in eine Website eingebettet wird, verlinkt der iframe zu einer Vollbildversion von MapComplete</li><li>Die Vollbildversion bietet Infos über OpenStreetMap</li><li>Das Betrachten funktioniert ohne Anmeldung, aber das Bearbeiten erfordert ein OSM-Konto.</li><li>Wenn Sie nicht angemeldet sind, werden Sie dazu aufgefordert</li><li>Sobald Sie eine Frage beantwortet haben, können Sie der Karte neue Punkte hinzufügen</li><li>Nach einer Weile werden aktuelle OSM-Tags angezeigt, die später mit dem Wiki verlinkt werden</li></ul><p></p><br><p>Haben Sie <b>ein Problem</b> bemerkt? Haben Sie einen <b>Funktionswunsch</b>? Möchten Sie <b>bei der Übersetzung helfen</b>? Besuchen Sie den <a href=\"https://github.com/pietervdvn/MapComplete\" target=\"_blank\">Quellcode</a> oder den <a href=\"https://github.com/pietervdvn/MapComplete/issues\" target=\"_blank\">Issue Tracker</a> </p><p>Möchten Sie <b>Ihren Fortschritt</b> sehen? Verfolgen Sie die Anzahl der Änderungen auf <a href=\"{osmcha_link}\" target=\"_blank\">OsmCha</a>.</p>",
"aboutMapcomplete": "<h3>Über MapComplete</h3><p>Nutzen Sie MapComplete, um OpenStreetMap-Informationen zu einem <b>einzigen Thema</b> hinzuzufügen. Beantworten Sie Fragen, und in wenigen Minuten sind Ihre Beiträge überall verfügbar. Der <b>Theme-Maintainer</b> definiert Elemente, Fragen und Sprachen dafür.</p><h3>Mehr erfahren</h3><p>MapComplete bietet immer <b>den nächsten Schritt</b>, um mehr über OpenStreetMap zu erfahren.</p><ul><li>In einer Website eingebettet, verlinkt der iframe zu einer Vollbildversion von MapComplete</li><li>Die Vollbildversion bietet Informationen über OpenStreetMap</li><li>Das Betrachten funktioniert ohne Anmeldung, aber das Bearbeiten erfordert ein OSM-Konto.</li><li>Wenn Sie nicht angemeldet sind, werden Sie dazu aufgefordert</li><li>Sobald Sie eine Frage beantwortet haben, können Sie der Karte neue Punkte hinzufügen</li><li>Nach einer Weile werden aktuelle OSM-Tags angezeigt, die später mit dem Wiki verlinkt werden</li></ul><p></p><br><p>Haben Sie <b>ein Problem</b> bemerkt? Haben Sie einen <b>Funktionswunsch</b>? Möchten Sie <b>bei der Übersetzung helfen</b>? Hier geht es zum <a href=\"https://github.com/pietervdvn/MapComplete\" target=\"_blank\">Quellcode</a> und <a href=\"https://github.com/pietervdvn/MapComplete/issues\" target=\"_blank\">Issue Tracker</a> </p><p>Möchten Sie <b>Ihren Fortschritt</b> sehen? Verfolgen Sie Ihre Änderungen auf <a href=\"{osmcha_link}\" target=\"_blank\">OsmCha</a>.</p>",
"add": {
"addNew": "Füge {category} hinzu",
"addNewMapLabel": "Hier klicken, um ein neues Element hinzuzufügen",
@ -76,9 +76,9 @@
"attribution": {
"attributionContent": "<p>Alle Daten wurden bereitgestellt von <a href=\"https://osm.org\" target=\"_blank\">OpenStreetMap</a>, frei verwendbar unter der <a href=\"https://osm.org/copyright\" target=\"_blank\">Open Database License</a>.</p>",
"attributionTitle": "Danksagung",
"codeContributionsBy": "MapComplete wurde von {contributors} und <a href=\"https://github.com/pietervdvn/MapComplete/graphs/contributors\" target=\"_blank\">{hiddenCount} weiteren Beitragenden</a> erstellt",
"donate": "Unterstütze MapComplete finanziell",
"editId": "Den OpenStreetMap online Editor hier öffnen",
"codeContributionsBy": "MapComplete wurde erstellt von {contributors} und <a href=\"https://github.com/pietervdvn/MapComplete/graphs/contributors\" target=\"_blank\">{hiddenCount} weiteren Beitragenden</a>",
"donate": "MapComplete finanziell unterstützen",
"editId": "Den OpenStreetMap Editor öffnen",
"editJosm": "Bearbeite mit JOSM",
"iconAttribution": {
"title": "Verwendete Symbole"
@ -86,12 +86,12 @@
"josmNotOpened": "JOSM konnte nicht erreicht werden. Stelle sicher, dass es geöffnet ist und Fernkontrolle aktiviert ist",
"josmOpened": "JOSM ist geöffnet",
"mapContributionsBy": "Die aktuell sichtbaren Daten wurden editiert durch {contributors}",
"mapContributionsByAndHidden": "Die aktuell sichtbaren Daten wurden editiert durch {contributors} und {hiddenCount} weitere Beitragende",
"openIssueTracker": "Melde einen Fehler",
"openMapillary": "Öffne Mapillary hier",
"mapContributionsByAndHidden": "Die aktuell sichtbaren Daten wurden editiert von {contributors} und {hiddenCount} weiteren Beitragenden",
"openIssueTracker": "Einen Fehler melden",
"openMapillary": "Mapillary öffnen",
"openOsmcha": "Letzte Bearbeitungen mit {theme} ansehen",
"themeBy": "Thema betreut von {author}",
"translatedBy": "MapComplete wurde von {contributors}, <a href=\"https://github.com/pietervdvn/MapComplete/graphs/contributors\" target=\"_blank\">{hiddenCount} und weiteren Mitwirkenden übersetzt.</a>"
"translatedBy": "MapComplete wurde übersetzt von {contributors}, <a href=\"https://github.com/pietervdvn/MapComplete/graphs/contributors\" target=\"_blank\">{hiddenCount} und weiteren Mitwirkenden</a>"
},
"back": "Zurück",
"backToMapcomplete": "Zurück zur Themenwahl",
@ -153,7 +153,7 @@
"noTagsSelected": "Keine Tags ausgewählt",
"number": "Zahl",
"oneSkippedQuestion": "Eine Frage wurde übersprungen",
"openStreetMapIntro": "<h3>Eine offene Karte</h3><p>Eine Karte, die jeder frei nutzen und bearbeiten kann. Ein einziger Ort, um alle Geoinformationen zu speichern. Unterschiedliche, kleine, inkompatible und veraltete Karten werden nirgendwo gebraucht.</p><p><b><a href=\"https://OpenStreetMap.org\" target=\"_blank\">OpenStreetMap</a></b> ist nicht die feindliche Karte. Die Kartendaten können frei verwendet werden (mit <a href=\"https://osm.org/copyright\" target=\"_blank\">Benennung und Veröffentlichung von Änderungen an diesen Daten</a>). Jeder kann neue Daten hinzufügen und Fehler korrigieren. Diese Webseite nutzt OpenStreetMap. Alle Daten stammen von dort, und Ihre Antworten und Korrekturen werden überall verwendet.</p><p>Viele Menschen und Anwendungen nutzen bereits OpenStreetMap: <a href=\"https://organicmaps.app/\" target=\"_blank\">Organic Maps</a>, <a href=\"https://osmAnd.net\" target=\"_blank\">OsmAnd</a>, aber auch die Karten bei Facebook, Instagram, Apple-maps und Bing-maps werden (teilweise) von OpenStreetMap bereichert.</p>",
"openStreetMapIntro": "<h3>Eine offene Karte</h3><p>Eine Karte, die jeder frei nutzen und bearbeiten kann. Ein einziger Ort, um alle Geoinformationen zu speichern. Unterschiedliche, kleine, inkompatible und veraltete Karten werden nirgendwo gebraucht.</p><p><b><a href=\"https://OpenStreetMap.org\" target=\"_blank\">OpenStreetMap</a></b> ist nicht die feindliche Karte. Die Kartendaten können frei verwendet werden (mit <a href=\"https://osm.org/copyright\" target=\"_blank\">Benennung und Veröffentlichung von Änderungen an diesen Daten</a>). Jeder kann neue Daten hinzufügen und Fehler korrigieren. Diese Webseite nutzt OpenStreetMap. Alle Daten stammen von dort, und Ihre Antworten und Korrekturen werden überall verwendet.</p><p>Viele Menschen und Anwendungen nutzen bereits OpenStreetMap: <a href=\"https://organicmaps.app/\" target=\"_blank\">Organic Maps</a>, <a href=\"https://osmAnd.net\" target=\"_blank\">OsmAnd</a>; auch die Kartendaten von Facebook, Instagram, Apple-maps und Bing-maps stammen (teilweise) von OpenStreetMap.</p>",
"openTheMap": "Karte öffnen",
"opening_hours": {
"closed_permanently": "Geschlossen auf unbestimmte Zeit",
@ -205,11 +205,11 @@
"editThisTheme": "Dieses Thema bearbeiten",
"embedIntro": "<h3>Auf Ihrer Website einbetten</h3>Bitte betten Sie diese Karte in Ihre Webseite ein. <br>Wir ermutigen Sie, es zu tun - Sie müssen nicht einmal um Erlaubnis fragen. <br> Es ist kostenlos und wird es immer sein. Je mehr Leute sie benutzen, desto wertvoller wird sie.",
"fsAddNew": "Schaltfläche 'neuen POI hinzufügen' aktivieren",
"fsGeolocation": "Die Schaltfläche 'Mich geolokalisieren' aktivieren (nur für Mobil)",
"fsIncludeCurrentBackgroundMap": "Den aktuellen Hintergrund übernehmen <b>{name}</b>",
"fsIncludeCurrentLayers": "Die aktuelle Ebenenauswahl übernehmen",
"fsGeolocation": "Schaltfläche 'Mich geolokalisieren' aktivieren (nur mobil)",
"fsIncludeCurrentBackgroundMap": "Aktuellen Hintergrund übernehmen <b>{name}</b>",
"fsIncludeCurrentLayers": "Aktuelle Ebenenauswahl übernehmen",
"fsIncludeCurrentLocation": "Aktuelle Position einbeziehen",
"fsLayerControlToggle": "Mit der ausgeklappter Ebenensteuerung beginnen",
"fsLayerControlToggle": "Ausgeklappte Ebenensteuerung anzeigen",
"fsLayers": "Ebenensteuerung aktivieren",
"fsSearch": "Suchleiste aktivieren",
"fsUserbadge": "Anmeldeschaltfläche aktivieren",
@ -390,15 +390,16 @@
"warnAnonymous": "Sie sind nicht eingeloggt. Wir sind nicht in der Lage, Sie zu kontaktieren, um Ihr Problem zu lösen."
},
"privacy": {
"editing": "Wenn Sie eine Änderung an der Karte vornehmen, wird diese Änderung auf OpenStreetMap aufgezeichnet und ist für jeden öffentlich zugänglich. Ein mit MapComplete vorgenommener Änderungssatz enthält die folgenden Daten: <ul><li>Die von Ihnen vorgenommenen Änderungen</li><li>Ihr Benutzername</li><li>Wann diese Änderung vorgenommen wurde</li><li>Das Thema, das Sie bei der Änderung verwendet haben</li><li>Die Sprache der Benutzeroberfläche</li><li>Eine Angabe darüber, wie nah Sie an geänderten Objekten waren. Andere Kartierer können diese Informationen nutzen, um festzustellen, ob eine Änderung auf Basis einer Vor-Ort Erkundung oder einer Fernerkundung vorgenommen wurde</li></ul> Ausführliche Informationen finden Sie in <a href=\"https://wiki.osmfoundation.org/wiki/Privacy_Policy\" target=\"_blank\">den Datenschutzbestimmungen auf OpenStreetMap.org</a>. Wir möchten Sie daran erinnern, dass Sie bei der Anmeldung einen fiktiven Namen verwenden können.",
"editing": "Wenn Sie Änderungen an der Karte vornehmen, werden diese auf OpenStreetMap gespeichert und sind für jeden öffentlich zugänglich. Ein mit MapComplete erstellter Änderungssatz enthält folgende Daten: <ul><li>Die von Ihnen vorgenommenen Änderungen</li><li>Ihren Benutzernamen</li><li>Den Zeitpunkt Ihrer Änderungen</li><li>Das MapComplete-Thema, das Sie bei Ihren Änderungen verwendet haben</li><li>Die Sprache Ihrer Benutzeroberfläche</li><li>Eine Angabe darüber, wie nah Sie an geänderten Objekten waren. Andere Kartierer können diese Informationen nutzen, um festzustellen, ob eine Änderung auf Basis einer Vor-Ort Erkundung oder einer Fernerkundung vorgenommen wurde</li></ul> Ausführliche Informationen finden Sie in <a href=\"https://wiki.osmfoundation.org/wiki/Privacy_Policy\" target=\"_blank\">den Datenschutzbestimmungen auf OpenStreetMap.org</a>. Wir möchten Sie daran erinnern, dass Sie bei der Anmeldung einen fiktiven Namen verwenden können.",
"editingTitle": "Ihre Änderungen",
"geodata": "Wenn MapComplete Ihre Standortdaten erhält, verbleiben Ihre Daten zum aktuellen Standort und zuvor besuchter Orte auf Ihrem Gerät. Ihre Standortdaten werden niemals automatisch an eine andere Stelle gesendet - es sei denn, eine Funktion gibt eindeutig etwas anderes an.",
"geodata": "Wenn MapComplete Ihre aktuellenStandortdaten ermittelt, verbleiben diese wie auch zuvor ermittelte Standorte auf Ihrem Gerät. Ihre Standortdaten werden niemals automatisch an eine andere Stelle gesendet - es sei denn, eine Funktion gibt eindeutig etwas anderes an.",
"geodataTitle": "Ihr Standort",
"intro": "Privatsphäre ist wichtig - sowohl für den Einzelnen als auch für die Gesellschaft. MapComplete versucht, Ihre Privatsphäre so weit wie möglich zu respektieren - bis zu dem Punkt, an dem kein lästiger Cookie-Banner mehr nötig ist. Dennoch möchten wir Sie darüber informieren, welche Informationen gesammelt und weitergegeben werden, unter welchen Umständen und warum diese Kompromisse gemacht werden.",
"miscCookies": "MapComplete integriert verschiedene andere Dienste, insbesondere um Bilder von Objekten zu laden. Bilder werden auf verschiedenen Servern von Drittanbietern gehostet, die möglicherweise eigene Cookies setzen.",
"miscCookiesTitle": "Andere Cookies",
"surveillance": "Da Sie die Datenschutzbestimmungen lesen, ist Ihnen der Datenschutz wahrscheinlich wichtig - uns auch! Wir haben sogar <a href=\"https://mapcomplete.osm.be/surveillance\">ein Thema gemacht, das Überwachungskameras zeigt.</a> Zögern Sie nicht, sie alle zu mappen!",
"title": "Datenschutzbestimmungen",
"tracking": "Um einen Einblick zu bekommen, wer unsere Website besucht, werden einige technische Informationen gesammelt. Dazu gehören das Land, aus dem Sie die Webseite besucht haben, die Webseite, die Sie auf MapComplete verwiesen hat, der Typ Ihres Geräts und die Bildschirmgröße. Ein Cookie wird auf Ihrem Gerät platziert, um anzuzeigen, dass Sie MapComplete heute bereits besucht haben. Diese Daten sind nicht detailliert genug, um Sie persönlich zu identifizieren. Diese Statistiken sind nur in aggregierter Form <a href=\"https://pietervdvn.goatcounter.com\" target=\"_blank\">öffentlich für jedermann zugänglich</a>",
"trackingTitle": "Statistische Daten",
"whileYoureHere": "Ist Ihnen die Privatsphäre wichtig?"
},
@ -529,7 +530,7 @@
"splitTitle": "Wählen Sie auf der Karte aus, wo die Straße geteilt werden soll"
},
"translations": {
"activateButton": "Bei der Übersetzung von MapComplete helfen",
"activateButton": "MapComplete übersetzen",
"completeness": "Die Übersetzungen für {theme} in {language} ist zu {percentage}% vollständig: {translated} Zeichenfolgen von {total} sind übersetzt",
"deactivate": "Schaltflächen für die Übersetzung deaktivieren",
"help": "Klicken Sie auf das 'translate'-Symbol neben einer Zeichenfolge, um einen Text einzugeben oder zu aktualisieren. Dazu benötigen Sie einen Weblate-Account. Erstellen Sie einen mit Ihrem OSM-Benutzernamen, um den Übersetzungsmodus automatisch freizuschalten.",

View file

@ -119,6 +119,7 @@
"title": "Download visible data",
"uploadGpx": "Upload your track to OpenStreetMap"
},
"error": "Something went wrong...",
"example": "Example",
"examples": "Examples",
"fewChangesBefore": "Please, answer a few questions of existing points before adding a new point.",
@ -151,6 +152,7 @@
"next": "Next",
"noNameCategory": "{category} without a name",
"noTagsSelected": "No tags selected",
"notValid": "Select a valid value to continue",
"number": "number",
"oneSkippedQuestion": "One question is skipped",
"openStreetMapIntro": "<h3>An Open Map</h3><p>One that everyone can use and edit freely. A single place to store all geo-info. Different, small, incompatible and outdated maps are not needed anywhere.</p><p><b><a href='https://OpenStreetMap.org' target='_blank'>OpenStreetMap</a></b> is not the enemy map. The map data can be used freely (with <a href='https://osm.org/copyright' target='_blank'>attribution and publication of changes to that data</a>). Everyone can add new data and fix errors. This website uses OpenStreetMap. All the data is from there, and your answers and corrections are used all over.</p><p>Many people and apps already use OpenStreetMap: <a href='https://organicmaps.app/' target='_blank'>Organic Maps</a>, <a href='https://osmAnd.net' target='_blank'>OsmAnd</a>, but also the maps at Facebook, Instagram, Apple-maps and Bing-maps are (partly) powered by OpenStreetMap.</p>",
@ -276,17 +278,80 @@
"willBePublished": "Your picture will be published "
},
"importHelper": {
"allAttributesSame": "All features to import have this tag",
"askMetadata": {
"downloadGeojson": "Download geojson",
"giveDescription": "Please, write a small description for someone who sees the note. A good note describes what the contributor has to do, e.g; <i>There might be a bench here. If you are around, could you please check and indicate if the bench exists or not?</i> (A link to MapComplete will be added automatically)",
"giveSource": "What is the source of this data? If 'source' is set in the feature, this value will be ignored",
"giveWikilink": "On what wikipage can one find more information about this import?",
"intro": "Before adding {count} notes, please provide some extra information.",
"orDownload": "Alternatively, you can download the dataset to import directly",
"shouldBeOsmWikilink": "Expected a link to a page on wiki.openstreetmap.org",
"shouldBeUrl": "Not a valid URL",
"shouldNotBeHomepage": "Nope, the home page isn't allowed either. Enter the URL of a proper wikipage documenting your import",
"title": "Set metadata"
},
"compareToAlreadyExistingNotes": {
"completelyImported": "All of the proposed points have (or had) an import note already",
"loading": "Fetching notes from OSM",
"loadingFailed": "Loading notes failed due to {error}",
"mapExplanation": "The red elements on the next map are all the data points from your dataset. There are <b>{length}</b> elements in your dataset.",
"noPreviousNotesFound": "No previous import notes found",
"nothingNearby": "All of the proposed points have don't have a previous import note nearby",
"someNearby": "{hasNearby} points do have an already existing import note within {distance} meter",
"title": "Compare with existing notes",
"titleLong": "Compare with already existing 'to-import'-notes",
"wontBeImported": "These data points will <i>not</i> be imported and are shown as red dots on the map below"
},
"confirmProcess": {
"contactedCommunity": "I did contact the (local) community about this import",
"licenseIsCompatible": "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",
"readImportGuidelines": "I have read the import guidelines on the OSM wiki",
"title": "License and community",
"titleLong": "Did you go through the import process?",
"wikipageIsMade": "The process is documented on the OSM-wiki (you'll need this link later)"
},
"conflationChecker": {
"cacheExpired": "Cache was expired",
"downloadOverpassData": "Download the loaded geojson from overpass",
"importCandidatesCount": "The {count} red elements on the following map are all your import candidates.",
"loadedDataAge": "Loaded data is from the cache and is {age} old",
"mapShowingNearbyIntro": "The following map shows features to import which have an OSM-feature within ",
"nearbyWarn": "The {count} red elements on the following map will <b>not</b> be imported!",
"nothingLoaded": "No elements are loaded from OpenStreetMap which match the current layer {name}",
"osmLoaded": "{count} elements are loaded from OpenStreetMap which match the layer <b>{name}</b>.",
"setRangeToZero": "Set the range to 0 or 1 if you want to import them all",
"states": {
"error": "Could not load latest data from overpass due to {error}",
"idle": "Checking local storage...",
"running": "Querying overpass...",
"unexpected": "Unexpected state {state}"
},
"title": "Compare with existing data",
"titleLive": "Live data on OSM",
"titleNearby": "Nearby features",
"zoomIn": "The live data is shown if the zoomlevel is at least {needed}. The current zoom level is {current}"
},
"createNotes": {
"creating": "Created <b>{count}</b> notes out of {total}",
"done": "All {count} notes have been created!",
"loading": "Hang on while we are loading...",
"openImportViewer": "Inspect the progress of your notes in the 'import_viewer'",
"title": "Note creation"
},
"gotoImportViewer": "Inspect your previous imports",
"introduction": {
"description": "The import helper converts an external dataset to notes. The external dataset must match one of the existing MapComplete layers. For every item you put in the importer, a single note will be created. These notes will be shown together with the relevant features in these maps to easily add them.",
"importFormat": "A text in a note should have the following format in order to be picked up: <br/><div class='literal-code'>[A bit of introduction]<br/>https://mapcomplete.osm.be/[themename].html?[parameters such as lat and lon]#import<br/>[all tags of the feature] </div>",
"inspectDataTitle": "Inspect data of {count} features to import",
"inspectDidAutoDected": "Layer was chosen automatically",
"inspectLooksCorrect": "These values look correct",
"importFormat": "A text in a note should have the following format in order to be picked up",
"title": "Introduction"
},
"login": {
"lockNotice": "This page is locked. You need {importHelperUnlock} changesets before you can access here.",
"locked": "You need at least {importHelperUnlock} to use the import helper",
"loggedInWith": "You are currently logged in as <b>{name}</b> and have made {csCount} changesets",
"loginIsCorrect": "<b>{name}</b> is the correct account to create the import notes with.",
"loginRequired": "You have to be logged in to continue",
"title": "Login",
"userAccountTitle": "Select user account"
},
"mapPreview": {
"autodetected": "The layer was automatically deducted based on the properties",
"confirm": "The features are on the right location on the map",
@ -294,6 +359,18 @@
"selectLayer": "Which layer does this import match with?",
"title": "Map preview"
},
"noteParts": {
"datasource": "Original data from {source}",
"importEasily": "Add this point easily with MapComplete:",
"wikilink": "More information about this import can be found at {wikilink}"
},
"previewAttributes": {
"allAttributesSame": "All features to import have this tag",
"inspectDataTitle": "Inspect data of {count} features to import",
"inspectLooksCorrect": "These values look correct",
"someHaveSame": "{count} features to import have this tag, this is {percentage}% of the total",
"title": "Inspect attributes"
},
"selectFile": {
"description": "Select a .csv or .geojson file to get started",
"errDuplicate": "Some columns have the same name",
@ -308,11 +385,19 @@
"noFilesLoaded": "No file is currently loaded",
"title": "Select file"
},
"selectLayer": "Select a layer...",
"someHaveSame": "{count} features to import have this tag, this is {percentage}% of the total",
"title": "Import helper",
"userAccountTitle": "Select user account",
"validateDataTitle": "Validate data"
"selectTheme": {
"displayNonMatchingCount": "{length} objects dont match any presets",
"intro": "All of the following themes will show the import notes. However, the note on OpenStreetMap can link to only one single theme. Choose which theme that the created notes will link to",
"misMatch": "Property with key {k} does not have expected value {v}; instead it is {properties}",
"missing": "Expected $k}={v}, but it is completely missing",
"needsTags": "{title} needs tags {tags}",
"noMatchingPresets": "This theme has no presets loaded. As a result, imports won't work here",
"notApplicable": "Preset {title} is not applicable:",
"title": "Select a theme",
"unmatchedTitle": "The following elements don't match any of the presets"
},
"testMode": "Testmode - won't actually import notes",
"title": "Import helper"
},
"importInspector": {
"title": "Inspect and manage import notes"

View file

@ -40,7 +40,15 @@
"about": "Madaling i-edit at mag-dagdag sa OpenStreetMap gamit ang mga partikular na tikha",
"add": {
"addNew": "Dagdagan ng {category}",
"addNewMapLabel": "I-click ito para mag-dagdag ng bagong bagay"
"addNewMapLabel": "I-click ito para mag-dagdag ng bagong bagay",
"disableFilters": "Huwag paganahin ang lahat ng filter",
"import": {
"hasBeenImported": "Ang object na ito ay nai-angkat na"
},
"disableFiltersExplanation": "May mga tampók na maaring nai-tago ng filter",
"hasBeenImported": "Ang bukóng ito ay nai-angkat na",
"confirmIntro": "<h3>Mag-dagdag ng {title}?</h3>Ang tampók na ida-dagdag mo ay <b>makikita ng lahat</b>. Paki-usap, mag-dagdag lamang ng mga bagay na tutuong umiiral. Marami pang mga aplikasyon ang gumagamit ng datos na ito.",
"confirmButton": "Magdagdag ng {category}<br><div class=\"alert\">Makikita ng lahat ang idinagdag mo</div>"
}
}
}

View file

@ -1141,7 +1141,7 @@
"playgrounds": {
"description": "Auf dieser Karte finden Sie Spielplätze und können weitere Informationen hinzufügen",
"shortDescription": "Eine Karte mit Spielplätzen",
"title": "Spielpläzte"
"title": "Spielplätze"
},
"postal_codes": {
"description": "Postleitzahlen",