forked from MapComplete/MapComplete
Download button: take advantage of MVT server, download button will now attempt to download everything
This commit is contained in:
parent
bccda67e1c
commit
e4eb8d6b52
21 changed files with 453 additions and 353 deletions
|
@ -1,8 +1,7 @@
|
|||
import { Geometry } from "geojson"
|
||||
import { Feature as GeojsonFeature } from "geojson"
|
||||
import { Feature as GeojsonFeature, Geometry } from "geojson"
|
||||
|
||||
import { Store, UIEventSource } from "../../UIEventSource"
|
||||
import { FeatureSourceForTile } from "../FeatureSource"
|
||||
import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
|
||||
import Pbf from "pbf"
|
||||
|
||||
type Coords = [number, number][]
|
||||
|
@ -205,6 +204,7 @@ class Layer {
|
|||
end
|
||||
)
|
||||
}
|
||||
|
||||
static _readField(tag, obj, pbf) {
|
||||
if (tag === 15) obj.version = pbf.readVarint()
|
||||
else if (tag === 1) obj.name = pbf.readString()
|
||||
|
@ -213,6 +213,7 @@ class Layer {
|
|||
else if (tag === 4) obj.values.push(Value.read(pbf, pbf.readVarint() + pbf.pos))
|
||||
else if (tag === 5) obj.extent = pbf.readVarint()
|
||||
}
|
||||
|
||||
public static write(obj, pbf) {
|
||||
if (obj.version) pbf.writeVarintField(15, obj.version)
|
||||
if (obj.name) pbf.writeStringField(1, obj.name)
|
||||
|
@ -230,12 +231,14 @@ class Feature {
|
|||
static read(pbf, end) {
|
||||
return pbf.readFields(Feature._readField, { id: 0, tags: [], type: 0, geometry: [] }, end)
|
||||
}
|
||||
|
||||
static _readField(tag, obj, pbf) {
|
||||
if (tag === 1) obj.id = pbf.readVarint()
|
||||
else if (tag === 2) pbf.readPackedVarint(obj.tags)
|
||||
else if (tag === 3) obj.type = pbf.readVarint()
|
||||
else if (tag === 4) pbf.readPackedVarint(obj.geometry)
|
||||
}
|
||||
|
||||
public static write(obj, pbf) {
|
||||
if (obj.id) pbf.writeVarintField(1, obj.id)
|
||||
if (obj.tags) pbf.writePackedVarint(2, obj.tags)
|
||||
|
@ -260,6 +263,7 @@ class Value {
|
|||
end
|
||||
)
|
||||
}
|
||||
|
||||
static _readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.string_value = pbf.readString()
|
||||
else if (tag === 2) obj.float_value = pbf.readFloat()
|
||||
|
@ -269,6 +273,7 @@ class Value {
|
|||
else if (tag === 6) obj.sint_value = pbf.readSVarint()
|
||||
else if (tag === 7) obj.bool_value = pbf.readBoolean()
|
||||
}
|
||||
|
||||
public static write(obj, pbf) {
|
||||
if (obj.string_value) pbf.writeStringField(1, obj.string_value)
|
||||
if (obj.float_value) pbf.writeFloatField(2, obj.float_value)
|
||||
|
@ -279,21 +284,10 @@ class Value {
|
|||
if (obj.bool_value) pbf.writeBooleanField(7, obj.bool_value)
|
||||
}
|
||||
}
|
||||
|
||||
class Tile {
|
||||
// code generated by pbf v3.2.1
|
||||
|
||||
public static read(pbf, end) {
|
||||
return pbf.readFields(Tile._readField, { layers: [] }, end)
|
||||
}
|
||||
static _readField(tag, obj, pbf) {
|
||||
if (tag === 3) obj.layers.push(Layer.read(pbf, pbf.readVarint() + pbf.pos))
|
||||
}
|
||||
static write(obj, pbf) {
|
||||
if (obj.layers)
|
||||
for (var i = 0; i < obj.layers.length; i++)
|
||||
pbf.writeMessage(3, Layer.write, obj.layers[i])
|
||||
}
|
||||
|
||||
static GeomType = {
|
||||
UNKNOWN: {
|
||||
value: 0,
|
||||
|
@ -312,10 +306,27 @@ class Tile {
|
|||
options: {},
|
||||
},
|
||||
}
|
||||
|
||||
public static read(pbf, end) {
|
||||
return pbf.readFields(Tile._readField, { layers: [] }, end)
|
||||
}
|
||||
|
||||
static _readField(tag, obj, pbf) {
|
||||
if (tag === 3) obj.layers.push(Layer.read(pbf, pbf.readVarint() + pbf.pos))
|
||||
}
|
||||
|
||||
static write(obj, pbf) {
|
||||
if (obj.layers)
|
||||
for (var i = 0; i < obj.layers.length; i++)
|
||||
pbf.writeMessage(3, Layer.write, obj.layers[i])
|
||||
}
|
||||
}
|
||||
|
||||
export default class MvtSource implements FeatureSourceForTile {
|
||||
export default class MvtSource implements FeatureSourceForTile, UpdatableFeatureSource {
|
||||
public readonly features: Store<GeojsonFeature<Geometry, { [name: string]: any }>[]>
|
||||
public readonly x: number
|
||||
public readonly y: number
|
||||
public readonly z: number
|
||||
private readonly _url: string
|
||||
private readonly _layerName: string
|
||||
private readonly _features: UIEventSource<
|
||||
|
@ -326,9 +337,7 @@ export default class MvtSource implements FeatureSourceForTile {
|
|||
}
|
||||
>[]
|
||||
> = new UIEventSource<GeojsonFeature<Geometry, { [p: string]: any }>[]>([])
|
||||
public readonly x: number
|
||||
public readonly y: number
|
||||
public readonly z: number
|
||||
private currentlyRunning: Promise<any>
|
||||
|
||||
constructor(
|
||||
url: string,
|
||||
|
@ -343,7 +352,7 @@ export default class MvtSource implements FeatureSourceForTile {
|
|||
this.x = x
|
||||
this.y = y
|
||||
this.z = z
|
||||
this.downloadSync()
|
||||
this.updateAsync()
|
||||
this.features = this._features.map(
|
||||
(fs) => {
|
||||
if (fs === undefined || isActive?.data === false) {
|
||||
|
@ -355,6 +364,13 @@ export default class MvtSource implements FeatureSourceForTile {
|
|||
)
|
||||
}
|
||||
|
||||
async updateAsync() {
|
||||
if (!this.currentlyRunning) {
|
||||
this.currentlyRunning = this.download()
|
||||
}
|
||||
await this.currentlyRunning
|
||||
}
|
||||
|
||||
private getValue(v: {
|
||||
// Exactly one of these values must be present in a valid message
|
||||
string_value?: string
|
||||
|
@ -389,47 +405,37 @@ export default class MvtSource implements FeatureSourceForTile {
|
|||
return undefined
|
||||
}
|
||||
|
||||
private downloadSync() {
|
||||
this.download()
|
||||
.then((d) => {
|
||||
if (d.length === 0) {
|
||||
return
|
||||
}
|
||||
return this._features.setData(d)
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
|
||||
private async download(): Promise<GeojsonFeature[]> {
|
||||
const result = await fetch(this._url)
|
||||
if (result.status !== 200) {
|
||||
console.error("Could not download tile " + this._url)
|
||||
return []
|
||||
}
|
||||
const buffer = await result.arrayBuffer()
|
||||
const data = Tile.read(new Pbf(buffer), undefined)
|
||||
const layers = data.layers
|
||||
let layer = data.layers[0]
|
||||
if (layers.length > 1) {
|
||||
if (!this._layerName) {
|
||||
throw "Multiple layers in the downloaded tile, but no layername is given to choose from"
|
||||
private async download(): Promise<void> {
|
||||
try {
|
||||
const result = await fetch(this._url)
|
||||
if (result.status !== 200) {
|
||||
console.error("Could not download tile " + this._url)
|
||||
return
|
||||
}
|
||||
layer = layers.find((l) => l.name === this._layerName)
|
||||
}
|
||||
if (!layer) {
|
||||
return []
|
||||
}
|
||||
const builder = new MvtFeatureBuilder(layer.extent, this.x, this.y, this.z)
|
||||
const features: GeojsonFeature[] = []
|
||||
const buffer = await result.arrayBuffer()
|
||||
const data = Tile.read(new Pbf(buffer), undefined)
|
||||
const layers = data.layers
|
||||
let layer = data.layers[0]
|
||||
if (layers.length > 1) {
|
||||
if (!this._layerName) {
|
||||
throw "Multiple layers in the downloaded tile, but no layername is given to choose from"
|
||||
}
|
||||
layer = layers.find((l) => l.name === this._layerName)
|
||||
}
|
||||
if (!layer) {
|
||||
return
|
||||
}
|
||||
const builder = new MvtFeatureBuilder(layer.extent, this.x, this.y, this.z)
|
||||
const features: GeojsonFeature[] = []
|
||||
|
||||
for (const feature of layer.features) {
|
||||
const properties = this.inflateProperties(feature.tags, layer.keys, layer.values)
|
||||
features.push(builder.toGeoJson(feature.geometry, feature.type, properties))
|
||||
for (const feature of layer.features) {
|
||||
const properties = this.inflateProperties(feature.tags, layer.keys, layer.values)
|
||||
features.push(builder.toGeoJson(feature.geometry, feature.type, properties))
|
||||
}
|
||||
this._features.setData(features)
|
||||
} catch (e) {
|
||||
console.error("Could not download MVT tile due to", e)
|
||||
}
|
||||
|
||||
return features
|
||||
}
|
||||
|
||||
private inflateProperties(tags: number[], keys: string[], values: { string_value: string }[]) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue