Refactoring: port import flow

This commit is contained in:
Pieter Vander Vennet 2023-06-01 02:52:21 +02:00
parent 8ed4da4e9d
commit ace7caada1
48 changed files with 852 additions and 574 deletions

View file

@ -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",

View file

@ -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
}

View file

@ -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"
}

View file

@ -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) {

View file

@ -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)

View file

@ -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,