import { TagsFilter } from "../../src/Logic/Tags/TagsFilter" import { Tag } from "../../src/Logic/Tags/Tag" import { And } from "../../src/Logic/Tags/And" import Script from "../Script" import fs from "fs" import { Or } from "../../src/Logic/Tags/Or" import { RegexTag } from "../../src/Logic/Tags/RegexTag" import { ValidateThemeEnsemble } from "../../src/Models/ThemeConfig/Conversion/Validation" import { AllKnownLayouts } from "../../src/Customizations/AllKnownLayouts" class LuaSnippets { public static helpers = [ "function countTbl(tbl)\n" + " local c = 0\n" + " for n in pairs(tbl) do \n" + " c = c + 1 \n" + " end\n" + " return c\n" + "end", ].join("\n") public static toLuaFilter(tag: TagsFilter, useParens: boolean = false): string { if (tag instanceof Tag) { return `object.tags["${tag.key}"] == "${tag.value}"` } if (tag instanceof And) { const expr = tag.and.map(t => this.toLuaFilter(t, true)).join(" and ") if (useParens) { return "(" + expr + ")" } return expr } if (tag instanceof Or) { const expr = tag.or.map(t => this.toLuaFilter(t, true)).join(" or ") if (useParens) { return "(" + expr + ")" } return expr } if (tag instanceof RegexTag) { let expr = LuaSnippets.regexTagToLua(tag) if (useParens) { expr = "(" + expr + ")" } return expr } let msg = "Could not handle" + tag.asHumanString(false, false, {}) console.error(msg) throw msg } private static regexTagToLua(tag: RegexTag) { if (typeof tag.value === "string" && tag.invert) { return `object.tags["${tag.key}"] ~= "${tag.value}"` } const v = (tag.value).source.replace(/\\\//g, "/") if ("" + tag.value === "/.+/is" && !tag.invert) { return `object.tags["${tag.key}"] ~= nil` } if ("" + tag.value === "/.+/is" && tag.invert) { return `object.tags["${tag.key}"] == nil` } if (tag.matchesEmpty && !tag.invert) { return `object.tags["${tag.key}"] == nil or object.tags["${tag.key}"] == ""` } if (tag.matchesEmpty && tag.invert) { return `object.tags["${tag.key}"] ~= nil or object.tags["${tag.key}"] ~= ""` } if (tag.invert) { return `object.tags["${tag.key}"] == nil or not string.find(object.tags["${tag.key}"], "${v}")` } return `(object.tags["${tag.key}"] ~= nil and string.find(object.tags["${tag.key}"], "${v}"))` } } class GenerateLayerLua { private readonly _id: string private readonly _tags: TagsFilter private readonly _foundInThemes: string[] constructor(id: string, tags: TagsFilter, foundInThemes: string[] = []) { this._tags = tags this._id = id this._foundInThemes = foundInThemes } public generateTables(): string { if (!this._tags) { return undefined } return [ `db_tables.pois_${this._id} = osm2pgsql.define_table({`, this._foundInThemes ? "-- used in themes: " + this._foundInThemes.join(", ") : "", ` name = 'pois_${this._id}',`, " ids = { type = 'any', type_column = 'osm_type', id_column = 'osm_id' },", " columns = {", " { column = 'tags', type = 'jsonb' },", " { column = 'geom', type = 'point', projection = 4326, not_null = true },", " }", "})", "", `db_tables.lines_${this._id} = osm2pgsql.define_table({`, this._foundInThemes ? "-- used in themes: " + this._foundInThemes.join(", ") : "", ` name = 'lines_${this._id}',`, " ids = { type = 'any', type_column = 'osm_type', id_column = 'osm_id' },", " columns = {", " { column = 'tags', type = 'jsonb' },", " { column = 'geom', type = 'linestring', projection = 4326, not_null = true },", " }", "})", `db_tables.polygons_${this._id} = osm2pgsql.define_table({`, this._foundInThemes ? "-- used in themes: " + this._foundInThemes.join(", ") : "", ` name = 'polygons_${this._id}',`, " ids = { type = 'any', type_column = 'osm_type', id_column = 'osm_id' },", " columns = {", " { column = 'tags', type = 'jsonb' },", " { column = 'geom', type = 'polygon', projection = 4326, not_null = true },", " }", "})", "", ].join("\n") } } class GenerateBuildDbScript extends Script { constructor() { super("Generates a .lua-file to use with osm2pgsql") } async main(args: string[]) { const allNeededLayers = new ValidateThemeEnsemble().convertStrict( AllKnownLayouts.allKnownLayouts.values(), ) const generators: GenerateLayerLua[] = [] allNeededLayers.forEach(({ tags, foundInTheme }, layerId) => { generators.push(new GenerateLayerLua(layerId, tags, foundInTheme)) }) const script = [ "local db_tables = {}", LuaSnippets.helpers, ...generators.map(g => g.generateTables()), this.generateProcessPoi(allNeededLayers), this.generateProcessWay(allNeededLayers), ].join("\n\n\n") const path = "build_db.lua" fs.writeFileSync(path, script, "utf-8") console.log("Written", path) console.log(allNeededLayers.size + " layers will be created with 3 tables each. Make sure to set 'max_connections' to at least " + (10 + 3 * allNeededLayers.size)) } private earlyAbort() { return [" if countTbl(object.tags) == 0 then", " return", " end", ""].join("\n") } private generateProcessPoi(allNeededLayers: Map) { const body: string[] = [] allNeededLayers.forEach(({ tags }, layerId) => { body.push( this.insertInto(tags, layerId, "pois_").join("\n"), ) }) return [ "function osm2pgsql.process_node(object)", this.earlyAbort(), " local geom = object:as_point()", " local matches_filter = false", body.join("\n"), "end", ].join("\n") } /** * If matches_filter * @param tags * @param layerId * @param tableprefix * @private */ private insertInto(tags: TagsFilter, layerId: string, tableprefix: "pois_" | "lines_" | "polygons_") { const filter = LuaSnippets.toLuaFilter(tags) return [ " matches_filter = " + filter, " if matches_filter then", " db_tables." + tableprefix + layerId + ":insert({", " geom = geom,", " tags = object.tags", " })", " end", ] } private generateProcessWay(allNeededLayers: Map) { const bodyLines: string[] = [] allNeededLayers.forEach(({ tags }, layerId) => { bodyLines.push(this.insertInto(tags, layerId, "lines_").join("\n")) }) const bodyPolygons: string[] = [] allNeededLayers.forEach(({ tags }, layerId) => { bodyPolygons.push(this.insertInto(tags, layerId, "polygons_").join("\n")) }) return [ "function process_polygon(object, geom)", " local matches_filter", ...bodyPolygons, "end", "function process_linestring(object, geom)", " local matches_filter", ...bodyLines, "end", "", "function osm2pgsql.process_way(object)", this.earlyAbort(), " if object.is_closed then", " process_polygon(object, object:as_polygon())", " else", " process_linestring(object, object:as_linestring())", " end", "end", ].join("\n") } } new GenerateBuildDbScript().run()