forked from MapComplete/MapComplete
First fully working version
This commit is contained in:
parent
995f48b97a
commit
1fa66e50f8
8 changed files with 73 additions and 88 deletions
|
@ -47,6 +47,10 @@ HP ProLiant DL360 G7 (1U): 2Rx4 DDR3-memory (PC3)
|
||||||
Intel Xeon X56**
|
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
|
## Deploying a tile server
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,12 @@
|
||||||
"point",
|
"point",
|
||||||
"centroid"
|
"centroid"
|
||||||
],
|
],
|
||||||
"iconSize": "25,25",
|
"iconSize": "40,40",
|
||||||
"label": {
|
"label": {
|
||||||
"render": "{total}"
|
"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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"oauth_secret": "NBWGhWDrD3QDB35xtVuxv4aExnmIt4FA_WgeLtwxasg",
|
"oauth_secret": "NBWGhWDrD3QDB35xtVuxv4aExnmIt4FA_WgeLtwxasg",
|
||||||
"url": "https://www.openstreetmap.org"
|
"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": {
|
"disabled:oauth_credentials": {
|
||||||
"##": "DEV",
|
"##": "DEV",
|
||||||
"#": "This client-id is registered by 'MapComplete' on https://master.apis.dev.openstreetmap.org/",
|
"#": "This client-id is registered by 'MapComplete' on https://master.apis.dev.openstreetmap.org/",
|
||||||
|
|
|
@ -4,12 +4,13 @@ import { BBox } from "../../BBox"
|
||||||
import { FeatureSource } from "../FeatureSource"
|
import { FeatureSource } from "../FeatureSource"
|
||||||
import FeatureSourceMerger from "../Sources/FeatureSourceMerger"
|
import FeatureSourceMerger from "../Sources/FeatureSourceMerger"
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* A tiled source which dynamically loads the required tiles at a fixed zoom level.
|
* 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
|
* A single featureSource will be initialized for every tile in view; which will later be merged into this featureSource
|
||||||
*/
|
*/
|
||||||
export default class DynamicTileSource<Src extends FeatureSource = FeatureSource> extends FeatureSourceMerger<Src> {
|
export default class DynamicTileSource<
|
||||||
|
Src extends FeatureSource = FeatureSource
|
||||||
|
> extends FeatureSourceMerger<Src> {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param zoomlevel If {z} is specified in the source, the 'zoomlevel' will be used as zoomlevel to download from
|
* @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<Src extends FeatureSource = FeatureSource
|
||||||
},
|
},
|
||||||
options?: {
|
options?: {
|
||||||
isActive?: Store<boolean>
|
isActive?: Store<boolean>
|
||||||
},
|
zDiff?: number
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
const loadedTiles = new Set<number>()
|
const loadedTiles = new Set<number>()
|
||||||
|
const zDiff = options?.zDiff ?? 0
|
||||||
const neededTiles: Store<number[]> = Stores.ListStabilized(
|
const neededTiles: Store<number[]> = Stores.ListStabilized(
|
||||||
mapProperties.bounds
|
mapProperties.bounds
|
||||||
.mapD(
|
.mapD(
|
||||||
|
@ -43,32 +46,32 @@ export default class DynamicTileSource<Src extends FeatureSource = FeatureSource
|
||||||
if (mapProperties.zoom.data < minzoom) {
|
if (mapProperties.zoom.data < minzoom) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
const z = Math.floor(zoomlevel.data)
|
const z = Math.floor(zoomlevel.data) + zDiff
|
||||||
const tileRange = Tiles.TileRangeBetween(
|
const tileRange = Tiles.TileRangeBetween(
|
||||||
z,
|
z,
|
||||||
bounds.getNorth(),
|
bounds.getNorth(),
|
||||||
bounds.getEast(),
|
bounds.getEast(),
|
||||||
bounds.getSouth(),
|
bounds.getSouth(),
|
||||||
bounds.getWest(),
|
bounds.getWest()
|
||||||
)
|
)
|
||||||
if (tileRange.total > 500) {
|
if (tileRange.total > 500) {
|
||||||
console.warn(
|
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
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const needed = Tiles.MapRange(tileRange, (x, y) =>
|
const needed = Tiles.MapRange(tileRange, (x, y) =>
|
||||||
Tiles.tile_index(z, x, y),
|
Tiles.tile_index(z, x, y)
|
||||||
).filter((i) => !loadedTiles.has(i))
|
).filter((i) => !loadedTiles.has(i))
|
||||||
if (needed.length === 0) {
|
if (needed.length === 0) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return needed
|
return needed
|
||||||
},
|
},
|
||||||
[options?.isActive, mapProperties.zoom],
|
[options?.isActive, mapProperties.zoom]
|
||||||
)
|
)
|
||||||
.stabilized(250),
|
.stabilized(250)
|
||||||
)
|
)
|
||||||
|
|
||||||
neededTiles.addCallbackAndRunD((neededIndexes) => {
|
neededTiles.addCallbackAndRunD((neededIndexes) => {
|
||||||
|
@ -79,5 +82,3 @@ export default class DynamicTileSource<Src extends FeatureSource = FeatureSource
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,13 @@ export class SummaryTileSource extends DynamicTileSource {
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const layersSummed = layers.join("+")
|
const layersSummed = layers.join("+")
|
||||||
|
const zDiff = 2
|
||||||
super(
|
super(
|
||||||
zoomRounded,
|
zoomRounded,
|
||||||
0, // minzoom
|
0, // minzoom
|
||||||
(tileIndex) => {
|
(tileIndex) => {
|
||||||
const [z, x, y] = Tiles.tile_from_index(tileIndex)
|
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(
|
const count = UIEventSource.FromPromiseWithErr(
|
||||||
Utils.downloadJson(`${cacheserver}/${layersSummed}/${z}/${x}/${y}.json`)
|
Utils.downloadJson(`${cacheserver}/${layersSummed}/${z}/${x}/${y}.json`)
|
||||||
|
@ -50,6 +51,21 @@ export class SummaryTileSource extends DynamicTileSource {
|
||||||
if (counts === undefined || counts["total"] === 0) {
|
if (counts === undefined || counts["total"] === 0) {
|
||||||
return SummaryTileSource.empty
|
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 [
|
return [
|
||||||
{
|
{
|
||||||
type: "Feature",
|
type: "Feature",
|
||||||
|
@ -57,6 +73,7 @@ export class SummaryTileSource extends DynamicTileSource {
|
||||||
id: "summary_" + tileIndex,
|
id: "summary_" + tileIndex,
|
||||||
summary: "yes",
|
summary: "yes",
|
||||||
...counts,
|
...counts,
|
||||||
|
total,
|
||||||
layers: layersSummed,
|
layers: layersSummed,
|
||||||
},
|
},
|
||||||
geometry: {
|
geometry: {
|
||||||
|
@ -69,7 +86,8 @@ export class SummaryTileSource extends DynamicTileSource {
|
||||||
return new StaticFeatureSource(
|
return new StaticFeatureSource(
|
||||||
features.map(
|
features.map(
|
||||||
(f) => {
|
(f) => {
|
||||||
if (z !== zoomRounded.data) {
|
console.log("z, zdiff, rounded:", z, zDiff, zoomRounded.data)
|
||||||
|
if (z - zDiff !== zoomRounded.data) {
|
||||||
return SummaryTileSource.empty
|
return SummaryTileSource.empty
|
||||||
}
|
}
|
||||||
return f
|
return f
|
||||||
|
@ -79,7 +97,7 @@ export class SummaryTileSource extends DynamicTileSource {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
mapProperties,
|
mapProperties,
|
||||||
options
|
{ ...options, zDiff }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
*/
|
*/
|
||||||
public readonly visualFeedback: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
public readonly visualFeedback: UIEventSource<boolean> = new UIEventSource<boolean>(false)
|
||||||
|
|
||||||
constructor(layout: LayoutConfig) {
|
constructor(layout: LayoutConfig, mvtAvailableLayers: Set<string>) {
|
||||||
Utils.initDomPurify()
|
Utils.initDomPurify()
|
||||||
this.layout = layout
|
this.layout = layout
|
||||||
this.featureSwitches = new FeatureSwitchState(layout)
|
this.featureSwitches = new FeatureSwitchState(layout)
|
||||||
|
@ -240,6 +240,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
this.mapProperties,
|
this.mapProperties,
|
||||||
this.osmConnection.Backend(),
|
this.osmConnection.Backend(),
|
||||||
(id) => self.layerState.filteredLayers.get(id).isDisplayed,
|
(id) => self.layerState.filteredLayers.get(id).isDisplayed,
|
||||||
|
mvtAvailableLayers,
|
||||||
this.fullNodeDatabase
|
this.fullNodeDatabase
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -662,10 +663,11 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
|
Constants.priviliged_layers.indexOf(<any>l.id) < 0 &&
|
||||||
l.source.geojsonSource === undefined
|
l.source.geojsonSource === undefined
|
||||||
)
|
)
|
||||||
|
const url = new URL(Constants.VectorTileServer)
|
||||||
return new SummaryTileSource(
|
return new SummaryTileSource(
|
||||||
"http://127.0.0.1:2345",
|
url.protocol + "//" + url.host + "/summary",
|
||||||
layers.map((l) => l.id),
|
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
|
this.mapProperties
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
78
src/Utils.ts
78
src/Utils.ts
|
@ -1128,42 +1128,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
element.click()
|
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
|
* 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)
|
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 {
|
public static HomepageLink(): string {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === "undefined") {
|
||||||
return "https://mapcomplete.org"
|
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)
|
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]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
15
src/index.ts
15
src/index.ts
|
@ -20,9 +20,15 @@ function webgl_support() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function availableLayers(): Promise<Set<string>> {
|
async function getAvailableLayers(): Promise<Set<string>> {
|
||||||
const status = await Utils.downloadJson(Constants.VectorTileServer + "/status.json")
|
try {
|
||||||
|
const host = new URL(Constants.VectorTileServer).host
|
||||||
|
const status = await Utils.downloadJson("https://" + host + "/summary/status.json")
|
||||||
return new Set<string>(status.layers)
|
return new Set<string>(status.layers)
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Could not get MVT available layers due to", e)
|
||||||
|
return new Set<string>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async function main() {
|
async function main() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -32,9 +38,10 @@ async function main() {
|
||||||
}
|
}
|
||||||
const [layout, availableLayers] = await Promise.all([
|
const [layout, availableLayers] = await Promise.all([
|
||||||
DetermineLayout.GetLayout(),
|
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 })
|
const main = new SvelteUIElement(ThemeViewGUI, { state })
|
||||||
main.AttachTo("maindiv")
|
main.AttachTo("maindiv")
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
Loading…
Reference in a new issue