diff --git a/Docs/SettingUpPSQL.md b/Docs/SettingUpPSQL.md index 05aa3e54b4..6a7f79c9ad 100644 --- a/Docs/SettingUpPSQL.md +++ b/Docs/SettingUpPSQL.md @@ -47,6 +47,10 @@ HP ProLiant DL360 G7 (1U): 2Rx4 DDR3-memory (PC3) Intel Xeon X56** +## Updating data + +`osm2pgsql-replication update -d postgresql://user:password@localhost:5444/osm-poi -- -O flex -S build_db.lua -s --flat-nodes=import-help-file` + ## Deploying a tile server diff --git a/assets/layers/summary/summary.json b/assets/layers/summary/summary.json index ecffca76a5..7cb5c1175f 100644 --- a/assets/layers/summary/summary.json +++ b/assets/layers/summary/summary.json @@ -15,11 +15,12 @@ "point", "centroid" ], - "iconSize": "25,25", + "iconSize": "40,40", "label": { "render": "{total}" }, - "labelCssClasses": "bg-white w-6 h-6 text-lg rounded-full" + "labelCss": "background: #ffffffbb", + "labelCssClasses": "w-12 text-lg rounded-xl p-1 px-2" } ] } diff --git a/package.json b/package.json index 8838b40060..c3c41ff4d9 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "oauth_secret": "NBWGhWDrD3QDB35xtVuxv4aExnmIt4FA_WgeLtwxasg", "url": "https://www.openstreetmap.org" }, - "mvt_layer_server": "http://cache.mapcomplete.org/mvt/public.{type}_{layer}/{z}/{x}/{y}.pbf", + "mvt_layer_server": "https://cache.mapcomplete.org/public.{type}_{layer}/{z}/{x}/{y}.pbf", "disabled:oauth_credentials": { "##": "DEV", "#": "This client-id is registered by 'MapComplete' on https://master.apis.dev.openstreetmap.org/", diff --git a/src/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts b/src/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts index efd109fd5f..4a429086cc 100644 --- a/src/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts +++ b/src/Logic/FeatureSource/TiledFeatureSource/DynamicTileSource.ts @@ -4,12 +4,13 @@ import { BBox } from "../../BBox" import { FeatureSource } from "../FeatureSource" import FeatureSourceMerger from "../Sources/FeatureSourceMerger" - /*** * A tiled source which dynamically loads the required tiles at a fixed zoom level. * A single featureSource will be initialized for every tile in view; which will later be merged into this featureSource */ -export default class DynamicTileSource extends FeatureSourceMerger { +export default class DynamicTileSource< + Src extends FeatureSource = FeatureSource +> extends FeatureSourceMerger { /** * * @param zoomlevel If {z} is specified in the source, the 'zoomlevel' will be used as zoomlevel to download from @@ -28,10 +29,12 @@ export default class DynamicTileSource - }, + zDiff?: number + } ) { super() const loadedTiles = new Set() + const zDiff = options?.zDiff ?? 0 const neededTiles: Store = Stores.ListStabilized( mapProperties.bounds .mapD( @@ -43,32 +46,32 @@ export default class DynamicTileSource 500) { console.warn( - "Got a really big tilerange, bounds and location might be out of sync", + "Got a really big tilerange, bounds and location might be out of sync" ) return undefined } const needed = Tiles.MapRange(tileRange, (x, y) => - Tiles.tile_index(z, x, y), + Tiles.tile_index(z, x, y) ).filter((i) => !loadedTiles.has(i)) if (needed.length === 0) { return undefined } return needed }, - [options?.isActive, mapProperties.zoom], + [options?.isActive, mapProperties.zoom] ) - .stabilized(250), + .stabilized(250) ) neededTiles.addCallbackAndRunD((neededIndexes) => { @@ -79,5 +82,3 @@ export default class DynamicTileSource { const [z, x, y] = Tiles.tile_from_index(tileIndex) - const coordinates = Tiles.centerPointOf(z, x, y) + let coordinates = Tiles.centerPointOf(z, x, y) const count = UIEventSource.FromPromiseWithErr( Utils.downloadJson(`${cacheserver}/${layersSummed}/${z}/${x}/${y}.json`) @@ -50,6 +51,21 @@ export class SummaryTileSource extends DynamicTileSource { if (counts === undefined || counts["total"] === 0) { return SummaryTileSource.empty } + const lat = counts["lat"] + const lon = counts["lon"] + const total = Utils.numberWithMetrixPrefix(Number(counts["total"])) + const tileBbox = new BBox(Tiles.tile_bounds_lon_lat(z, x, y)) + if (!tileBbox.contains([lon, lat])) { + console.error( + "Average coordinate is outside of bbox!?", + lon, + lat, + tileBbox, + counts + ) + } else { + coordinates = [lon, lat] + } return [ { type: "Feature", @@ -57,6 +73,7 @@ export class SummaryTileSource extends DynamicTileSource { id: "summary_" + tileIndex, summary: "yes", ...counts, + total, layers: layersSummed, }, geometry: { @@ -69,7 +86,8 @@ export class SummaryTileSource extends DynamicTileSource { return new StaticFeatureSource( features.map( (f) => { - if (z !== zoomRounded.data) { + console.log("z, zdiff, rounded:", z, zDiff, zoomRounded.data) + if (z - zDiff !== zoomRounded.data) { return SummaryTileSource.empty } return f @@ -79,7 +97,7 @@ export class SummaryTileSource extends DynamicTileSource { ) }, mapProperties, - options + { ...options, zDiff } ) } } diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index b7a30a52cc..08b303573e 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -145,7 +145,7 @@ export default class ThemeViewState implements SpecialVisualizationState { */ public readonly visualFeedback: UIEventSource = new UIEventSource(false) - constructor(layout: LayoutConfig) { + constructor(layout: LayoutConfig, mvtAvailableLayers: Set) { Utils.initDomPurify() this.layout = layout this.featureSwitches = new FeatureSwitchState(layout) @@ -240,6 +240,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.mapProperties, this.osmConnection.Backend(), (id) => self.layerState.filteredLayers.get(id).isDisplayed, + mvtAvailableLayers, this.fullNodeDatabase ) @@ -662,10 +663,11 @@ export default class ThemeViewState implements SpecialVisualizationState { Constants.priviliged_layers.indexOf(l.id) < 0 && l.source.geojsonSource === undefined ) + const url = new URL(Constants.VectorTileServer) return new SummaryTileSource( - "http://127.0.0.1:2345", + url.protocol + "//" + url.host + "/summary", layers.map((l) => l.id), - this.mapProperties.zoom.map((z) => Math.max(Math.ceil(z) + 1, 0)), + this.mapProperties.zoom.map((z) => Math.max(Math.ceil(z), 0)), this.mapProperties ) } diff --git a/src/Utils.ts b/src/Utils.ts index 0ca204a346..3c673f2d93 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1128,42 +1128,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be element.click() } - public static ColourNameToHex(color: string): string { - return colors[color.toLowerCase()] ?? color - } - - public static HexToColourName(hex: string): string { - hex = hex.toLowerCase() - if (!hex.startsWith("#")) { - return hex - } - const c = Utils.color(hex) - - let smallestDiff = Number.MAX_VALUE - let bestColor = undefined - for (const color in colors) { - if (!colors.hasOwnProperty(color)) { - continue - } - const foundhex = colors[color] - if (typeof foundhex !== "string") { - continue - } - if (foundhex === hex) { - return color - } - const diff = this.colorDiff(Utils.color(foundhex), c) - if (diff > 50) { - continue - } - if (diff < smallestDiff) { - smallestDiff = diff - bestColor = color - } - } - return bestColor ?? hex - } - /** * Reorders an object: creates a new object where the keys have been added alphabetically * @@ -1204,33 +1168,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be return hours + ":" + Utils.TwoDigits(minutes) + ":" + Utils.TwoDigits(seconds) } - public static DisableLongPresses() { - // Remove all context event listeners on mobile to prevent long presses - window.addEventListener( - "contextmenu", - (e) => { - // Not compatible with IE < 9 - - if (e.target["nodeName"] === "INPUT") { - return - } - e.preventDefault() - return false - }, - false - ) - } - - public static preventDefaultOnMouseEvent(event: any) { - event?.originalEvent?.preventDefault() - event?.originalEvent?.stopPropagation() - event?.originalEvent?.stopImmediatePropagation() - if (event?.originalEvent) { - // This is a total workaround, as 'preventDefault' and everything above seems to be not working - event.originalEvent["dismissed"] = true - } - } - public static HomepageLink(): string { if (typeof window === "undefined") { return "https://mapcomplete.org" @@ -1711,4 +1648,19 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be ) { return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) + Math.abs(c0.b - c1.b) } + + private static readonly _metrixPrefixes = ["", "k", "M", "G", "T", "P", "E"] + /** + * Converts a big number (e.g. 1000000) into a rounded postfixed verion (e.g. 1M) + * + * Supported metric prefixes are: [k, M, G, T, P, E] + */ + public static numberWithMetrixPrefix(n: number) { + let index = 0 + while (n > 1000) { + n = Math.round(n / 1000) + index++ + } + return n + Utils._metrixPrefixes[index] + } } diff --git a/src/index.ts b/src/index.ts index e0df3ec274..1eac36b032 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,9 +20,15 @@ function webgl_support() { return false } } -async function availableLayers(): Promise> { - const status = await Utils.downloadJson(Constants.VectorTileServer + "/status.json") - return new Set(status.layers) +async function getAvailableLayers(): Promise> { + try { + const host = new URL(Constants.VectorTileServer).host + const status = await Utils.downloadJson("https://" + host + "/summary/status.json") + return new Set(status.layers) + } catch (e) { + console.error("Could not get MVT available layers due to", e) + return new Set() + } } async function main() { // @ts-ignore @@ -32,9 +38,10 @@ async function main() { } const [layout, availableLayers] = await Promise.all([ DetermineLayout.GetLayout(), - await availableLayers(), + await getAvailableLayers(), ]) - const state = new ThemeViewState(layout) + console.log("The available layers on server are", Array.from(availableLayers)) + const state = new ThemeViewState(layout, availableLayers) const main = new SvelteUIElement(ThemeViewGUI, { state }) main.AttachTo("maindiv") } catch (err) {