Fix import flow, add typing
This commit is contained in:
parent
97334fc369
commit
4246221e8e
29 changed files with 396 additions and 309 deletions
|
@ -3,6 +3,7 @@ import {ImmutableStore, Store, UIEventSource} from "../../UIEventSource";
|
||||||
import {stat} from "fs";
|
import {stat} from "fs";
|
||||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
import FilteredLayer from "../../../Models/FilteredLayer";
|
||||||
import {BBox} from "../../BBox";
|
import {BBox} from "../../BBox";
|
||||||
|
import {Feature} from "@turf/turf";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple, read only feature store.
|
* A simple, read only feature store.
|
||||||
|
@ -11,7 +12,7 @@ export default class StaticFeatureSource implements FeatureSource {
|
||||||
public readonly features: Store<{ feature: any; freshness: Date }[]>;
|
public readonly features: Store<{ feature: any; freshness: Date }[]>;
|
||||||
public readonly name: string
|
public readonly name: string
|
||||||
|
|
||||||
constructor(features: Store<{ feature: any, freshness: Date }[]>, name = "StaticFeatureSource") {
|
constructor(features: Store<{ feature: Feature, freshness: Date }[]>, name = "StaticFeatureSource") {
|
||||||
if (features === undefined) {
|
if (features === undefined) {
|
||||||
throw "Static feature source received undefined as source"
|
throw "Static feature source received undefined as source"
|
||||||
}
|
}
|
||||||
|
@ -19,17 +20,23 @@ export default class StaticFeatureSource implements FeatureSource {
|
||||||
this.features = features;
|
this.features = features;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fromGeojsonAndDate(features: { feature: any, freshness: Date }[], name = "StaticFeatureSourceFromGeojsonAndDate"): StaticFeatureSource {
|
public static fromGeojsonAndDate(features: { feature: Feature, freshness: Date }[], name = "StaticFeatureSourceFromGeojsonAndDate"): StaticFeatureSource {
|
||||||
return new StaticFeatureSource(new ImmutableStore(features), name);
|
return new StaticFeatureSource(new ImmutableStore(features), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static fromGeojson(geojson: any[], name = "StaticFeatureSourceFromGeojson"): StaticFeatureSource {
|
public static fromGeojson(geojson: Feature[], name = "StaticFeatureSourceFromGeojson"): StaticFeatureSource {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return StaticFeatureSource.fromGeojsonAndDate(geojson.map(feature => ({feature, freshness: now})), name);
|
return StaticFeatureSource.fromGeojsonAndDate(geojson.map(feature => ({feature, freshness: now})), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromDateless(featureSource: Store<{ feature: any }[]>, name = "StaticFeatureSourceFromDateless") {
|
public static fromGeojsonStore(geojson: Store<Feature[]>, name = "StaticFeatureSourceFromGeojson"): StaticFeatureSource {
|
||||||
|
const now = new Date();
|
||||||
|
const mapped : Store<{feature: Feature, freshness: Date}[]> = geojson.map(features => features.map(feature => ({feature, freshness: now})))
|
||||||
|
return new StaticFeatureSource(mapped, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromDateless(featureSource: Store<{ feature: Feature }[]>, name = "StaticFeatureSourceFromDateless") {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return new StaticFeatureSource(featureSource.map(features => features.map(feature => ({
|
return new StaticFeatureSource(featureSource.map(features => features.map(feature => ({
|
||||||
feature: feature.feature,
|
feature: feature.feature,
|
||||||
|
|
|
@ -11,6 +11,7 @@ import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig";
|
||||||
import {Or} from "../../Tags/Or";
|
import {Or} from "../../Tags/Or";
|
||||||
import {TagsFilter} from "../../Tags/TagsFilter";
|
import {TagsFilter} from "../../Tags/TagsFilter";
|
||||||
import {OsmObject} from "../../Osm/OsmObject";
|
import {OsmObject} from "../../Osm/OsmObject";
|
||||||
|
import {FeatureCollection} from "@turf/turf";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a tile is needed (requested via the UIEventSource in the constructor), will download the appropriate tile and pass it via 'handleTile'
|
* If a tile is needed (requested via the UIEventSource in the constructor), will download the appropriate tile and pass it via 'handleTile'
|
||||||
|
@ -136,7 +137,7 @@ export default class OsmFeatureSource {
|
||||||
|
|
||||||
console.log("Got tile", z, x, y, "from the osm api")
|
console.log("Got tile", z, x, y, "from the osm api")
|
||||||
this.rawDataHandlers.forEach(handler => handler(osmJson, Tiles.tile_index(z, x, y)))
|
this.rawDataHandlers.forEach(handler => handler(osmJson, Tiles.tile_index(z, x, y)))
|
||||||
const geojson = OsmToGeoJson.default(osmJson,
|
const geojson = <FeatureCollection<any , {id: string}>> OsmToGeoJson.default(osmJson,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
{
|
{
|
||||||
flatProperties: true
|
flatProperties: true
|
||||||
|
|
|
@ -11,7 +11,7 @@ import ChangeTagAction from "./ChangeTagAction";
|
||||||
import {And} from "../../Tags/And";
|
import {And} from "../../Tags/And";
|
||||||
import {Utils} from "../../../Utils";
|
import {Utils} from "../../../Utils";
|
||||||
import {OsmConnection} from "../OsmConnection";
|
import {OsmConnection} from "../OsmConnection";
|
||||||
import {GeoJSONObject} from "@turf/turf";
|
import {Feature} from "@turf/turf";
|
||||||
import FeaturePipeline from "../../FeatureSource/FeaturePipeline";
|
import FeaturePipeline from "../../FeatureSource/FeaturePipeline";
|
||||||
|
|
||||||
export default class ReplaceGeometryAction extends OsmChangeAction {
|
export default class ReplaceGeometryAction extends OsmChangeAction {
|
||||||
|
@ -83,7 +83,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction {
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols
|
||||||
public async getPreview(): Promise<FeatureSource> {
|
public async getPreview(): Promise<FeatureSource> {
|
||||||
const {closestIds, allNodesById, detachedNodes, reprojectedNodes} = await this.GetClosestIds();
|
const {closestIds, allNodesById, detachedNodes, reprojectedNodes} = await this.GetClosestIds();
|
||||||
const preview: GeoJSONObject[] = closestIds.map((newId, i) => {
|
const preview: Feature[] = closestIds.map((newId, i) => {
|
||||||
if (this.identicalTo[i] !== undefined) {
|
if (this.identicalTo[i] !== undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction {
|
||||||
reprojectedNodes.forEach(({newLat, newLon, nodeId}) => {
|
reprojectedNodes.forEach(({newLat, newLon, nodeId}) => {
|
||||||
|
|
||||||
const origNode = allNodesById.get(nodeId);
|
const origNode = allNodesById.get(nodeId);
|
||||||
const feature = {
|
const feature : Feature = {
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
properties: {
|
properties: {
|
||||||
"move": "yes",
|
"move": "yes",
|
||||||
|
@ -142,7 +142,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction {
|
||||||
|
|
||||||
detachedNodes.forEach(({reason}, id) => {
|
detachedNodes.forEach(({reason}, id) => {
|
||||||
const origNode = allNodesById.get(id);
|
const origNode = allNodesById.get(id);
|
||||||
const feature = {
|
const feature : Feature = {
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
properties: {
|
properties: {
|
||||||
"detach": "yes",
|
"detach": "yes",
|
||||||
|
|
|
@ -35,7 +35,7 @@ export class Overpass {
|
||||||
this._relationTracker = relationTracker
|
this._relationTracker = relationTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
public async queryGeoJson(bounds: BBox, ): Promise<[FeatureCollection, Date]> {
|
public async queryGeoJson(bounds: BBox): Promise<[FeatureCollection, Date]> {
|
||||||
const bbox = "[bbox:" + bounds.getSouth() + "," + bounds.getWest() + "," + bounds.getNorth() + "," + bounds.getEast() + "]";
|
const bbox = "[bbox:" + bounds.getSouth() + "," + bounds.getWest() + "," + bounds.getNorth() + "," + bounds.getEast() + "]";
|
||||||
const query = this.buildScript(bbox)
|
const query = this.buildScript(bbox)
|
||||||
return this.ExecuteQuery(query);
|
return this.ExecuteQuery(query);
|
||||||
|
|
|
@ -7,8 +7,12 @@ import {Utils} from "../../Utils";
|
||||||
*/
|
*/
|
||||||
export class IdbLocalStorage {
|
export class IdbLocalStorage {
|
||||||
|
|
||||||
|
private static readonly _sourceCache: Record<string, UIEventSource<any>> = {}
|
||||||
|
|
||||||
public static Get<T>(key: string, options?: { defaultValue?: T, whenLoaded?: (t: T | null) => void }): UIEventSource<T> {
|
public static Get<T>(key: string, options?: { defaultValue?: T, whenLoaded?: (t: T | null) => void }): UIEventSource<T> {
|
||||||
|
if(IdbLocalStorage._sourceCache[key] !== undefined){
|
||||||
|
return IdbLocalStorage._sourceCache[key]
|
||||||
|
}
|
||||||
const src = new UIEventSource<T>(options?.defaultValue, "idb-local-storage:" + key)
|
const src = new UIEventSource<T>(options?.defaultValue, "idb-local-storage:" + key)
|
||||||
if (Utils.runningFromConsole) {
|
if (Utils.runningFromConsole) {
|
||||||
return src;
|
return src;
|
||||||
|
@ -26,6 +30,7 @@ export class IdbLocalStorage {
|
||||||
options?.whenLoaded(null)
|
options?.whenLoaded(null)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
IdbLocalStorage._sourceCache[key] = src;
|
||||||
return src;
|
return src;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ export class DoesImageExist extends DesugaringStep<string> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._knownImagePaths !== undefined && !this._knownImagePaths.has(image)) {
|
if (!this._knownImagePaths.has(image)) {
|
||||||
if (this.doesPathExist === undefined) {
|
if (this.doesPathExist === undefined) {
|
||||||
errors.push(`Image with path ${image} not found or not attributed; it is used in ${context}`)
|
errors.push(`Image with path ${image} not found or not attributed; it is used in ${context}`)
|
||||||
} else if (!this.doesPathExist(image)) {
|
} else if (!this.doesPathExist(image)) {
|
||||||
|
|
|
@ -28,7 +28,10 @@ export default class ScrollableFullScreen extends UIElement {
|
||||||
constructor(title: ((options: { mode: string }) => BaseUIElement),
|
constructor(title: ((options: { mode: string }) => BaseUIElement),
|
||||||
content: ((options: { mode: string, resetScrollSignal: UIEventSource<void> }) => BaseUIElement),
|
content: ((options: { mode: string, resetScrollSignal: UIEventSource<void> }) => BaseUIElement),
|
||||||
hashToShow: string,
|
hashToShow: string,
|
||||||
isShown: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
isShown: UIEventSource<boolean> = new UIEventSource<boolean>(false),
|
||||||
|
options?: {
|
||||||
|
setHash?: true | boolean
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.hashToShow = hashToShow;
|
this.hashToShow = hashToShow;
|
||||||
|
@ -53,16 +56,21 @@ export default class ScrollableFullScreen extends UIElement {
|
||||||
|
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
const setHash = options?.setHash ?? true;
|
||||||
|
if(setHash){
|
||||||
Hash.hash.addCallback(h => {
|
Hash.hash.addCallback(h => {
|
||||||
if (h === undefined) {
|
if (h === undefined) {
|
||||||
isShown.setData(false)
|
isShown.setData(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
isShown.addCallback(isShown => {
|
isShown.addCallback(isShown => {
|
||||||
if (isShown) {
|
if (isShown) {
|
||||||
// We first must set the hash, then activate the panel
|
// We first must set the hash, then activate the panel
|
||||||
// If the order is wrong, this will cause the panel to disactivate again
|
// If the order is wrong, this will cause the panel to disactivate again
|
||||||
|
if(setHash){
|
||||||
Hash.hash.setData(hashToShow)
|
Hash.hash.setData(hashToShow)
|
||||||
|
}
|
||||||
self.Activate();
|
self.Activate();
|
||||||
} else {
|
} else {
|
||||||
// Some cleanup...
|
// Some cleanup...
|
||||||
|
|
|
@ -159,6 +159,12 @@ class SingleLayerSelectionButton extends Toggle {
|
||||||
|
|
||||||
export default class BackgroundMapSwitch extends Combine {
|
export default class BackgroundMapSwitch extends Combine {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Three buttons to easily switch map layers between OSM, aerial and some map.
|
||||||
|
* @param state
|
||||||
|
* @param currentBackground
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
state: {
|
state: {
|
||||||
locationControl: UIEventSource<Loc>,
|
locationControl: UIEventSource<Loc>,
|
||||||
|
|
|
@ -48,7 +48,10 @@ export default class LeftControls extends Combine {
|
||||||
}
|
}
|
||||||
return new Lazy(() => {
|
return new Lazy(() => {
|
||||||
const tagsSource = state.allElements.getEventSourceById(feature.properties.id)
|
const tagsSource = state.allElements.getEventSourceById(feature.properties.id)
|
||||||
return new FeatureInfoBox(tagsSource, currentViewFL.layerDef, state, "currentview", guiState.currentViewControlIsOpened)
|
return new FeatureInfoBox(tagsSource, currentViewFL.layerDef, state, {
|
||||||
|
hashToShow: "currentview",
|
||||||
|
isShown: guiState.currentViewControlIsOpened
|
||||||
|
})
|
||||||
.SetClass("md:floating-element-width")
|
.SetClass("md:floating-element-width")
|
||||||
})
|
})
|
||||||
})).SetStyle("width: 40rem").SetClass("block")
|
})).SetStyle("width: 40rem").SetClass("block")
|
||||||
|
|
|
@ -22,12 +22,17 @@ import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
|
||||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource";
|
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource";
|
||||||
import ValidatedTextField from "../Input/ValidatedTextField";
|
import ValidatedTextField from "../Input/ValidatedTextField";
|
||||||
import {LocalStorageSource} from "../../Logic/Web/LocalStorageSource";
|
import {LocalStorageSource} from "../../Logic/Web/LocalStorageSource";
|
||||||
import * as currentview from "../../assets/layers/current_view/current_view.json"
|
|
||||||
import * as import_candidate from "../../assets/layers/import_candidate/import_candidate.json"
|
import * as import_candidate from "../../assets/layers/import_candidate/import_candidate.json"
|
||||||
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";
|
import Translations from "../i18n/Translations";
|
||||||
|
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer";
|
||||||
|
import FilteredLayer, {FilterState} from "../../Models/FilteredLayer";
|
||||||
|
import {Feature, FeatureCollection} from "@turf/turf";
|
||||||
|
import * as currentview from "../../assets/layers/current_view/current_view.json"
|
||||||
|
import {CheckBox} from "../Input/Checkboxes";
|
||||||
|
import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
@ -41,15 +46,16 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
state,
|
state,
|
||||||
params: { bbox: BBox, layer: LayerConfig, theme: string, features: any[] }) {
|
params: { bbox: BBox, layer: LayerConfig, theme: string, features: any[] }) {
|
||||||
|
|
||||||
|
const t = Translations.t.importHelper.conflationChecker
|
||||||
|
|
||||||
const bbox = params.bbox.padAbsolute(0.0001)
|
const bbox = params.bbox.padAbsolute(0.0001)
|
||||||
const layer = params.layer;
|
const layer = params.layer;
|
||||||
const toImport: {features: any[]} = params;
|
|
||||||
|
const toImport: { features: any[] } = params;
|
||||||
let overpassStatus = new UIEventSource<{ error: string } | "running" | "success" | "idle" | "cached">("idle")
|
let overpassStatus = new UIEventSource<{ error: string } | "running" | "success" | "idle" | "cached">("idle")
|
||||||
const cacheAge = new UIEventSource<number>(undefined);
|
|
||||||
|
|
||||||
|
|
||||||
function loadDataFromOverpass(){
|
function loadDataFromOverpass() {
|
||||||
// Load the data!
|
// Load the data!
|
||||||
const url = Constants.defaultOverpassUrls[1]
|
const url = Constants.defaultOverpassUrls[1]
|
||||||
const relationTracker = new RelationsTracker()
|
const relationTracker = new RelationsTracker()
|
||||||
|
@ -73,23 +79,29 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
whenLoaded: (v) => {
|
whenLoaded: (v) => {
|
||||||
if (v !== undefined && v !== null) {
|
if (v !== undefined && v !== null) {
|
||||||
console.log("Loaded from local storage:", v)
|
console.log("Loaded from local storage:", v)
|
||||||
const [geojson, date] = v;
|
overpassStatus.setData("cached")
|
||||||
const timeDiff = (new Date().getTime() - date.getTime()) / 1000;
|
}
|
||||||
console.log("Loaded ", geojson.features.length, " features; cache is ", timeDiff, "seconds old")
|
}
|
||||||
cacheAge.setData(timeDiff)
|
});
|
||||||
|
|
||||||
|
const cacheAge = fromLocalStorage.map(d => {
|
||||||
|
if(d === undefined || d[1] === undefined){
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const [_, loadedDate] = d
|
||||||
|
return (new Date().getTime() - loadedDate.getTime()) / 1000;
|
||||||
|
})
|
||||||
|
cacheAge.addCallbackD(timeDiff => {
|
||||||
if (timeDiff < 24 * 60 * 60) {
|
if (timeDiff < 24 * 60 * 60) {
|
||||||
// Recently cached!
|
// Recently cached!
|
||||||
overpassStatus.setData("cached")
|
overpassStatus.setData("cached")
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
cacheAge.setData(-1)
|
|
||||||
}
|
|
||||||
loadDataFromOverpass()
|
loadDataFromOverpass()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
|
const geojson: Store<FeatureCollection> = fromLocalStorage.map(d => {
|
||||||
const geojson: Store<any> = fromLocalStorage.map(d => {
|
|
||||||
if (d === undefined) {
|
if (d === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -99,9 +111,10 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
const background = new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
|
const background = new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
|
||||||
const location = new UIEventSource<Loc>({lat: 0, lon: 0, zoom: 1})
|
const location = new UIEventSource<Loc>({lat: 0, lon: 0, zoom: 1})
|
||||||
const currentBounds = new UIEventSource<BBox>(undefined)
|
const currentBounds = new UIEventSource<BBox>(undefined)
|
||||||
const zoomLevel = ValidatedTextField.ForType("pnat").ConstructInputElement()
|
const zoomLevel = ValidatedTextField.ForType("pnat").ConstructInputElement({
|
||||||
|
value: LocalStorageSource.GetParsed<string>("importer-zoom-level", "0")
|
||||||
|
})
|
||||||
zoomLevel.SetClass("ml-1 border border-black")
|
zoomLevel.SetClass("ml-1 border border-black")
|
||||||
zoomLevel.GetValue().syncWith(LocalStorageSource.Get("importer-zoom-level", "14"), true)
|
|
||||||
const osmLiveData = Minimap.createMiniMap({
|
const osmLiveData = Minimap.createMiniMap({
|
||||||
allowMoving: true,
|
allowMoving: true,
|
||||||
location,
|
location,
|
||||||
|
@ -110,18 +123,24 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
attribution: new Attribution(location, state.osmConnection.userDetails, undefined, currentBounds)
|
attribution: new Attribution(location, state.osmConnection.userDetails, undefined, currentBounds)
|
||||||
})
|
})
|
||||||
osmLiveData.SetClass("w-full").SetStyle("height: 500px")
|
osmLiveData.SetClass("w-full").SetStyle("height: 500px")
|
||||||
const preview = new StaticFeatureSource(geojson.map(geojson => {
|
|
||||||
|
const geojsonFeatures : Store<Feature[]> = geojson.map(geojson => {
|
||||||
if (geojson?.features === undefined) {
|
if (geojson?.features === undefined) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
const zoomedEnough: boolean = osmLiveData.location.data.zoom >= Number(zoomLevel.GetValue().data)
|
const currentZoom = zoomLevel.GetValue().data
|
||||||
if (!zoomedEnough) {
|
const zoomedEnough: boolean = osmLiveData.location.data.zoom >= Number(currentZoom)
|
||||||
|
if (currentZoom !== undefined && !zoomedEnough) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
const bounds = osmLiveData.bounds.data
|
const bounds = osmLiveData.bounds.data
|
||||||
|
if(bounds === undefined){
|
||||||
|
return geojson.features;
|
||||||
|
}
|
||||||
return geojson.features.filter(f => BBox.get(f).overlapsWith(bounds))
|
return geojson.features.filter(f => BBox.get(f).overlapsWith(bounds))
|
||||||
}, [osmLiveData.bounds, zoomLevel.GetValue()]));
|
}, [osmLiveData.bounds, zoomLevel.GetValue()])
|
||||||
|
|
||||||
|
const preview = StaticFeatureSource.fromGeojsonStore(geojsonFeatures)
|
||||||
|
|
||||||
new ShowDataLayer({
|
new ShowDataLayer({
|
||||||
layerToShow: new LayerConfig(currentview),
|
layerToShow: new LayerConfig(currentview),
|
||||||
|
@ -134,12 +153,16 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
new ShowDataMultiLayer({
|
||||||
new ShowDataLayer({
|
//layerToShow: layer,
|
||||||
layerToShow: layer,
|
layers: new UIEventSource<FilteredLayer[]>([{
|
||||||
|
layerDef: layer,
|
||||||
|
isDisplayed: new UIEventSource<boolean>(true),
|
||||||
|
appliedFilters: new UIEventSource<Map<string, FilterState>>(undefined)
|
||||||
|
}]),
|
||||||
state,
|
state,
|
||||||
leafletMap: osmLiveData.leafletMap,
|
leafletMap: osmLiveData.leafletMap,
|
||||||
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),
|
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state, {setHash: false}),
|
||||||
zoomToFeatures: false,
|
zoomToFeatures: false,
|
||||||
features: preview
|
features: preview
|
||||||
})
|
})
|
||||||
|
@ -148,7 +171,7 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
layerToShow: new LayerConfig(import_candidate),
|
layerToShow: new LayerConfig(import_candidate),
|
||||||
state,
|
state,
|
||||||
leafletMap: osmLiveData.leafletMap,
|
leafletMap: osmLiveData.leafletMap,
|
||||||
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),
|
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state, {setHash: false}),
|
||||||
zoomToFeatures: false,
|
zoomToFeatures: false,
|
||||||
features: StaticFeatureSource.fromGeojson(toImport.features)
|
features: StaticFeatureSource.fromGeojson(toImport.features)
|
||||||
})
|
})
|
||||||
|
@ -164,7 +187,7 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
matchedFeaturesMap.SetClass("w-full").SetStyle("height: 500px")
|
matchedFeaturesMap.SetClass("w-full").SetStyle("height: 500px")
|
||||||
|
|
||||||
// Featuresource showing OSM-features which are nearby a toImport-feature
|
// Featuresource showing OSM-features which are nearby a toImport-feature
|
||||||
const nearbyFeatures = new StaticFeatureSource(geojson.map(osmData => {
|
const geojsonMapped: Store<Feature[]> = geojson.map(osmData => {
|
||||||
if (osmData?.features === undefined) {
|
if (osmData?.features === undefined) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -172,31 +195,36 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
return osmData.features.filter(f =>
|
return osmData.features.filter(f =>
|
||||||
toImport.features.some(imp =>
|
toImport.features.some(imp =>
|
||||||
maxDist >= GeoOperations.distanceBetween(imp.geometry.coordinates, GeoOperations.centerpointCoordinates(f))))
|
maxDist >= GeoOperations.distanceBetween(imp.geometry.coordinates, GeoOperations.centerpointCoordinates(f))))
|
||||||
}, [nearbyCutoff.GetValue().stabilized(500)]));
|
}, [nearbyCutoff.GetValue().stabilized(500)])
|
||||||
|
const nearbyFeatures = StaticFeatureSource.fromGeojsonStore(geojsonMapped);
|
||||||
const paritionedImport = ImportUtils.partitionFeaturesIfNearby(toImport, geojson, nearbyCutoff.GetValue().map(Number));
|
const paritionedImport = ImportUtils.partitionFeaturesIfNearby(toImport, geojson, nearbyCutoff.GetValue().map(Number));
|
||||||
|
|
||||||
// Featuresource showing OSM-features which are nearby a toImport-feature
|
// Featuresource showing OSM-features which are nearby a toImport-feature
|
||||||
const toImportWithNearby = new StaticFeatureSource(paritionedImport.map(els => els?.hasNearby ?? []));
|
const toImportWithNearby = StaticFeatureSource.fromGeojsonStore(paritionedImport.map(els => els?.hasNearby ?? []));
|
||||||
|
toImportWithNearby.features.addCallback(nearby => console.log("The following features are near an already existing object:", nearby))
|
||||||
new ShowDataLayer({
|
|
||||||
layerToShow: layer,
|
|
||||||
state,
|
|
||||||
leafletMap: matchedFeaturesMap.leafletMap,
|
|
||||||
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),
|
|
||||||
zoomToFeatures: true,
|
|
||||||
features: nearbyFeatures
|
|
||||||
})
|
|
||||||
|
|
||||||
new ShowDataLayer({
|
new ShowDataLayer({
|
||||||
layerToShow: new LayerConfig(import_candidate),
|
layerToShow: new LayerConfig(import_candidate),
|
||||||
state,
|
state,
|
||||||
leafletMap: matchedFeaturesMap.leafletMap,
|
leafletMap: matchedFeaturesMap.leafletMap,
|
||||||
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state),
|
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state, {setHash: false}),
|
||||||
zoomToFeatures: false,
|
zoomToFeatures: false,
|
||||||
features: toImportWithNearby
|
features: toImportWithNearby
|
||||||
})
|
})
|
||||||
|
const showOsmLayer = new CheckBox(t.showOsmLayerInConflationMap, true)
|
||||||
|
new ShowDataLayer({
|
||||||
|
layerToShow: layer,
|
||||||
|
state,
|
||||||
|
leafletMap: matchedFeaturesMap.leafletMap,
|
||||||
|
popup: (tags, layer) => new FeatureInfoBox(tags, layer, state, {setHash: false}),
|
||||||
|
zoomToFeatures: true,
|
||||||
|
features: nearbyFeatures,
|
||||||
|
doShowLayer: showOsmLayer.GetValue()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const t = Translations.t.importHelper.conflationChecker
|
|
||||||
|
|
||||||
const conflationMaps = new Combine([
|
const conflationMaps = new Combine([
|
||||||
new VariableUiElement(
|
new VariableUiElement(
|
||||||
|
@ -225,9 +253,9 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
})),
|
})),
|
||||||
|
|
||||||
new Title(t.titleLive),
|
new Title(t.titleLive),
|
||||||
t.importCandidatesCount.Subs({count:toImport.features.length }),
|
t.importCandidatesCount.Subs({count: toImport.features.length}),
|
||||||
new VariableUiElement(geojson.map(geojson => {
|
new VariableUiElement(geojson.map(geojson => {
|
||||||
if(geojson?.features?.length === undefined || geojson?.features?.length === 0){
|
if (geojson?.features?.length === undefined || geojson?.features?.length === 0) {
|
||||||
return t.nothingLoaded.Subs(layer).SetClass("alert")
|
return t.nothingLoaded.Subs(layer).SetClass("alert")
|
||||||
}
|
}
|
||||||
return new Combine([
|
return new Combine([
|
||||||
|
@ -236,16 +264,26 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
])
|
])
|
||||||
})),
|
})),
|
||||||
osmLiveData,
|
osmLiveData,
|
||||||
|
new Combine([
|
||||||
|
t.zoomLevelSelection,
|
||||||
|
zoomLevel,
|
||||||
new VariableUiElement(osmLiveData.location.map(location => {
|
new VariableUiElement(osmLiveData.location.map(location => {
|
||||||
return t.zoomIn.Subs({needed:zoomLevel, current: location.zoom })
|
return t.zoomIn.Subs(<any>{current: location.zoom})
|
||||||
} )),
|
})),
|
||||||
|
]).SetClass("flex"),
|
||||||
new Title(t.titleNearby),
|
new Title(t.titleNearby),
|
||||||
new Combine([t.mapShowingNearbyIntro, nearbyCutoff]).SetClass("flex"),
|
new Combine([t.mapShowingNearbyIntro, nearbyCutoff]).SetClass("flex"),
|
||||||
new VariableUiElement(toImportWithNearby.features.map(feats =>
|
new VariableUiElement(toImportWithNearby.features.map(feats =>
|
||||||
t.nearbyWarn.Subs({count: feats.length}).SetClass("alert"))),
|
t.nearbyWarn.Subs({count: feats.length}).SetClass("alert"))),
|
||||||
t.setRangeToZero,
|
t.setRangeToZero,
|
||||||
matchedFeaturesMap]).SetClass("flex flex-col")
|
matchedFeaturesMap,
|
||||||
|
new Combine([
|
||||||
|
new BackgroundMapSwitch({backgroundLayer: background, locationControl: matchedFeaturesMap.location}, background),
|
||||||
|
showOsmLayer,
|
||||||
|
|
||||||
|
]).SetClass("flex")
|
||||||
|
|
||||||
|
]).SetClass("flex flex-col")
|
||||||
super([
|
super([
|
||||||
new Title(t.title),
|
new Title(t.title),
|
||||||
new VariableUiElement(overpassStatus.map(d => {
|
new VariableUiElement(overpassStatus.map(d => {
|
||||||
|
@ -270,7 +308,11 @@ export default class ConflationChecker extends Combine implements FlowStep<{ fea
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
this.Value = paritionedImport.map(feats => ({theme: params.theme, features: feats?.noNearby, layer: params.layer}))
|
this.Value = paritionedImport.map(feats => ({
|
||||||
|
theme: params.theme,
|
||||||
|
features: feats?.noNearby,
|
||||||
|
layer: params.layer
|
||||||
|
}))
|
||||||
this.IsValid = this.Value.map(v => v?.features !== undefined && v.features.length > 0)
|
this.IsValid = this.Value.map(v => v?.features !== undefined && v.features.length > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
import {Store} from "../../Logic/UIEventSource";
|
import {Store} from "../../Logic/UIEventSource";
|
||||||
import {GeoOperations} from "../../Logic/GeoOperations";
|
import {GeoOperations} from "../../Logic/GeoOperations";
|
||||||
|
import {Feature, Geometry} from "@turf/turf";
|
||||||
|
|
||||||
export class ImportUtils {
|
export class ImportUtils {
|
||||||
public static partitionFeaturesIfNearby(toPartitionFeatureCollection: ({ features: any[] }), compareWith: Store<{ features: any[] }>, cutoffDistanceInMeters: Store<number>): Store<{ hasNearby: any[], noNearby: any[] }> {
|
public static partitionFeaturesIfNearby(
|
||||||
|
toPartitionFeatureCollection: ({ features: Feature<Geometry>[] }),
|
||||||
|
compareWith: Store<{ features: Feature[] }>,
|
||||||
|
cutoffDistanceInMeters: Store<number>)
|
||||||
|
: Store<{ hasNearby: Feature[], noNearby: Feature[] }> {
|
||||||
return compareWith.map(osmData => {
|
return compareWith.map(osmData => {
|
||||||
if (osmData?.features === undefined) {
|
if (osmData?.features === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
|
@ -16,7 +21,7 @@ export class ImportUtils {
|
||||||
const noNearby = []
|
const noNearby = []
|
||||||
for (const toImportElement of toPartitionFeatureCollection.features) {
|
for (const toImportElement of toPartitionFeatureCollection.features) {
|
||||||
const hasNearbyFeature = osmData.features.some(f =>
|
const hasNearbyFeature = osmData.features.some(f =>
|
||||||
maxDist >= GeoOperations.distanceBetween(toImportElement.geometry.coordinates, GeoOperations.centerpointCoordinates(f)))
|
maxDist >= GeoOperations.distanceBetween(<any> toImportElement.geometry.coordinates, GeoOperations.centerpointCoordinates(f)))
|
||||||
if (hasNearbyFeature) {
|
if (hasNearbyFeature) {
|
||||||
hasNearby.push(toImportElement)
|
hasNearby.push(toImportElement)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import ScrollableFullScreen from "../Base/ScrollableFullScreen";
|
||||||
import Title from "../Base/Title";
|
import Title from "../Base/Title";
|
||||||
import CheckBoxes from "../Input/Checkboxes";
|
import CheckBoxes from "../Input/Checkboxes";
|
||||||
import {AllTagsPanel} from "../AllTagsPanel";
|
import {AllTagsPanel} from "../AllTagsPanel";
|
||||||
|
import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch";
|
||||||
|
|
||||||
class PreviewPanel extends ScrollableFullScreen {
|
class PreviewPanel extends ScrollableFullScreen {
|
||||||
|
|
||||||
|
@ -109,6 +110,10 @@ export class MapPreview extends Combine implements FlowStep<{ bbox: BBox, layer:
|
||||||
bounds: currentBounds,
|
bounds: currentBounds,
|
||||||
attribution: new Attribution(location, state.osmConnection.userDetails, undefined, currentBounds)
|
attribution: new Attribution(location, state.osmConnection.userDetails, undefined, currentBounds)
|
||||||
})
|
})
|
||||||
|
const layerControl = new BackgroundMapSwitch( {
|
||||||
|
backgroundLayer: background,
|
||||||
|
locationControl: location
|
||||||
|
},background)
|
||||||
map.SetClass("w-full").SetStyle("height: 500px")
|
map.SetClass("w-full").SetStyle("height: 500px")
|
||||||
|
|
||||||
new ShowDataMultiLayer({
|
new ShowDataMultiLayer({
|
||||||
|
@ -147,6 +152,7 @@ export class MapPreview extends Combine implements FlowStep<{ bbox: BBox, layer:
|
||||||
|
|
||||||
mismatchIndicator,
|
mismatchIndicator,
|
||||||
map,
|
map,
|
||||||
|
layerControl,
|
||||||
confirm
|
confirm
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ export class CheckBox extends InputElementMap<number[], boolean> {
|
||||||
new CheckBoxes([el]),
|
new CheckBoxes([el]),
|
||||||
(x0, x1) => x0 === x1,
|
(x0, x1) => x0 === x1,
|
||||||
t => t.length > 0,
|
t => t.length > 0,
|
||||||
x => x ? [0] : []
|
x => x ? [0] : [],
|
||||||
);
|
);
|
||||||
if(defaultValue !== undefined){
|
if(defaultValue !== undefined){
|
||||||
this.GetValue().setData(defaultValue)
|
this.GetValue().setData(defaultValue)
|
||||||
|
|
|
@ -25,16 +25,20 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||||
tags: UIEventSource<any>,
|
tags: UIEventSource<any>,
|
||||||
layerConfig: LayerConfig,
|
layerConfig: LayerConfig,
|
||||||
state: FeaturePipelineState,
|
state: FeaturePipelineState,
|
||||||
|
options?: {
|
||||||
hashToShow?: string,
|
hashToShow?: string,
|
||||||
isShown?: UIEventSource<boolean>,
|
isShown?: UIEventSource<boolean>,
|
||||||
|
setHash?: true | boolean
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
if (state === undefined) {
|
if (state === undefined) {
|
||||||
throw "State is undefined!"
|
throw "State is undefined!"
|
||||||
}
|
}
|
||||||
super(() => FeatureInfoBox.GenerateTitleBar(tags, layerConfig, state),
|
super(() => FeatureInfoBox.GenerateTitleBar(tags, layerConfig, state),
|
||||||
() => FeatureInfoBox.GenerateContent(tags, layerConfig, state),
|
() => FeatureInfoBox.GenerateContent(tags, layerConfig, state),
|
||||||
hashToShow ?? tags.data.id ?? "item",
|
options?.hashToShow ?? tags.data.id ?? "item",
|
||||||
isShown);
|
options?.isShown,
|
||||||
|
options);
|
||||||
|
|
||||||
if (layerConfig === undefined) {
|
if (layerConfig === undefined) {
|
||||||
throw "Undefined layerconfig";
|
throw "Undefined layerconfig";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
import {Store, UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||||
import {ShowDataLayerOptions} from "./ShowDataLayerOptions";
|
import {ShowDataLayerOptions} from "./ShowDataLayerOptions";
|
||||||
import {ElementStorage} from "../../Logic/ElementStorage";
|
import {ElementStorage} from "../../Logic/ElementStorage";
|
||||||
|
@ -20,7 +20,7 @@ We don't actually import it here. It is imported in the 'MinimapImplementation'-
|
||||||
export default class ShowDataLayerImplementation {
|
export default class ShowDataLayerImplementation {
|
||||||
|
|
||||||
private static dataLayerIds = 0
|
private static dataLayerIds = 0
|
||||||
private readonly _leafletMap: UIEventSource<L.Map>;
|
private readonly _leafletMap: Store<L.Map>;
|
||||||
private readonly _enablePopups: boolean;
|
private readonly _enablePopups: boolean;
|
||||||
private readonly _features: RenderingMultiPlexerFeatureSource
|
private readonly _features: RenderingMultiPlexerFeatureSource
|
||||||
private readonly _layerToShow: LayerConfig;
|
private readonly _layerToShow: LayerConfig;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import ScrollableFullScreen from "../Base/ScrollableFullScreen";
|
||||||
export interface ShowDataLayerOptions {
|
export interface ShowDataLayerOptions {
|
||||||
features: FeatureSource,
|
features: FeatureSource,
|
||||||
selectedElement?: UIEventSource<any>,
|
selectedElement?: UIEventSource<any>,
|
||||||
leafletMap: UIEventSource<L.Map>,
|
leafletMap: Store<L.Map>,
|
||||||
popup?: undefined | ((tags: UIEventSource<any>, layer: LayerConfig) => ScrollableFullScreen),
|
popup?: undefined | ((tags: UIEventSource<any>, layer: LayerConfig) => ScrollableFullScreen),
|
||||||
zoomToFeatures?: false | boolean,
|
zoomToFeatures?: false | boolean,
|
||||||
doShowLayer?: Store<boolean>,
|
doShowLayer?: Store<boolean>,
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
/**
|
/**
|
||||||
* SHows geojson on the given leaflet map, but attempts to figure out the correct layer first
|
* SHows geojson on the given leaflet map, but attempts to figure out the correct layer first
|
||||||
*/
|
*/
|
||||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
import {Store} from "../../Logic/UIEventSource";
|
||||||
import ShowDataLayer from "./ShowDataLayer";
|
import ShowDataLayer from "./ShowDataLayer";
|
||||||
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter";
|
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter";
|
||||||
import FilteredLayer from "../../Models/FilteredLayer";
|
import FilteredLayer from "../../Models/FilteredLayer";
|
||||||
import {ShowDataLayerOptions} from "./ShowDataLayerOptions";
|
import {ShowDataLayerOptions} from "./ShowDataLayerOptions";
|
||||||
|
|
||||||
export default class ShowDataMultiLayer {
|
export default class ShowDataMultiLayer {
|
||||||
constructor(options: ShowDataLayerOptions & { layers: UIEventSource<FilteredLayer[]> }) {
|
constructor(options: ShowDataLayerOptions & { layers: Store<FilteredLayer[]> }) {
|
||||||
|
|
||||||
new PerLayerFeatureSourceSplitter(options.layers, (perLayer => {
|
new PerLayerFeatureSourceSplitter(options.layers, (perLayer => {
|
||||||
const newOptions = {
|
const newOptions = {
|
||||||
|
|
|
@ -49,6 +49,9 @@ export class SubstitutedTranslation extends VariableUiElement {
|
||||||
const allElements = SubstitutedTranslation.ExtractSpecialComponents(txt, extraMappings).map(
|
const allElements = SubstitutedTranslation.ExtractSpecialComponents(txt, extraMappings).map(
|
||||||
proto => {
|
proto => {
|
||||||
if (proto.fixed !== undefined) {
|
if (proto.fixed !== undefined) {
|
||||||
|
if(tagsSource === undefined){
|
||||||
|
return Utils.SubstituteKeys(proto.fixed, undefined)
|
||||||
|
}
|
||||||
return new VariableUiElement(tagsSource.map(tags => Utils.SubstituteKeys(proto.fixed, tags)));
|
return new VariableUiElement(tagsSource.map(tags => Utils.SubstituteKeys(proto.fixed, tags)));
|
||||||
}
|
}
|
||||||
const viz = proto.special;
|
const viz = proto.special;
|
||||||
|
|
4
Utils.ts
4
Utils.ts
|
@ -284,7 +284,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
* @param useLang
|
* @param useLang
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
public static SubstituteKeys(txt: string | undefined, tags: any, useLang?: string): string | undefined {
|
public static SubstituteKeys(txt: string | undefined, tags?: any, useLang?: string): string | undefined {
|
||||||
if (txt === undefined) {
|
if (txt === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -294,7 +294,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
|
|
||||||
while (match) {
|
while (match) {
|
||||||
const key = match[1]
|
const key = match[1]
|
||||||
let v = tags[key]
|
let v = tags === undefined ? undefined : tags[key]
|
||||||
if (v !== undefined) {
|
if (v !== undefined) {
|
||||||
|
|
||||||
if (v["toISOString"] != undefined) {
|
if (v["toISOString"] != undefined) {
|
||||||
|
|
|
@ -121,10 +121,12 @@
|
||||||
{
|
{
|
||||||
"icon": {
|
"icon": {
|
||||||
"render": "circle:white;./assets/layers/doctors/doctors.svg",
|
"render": "circle:white;./assets/layers/doctors/doctors.svg",
|
||||||
"mappings": [{
|
"mappings": [
|
||||||
|
{
|
||||||
"if": "amenity=dentist",
|
"if": "amenity=dentist",
|
||||||
"then": "circle:white;./assets/layers/doctors/dentist.svg"
|
"then": "circle:white;./assets/layers/doctors/dentist.svg"
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"iconSize": "40,40,center",
|
"iconSize": "40,40,center",
|
||||||
"location": [
|
"location": [
|
||||||
|
|
|
@ -42,4 +42,3 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
"description": "Layer containing various presets and questions generated by ID. These are meant to be reused in other layers by importing the tagRenderings with `id_preset.<tagrendering>",
|
"description": "Layer containing various presets and questions generated by ID. These are meant to be reused in other layers by importing the tagRenderings with `id_preset.<tagrendering>",
|
||||||
"#dont-translate": "*",
|
"#dont-translate": "*",
|
||||||
"source": {
|
"source": {
|
||||||
"osmTags": {
|
"osmTags": "id~*"
|
||||||
"and": []
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"mapRendering": null,
|
"mapRendering": null,
|
||||||
"tagRenderings": [
|
"tagRenderings": [
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minzoom":13,
|
"minzoom": 13,
|
||||||
"tagRenderings": [
|
"tagRenderings": [
|
||||||
"images",
|
"images",
|
||||||
"opening_hours",
|
"opening_hours",
|
||||||
|
@ -24,9 +24,6 @@
|
||||||
"website",
|
"website",
|
||||||
{
|
{
|
||||||
"id": "wheelchair",
|
"id": "wheelchair",
|
||||||
"render": {
|
|
||||||
"en": "Easily accessible for wheelchair users: {wheelchair}"
|
|
||||||
},
|
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Is this pharmacy easy to access on a wheelchair?"
|
"en": "Is this pharmacy easy to access on a wheelchair?"
|
||||||
},
|
},
|
||||||
|
@ -42,7 +39,6 @@
|
||||||
"then": {
|
"then": {
|
||||||
"en": "This pharmacy is hard to access on a wheelchair"
|
"en": "This pharmacy is hard to access on a wheelchair"
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"if": "wheelchair=limited",
|
"if": "wheelchair=limited",
|
||||||
|
|
|
@ -332,7 +332,7 @@
|
||||||
"title": "Sammenlign med eksisterende data",
|
"title": "Sammenlign med eksisterende data",
|
||||||
"titleLive": "Live data på OSM",
|
"titleLive": "Live data på OSM",
|
||||||
"titleNearby": "Elementer i nærheden",
|
"titleNearby": "Elementer i nærheden",
|
||||||
"zoomIn": "Live data bliver vist på zoomniveau mindst {needed}. Det aktuelle zoomniveau er {current}"
|
"zoomIn": "Det aktuelle zoomniveau er {current}"
|
||||||
},
|
},
|
||||||
"createNotes": {
|
"createNotes": {
|
||||||
"creating": "Oprettede <b>{count}</b> noter ud af {total}",
|
"creating": "Oprettede <b>{count}</b> noter ud af {total}",
|
||||||
|
|
|
@ -360,7 +360,7 @@
|
||||||
"title": "Mit vorhandenen Daten vergleichen",
|
"title": "Mit vorhandenen Daten vergleichen",
|
||||||
"titleLive": "Live-Daten auf OSM",
|
"titleLive": "Live-Daten auf OSM",
|
||||||
"titleNearby": "Objekte in der Nähe",
|
"titleNearby": "Objekte in der Nähe",
|
||||||
"zoomIn": "Live-Daten werden ab Zoomstufe {needed} angezeigt. Die aktuelle Zoomstufe ist {current}"
|
"zoomIn": "Die aktuelle Zoomstufe ist {current}"
|
||||||
},
|
},
|
||||||
"createNotes": {
|
"createNotes": {
|
||||||
"creating": "<b>{count}</b> Notizen von {total} erstellt",
|
"creating": "<b>{count}</b> Notizen von {total} erstellt",
|
||||||
|
|
|
@ -355,6 +355,7 @@
|
||||||
"osmLoaded": "{count} elements are loaded from OpenStreetMap which match the layer <b>{name}</b>.",
|
"osmLoaded": "{count} elements are loaded from OpenStreetMap which match the layer <b>{name}</b>.",
|
||||||
"reloadTheCache": "Clear the cache and query overpass again",
|
"reloadTheCache": "Clear the cache and query overpass again",
|
||||||
"setRangeToZero": "Set the range to 0 or 1 if you want to import them all",
|
"setRangeToZero": "Set the range to 0 or 1 if you want to import them all",
|
||||||
|
"showOsmLayerInConflationMap": "Show the OSM data",
|
||||||
"states": {
|
"states": {
|
||||||
"error": "Could not load latest data from overpass due to {error}",
|
"error": "Could not load latest data from overpass due to {error}",
|
||||||
"idle": "Checking local storage…",
|
"idle": "Checking local storage…",
|
||||||
|
@ -364,7 +365,8 @@
|
||||||
"title": "Compare with existing data",
|
"title": "Compare with existing data",
|
||||||
"titleLive": "Live data on OSM",
|
"titleLive": "Live data on OSM",
|
||||||
"titleNearby": "Nearby features",
|
"titleNearby": "Nearby features",
|
||||||
"zoomIn": "The live data is shown if the zoomlevel is at least {needed}. The current zoom level is {current}"
|
"zoomIn": "The current zoom level is {current}",
|
||||||
|
"zoomLevelSelection": "The live data is shown if the zoomlevel is at least: "
|
||||||
},
|
},
|
||||||
"createNotes": {
|
"createNotes": {
|
||||||
"creating": "Created <b>{count}</b> notes out of {total}",
|
"creating": "Created <b>{count}</b> notes out of {total}",
|
||||||
|
|
|
@ -309,7 +309,7 @@
|
||||||
"title": "Sammenlign med eksisterende data",
|
"title": "Sammenlign med eksisterende data",
|
||||||
"titleLive": "Sanntidsdata på OSM",
|
"titleLive": "Sanntidsdata på OSM",
|
||||||
"titleNearby": "Funksjoner i nærheten",
|
"titleNearby": "Funksjoner i nærheten",
|
||||||
"zoomIn": "Sanntidsdata vises hvis forstørrelsesnivået er minst {needed}. Nåværende forstørrelsesnivå er {current}."
|
"zoomIn": "Nåværende forstørrelsesnivå er {current}."
|
||||||
},
|
},
|
||||||
"createNotes": {
|
"createNotes": {
|
||||||
"creating": "Opprettet <b>{count}</b> notater av {total}",
|
"creating": "Opprettet <b>{count}</b> notater av {total}",
|
||||||
|
|
|
@ -360,7 +360,7 @@
|
||||||
"title": "Vergelijking met bestaande data",
|
"title": "Vergelijking met bestaande data",
|
||||||
"titleLive": "Data van OSM",
|
"titleLive": "Data van OSM",
|
||||||
"titleNearby": "Objecten in de buurt",
|
"titleNearby": "Objecten in de buurt",
|
||||||
"zoomIn": "De OSM-data wordt getoond vanaf zoomniveau {needed}. Het huidige zoomniveau is {current}"
|
"zoomIn": "Het huidige zoomniveau is {current}"
|
||||||
},
|
},
|
||||||
"createNotes": {
|
"createNotes": {
|
||||||
"creating": "<b>{count}</b> van {total} kaartnota's werden gemaakt",
|
"creating": "<b>{count}</b> van {total} kaartnota's werden gemaakt",
|
||||||
|
|
|
@ -248,7 +248,7 @@ class TranslationPart {
|
||||||
|
|
||||||
if (lang === "en" || usedByLanguage === "en") {
|
if (lang === "en" || usedByLanguage === "en") {
|
||||||
errors.push({
|
errors.push({
|
||||||
error: `The translation for ${key} does not have the required subpart ${part}.
|
error: `The translation for ${key} does not have the required subpart ${part} (in ${usedByLanguage}).
|
||||||
\tThe full translation is ${value}
|
\tThe full translation is ${value}
|
||||||
\t${fixLink}`,
|
\t${fixLink}`,
|
||||||
path: path
|
path: path
|
||||||
|
|
Loading…
Reference in a new issue