forked from MapComplete/MapComplete
Refactoring: port import flow
This commit is contained in:
parent
8ed4da4e9d
commit
ace7caada1
48 changed files with 852 additions and 574 deletions
|
@ -14,8 +14,8 @@ import {TagConfigJson} from "../Json/TagConfigJson"
|
|||
import PointRenderingConfigJson from "../Json/PointRenderingConfigJson"
|
||||
import LineRenderingConfigJson from "../Json/LineRenderingConfigJson"
|
||||
import ValidationUtils from "./ValidationUtils"
|
||||
import { RenderingSpecification } from "../../../UI/SpecialVisualization"
|
||||
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
|
||||
import {RenderingSpecification} from "../../../UI/SpecialVisualization"
|
||||
import {QuestionableTagRenderingConfigJson} from "../Json/QuestionableTagRenderingConfigJson"
|
||||
|
||||
class ExpandFilter extends DesugaringStep<LayerConfigJson> {
|
||||
private static readonly predefinedFilters = ExpandFilter.load_filters()
|
||||
|
@ -201,7 +201,7 @@ class ExpandTagRendering extends Conversion<
|
|||
if (state.tagRenderings.has(name)) {
|
||||
return [state.tagRenderings.get(name)]
|
||||
}
|
||||
if(this._tagRenderingsByLabel.has(name)){
|
||||
if (this._tagRenderingsByLabel.has(name)) {
|
||||
return this._tagRenderingsByLabel.get(name)
|
||||
}
|
||||
|
||||
|
@ -437,11 +437,11 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
|
|||
information?: string[]
|
||||
} {
|
||||
if (json.freeform === undefined) {
|
||||
return { result: json }
|
||||
return {result: json}
|
||||
}
|
||||
let spec: Record<string, string>
|
||||
if (typeof json.render === "string") {
|
||||
spec = { "*": json.render }
|
||||
spec = {"*": json.render}
|
||||
} else {
|
||||
spec = <Record<string, string>>json.render
|
||||
}
|
||||
|
@ -450,7 +450,7 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
|
|||
if (spec[key].indexOf("<a ") >= 0) {
|
||||
// We have a link element, it probably contains something that needs to be substituted...
|
||||
// Let's play this safe and not inline it
|
||||
return { result: json }
|
||||
return {result: json}
|
||||
}
|
||||
const fullSpecification = SpecialVisualizations.constructSpecification(spec[key])
|
||||
if (fullSpecification.length > 1) {
|
||||
|
@ -458,19 +458,19 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
|
|||
if (json.freeform.inline === true) {
|
||||
errors.push(
|
||||
"At " +
|
||||
context +
|
||||
": 'inline' is set, but the rendering contains a special visualisation...\n " +
|
||||
spec[key]
|
||||
context +
|
||||
": 'inline' is set, but the rendering contains a special visualisation...\n " +
|
||||
spec[key]
|
||||
)
|
||||
}
|
||||
json = JSON.parse(JSON.stringify(json))
|
||||
json.freeform.inline = false
|
||||
return { result: json, errors }
|
||||
return {result: json, errors}
|
||||
}
|
||||
}
|
||||
json = JSON.parse(JSON.stringify(json))
|
||||
json.freeform.inline ??= true
|
||||
return { result: json, errors }
|
||||
return {result: json, errors}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,7 +491,7 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
|||
json.tagRenderings === undefined ||
|
||||
json.tagRenderings.some((tr) => tr["id"] === "leftover-questions")
|
||||
) {
|
||||
return { result: json }
|
||||
return {result: json}
|
||||
}
|
||||
json = JSON.parse(JSON.stringify(json))
|
||||
const allSpecials: Exclude<RenderingSpecification, string>[] = []
|
||||
|
@ -512,8 +512,8 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
|||
if (noLabels.length > 1) {
|
||||
errors.push(
|
||||
"At " +
|
||||
context +
|
||||
": multiple 'questions'-visualisations found which would show _all_ questions. Don't do this"
|
||||
context +
|
||||
": multiple 'questions'-visualisations found which would show _all_ questions. Don't do this"
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -537,24 +537,24 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
|||
if (blacklisted?.length > 0 && used?.length > 0) {
|
||||
errors.push(
|
||||
"At " +
|
||||
context +
|
||||
": the {questions()}-special rendering only supports either a blacklist OR a whitelist, but not both." +
|
||||
"\n Whitelisted: " +
|
||||
used.join(", ") +
|
||||
"\n Blacklisted: " +
|
||||
blacklisted.join(", ")
|
||||
context +
|
||||
": the {questions()}-special rendering only supports either a blacklist OR a whitelist, but not both." +
|
||||
"\n Whitelisted: " +
|
||||
used.join(", ") +
|
||||
"\n Blacklisted: " +
|
||||
blacklisted.join(", ")
|
||||
)
|
||||
}
|
||||
for (const usedLabel of used) {
|
||||
if (!allLabels.has(usedLabel)) {
|
||||
errors.push(
|
||||
"At " +
|
||||
context +
|
||||
": this layers specifies a special question element for label `" +
|
||||
usedLabel +
|
||||
"`, but this label doesn't exist.\n" +
|
||||
" Available labels are " +
|
||||
Array.from(allLabels).join(", ")
|
||||
context +
|
||||
": this layers specifies a special question element for label `" +
|
||||
usedLabel +
|
||||
"`, but this label doesn't exist.\n" +
|
||||
" Available labels are " +
|
||||
Array.from(allLabels).join(", ")
|
||||
)
|
||||
}
|
||||
seen.add(usedLabel)
|
||||
|
@ -583,6 +583,7 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
|
|||
|
||||
export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
|
||||
private readonly _desugaring: DesugaringContext
|
||||
|
||||
constructor(desugaring: DesugaringContext) {
|
||||
super(
|
||||
"Add some editing elements, such as the delete button or the move button if they are configured. These used to be handled by the feature info box, but this has been replaced by special visualisation elements",
|
||||
|
@ -609,7 +610,7 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
|
|||
if (json.allowSplit && !ValidationUtils.hasSpecialVisualisation(json, "split_button")) {
|
||||
json.tagRenderings.push({
|
||||
id: "split-button",
|
||||
render: { "*": "{split_button()}" },
|
||||
render: {"*": "{split_button()}"},
|
||||
})
|
||||
delete json.allowSplit
|
||||
}
|
||||
|
@ -617,13 +618,13 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
|
|||
if (json.allowMove && !ValidationUtils.hasSpecialVisualisation(json, "move_button")) {
|
||||
json.tagRenderings.push({
|
||||
id: "move-button",
|
||||
render: { "*": "{move_button()}" },
|
||||
render: {"*": "{move_button()}"},
|
||||
})
|
||||
}
|
||||
if (json.deletion && !ValidationUtils.hasSpecialVisualisation(json, "delete_button")) {
|
||||
json.tagRenderings.push({
|
||||
id: "delete-button",
|
||||
render: { "*": "{delete_button()}" },
|
||||
render: {"*": "{delete_button()}"},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -640,7 +641,7 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
|
|||
if (!ValidationUtils.hasSpecialVisualisation(json, "all_tags")) {
|
||||
const trc: TagRenderingConfigJson = {
|
||||
id: "all-tags",
|
||||
render: { "*": "{all_tags()}" },
|
||||
render: {"*": "{all_tags()}"},
|
||||
|
||||
metacondition: {
|
||||
or: [
|
||||
|
@ -653,7 +654,7 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
|
|||
json.tagRenderings?.push(trc)
|
||||
}
|
||||
|
||||
return { result: json }
|
||||
return {result: json}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1149,6 +1150,36 @@ class PreparePointRendering extends Fuse<PointRenderingConfigJson | LineRenderin
|
|||
}
|
||||
}
|
||||
|
||||
class SetFullNodeDatabase extends DesugaringStep<LayerConfigJson> {
|
||||
constructor() {
|
||||
super("sets the fullNodeDatabase-bit if needed",
|
||||
["fullNodeDatabase"],
|
||||
"SetFullNodeDatabase")
|
||||
}
|
||||
|
||||
convert(json: LayerConfigJson, context: string): {
|
||||
result: LayerConfigJson;
|
||||
errors?: string[];
|
||||
warnings?: string[];
|
||||
information?: string[]
|
||||
} {
|
||||
const needsSpecial = json.tagRenderings?.some(tr => {
|
||||
if (typeof tr === "string") {
|
||||
return false
|
||||
}
|
||||
const specs = ValidationUtils.getSpecialVisualisations(<TagRenderingConfigJson>tr)
|
||||
return specs?.some(sp => sp.needsNodeDatabase)
|
||||
}) ?? false
|
||||
if (!needsSpecial) {
|
||||
return {result: json}
|
||||
}
|
||||
return {
|
||||
result: {...json, fullNodeDatabase: true},
|
||||
information: ["Layer " + json.id + " needs the fullNodeDatabase"]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
|
||||
private readonly _state: DesugaringContext
|
||||
|
||||
|
@ -1163,19 +1194,19 @@ export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
|
|||
|
||||
convert(layerConfig: LayerConfigJson, context: string): { result: LayerConfigJson } {
|
||||
if (!layerConfig.tagRenderings || layerConfig.source === "special") {
|
||||
return { result: layerConfig }
|
||||
return {result: layerConfig}
|
||||
}
|
||||
const state = this._state
|
||||
const hasMinimap = ValidationUtils.hasSpecialVisualisation(layerConfig, "minimap")
|
||||
if (!hasMinimap) {
|
||||
layerConfig = { ...layerConfig }
|
||||
layerConfig = {...layerConfig}
|
||||
layerConfig.tagRenderings = [...layerConfig.tagRenderings]
|
||||
const minimap = state.tagRenderings.get("minimap")
|
||||
if(minimap === undefined){
|
||||
if(state.tagRenderings.size > 0){
|
||||
if (minimap === undefined) {
|
||||
if (state.tagRenderings.size > 0) {
|
||||
throw "The 'minimap'-builtin tagrendering is not defined. As such, it cannot be added automatically"
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
layerConfig.tagRenderings.push(minimap)
|
||||
}
|
||||
}
|
||||
|
@ -1197,6 +1228,7 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
|
|||
new AddQuestionBox(),
|
||||
new AddMiniMap(state),
|
||||
new AddEditingElements(state),
|
||||
new SetFullNodeDatabase(),
|
||||
new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
|
||||
new On<(PointRenderingConfigJson | LineRenderingConfigJson)[], LayerConfigJson>(
|
||||
"mapRendering",
|
||||
|
|
|
@ -124,9 +124,6 @@ export interface LayerConfigJson {
|
|||
* If set, only features matching this extra tag will be shown.
|
||||
* This is useful to hide certain features from view.
|
||||
*
|
||||
* Important: hiding features does not work dynamically, but is only calculated when the data is first renders.
|
||||
* This implies that it is not possible to hide a feature after a tagging change
|
||||
*
|
||||
* The default value is 'yes'
|
||||
*/
|
||||
isShown?: TagConfigJson
|
||||
|
@ -404,4 +401,9 @@ export interface LayerConfigJson {
|
|||
* If set, open the selectedElementView in a floatOver instead of on the right
|
||||
*/
|
||||
popupInFloatover?: boolean
|
||||
|
||||
/**
|
||||
* _Set automatically by MapComplete, please ignore_
|
||||
*/
|
||||
fullNodeDatabase?: boolean
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ export default class LayerConfig extends WithContextLoader {
|
|||
public readonly forceLoad: boolean
|
||||
public readonly syncSelection: typeof LayerConfig.syncSelectionAllowed[number] // this is a trick to conver a constant array of strings into a type union of these values
|
||||
|
||||
public readonly _needsFullNodeDatabase = false
|
||||
public readonly _needsFullNodeDatabase: boolean
|
||||
public readonly popupInFloatover
|
||||
|
||||
constructor(json: LayerConfigJson, context?: string, official: boolean = true) {
|
||||
|
@ -217,6 +217,7 @@ export default class LayerConfig extends WithContextLoader {
|
|||
this.doNotDownload = json.doNotDownload ?? false
|
||||
this.passAllFeatures = json.passAllFeatures ?? false
|
||||
this.minzoom = json.minzoom ?? 0
|
||||
this._needsFullNodeDatabase = json.fullNodeDatabase ?? false
|
||||
if (json["minZoom"] !== undefined) {
|
||||
throw "At " + context + ": minzoom is written all lowercase"
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ export default class WithContextLoader {
|
|||
*
|
||||
* The found value is interpreted as a tagrendering and fetched/parsed
|
||||
* */
|
||||
public tr(key: string, deflt: undefined, translationContext?: string) {
|
||||
public tr(key: string, deflt?: string, translationContext?: string) {
|
||||
const v = this._json[key]
|
||||
if (v === undefined || v === null) {
|
||||
if (deflt === undefined) {
|
||||
|
|
|
@ -146,7 +146,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
rasterInfo.defaultState ?? true,
|
||||
"Wether or not overlayer layer " + rasterInfo.id + " is shown"
|
||||
)
|
||||
const state = { isDisplayed }
|
||||
const state = {isDisplayed}
|
||||
overlayLayerStates.set(rasterInfo.id, state)
|
||||
new ShowOverlayRasterLayer(rasterInfo, this.map, this.mapProperties, state)
|
||||
}
|
||||
|
@ -158,18 +158,34 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
* A bit tricky, as this is heavily intertwined with the 'changes'-element, which generate a stream of new and changed features too
|
||||
*/
|
||||
|
||||
|
||||
if(this.layout.layers.some(l => l._needsFullNodeDatabase)){
|
||||
this.fullNodeDatabase = new FullNodeDatabaseSource()
|
||||
}
|
||||
|
||||
const layoutSource = new LayoutSource(
|
||||
layout.layers,
|
||||
this.featureSwitches,
|
||||
this.mapProperties,
|
||||
this.osmConnection.Backend(),
|
||||
(id) => self.layerState.filteredLayers.get(id).isDisplayed
|
||||
(id) => self.layerState.filteredLayers.get(id).isDisplayed,
|
||||
this.fullNodeDatabase
|
||||
)
|
||||
this.indexedFeatures = layoutSource
|
||||
const empty = []
|
||||
let currentViewIndex = 0
|
||||
this.currentView = new StaticFeatureSource(
|
||||
this.mapProperties.bounds.map((bbox) =>
|
||||
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({ id: "current_view" })]
|
||||
this.mapProperties.bounds.map((bbox) => {
|
||||
if (!bbox) {
|
||||
return empty
|
||||
}
|
||||
currentViewIndex++
|
||||
return <Feature[]>[bbox.asGeoJson({
|
||||
zoom: this.mapProperties.zoom.data,
|
||||
...this.mapProperties.location.data,
|
||||
id: "current_view" }
|
||||
)];
|
||||
}
|
||||
)
|
||||
)
|
||||
this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds)
|
||||
|
@ -270,7 +286,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
for (const l of levels) {
|
||||
floors.add(l)
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
floors.add("0") // '0' is the default and is thus _always_ present
|
||||
}
|
||||
}
|
||||
|
@ -305,7 +321,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
this.drawSpecialLayers()
|
||||
this.initHotkeys()
|
||||
this.miscSetup()
|
||||
if(!Utils.runningFromConsole){
|
||||
if (!Utils.runningFromConsole) {
|
||||
console.log("State setup completed", this)
|
||||
}
|
||||
}
|
||||
|
@ -333,7 +349,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
|
||||
private initHotkeys() {
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "Escape", onUp: true },
|
||||
{nomod: "Escape", onUp: true},
|
||||
Translations.t.hotkeyDocumentation.closeSidebar,
|
||||
() => {
|
||||
this.selectedElement.setData(undefined)
|
||||
|
@ -354,7 +370,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
)
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ shift: "O" },
|
||||
{shift: "O"},
|
||||
Translations.t.hotkeyDocumentation.selectMapnik,
|
||||
() => {
|
||||
this.mapProperties.rasterLayer.setData(AvailableRasterLayers.osmCarto)
|
||||
|
@ -373,17 +389,17 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
}
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "O" },
|
||||
{nomod: "O"},
|
||||
Translations.t.hotkeyDocumentation.selectOsmbasedmap,
|
||||
() => setLayerCategory("osmbasedmap")
|
||||
)
|
||||
|
||||
Hotkeys.RegisterHotkey({ nomod: "M" }, Translations.t.hotkeyDocumentation.selectMap, () =>
|
||||
Hotkeys.RegisterHotkey({nomod: "M"}, Translations.t.hotkeyDocumentation.selectMap, () =>
|
||||
setLayerCategory("map")
|
||||
)
|
||||
|
||||
Hotkeys.RegisterHotkey(
|
||||
{ nomod: "P" },
|
||||
{nomod: "P"},
|
||||
Translations.t.hotkeyDocumentation.selectAerial,
|
||||
() => setLayerCategory("photo")
|
||||
)
|
||||
|
@ -451,7 +467,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
),
|
||||
range: new StaticFeatureSource(
|
||||
this.mapProperties.maxbounds.map((bbox) =>
|
||||
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({ id: "range" })]
|
||||
bbox === undefined ? empty : <Feature[]>[bbox.asGeoJson({id: "range"})]
|
||||
)
|
||||
),
|
||||
current_view: this.currentView
|
||||
|
@ -465,6 +481,14 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
this.featureSwitches.featureSwitchIsTesting
|
||||
)
|
||||
}
|
||||
const currentViewLayer = this.layout.layers.find(l => l.id === "current_view")
|
||||
if (currentViewLayer?.tagRenderings?.length > 0) {
|
||||
const params = MetaTagging.createExtraFuncParams(this)
|
||||
this.featureProperties.trackFeatureSource(specialLayers.current_view)
|
||||
specialLayers.current_view.features.addCallbackAndRunD(features => {
|
||||
MetaTagging.addMetatags(features, params, currentViewLayer, this.layout, this.osmObjectDownloader, this.featureProperties)
|
||||
})
|
||||
}
|
||||
|
||||
this.layerState.filteredLayers
|
||||
.get("range")
|
||||
|
@ -545,7 +569,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
}
|
||||
{
|
||||
this.selectedElement.addCallback(selected => {
|
||||
if(selected === undefined){
|
||||
if (selected === undefined) {
|
||||
// We did _unselect_ an item - we always remove the lastclick-object
|
||||
this.lastClickObject.features.setData([])
|
||||
this.selectedLayer.setData(undefined)
|
||||
|
|
|
@ -97,6 +97,16 @@ export class Tiles {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a tilerange which (at least) contains the given coordinates.
|
||||
* This means that the actual iterated area might be a bit bigger then the the passed in coordinates
|
||||
* @param zoomlevel
|
||||
* @param lat0
|
||||
* @param lon0
|
||||
* @param lat1
|
||||
* @param lon1
|
||||
* @constructor
|
||||
*/
|
||||
static TileRangeBetween(
|
||||
zoomlevel: number,
|
||||
lat0: number,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue