forked from MapComplete/MapComplete
Merge master
This commit is contained in:
commit
49d99930e8
18 changed files with 413 additions and 209 deletions
|
@ -39,6 +39,39 @@ export class AddContextToTranslations<T> extends DesugaringStep<T> {
|
||||||
* }
|
* }
|
||||||
* rewritten // => expected
|
* 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
|
* // should preserve nulls
|
||||||
* const theme = {
|
* const theme = {
|
||||||
* layers: [
|
* layers: [
|
||||||
|
@ -70,6 +103,17 @@ export class AddContextToTranslations<T> extends DesugaringStep<T> {
|
||||||
return leaf
|
return leaf
|
||||||
}
|
}
|
||||||
if (typeof leaf === "object") {
|
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(".")}
|
return {...leaf, _context: this._prefix + context + "." + path.join(".")}
|
||||||
} else {
|
} else {
|
||||||
return leaf
|
return leaf
|
||||||
|
|
|
@ -4,8 +4,12 @@ import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import ValidatedTextField from "../Input/ValidatedTextField";
|
import ValidatedTextField from "../Input/ValidatedTextField";
|
||||||
import {LocalStorageSource} from "../../Logic/Web/LocalStorageSource";
|
import {LocalStorageSource} from "../../Logic/Web/LocalStorageSource";
|
||||||
import Title from "../Base/Title";
|
import Title from "../Base/Title";
|
||||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
|
||||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
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<{
|
export class AskMetadata extends Combine implements FlowStep<{
|
||||||
features: any[],
|
features: any[],
|
||||||
|
@ -25,7 +29,7 @@ export class AskMetadata extends Combine implements FlowStep<{
|
||||||
public readonly IsValid: UIEventSource<boolean>;
|
public readonly IsValid: UIEventSource<boolean>;
|
||||||
|
|
||||||
constructor(params: ({ features: any[], theme: string })) {
|
constructor(params: ({ features: any[], theme: string })) {
|
||||||
|
const t = Translations.t.importHelper.askMetadata
|
||||||
const introduction = ValidatedTextField.ForType("text").ConstructInputElement({
|
const introduction = ValidatedTextField.ForType("text").ConstructInputElement({
|
||||||
value: LocalStorageSource.Get("import-helper-introduction-text"),
|
value: LocalStorageSource.Get("import-helper-introduction-text"),
|
||||||
inputStyle: "width: 100%"
|
inputStyle: "width: 100%"
|
||||||
|
@ -42,28 +46,39 @@ export class AskMetadata extends Combine implements FlowStep<{
|
||||||
})
|
})
|
||||||
|
|
||||||
super([
|
super([
|
||||||
new Title("Set metadata"),
|
new Title(t.title),
|
||||||
"Before adding " + params.features.length + " notes, please provide some extra information.",
|
t.intro.Subs({count: params.features.length}),
|
||||||
"Please, write an introduction for someone who sees the note",
|
t.giveDescription,
|
||||||
introduction.SetClass("w-full border border-black"),
|
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"),
|
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"),
|
wikilink.SetClass("w-full border border-black"),
|
||||||
new VariableUiElement(wikilink.GetValue().map(wikilink => {
|
new VariableUiElement(wikilink.GetValue().map(wikilink => {
|
||||||
try{
|
try{
|
||||||
const url = new URL(wikilink)
|
const url = new URL(wikilink)
|
||||||
if(url.hostname.toLowerCase() !== "wiki.openstreetmap.org"){
|
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"){
|
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){
|
}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")
|
this.SetClass("flex flex-col")
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
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";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
@ -28,11 +29,11 @@ import {LayerConfigJson} from "../../Models/ThemeConfig/Json/LayerConfigJson";
|
||||||
export class CompareToAlreadyExistingNotes extends Combine implements FlowStep<{ bbox: BBox, layer: LayerConfig, features: any[], theme: string }> {
|
export class CompareToAlreadyExistingNotes extends Combine implements FlowStep<{ bbox: BBox, layer: LayerConfig, features: any[], theme: string }> {
|
||||||
|
|
||||||
public IsValid: UIEventSource<boolean>
|
public IsValid: UIEventSource<boolean>
|
||||||
public Value: UIEventSource<{ bbox: BBox, layer: LayerConfig, features: any[] , theme: string}>
|
public Value: UIEventSource<{ bbox: BBox, layer: LayerConfig, features: any[], theme: string }>
|
||||||
|
|
||||||
|
|
||||||
constructor(state, params: { bbox: BBox, layer: LayerConfig, features: any[], theme: string }) {
|
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]
|
const layerConfig = known_layers.layers.filter(l => l.id === params.layer.id)[0]
|
||||||
if (layerConfig === undefined) {
|
if (layerConfig === undefined) {
|
||||||
console.error("WEIRD: layer not found in the builtin layer overview")
|
console.error("WEIRD: layer not found in the builtin layer overview")
|
||||||
|
@ -45,7 +46,7 @@ export class CompareToAlreadyExistingNotes extends Combine implements FlowStep<{
|
||||||
layerDef: importLayer
|
layerDef: importLayer
|
||||||
}
|
}
|
||||||
const allNotesWithinBbox = new GeoJsonSource(flayer, params.bbox.padAbsolute(0.0001))
|
const allNotesWithinBbox = new GeoJsonSource(flayer, params.bbox.padAbsolute(0.0001))
|
||||||
|
|
||||||
allNotesWithinBbox.features.map(f => MetaTagging.addMetatags(
|
allNotesWithinBbox.features.map(f => MetaTagging.addMetatags(
|
||||||
f,
|
f,
|
||||||
{
|
{
|
||||||
|
@ -63,7 +64,6 @@ export class CompareToAlreadyExistingNotes extends Combine implements FlowStep<{
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
const alreadyOpenImportNotes = new FilteringFeatureSource(state, undefined, allNotesWithinBbox)
|
const alreadyOpenImportNotes = new FilteringFeatureSource(state, undefined, allNotesWithinBbox)
|
||||||
alreadyOpenImportNotes.features.addCallbackD(features => console.log("Loaded and filtered features are", features))
|
|
||||||
const map = Minimap.createMiniMap()
|
const map = Minimap.createMiniMap()
|
||||||
map.SetClass("w-full").SetStyle("height: 500px")
|
map.SetClass("w-full").SetStyle("height: 500px")
|
||||||
|
|
||||||
|
@ -99,43 +99,46 @@ export class CompareToAlreadyExistingNotes extends Combine implements FlowStep<{
|
||||||
})
|
})
|
||||||
|
|
||||||
super([
|
super([
|
||||||
new Title("Compare with already existing 'to-import'-notes"),
|
new Title(t.titleLong),
|
||||||
new VariableUiElement(
|
new VariableUiElement(
|
||||||
alreadyOpenImportNotes.features.map(notesWithImport => {
|
alreadyOpenImportNotes.features.map(notesWithImport => {
|
||||||
if(allNotesWithinBbox.state.data !== undefined && allNotesWithinBbox.state.data["error"] !== undefined){
|
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) {
|
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) {
|
if (notesWithImport.length === 0) {
|
||||||
return new FixedUiElement("No previous import notes found").SetClass("thanks")
|
return t.noPreviousNotesFound.SetClass("thanks")
|
||||||
}
|
}
|
||||||
return new Combine([
|
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,
|
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
}
|
new VariableUiElement(partitionedImportPoints.map(({noNearby, hasNearby}) => {
|
||||||
|
|
||||||
return new Combine([
|
if (noNearby.length === 0) {
|
||||||
new FixedUiElement(hasNearby.length+" points do have an already existing import note within "+maxDistance.data+" meter.").SetClass("alert"),
|
// Nothing can be imported
|
||||||
"These data points will <i>not</i> be imported and are shown as red dots on the map below",
|
return t.completelyImported.SetClass("alert w-full block").SetStyle("padding: 0.5rem")
|
||||||
comparisonMap.SetClass("w-full")
|
}
|
||||||
]).SetClass("w-full")
|
|
||||||
|
if (hasNearby.length === 0) {
|
||||||
|
// All points can be imported
|
||||||
|
return t.nothingNearby.SetClass("thanks w-full block").SetStyle("padding: 0.5rem")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Combine([
|
||||||
|
t.someNearby.Subs({
|
||||||
|
hasNearby: hasNearby.length,
|
||||||
|
distance: maxDistance.data
|
||||||
|
}).SetClass("alert"),
|
||||||
|
t.wontBeImported,
|
||||||
|
comparisonMap.SetClass("w-full")
|
||||||
|
]).SetClass("w-full")
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
]).SetClass("flex flex-col")
|
]).SetClass("flex flex-col")
|
||||||
|
|
||||||
}, [allNotesWithinBbox.features, allNotesWithinBbox.state])
|
}, [allNotesWithinBbox.features, allNotesWithinBbox.state])
|
||||||
|
|
|
@ -2,45 +2,30 @@ import Combine from "../Base/Combine";
|
||||||
import {FlowStep} from "./FlowStep";
|
import {FlowStep} from "./FlowStep";
|
||||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import Link from "../Base/Link";
|
import Link from "../Base/Link";
|
||||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
|
||||||
import CheckBoxes from "../Input/Checkboxes";
|
import CheckBoxes from "../Input/Checkboxes";
|
||||||
import Title from "../Base/Title";
|
import Title from "../Base/Title";
|
||||||
import {SubtleButton} from "../Base/SubtleButton";
|
import Translations from "../i18n/Translations";
|
||||||
import Svg from "../../Svg";
|
|
||||||
import {Utils} from "../../Utils";
|
|
||||||
|
|
||||||
export class ConfirmProcess extends Combine implements FlowStep<{ features: any[], theme: string }> {
|
export class ConfirmProcess extends Combine implements FlowStep<{ features: any[], theme: string }> {
|
||||||
|
|
||||||
public IsValid: UIEventSource<boolean>
|
public IsValid: UIEventSource<boolean>
|
||||||
public Value: UIEventSource<{ features: any[],theme: string }>
|
public Value: UIEventSource<{ features: any[], theme: string }>
|
||||||
|
|
||||||
constructor(v: { features: any[], theme: string }) {
|
|
||||||
|
|
||||||
|
constructor(v: { features: any[], theme: string }) {
|
||||||
|
const t = Translations.t.importHelper.confirmProcess;
|
||||||
const toConfirm = [
|
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 Link(t.readImportGuidelines, "https://wiki.openstreetmap.org/wiki/Import_guidelines", true),
|
||||||
new FixedUiElement("I did contact the (local) community about this import"),
|
t.contactedCommunity,
|
||||||
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"),
|
t.licenseIsCompatible,
|
||||||
new FixedUiElement("The process is documented on the OSM-wiki (you'll need this link later)")
|
t.wikipageIsMade
|
||||||
];
|
];
|
||||||
|
|
||||||
const licenseClear = new CheckBoxes(toConfirm)
|
|
||||||
super([
|
super([
|
||||||
new Title("Did you go through the import process?"),
|
new Title(t.titleLong),
|
||||||
licenseClear,
|
new CheckBoxes(toConfirm),
|
||||||
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"
|
|
||||||
})
|
|
||||||
})
|
|
||||||
]);
|
]);
|
||||||
this.SetClass("link-underline")
|
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)
|
this.Value = new UIEventSource<{ features: any[], theme: string }>(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,6 @@ import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import Constants from "../../Models/Constants";
|
import Constants from "../../Models/Constants";
|
||||||
import RelationsTracker from "../../Logic/Osm/RelationsTracker";
|
import RelationsTracker from "../../Logic/Osm/RelationsTracker";
|
||||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
|
||||||
import {FlowStep} from "./FlowStep";
|
import {FlowStep} from "./FlowStep";
|
||||||
import Loading from "../Base/Loading";
|
import Loading from "../Base/Loading";
|
||||||
import {SubtleButton} from "../Base/SubtleButton";
|
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 {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";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
@ -190,6 +190,7 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
features: toImportWithNearby
|
features: toImportWithNearby
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const t = Translations.t.importHelper.conflationChecker
|
||||||
|
|
||||||
const conflationMaps = new Combine([
|
const conflationMaps = new Combine([
|
||||||
new VariableUiElement(
|
new VariableUiElement(
|
||||||
|
@ -197,7 +198,7 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
if (geojson === undefined) {
|
if (geojson === undefined) {
|
||||||
return 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", {
|
Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson, null, " "), "mapcomplete-" + layer.id + ".geojson", {
|
||||||
mimetype: "application/json+geo"
|
mimetype: "application/json+geo"
|
||||||
})
|
})
|
||||||
|
@ -208,43 +209,53 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (age < 0) {
|
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"),
|
new Title(t.titleLive),
|
||||||
"The "+toImport.features.length+" red elements on the following map are all your import candidates.",
|
t.importCandidatesCount.Subs({count:toImport.features.length }),
|
||||||
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"))),
|
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}),
|
||||||
|
|
||||||
|
])
|
||||||
|
})),
|
||||||
osmLiveData,
|
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 VariableUiElement(osmLiveData.location.map(location => {
|
||||||
|
return t.zoomIn.Subs({needed:zoomLevel, current: location.zoom })
|
||||||
new Title("Nearby features"),
|
} )),
|
||||||
new Combine(["The following map shows features to import which have an OSM-feature within ", nearbyCutoff, "meter"]).SetClass("flex"),
|
new Title(t.titleNearby),
|
||||||
|
new Combine([t.mapShowingNearbyIntro, nearbyCutoff]).SetClass("flex"),
|
||||||
new VariableUiElement(toImportWithNearby.features.map(feats =>
|
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"))),
|
t.nearbyWarn.Subs({count: feats.length}).SetClass("alert"))),
|
||||||
"Set the range to 0 or 1 if you want to import them all",
|
,t.setRangeToZero,
|
||||||
matchedFeaturesMap]).SetClass("flex flex-col")
|
matchedFeaturesMap]).SetClass("flex flex-col")
|
||||||
|
|
||||||
super([
|
super([
|
||||||
new Title("Comparison with existing data"),
|
new Title(t.title),
|
||||||
new VariableUiElement(overpassStatus.map(d => {
|
new VariableUiElement(overpassStatus.map(d => {
|
||||||
if (d === "idle") {
|
if (d === "idle") {
|
||||||
return new Loading("Checking local storage...")
|
return new Loading(t.states.idle)
|
||||||
}
|
|
||||||
if (d["error"] !== undefined) {
|
|
||||||
return new FixedUiElement("Could not load latest data from overpass: " + d["error"]).SetClass("alert")
|
|
||||||
}
|
}
|
||||||
if (d === "running") {
|
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") {
|
if (d === "cached") {
|
||||||
return conflationMaps
|
return conflationMaps
|
||||||
}
|
}
|
||||||
if (d === "success") {
|
if (d === "success") {
|
||||||
return conflationMaps
|
return conflationMaps
|
||||||
}
|
}
|
||||||
return new FixedUiElement("Unexpected state " + d).SetClass("alert")
|
return t.states.unexpected.Subs({state: d}).SetClass("alert")
|
||||||
}))
|
}))
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
|
@ -8,47 +8,58 @@ import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
import {SubtleButton} from "../Base/SubtleButton";
|
import {SubtleButton} from "../Base/SubtleButton";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
|
import Translations from "../i18n/Translations";
|
||||||
|
|
||||||
export class CreateNotes extends Combine {
|
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 }) {
|
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 createdNotes: UIEventSource<number[]> = new UIEventSource<number[]>([])
|
||||||
const failed = new UIEventSource<string[]>([])
|
const failed = new UIEventSource<string[]>([])
|
||||||
const currentNote = createdNotes.map(n => n.length)
|
const currentNote = createdNotes.map(n => n.length)
|
||||||
|
|
||||||
for (const f of v.features) {
|
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 lat = f.geometry.coordinates[1]
|
||||||
const lon = f.geometry.coordinates[0]
|
const lon = f.geometry.coordinates[0]
|
||||||
const text = [v.intro,
|
const text = CreateNotes.createNoteContents(f, v).join("\n")
|
||||||
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")
|
|
||||||
|
|
||||||
state.osmConnection.openNote(
|
state.osmConnection.openNote(
|
||||||
lat, lon, text)
|
lat, lon, text)
|
||||||
|
@ -62,13 +73,19 @@ export class CreateNotes extends Combine {
|
||||||
}
|
}
|
||||||
|
|
||||||
super([
|
super([
|
||||||
new Title("Creating notes"),
|
new Title(t.title),
|
||||||
"Hang on while we are importing...",
|
t.loading ,
|
||||||
new Toggle(
|
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 Combine([
|
||||||
new FixedUiElement("All done!").SetClass("thanks"),
|
Svg.party_svg().SetClass("w-24"),
|
||||||
new SubtleButton(Svg.note_svg(), "Inspect the progress of your notes in the 'import_viewer'", {
|
t.done.Subs(v.features.length).SetClass("thanks"),
|
||||||
|
new SubtleButton(Svg.note_svg(),
|
||||||
|
t.openImportViewer , {
|
||||||
url: "import_viewer.html"
|
url: "import_viewer.html"
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,15 +25,15 @@ export class FlowPanelFactory<T> {
|
||||||
this._stepNames = stepNames;
|
this._stepNames = stepNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static start<TOut>(name: string | BaseUIElement, step: FlowStep<TOut>): FlowPanelFactory<TOut> {
|
public static start<TOut>(name:{title: BaseUIElement}, step: FlowStep<TOut>): FlowPanelFactory<TOut> {
|
||||||
return new FlowPanelFactory(step, [], [name])
|
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>(
|
return new FlowPanelFactory<TOut>(
|
||||||
this._initial,
|
this._initial,
|
||||||
this._steps.concat([construct]),
|
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)
|
isError.setData(true)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
"Select a valid value to continue",
|
new SubtleButton(Svg.invalid_svg(), t.notValid),
|
||||||
initial.IsValid
|
initial.IsValid
|
||||||
),
|
),
|
||||||
new Toggle(
|
new Toggle(
|
||||||
new FixedUiElement("Something went wrong...").SetClass("alert"),
|
t.error.SetClass("alert"),
|
||||||
undefined,
|
undefined,
|
||||||
isError),
|
isError),
|
||||||
]).SetClass("flex w-full justify-end space-x-2"),
|
]).SetClass("flex w-full justify-end space-x-2"),
|
||||||
|
|
|
@ -7,7 +7,7 @@ import MinimapImplementation from "../Base/MinimapImplementation";
|
||||||
import Translations from "../i18n/Translations";
|
import Translations from "../i18n/Translations";
|
||||||
import {FlowPanelFactory} from "./FlowStep";
|
import {FlowPanelFactory} from "./FlowStep";
|
||||||
import {RequestFile} from "./RequestFile";
|
import {RequestFile} from "./RequestFile";
|
||||||
import {PreviewPanel} from "./PreviewPanel";
|
import {PreviewAttributesPanel} from "./PreviewPanel";
|
||||||
import ConflationChecker from "./ConflationChecker";
|
import ConflationChecker from "./ConflationChecker";
|
||||||
import {AskMetadata} from "./AskMetadata";
|
import {AskMetadata} from "./AskMetadata";
|
||||||
import {ConfirmProcess} from "./ConfirmProcess";
|
import {ConfirmProcess} from "./ConfirmProcess";
|
||||||
|
@ -26,20 +26,20 @@ import SelectTheme from "./SelectTheme";
|
||||||
export default class ImportHelperGui extends LeftIndex {
|
export default class ImportHelperGui extends LeftIndex {
|
||||||
constructor() {
|
constructor() {
|
||||||
const state = new UserRelatedState(undefined)
|
const state = new UserRelatedState(undefined)
|
||||||
|
const t = Translations.t.importHelper;
|
||||||
const {flow, furthestStep, titles} =
|
const {flow, furthestStep, titles} =
|
||||||
FlowPanelFactory
|
FlowPanelFactory
|
||||||
.start("Introduction", new Introdution())
|
.start(t.introduction, new Introdution())
|
||||||
.then("Login", _ => new LoginToImport(state))
|
.then(t.login, _ => new LoginToImport(state))
|
||||||
.then("Select file", _ => new RequestFile())
|
.then(t.selectFile, _ => new RequestFile())
|
||||||
.then("Inspect attributes", geojson => new PreviewPanel(state, geojson))
|
.then(t.previewAttributes, geojson => new PreviewAttributesPanel(state, geojson))
|
||||||
.then("Inspect data", geojson => new MapPreview(state, geojson))
|
.then(t.mapPreview, geojson => new MapPreview(state, geojson))
|
||||||
.then("Select theme", v => new SelectTheme(v))
|
.then(t.selectTheme, v => new SelectTheme(v))
|
||||||
.then("Compare with open notes", v => new CompareToAlreadyExistingNotes(state, v))
|
.then(t.compareToAlreadyExistingNotes, v => new CompareToAlreadyExistingNotes(state, v))
|
||||||
.then("Compare with existing data", v => new ConflationChecker(state, v))
|
.then(t.conflationChecker, v => new ConflationChecker(state, v))
|
||||||
.then("License and community check", v => new ConfirmProcess(v))
|
.then(t.confirmProcess, v => new ConfirmProcess(v))
|
||||||
.then("Metadata", (v) => new AskMetadata(v))
|
.then(t.askMetadata, (v) => new AskMetadata(v))
|
||||||
.finish("Note creation", v => new CreateNotes(state, v));
|
.finish(t.createNotes.title, v => new CreateNotes(state, v));
|
||||||
|
|
||||||
const toc = new List(
|
const toc = new List(
|
||||||
titles.map((title, i) => new VariableUiElement(furthestStep.map(currentStep => {
|
titles.map((title, i) => new VariableUiElement(furthestStep.map(currentStep => {
|
||||||
|
@ -58,11 +58,11 @@ export default class ImportHelperGui extends LeftIndex {
|
||||||
, true)
|
, true)
|
||||||
|
|
||||||
const leftContents: BaseUIElement[] = [
|
const leftContents: BaseUIElement[] = [
|
||||||
new SubtleButton(undefined, "Inspect your preview imports", {
|
new SubtleButton(undefined, t.gotoImportViewer, {
|
||||||
url: "import_viewer.html"
|
url: "import_viewer.html"
|
||||||
}),
|
}),
|
||||||
toc,
|
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"),
|
LanguagePicker.CreateLanguagePicker(Translations.t.importHelper.title.SupportedLanguages())?.SetClass("mt-4 self-end flex-col"),
|
||||||
].map(el => el?.SetClass("pl-4"))
|
].map(el => el?.SetClass("pl-4"))
|
||||||
|
|
||||||
|
|
|
@ -3,18 +3,42 @@ import {FlowStep} from "./FlowStep";
|
||||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import Translations from "../i18n/Translations";
|
import Translations from "../i18n/Translations";
|
||||||
import Title from "../Base/Title";
|
import Title from "../Base/Title";
|
||||||
|
import {CreateNotes} from "./CreateNotes";
|
||||||
|
|
||||||
export default class Introdution extends Combine implements FlowStep<void> {
|
export default class Introdution extends Combine implements FlowStep<void> {
|
||||||
readonly IsValid: UIEventSource<boolean> = new UIEventSource<boolean>(true);
|
readonly IsValid: UIEventSource<boolean>;
|
||||||
readonly Value: UIEventSource<void> = new UIEventSource<void>(undefined);
|
readonly Value: UIEventSource<void>;
|
||||||
|
|
||||||
constructor() {
|
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([
|
super([
|
||||||
new Title(Translations.t.importHelper.title),
|
new Title(Translations.t.importHelper.introduction.title),
|
||||||
Translations.t.importHelper.description,
|
Translations.t.importHelper.introduction.description,
|
||||||
Translations.t.importHelper.importFormat,
|
Translations.t.importHelper.introduction.importFormat,
|
||||||
|
new Combine(
|
||||||
|
[new Combine(
|
||||||
|
example
|
||||||
|
).SetClass("flex flex-col")
|
||||||
|
] ).SetClass("literal-code")
|
||||||
]);
|
]);
|
||||||
this.SetClass("flex flex-col")
|
this.SetClass("flex flex-col")
|
||||||
|
this. IsValid= new UIEventSource<boolean>(true);
|
||||||
|
this. Value = new UIEventSource<void>(undefined);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -21,7 +21,7 @@ export default class LoginToImport extends Combine implements FlowStep<UserRelat
|
||||||
private static readonly whitelist = [15015689];
|
private static readonly whitelist = [15015689];
|
||||||
|
|
||||||
constructor(state: UserRelatedState) {
|
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 check = new CheckBoxes([new VariableUiElement(state.osmConnection.userDetails.map(ud => t.loginIsCorrect.Subs(ud)))])
|
||||||
const isValid = state.osmConnection.userDetails.map(ud =>
|
const isValid = state.osmConnection.userDetails.map(ud =>
|
||||||
LoginToImport.whitelist.indexOf(ud.uid) >= 0 || ud.csCount >= Constants.userJourney.importHelperUnlock)
|
LoginToImport.whitelist.indexOf(ud.uid) >= 0 || ud.csCount >= Constants.userJourney.importHelperUnlock)
|
||||||
|
|
|
@ -12,16 +12,16 @@ import List from "../Base/List";
|
||||||
import CheckBoxes from "../Input/Checkboxes";
|
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 IsValid: UIEventSource<boolean>;
|
||||||
public readonly Value: UIEventSource<{ features: { properties: any, geometry: { coordinates: [number, number] } }[] }>
|
public readonly Value: UIEventSource<{ features: { properties: any, geometry: { coordinates: [number, number] } }[] }>
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
state: UserRelatedState,
|
state: UserRelatedState,
|
||||||
geojson: { features: { properties: any, geometry: { coordinates: [number, number] } }[] }) {
|
geojson: { features: { properties: any, geometry: { coordinates: [number, number] } }[] }) {
|
||||||
const t = Translations.t.importHelper;
|
const t = Translations.t.importHelper.previewAttributes;
|
||||||
|
|
||||||
const propertyKeys = new Set<string>()
|
const propertyKeys = new Set<string>()
|
||||||
for (const f of geojson.features) {
|
for (const f of geojson.features) {
|
||||||
|
|
|
@ -10,12 +10,12 @@ import Title from "../Base/Title";
|
||||||
import {RadioButton} from "../Input/RadioButton";
|
import {RadioButton} from "../Input/RadioButton";
|
||||||
import {And} from "../../Logic/Tags/And";
|
import {And} from "../../Logic/Tags/And";
|
||||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
|
||||||
import Toggleable from "../Base/Toggleable";
|
import Toggleable from "../Base/Toggleable";
|
||||||
import {BBox} from "../../Logic/BBox";
|
import {BBox} from "../../Logic/BBox";
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
import PresetConfig from "../../Models/ThemeConfig/PresetConfig";
|
import PresetConfig from "../../Models/ThemeConfig/PresetConfig";
|
||||||
import List from "../Base/List";
|
import List from "../Base/List";
|
||||||
|
import Translations from "../i18n/Translations";
|
||||||
|
|
||||||
export default class SelectTheme extends Combine implements FlowStep<{
|
export default class SelectTheme extends Combine implements FlowStep<{
|
||||||
features: any[],
|
features: any[],
|
||||||
|
@ -33,7 +33,7 @@ export default class SelectTheme extends Combine implements FlowStep<{
|
||||||
public readonly IsValid: UIEventSource<boolean>;
|
public readonly IsValid: UIEventSource<boolean>;
|
||||||
|
|
||||||
constructor(params: ({ features: any[], layer: LayerConfig, bbox: BBox, })) {
|
constructor(params: ({ features: any[], layer: LayerConfig, bbox: BBox, })) {
|
||||||
|
const t = Translations.t.importHelper.selectTheme
|
||||||
let options: InputElement<string>[] = AllKnownLayouts.layoutsList
|
let options: InputElement<string>[] = AllKnownLayouts.layoutsList
|
||||||
.filter(th => th.layers.some(l => l.id === params.layer.id))
|
.filter(th => th.layers.some(l => l.id === params.layer.id))
|
||||||
.filter(th => th.id !== "personal")
|
.filter(th => th.id !== "personal")
|
||||||
|
@ -69,15 +69,15 @@ export default class SelectTheme extends Combine implements FlowStep<{
|
||||||
})
|
})
|
||||||
|
|
||||||
super([
|
super([
|
||||||
new Title("Select a theme"),
|
new Title(t.title),
|
||||||
"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",
|
t.intro,
|
||||||
themeRadios,
|
themeRadios,
|
||||||
new VariableUiElement(applicablePresets.map(applicablePresets => {
|
new VariableUiElement(applicablePresets.map(applicablePresets => {
|
||||||
if (themeRadios.GetValue().data === undefined) {
|
if (themeRadios.GetValue().data === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
if (applicablePresets === undefined || applicablePresets.length === 0) {
|
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()])),
|
}, [themeRadios.GetValue()])),
|
||||||
|
|
||||||
|
@ -115,11 +115,14 @@ export default class SelectTheme extends Combine implements FlowStep<{
|
||||||
if (unmatched === undefined || unmatched.length === 0) {
|
if (unmatched === undefined || unmatched.length === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const t = Translations.t.importHelper.selectTheme
|
||||||
const applicablePresetsOverview = applicablePresets.map(preset => new Combine([
|
|
||||||
preset.title.txt, "needs tags",
|
const applicablePresetsOverview = applicablePresets.map(preset =>
|
||||||
new FixedUiElement(preset.tags.map(t => t.asHumanString()).join(" & ")).SetClass("thanks")
|
t.needsTags.Subs(
|
||||||
]))
|
{title: preset.title,
|
||||||
|
tags:preset.tags.map(t => t.asHumanString()).join(" & ") })
|
||||||
|
.SetClass("thanks")
|
||||||
|
);
|
||||||
|
|
||||||
const unmatchedPanels: BaseUIElement[] = []
|
const unmatchedPanels: BaseUIElement[] = []
|
||||||
for (const feat of unmatched) {
|
for (const feat of unmatched) {
|
||||||
|
@ -133,20 +136,16 @@ export default class SelectTheme extends Combine implements FlowStep<{
|
||||||
const missing = []
|
const missing = []
|
||||||
for (const {k, v} of tags) {
|
for (const {k, v} of tags) {
|
||||||
if (preset[k] === undefined) {
|
if (preset[k] === undefined) {
|
||||||
missing.push(
|
missing.push(t.missing.Subs({k,v}))
|
||||||
`Expected ${k}=${v}, but it is completely missing`
|
|
||||||
)
|
|
||||||
} else if (feat.properties[k] !== v) {
|
} else if (feat.properties[k] !== v) {
|
||||||
missing.push(
|
missing.push(t.misMatch.Subs({k, v, properties: feat.properties}))
|
||||||
`Property with key ${k} does not have expected value ${v}; instead it is ${feat.properties}`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (missing.length > 0) {
|
if (missing.length > 0) {
|
||||||
parts.push(
|
parts.push(
|
||||||
new Combine([
|
new Combine([
|
||||||
new FixedUiElement(`Preset ${preset.title.txt} is not applicable:`),
|
t.notApplicable.Subs(preset),
|
||||||
new List(missing)
|
new List(missing)
|
||||||
]).SetClass("flex flex-col alert")
|
]).SetClass("flex flex-col alert")
|
||||||
)
|
)
|
||||||
|
@ -158,9 +157,9 @@ export default class SelectTheme extends Combine implements FlowStep<{
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Combine([
|
return new Combine([
|
||||||
new FixedUiElement(unmatched.length + " objects dont match any presets").SetClass("alert"),
|
t.displayNonMatchingCount.Subs(unmatched).SetClass("alert"),
|
||||||
...applicablePresetsOverview,
|
...applicablePresetsOverview,
|
||||||
new Toggleable(new Title("The following elements don't match any of the presets"),
|
new Toggleable(new Title(t.unmatchedTitle),
|
||||||
new Combine(unmatchedPanels))
|
new Combine(unmatchedPanels))
|
||||||
]).SetClass("flex flex-col")
|
]).SetClass("flex flex-col")
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,8 @@ export default class InputElementWrapper<T> extends InputElement<T> {
|
||||||
private readonly _inputElement: InputElement<T>;
|
private readonly _inputElement: InputElement<T>;
|
||||||
private readonly _renderElement: BaseUIElement
|
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()
|
super()
|
||||||
this._inputElement = inputElement;
|
this._inputElement = inputElement;
|
||||||
const mapping = new Map<string, BaseUIElement>()
|
const mapping = new Map<string, BaseUIElement>()
|
||||||
|
|
|
@ -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",
|
"start_date": "2022-04-18",
|
||||||
"end_date": "2022-04-24",
|
"end_date": "2022-04-24",
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"about": "OpenStreetMap für ein bestimmtes Thema einfach bearbeiten und hinzufügen",
|
"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": {
|
"add": {
|
||||||
"addNew": "Füge {category} hinzu",
|
"addNew": "Füge {category} hinzu",
|
||||||
"addNewMapLabel": "Hier klicken, um ein neues Element hinzuzufügen",
|
"addNewMapLabel": "Hier klicken, um ein neues Element hinzuzufügen",
|
||||||
|
@ -76,9 +76,9 @@
|
||||||
"attribution": {
|
"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>",
|
"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",
|
"attributionTitle": "Danksagung",
|
||||||
"codeContributionsBy": "MapComplete wurde von {contributors} und <a href=\"https://github.com/pietervdvn/MapComplete/graphs/contributors\" target=\"_blank\">{hiddenCount} weiteren Beitragenden</a> erstellt",
|
"codeContributionsBy": "MapComplete wurde erstellt von {contributors} und <a href=\"https://github.com/pietervdvn/MapComplete/graphs/contributors\" target=\"_blank\">{hiddenCount} weiteren Beitragenden</a>",
|
||||||
"donate": "Unterstütze MapComplete finanziell",
|
"donate": "MapComplete finanziell unterstützen",
|
||||||
"editId": "Den OpenStreetMap online Editor hier öffnen",
|
"editId": "Den OpenStreetMap Editor öffnen",
|
||||||
"editJosm": "Bearbeite mit JOSM",
|
"editJosm": "Bearbeite mit JOSM",
|
||||||
"iconAttribution": {
|
"iconAttribution": {
|
||||||
"title": "Verwendete Symbole"
|
"title": "Verwendete Symbole"
|
||||||
|
@ -86,12 +86,12 @@
|
||||||
"josmNotOpened": "JOSM konnte nicht erreicht werden. Stelle sicher, dass es geöffnet ist und Fernkontrolle aktiviert ist",
|
"josmNotOpened": "JOSM konnte nicht erreicht werden. Stelle sicher, dass es geöffnet ist und Fernkontrolle aktiviert ist",
|
||||||
"josmOpened": "JOSM ist geöffnet",
|
"josmOpened": "JOSM ist geöffnet",
|
||||||
"mapContributionsBy": "Die aktuell sichtbaren Daten wurden editiert durch {contributors}",
|
"mapContributionsBy": "Die aktuell sichtbaren Daten wurden editiert durch {contributors}",
|
||||||
"mapContributionsByAndHidden": "Die aktuell sichtbaren Daten wurden editiert durch {contributors} und {hiddenCount} weitere Beitragende",
|
"mapContributionsByAndHidden": "Die aktuell sichtbaren Daten wurden editiert von {contributors} und {hiddenCount} weiteren Beitragenden",
|
||||||
"openIssueTracker": "Melde einen Fehler",
|
"openIssueTracker": "Einen Fehler melden",
|
||||||
"openMapillary": "Öffne Mapillary hier",
|
"openMapillary": "Mapillary öffnen",
|
||||||
"openOsmcha": "Letzte Bearbeitungen mit {theme} ansehen",
|
"openOsmcha": "Letzte Bearbeitungen mit {theme} ansehen",
|
||||||
"themeBy": "Thema betreut von {author}",
|
"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",
|
"back": "Zurück",
|
||||||
"backToMapcomplete": "Zurück zur Themenwahl",
|
"backToMapcomplete": "Zurück zur Themenwahl",
|
||||||
|
@ -153,7 +153,7 @@
|
||||||
"noTagsSelected": "Keine Tags ausgewählt",
|
"noTagsSelected": "Keine Tags ausgewählt",
|
||||||
"number": "Zahl",
|
"number": "Zahl",
|
||||||
"oneSkippedQuestion": "Eine Frage wurde übersprungen",
|
"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",
|
"openTheMap": "Karte öffnen",
|
||||||
"opening_hours": {
|
"opening_hours": {
|
||||||
"closed_permanently": "Geschlossen auf unbestimmte Zeit",
|
"closed_permanently": "Geschlossen auf unbestimmte Zeit",
|
||||||
|
@ -205,11 +205,11 @@
|
||||||
"editThisTheme": "Dieses Thema bearbeiten",
|
"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.",
|
"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",
|
"fsAddNew": "Schaltfläche 'neuen POI hinzufügen' aktivieren",
|
||||||
"fsGeolocation": "Die Schaltfläche 'Mich geolokalisieren' aktivieren (nur für Mobil)",
|
"fsGeolocation": "Schaltfläche 'Mich geolokalisieren' aktivieren (nur mobil)",
|
||||||
"fsIncludeCurrentBackgroundMap": "Den aktuellen Hintergrund übernehmen <b>{name}</b>",
|
"fsIncludeCurrentBackgroundMap": "Aktuellen Hintergrund übernehmen <b>{name}</b>",
|
||||||
"fsIncludeCurrentLayers": "Die aktuelle Ebenenauswahl übernehmen",
|
"fsIncludeCurrentLayers": "Aktuelle Ebenenauswahl übernehmen",
|
||||||
"fsIncludeCurrentLocation": "Aktuelle Position einbeziehen",
|
"fsIncludeCurrentLocation": "Aktuelle Position einbeziehen",
|
||||||
"fsLayerControlToggle": "Mit der ausgeklappter Ebenensteuerung beginnen",
|
"fsLayerControlToggle": "Ausgeklappte Ebenensteuerung anzeigen",
|
||||||
"fsLayers": "Ebenensteuerung aktivieren",
|
"fsLayers": "Ebenensteuerung aktivieren",
|
||||||
"fsSearch": "Suchleiste aktivieren",
|
"fsSearch": "Suchleiste aktivieren",
|
||||||
"fsUserbadge": "Anmeldeschaltfläche 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."
|
"warnAnonymous": "Sie sind nicht eingeloggt. Wir sind nicht in der Lage, Sie zu kontaktieren, um Ihr Problem zu lösen."
|
||||||
},
|
},
|
||||||
"privacy": {
|
"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",
|
"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",
|
"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.",
|
"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.",
|
"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",
|
"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!",
|
"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",
|
"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",
|
"trackingTitle": "Statistische Daten",
|
||||||
"whileYoureHere": "Ist Ihnen die Privatsphäre wichtig?"
|
"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"
|
"splitTitle": "Wählen Sie auf der Karte aus, wo die Straße geteilt werden soll"
|
||||||
},
|
},
|
||||||
"translations": {
|
"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",
|
"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",
|
"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.",
|
"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.",
|
||||||
|
|
117
langs/en.json
117
langs/en.json
|
@ -119,6 +119,7 @@
|
||||||
"title": "Download visible data",
|
"title": "Download visible data",
|
||||||
"uploadGpx": "Upload your track to OpenStreetMap"
|
"uploadGpx": "Upload your track to OpenStreetMap"
|
||||||
},
|
},
|
||||||
|
"error": "Something went wrong...",
|
||||||
"example": "Example",
|
"example": "Example",
|
||||||
"examples": "Examples",
|
"examples": "Examples",
|
||||||
"fewChangesBefore": "Please, answer a few questions of existing points before adding a new point.",
|
"fewChangesBefore": "Please, answer a few questions of existing points before adding a new point.",
|
||||||
|
@ -151,6 +152,7 @@
|
||||||
"next": "Next",
|
"next": "Next",
|
||||||
"noNameCategory": "{category} without a name",
|
"noNameCategory": "{category} without a name",
|
||||||
"noTagsSelected": "No tags selected",
|
"noTagsSelected": "No tags selected",
|
||||||
|
"notValid": "Select a valid value to continue",
|
||||||
"number": "number",
|
"number": "number",
|
||||||
"oneSkippedQuestion": "One question is skipped",
|
"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>",
|
"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 "
|
"willBePublished": "Your picture will be published "
|
||||||
},
|
},
|
||||||
"importHelper": {
|
"importHelper": {
|
||||||
"allAttributesSame": "All features to import have this tag",
|
"askMetadata": {
|
||||||
"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.",
|
"downloadGeojson": "Download geojson",
|
||||||
"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>",
|
"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)",
|
||||||
"inspectDataTitle": "Inspect data of {count} features to import",
|
"giveSource": "What is the source of this data? If 'source' is set in the feature, this value will be ignored",
|
||||||
"inspectDidAutoDected": "Layer was chosen automatically",
|
"giveWikilink": "On what wikipage can one find more information about this import?",
|
||||||
"inspectLooksCorrect": "These values look correct",
|
"intro": "Before adding {count} notes, please provide some extra information.",
|
||||||
"lockNotice": "This page is locked. You need {importHelperUnlock} changesets before you can access here.",
|
"orDownload": "Alternatively, you can download the dataset to import directly",
|
||||||
"locked": "You need at least {importHelperUnlock} to use the import helper",
|
"shouldBeOsmWikilink": "Expected a link to a page on wiki.openstreetmap.org",
|
||||||
"loggedInWith": "You are currently logged in as <b>{name}</b> and have made {csCount} changesets",
|
"shouldBeUrl": "Not a valid URL",
|
||||||
"loginIsCorrect": "<b>{name}</b> is the correct account to create the import notes with.",
|
"shouldNotBeHomepage": "Nope, the home page isn't allowed either. Enter the URL of a proper wikipage documenting your import",
|
||||||
"loginRequired": "You have to be logged in to continue",
|
"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",
|
||||||
|
"title": "Introduction"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"lockNotice": "This page is locked. You need {importHelperUnlock} changesets before you can access here.",
|
||||||
|
"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": {
|
"mapPreview": {
|
||||||
"autodetected": "The layer was automatically deducted based on the properties",
|
"autodetected": "The layer was automatically deducted based on the properties",
|
||||||
"confirm": "The features are on the right location on the map",
|
"confirm": "The features are on the right location on the map",
|
||||||
|
@ -294,6 +359,18 @@
|
||||||
"selectLayer": "Which layer does this import match with?",
|
"selectLayer": "Which layer does this import match with?",
|
||||||
"title": "Map preview"
|
"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": {
|
"selectFile": {
|
||||||
"description": "Select a .csv or .geojson file to get started",
|
"description": "Select a .csv or .geojson file to get started",
|
||||||
"errDuplicate": "Some columns have the same name",
|
"errDuplicate": "Some columns have the same name",
|
||||||
|
@ -308,11 +385,19 @@
|
||||||
"noFilesLoaded": "No file is currently loaded",
|
"noFilesLoaded": "No file is currently loaded",
|
||||||
"title": "Select file"
|
"title": "Select file"
|
||||||
},
|
},
|
||||||
"selectLayer": "Select a layer...",
|
"selectTheme": {
|
||||||
"someHaveSame": "{count} features to import have this tag, this is {percentage}% of the total",
|
"displayNonMatchingCount": "{length} objects dont match any presets",
|
||||||
"title": "Import helper",
|
"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",
|
||||||
"userAccountTitle": "Select user account",
|
"misMatch": "Property with key {k} does not have expected value {v}; instead it is {properties}",
|
||||||
"validateDataTitle": "Validate data"
|
"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": {
|
"importInspector": {
|
||||||
"title": "Inspect and manage import notes"
|
"title": "Inspect and manage import notes"
|
||||||
|
|
|
@ -40,7 +40,15 @@
|
||||||
"about": "Madaling i-edit at mag-dagdag sa OpenStreetMap gamit ang mga partikular na tikha",
|
"about": "Madaling i-edit at mag-dagdag sa OpenStreetMap gamit ang mga partikular na tikha",
|
||||||
"add": {
|
"add": {
|
||||||
"addNew": "Dagdagan ng {category}",
|
"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>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1141,7 +1141,7 @@
|
||||||
"playgrounds": {
|
"playgrounds": {
|
||||||
"description": "Auf dieser Karte finden Sie Spielplätze und können weitere Informationen hinzufügen",
|
"description": "Auf dieser Karte finden Sie Spielplätze und können weitere Informationen hinzufügen",
|
||||||
"shortDescription": "Eine Karte mit Spielplätzen",
|
"shortDescription": "Eine Karte mit Spielplätzen",
|
||||||
"title": "Spielpläzte"
|
"title": "Spielplätze"
|
||||||
},
|
},
|
||||||
"postal_codes": {
|
"postal_codes": {
|
||||||
"description": "Postleitzahlen",
|
"description": "Postleitzahlen",
|
||||||
|
@ -1326,4 +1326,4 @@
|
||||||
"shortDescription": "Eine Karte mit Abfalleimern",
|
"shortDescription": "Eine Karte mit Abfalleimern",
|
||||||
"title": "Abfalleimer"
|
"title": "Abfalleimer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue