From 89d31f54bcd9f7f2bf62a663d67aa5bf1308307a Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 28 Sep 2024 12:24:03 +0200 Subject: [PATCH 1/6] Fix: add panoramax.xyz to CSP list --- scripts/generateLayouts.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/generateLayouts.ts b/scripts/generateLayouts.ts index 1297b0eef..006cc6b7a 100644 --- a/scripts/generateLayouts.ts +++ b/scripts/generateLayouts.ts @@ -332,6 +332,8 @@ class GenerateLayouts extends Script { "https://www.openstreetmap.org", "https://api.openstreetmap.org", "https://pietervdvn.goatcounter.com", + "https://api.panoramax.xyz", + "https://panoramax.mapcomplete.org" ].concat(...(await this.eliUrls())) SpecialVisualizations.specialVisualizations.forEach((sv) => { From df9c44aaed7b2670134e8f9128343022e92c6e8d Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 28 Sep 2024 22:42:56 +0200 Subject: [PATCH 2/6] Fix: load external panoramax images --- package-lock.json | 14 +++++++------- package.json | 2 +- src/Logic/ImageProviders/Panoramax.ts | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 62850a967..244e83b8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,7 @@ "opening_hours": "^3.6.0", "osm-auth": "^2.5.0", "osmtogeojson": "^3.0.0-beta.5", - "panoramax-js": "^0.1.4", + "panoramax-js": "^0.1.7", "panzoom": "^9.4.3", "papaparse": "^5.3.1", "pbf": "^3.2.1", @@ -15994,9 +15994,9 @@ "license": "MIT" }, "node_modules/panoramax-js": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.1.4.tgz", - "integrity": "sha512-X7plFMH1ndxiiyVFEluDloNiEBH0nEkurCPJ7zAInxbgv21pp/EGFwu3ynmF5ETyyXB9zu0n309juyjTdJ5pnQ==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.1.7.tgz", + "integrity": "sha512-G5m0cs+kODGyxmuLI06m+BcQecey2d6zhk+bajUMTkeXqvIIxP+CUS6NKnmN0WyIntd4q91gdGJeOR0Q2I/jeg==", "dependencies": { "@ogcapi-js/features": "^1.1.1", "@ogcapi-js/shared": "^1.1.1", @@ -32056,9 +32056,9 @@ "version": "1.0.0" }, "panoramax-js": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.1.4.tgz", - "integrity": "sha512-X7plFMH1ndxiiyVFEluDloNiEBH0nEkurCPJ7zAInxbgv21pp/EGFwu3ynmF5ETyyXB9zu0n309juyjTdJ5pnQ==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.1.7.tgz", + "integrity": "sha512-G5m0cs+kODGyxmuLI06m+BcQecey2d6zhk+bajUMTkeXqvIIxP+CUS6NKnmN0WyIntd4q91gdGJeOR0Q2I/jeg==", "requires": { "@ogcapi-js/features": "^1.1.1", "@ogcapi-js/shared": "^1.1.1", diff --git a/package.json b/package.json index 451e73084..8182bd57f 100644 --- a/package.json +++ b/package.json @@ -205,7 +205,7 @@ "opening_hours": "^3.6.0", "osm-auth": "^2.5.0", "osmtogeojson": "^3.0.0-beta.5", - "panoramax-js": "^0.1.4", + "panoramax-js": "^0.1.7", "panzoom": "^9.4.3", "papaparse": "^5.3.1", "pbf": "^3.2.1", diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index eb73b9636..9536facb6 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -1,10 +1,9 @@ import { ImageUploader } from "./ImageUploader" -import { AuthorizedPanoramax, PanoramaxXYZ, ImageData } from "panoramax-js/dist" +import { AuthorizedPanoramax, ImageData, Panoramax, PanoramaxXYZ } from "panoramax-js/dist" import ExifReader from "exifreader" import ImageProvider, { ProvidedImage } from "./ImageProvider" import BaseUIElement from "../../UI/BaseUIElement" import { LicenseInfo } from "./LicenseInfo" -import { Utils } from "../../Utils" import { GeoOperations } from "../GeoOperations" import Constants from "../../Models/Constants" import { Store, Stores, UIEventSource } from "../UIEventSource" @@ -87,9 +86,9 @@ export default class PanoramaxImageProvider extends ImageProvider { } const cached = PanoramaxImageProvider.knownMeta[id] if (cached) { - if(new Date().getTime() - cached.time.getTime() < 1000){ + if (new Date().getTime() - cached.time.getTime() < 1000) { - return { data: cached.data, url: undefined } + return { data: cached.data, url: undefined } } } try { @@ -100,13 +99,14 @@ export default class PanoramaxImageProvider extends ImageProvider { try { return await this.getInfoFromXYZ(id) } catch (e) { - console.debug(e) + console.debug(e) } return undefined } - - public async ExtractUrls(key: string, value: string): Promise { + if(!Panoramax.isId(value)){ + return undefined + } return [await this.getInfoFor(value).then(r => this.featureToImage(r))] } @@ -115,7 +115,7 @@ export default class PanoramaxImageProvider extends ImageProvider { const source = UIEventSource.FromPromise(super.getRelevantUrlsFor(tags, prefixes)) function hasLoading(data: ProvidedImage[]) { - if(data === undefined){ + if (data === undefined) { return true } return data?.some(img => img?.status !== undefined && img?.status !== "ready" && img?.status !== "broken") From 308d2ba4c62cc66d9c30cd1536549411c0abcb88 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 29 Sep 2024 00:26:07 +0200 Subject: [PATCH 3/6] Themes(drinking water): fix #2184 --- .../layers/drinking_water/drinking_water.json | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index fc0e74030..8a609eeb8 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -160,9 +160,13 @@ }, "mappings": [ { - "if": "operational_status=", + "if": { + "and": [ + "operational_status=", + "disused:amenity=" + ] + }, "addExtraTags": [ - "disused:amenity=", "amenity=drinking_water" ], "then": { @@ -204,6 +208,15 @@ "ca": "Aquesta font d'aigua potable està tancada", "cs": "Tato pitná voda je uzavřena" } + }, + { + "if": "disused:amenity=drinking_water", + "addExtraTags": [ + "amenity=" + ], + "then": { + "en": "This drinking water is permanently closed" + } } ], "id": "Still in use?" From d079f8379b2c4203efe4e65e6234c73f08b9c46a Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 29 Sep 2024 01:04:31 +0200 Subject: [PATCH 4/6] Chore: move code to external package --- package-lock.json | 20 +- package.json | 2 +- src/Logic/FeatureSource/Sources/MvtSource.ts | 405 +------------------ 3 files changed, 24 insertions(+), 403 deletions(-) diff --git a/package-lock.json b/package-lock.json index 244e83b8c..cea1831a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,6 +58,7 @@ "maplibre-gl": "^4.1.1", "marked": "^12.0.2", "monaco-editor": "^0.46.0", + "mvt-to-geojson": "^0.0.2", "name-suggestion-index": "^6.0.20240422", "npm": "^10.7.0", "opening_hours": "^3.6.0", @@ -66,7 +67,6 @@ "panoramax-js": "^0.1.7", "panzoom": "^9.4.3", "papaparse": "^5.3.1", - "pbf": "^3.2.1", "pg": "^8.11.3", "pic4carto": "^2.1.15", "pmtiles": "^3.0.5", @@ -13145,6 +13145,15 @@ "version": "1.0.0", "license": "MIT" }, + "node_modules/mvt-to-geojson": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/mvt-to-geojson/-/mvt-to-geojson-0.0.2.tgz", + "integrity": "sha512-DLi1Hgowqy6ygOUIuOhtI5RLdph9VpQSP6Ct3qbOY/y0dSTBjTvpIXgbK3J0XSO9feBRrhjcQAMkzOVD/KkuDA==", + "dependencies": { + "@types/geojson": "^7946.0.14", + "pbf": "^3.2.1" + } + }, "node_modules/n3": { "version": "1.17.3", "license": "MIT", @@ -30231,6 +30240,15 @@ "murmurhash-js": { "version": "1.0.0" }, + "mvt-to-geojson": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/mvt-to-geojson/-/mvt-to-geojson-0.0.2.tgz", + "integrity": "sha512-DLi1Hgowqy6ygOUIuOhtI5RLdph9VpQSP6Ct3qbOY/y0dSTBjTvpIXgbK3J0XSO9feBRrhjcQAMkzOVD/KkuDA==", + "requires": { + "@types/geojson": "^7946.0.14", + "pbf": "^3.2.1" + } + }, "n3": { "version": "1.17.3", "requires": { diff --git a/package.json b/package.json index 8182bd57f..2f161e592 100644 --- a/package.json +++ b/package.json @@ -200,6 +200,7 @@ "maplibre-gl": "^4.1.1", "marked": "^12.0.2", "monaco-editor": "^0.46.0", + "mvt-to-geojson": "^0.0.2", "name-suggestion-index": "^6.0.20240422", "npm": "^10.7.0", "opening_hours": "^3.6.0", @@ -208,7 +209,6 @@ "panoramax-js": "^0.1.7", "panzoom": "^9.4.3", "papaparse": "^5.3.1", - "pbf": "^3.2.1", "pg": "^8.11.3", "pic4carto": "^2.1.15", "pmtiles": "^3.0.5", diff --git a/src/Logic/FeatureSource/Sources/MvtSource.ts b/src/Logic/FeatureSource/Sources/MvtSource.ts index 1605ba177..b65595016 100644 --- a/src/Logic/FeatureSource/Sources/MvtSource.ts +++ b/src/Logic/FeatureSource/Sources/MvtSource.ts @@ -2,332 +2,8 @@ import { Feature as GeojsonFeature, Geometry } from "geojson" import { Store, UIEventSource } from "../../UIEventSource" import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource" -import Pbf from "pbf" +import { MvtToGeojson } from "mvt-to-geojson" -type Coords = [number, number][] - -class MvtFeatureBuilder { - private static readonly geom_types = ["Unknown", "Point", "LineString", "Polygon"] as const - private readonly _size: number - private readonly _x0: number - private readonly _y0: number - - constructor(extent: number, x: number, y: number, z: number) { - this._size = extent * Math.pow(2, z) - this._x0 = extent * x - this._y0 = extent * y - } - - private static signedArea(ring: Coords): number { - let sum = 0 - const len = ring.length - // J is basically (i - 1) % len - let j = len - 1 - let p1 - let p2 - for (let i = 0; i < len; i++) { - p1 = ring[i] - p2 = ring[j] - sum += (p2.x - p1.x) * (p1.y + p2.y) - j = i - } - return sum - } - - /** - * - * const rings = [ [ [ 3.208361864089966, 51.186908820014736 ], [ 3.2084155082702637, 51.18689537073311 ], [ 3.208436965942383, 51.186888646090836 ], [ 3.2084155082702637, 51.18686174751187 ], [ 3.2084155082702637, 51.18685502286465 ], [ 3.2083725929260254, 51.18686847215807 ], [ 3.2083404064178467, 51.18687519680333 ], [ 3.208361864089966, 51.186908820014736 ] ] ] - * MvtFeatureBuilder.classifyRings(rings) // => [rings] - */ - private static classifyRings(rings: Coords[]): Coords[][] { - if (rings.length <= 0) { - throw "Now rings in polygon found" - } - if (rings.length == 1) { - return [rings] - } - - const polygons: Coords[][] = [] - let currentPolygon: Coords[] - - for (let i = 0; i < rings.length; i++) { - let ring = rings[i] - const area = this.signedArea(ring) - if (area === 0) { - // Weird, degenerate ring - continue - } - const ccw = area < 0 - - if (ccw === area < 0) { - if (currentPolygon) { - polygons.push(currentPolygon) - } - currentPolygon = [ring] - } else { - currentPolygon.push(ring) - } - } - if (currentPolygon) { - polygons.push(currentPolygon) - } - - return polygons - } - - public toGeoJson(geometry: number[], typeIndex: 1 | 2 | 3, properties: any): GeojsonFeature { - let coords: Coords[] = this.encodeGeometry(geometry) - let classified = undefined - switch (typeIndex) { - case 1: - const points = [] - for (let i = 0; i < coords.length; i++) { - points[i] = coords[i][0] - } - coords = points - this.project(coords) - break - - case 2: - for (let i = 0; i < coords.length; i++) { - this.project(coords[i]) - } - break - - case 3: - classified = MvtFeatureBuilder.classifyRings(coords) - for (let i = 0; i < classified.length; i++) { - for (let j = 0; j < classified[i].length; j++) { - this.project(classified[i][j]) - } - } - break - } - - let type: string = MvtFeatureBuilder.geom_types[typeIndex] - let polygonCoords: Coords | Coords[] | Coords[][] - if (coords.length === 1) { - polygonCoords = (classified ?? coords)[0] - } else { - polygonCoords = classified ?? coords - type = "Multi" + type - } - - return { - type: "Feature", - geometry: { - type: type, - coordinates: polygonCoords, - }, - properties, - } - } - - /** - * - * const geometry = [9,233,8704,130,438,1455,270,653,248,423,368,493,362,381,330,267,408,301,406,221,402,157,1078,429,1002,449,1036,577,800,545,1586,1165,164,79,40] - * const builder = new MvtFeatureBuilder(4096, 66705, 43755, 17) - * const expected = [[3.2106759399175644,51.213658395282124],[3.2108227908611298,51.21396418776169],[3.2109133154153824,51.21410154168976],[3.210996463894844,51.214190590500664],[3.211119845509529,51.214294340548975],[3.211241215467453,51.2143745681588],[3.2113518565893173,51.21443085341426],[3.211488649249077,51.21449427925393],[3.2116247713565826,51.214540903490956],[3.211759552359581,51.21457408647774],[3.2121209800243378,51.214664394485254],[3.212456926703453,51.21475890267553],[3.2128042727708817,51.214880292910834],[3.213072493672371,51.214994962285544],[3.2136042416095734,51.21523984134939],[3.2136592268943787,51.21525664260963],[3.213672637939453,51.21525664260963]] - * builder.project(builder.encodeGeometry(geometry)[0]) // => expected - * @param geometry - * @private - */ - private encodeGeometry(geometry: number[]): Coords[] { - let cX = 0 - let cY = 0 - const coordss: Coords[] = [] - let currentRing: Coords = [] - for (let i = 0; i < geometry.length; i++) { - const commandInteger = geometry[i] - const commandId = commandInteger & 0x7 - const commandCount = commandInteger >> 3 - /* - Command Id Parameters Parameter Count - MoveTo 1 dX, dY 2 - LineTo 2 dX, dY 2 - ClosePath 7 No parameters 0 - */ - if (commandId === 1) { - // MoveTo means: we start a new ring - if (currentRing.length !== 0) { - coordss.push(currentRing) - currentRing = [] - } - } - if (commandId === 1 || commandId === 2) { - for (let j = 0; j < commandCount; j++) { - const dx = geometry[i + j * 2 + 1] - cX += (dx >> 1) ^ -(dx & 1) - const dy = geometry[i + j * 2 + 2] - cY += (dy >> 1) ^ -(dy & 1) - currentRing.push([cX, cY]) - } - i += commandCount * 2 - } - if (commandId === 7) { - if (currentRing.length === 0) { - console.error( - "Invalid MVT file: got a 'closePath', but the currentRing is empty. Full command:", - commandInteger - ) - } else { - currentRing.push([...currentRing[0]]) - } - i++ - } - } - if (currentRing.length > 0) { - coordss.push(currentRing) - } - return coordss - } - - /** - * Inline replacement of the location by projecting - * @param line the line which will be rewritten inline - * @return line - */ - private project(line: Coords) { - const y0 = this._y0 - const x0 = this._x0 - const size = this._size - for (let i = 0; i < line.length; i++) { - let p = line[i] - let y2 = 180 - ((p[1] + y0) * 360) / size - line[i] = [ - ((p[0] + x0) * 360) / size - 180, - (360 / Math.PI) * Math.atan(Math.exp((y2 * Math.PI) / 180)) - 90, - ] - } - return line - } -} - -class Layer { - public static read(pbf, end) { - return pbf.readFields( - Layer._readField, - { version: 0, name: "", features: [], keys: [], values: [], extent: 0 }, - end - ) - } - - static _readField(tag, obj, pbf) { - if (tag === 15) obj.version = pbf.readVarint() - else if (tag === 1) obj.name = pbf.readString() - else if (tag === 2) obj.features.push(Feature.read(pbf, pbf.readVarint() + pbf.pos)) - else if (tag === 3) obj.keys.push(pbf.readString()) - 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) - if (obj.features) - for (var i = 0; i < obj.features.length; i++) - pbf.writeMessage(2, Feature.write, obj.features[i]) - if (obj.keys) for (i = 0; i < obj.keys.length; i++) pbf.writeStringField(3, obj.keys[i]) - if (obj.values) - for (i = 0; i < obj.values.length; i++) pbf.writeMessage(4, Value.write, obj.values[i]) - if (obj.extent) pbf.writeVarintField(5, obj.extent) - } -} - -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) - if (obj.type) pbf.writeVarintField(3, obj.type) - if (obj.geometry) pbf.writePackedVarint(4, obj.geometry) - } -} - -class Value { - public static read(pbf, end) { - return pbf.readFields( - Value._readField, - { - string_value: "", - float_value: 0, - double_value: 0, - int_value: 0, - uint_value: 0, - sint_value: 0, - bool_value: false, - }, - end - ) - } - - static _readField = function (tag, obj, pbf) { - if (tag === 1) obj.string_value = pbf.readString() - else if (tag === 2) obj.float_value = pbf.readFloat() - else if (tag === 3) obj.double_value = pbf.readDouble() - else if (tag === 4) obj.int_value = pbf.readVarint(true) - else if (tag === 5) obj.uint_value = pbf.readVarint() - 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) - if (obj.double_value) pbf.writeDoubleField(3, obj.double_value) - if (obj.int_value) pbf.writeVarintField(4, obj.int_value) - if (obj.uint_value) pbf.writeVarintField(5, obj.uint_value) - if (obj.sint_value) pbf.writeSVarintField(6, obj.sint_value) - if (obj.bool_value) pbf.writeBooleanField(7, obj.bool_value) - } -} - -class Tile { - // code generated by pbf v3.2.1 - - static GeomType = { - UNKNOWN: { - value: 0, - options: {}, - }, - POINT: { - value: 1, - options: {}, - }, - LINESTRING: { - value: 2, - options: {}, - }, - POLYGON: { - value: 3, - 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, UpdatableFeatureSource { public readonly features: Store[]> @@ -352,7 +28,7 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature y: number, z: number, layerName?: string, - isActive?: Store + isActive?: Store, ) { this._url = url this._layerName = layerName @@ -367,7 +43,7 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature } return fs }, - [isActive] + [isActive], ) } @@ -378,39 +54,6 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature await this.currentlyRunning } - private getValue(v: { - // Exactly one of these values must be present in a valid message - string_value?: string - float_value?: number - double_value?: number - int_value?: number - uint_value?: number - sint_value?: number - bool_value?: boolean - }): string | number | undefined | boolean { - if (v.string_value !== "") { - return v.string_value - } - if (v.double_value !== 0) { - return v.double_value - } - if (v.float_value !== 0) { - return v.float_value - } - if (v.int_value !== 0) { - return v.int_value - } - if (v.uint_value !== 0) { - return v.uint_value - } - if (v.sint_value !== 0) { - return v.sint_value - } - if (v.bool_value !== false) { - return v.bool_value - } - return undefined - } private async download(): Promise { try { @@ -420,52 +63,12 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature 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" - } - 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)) - } + const features = await MvtToGeojson.fromBuffer(buffer, this.x, this.y, this.z) this._features.setData(features) } catch (e) { console.error("Could not download MVT " + this._url + " tile due to", e) } } - private inflateProperties(tags: number[], keys: string[], values: { string_value: string }[]) { - const properties = {} - for (let i = 0; i < tags.length; i += 2) { - properties[keys[tags[i]]] = this.getValue(values[tags[i + 1]]) - } - let type: string - switch (properties["osm_type"]) { - case "N": - type = "node" - break - case "W": - type = "way" - break - case "R": - type = "relation" - break - } - properties["id"] = type + "/" + properties["osm_id"] - delete properties["osm_id"] - delete properties["osm_type"] - return properties - } } From 5fd1d75efb0fbd49cac3eb5f7a37104887f92f10 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 30 Sep 2024 01:08:07 +0200 Subject: [PATCH 5/6] Feat: more integrations of panoramax: add panoramax to nearby images view, add to drawer, add icon, see #1451 --- assets/svg/license_info.json | 20 ++ assets/svg/panoramax.svg | 187 ++++++++++++++++ assets/svg/panoramax.svg.license | 2 + assets/svg/panoramax_bw.svg | 217 +++++++++++++++++++ assets/svg/panoramax_bw.svg.license | 2 + langs/en.json | 2 + langs/layers/en.json | 3 + package-lock.json | 28 +-- package.json | 4 +- src/Logic/BBox.ts | 7 + src/Logic/FeatureSource/Sources/MvtSource.ts | 23 +- src/Logic/GeoOperations.ts | 7 + src/Logic/ImageProviders/ImageProvider.ts | 5 +- src/Logic/ImageProviders/Mapillary.ts | 3 +- src/Logic/ImageProviders/Panoramax.ts | 21 +- src/Logic/Web/NearbyImagesSearch.ts | 62 +++++- src/UI/BigComponents/MenuDrawer.svelte | 2 + src/UI/BigComponents/PanoramaxLink.svelte | 40 ++++ src/UI/Image/ImageAttribution.svelte | 2 +- src/UI/Image/NearbyImages.svelte | 2 +- src/Utils.ts | 1 - src/assets/svg/Circle.svelte | 2 +- src/assets/svg/Panoramax.svelte | 4 + src/assets/svg/Panoramax_bw.svelte | 4 + 24 files changed, 617 insertions(+), 33 deletions(-) create mode 100644 assets/svg/panoramax.svg create mode 100644 assets/svg/panoramax.svg.license create mode 100644 assets/svg/panoramax_bw.svg create mode 100644 assets/svg/panoramax_bw.svg.license create mode 100644 src/UI/BigComponents/PanoramaxLink.svelte create mode 100644 src/assets/svg/Panoramax.svelte create mode 100644 src/assets/svg/Panoramax_bw.svelte diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 9a6714190..8543720f2 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -877,6 +877,26 @@ "https://www.OpenStreetMap.org" ] }, + { + "path": "panoramax.svg", + "license": "LOGO", + "authors": [ + "Panoramax" + ], + "sources": [ + "https://commons.wikimedia.org/wiki/File:Panoramax.svg" + ] + }, + { + "path": "panoramax_bw.svg", + "license": "LOGO", + "authors": [ + "Panoramax" + ], + "sources": [ + "https://commons.wikimedia.org/wiki/File:Panoramax.svg" + ] + }, { "path": "party.svg", "license": "CC-BY-4.0", diff --git a/assets/svg/panoramax.svg b/assets/svg/panoramax.svg new file mode 100644 index 000000000..ed1746c88 --- /dev/null +++ b/assets/svg/panoramax.svg @@ -0,0 +1,187 @@ + + + +image/svg+xml diff --git a/assets/svg/panoramax.svg.license b/assets/svg/panoramax.svg.license new file mode 100644 index 000000000..f0263b4cb --- /dev/null +++ b/assets/svg/panoramax.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Panoramax +SPDX-License-Identifier: LicenseRef-LOGO \ No newline at end of file diff --git a/assets/svg/panoramax_bw.svg b/assets/svg/panoramax_bw.svg new file mode 100644 index 000000000..e50264ff3 --- /dev/null +++ b/assets/svg/panoramax_bw.svg @@ -0,0 +1,217 @@ + + + +image/svg+xml + + + + + + diff --git a/assets/svg/panoramax_bw.svg.license b/assets/svg/panoramax_bw.svg.license new file mode 100644 index 000000000..f0263b4cb --- /dev/null +++ b/assets/svg/panoramax_bw.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Panoramax +SPDX-License-Identifier: LicenseRef-LOGO \ No newline at end of file diff --git a/langs/en.json b/langs/en.json index 300dca8a2..7e5907479 100644 --- a/langs/en.json +++ b/langs/en.json @@ -202,7 +202,9 @@ "openMapillary": "Open Mapillary here", "openOsmcha": "See latest edits made with {theme}", "openOsmchaLastWeek": "See edits from the last 7 days", + "openPanoramax": "Open Panoramax here", "openThemeDocumentation": "Open the documentation for thematic map {name}", + "panoramaxHelp": "Panoramax is an online service which gathers street-level pictures and offers them under a free license. Contributors are allowed to use these pictures to improve OpenStreetMap", "seeOnMapillary": "See this image on Mapillary", "themeBy": "Theme maintained by {author}", "title": "Copyright and attribution", diff --git a/langs/layers/en.json b/langs/layers/en.json index 9158b0c40..60daa8113 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4142,6 +4142,9 @@ }, "2": { "then": "This drinking water is closed" + }, + "3": { + "then": "This drinking water is permanently closed" } }, "question": "Is this drinking water spot still operational?", diff --git a/package-lock.json b/package-lock.json index cea1831a3..ef5e1ba5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,13 +58,13 @@ "maplibre-gl": "^4.1.1", "marked": "^12.0.2", "monaco-editor": "^0.46.0", - "mvt-to-geojson": "^0.0.2", + "mvt-to-geojson": "^0.0.5", "name-suggestion-index": "^6.0.20240422", "npm": "^10.7.0", "opening_hours": "^3.6.0", "osm-auth": "^2.5.0", "osmtogeojson": "^3.0.0-beta.5", - "panoramax-js": "^0.1.7", + "panoramax-js": "^0.3.6", "panzoom": "^9.4.3", "papaparse": "^5.3.1", "pg": "^8.11.3", @@ -13146,9 +13146,9 @@ "license": "MIT" }, "node_modules/mvt-to-geojson": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/mvt-to-geojson/-/mvt-to-geojson-0.0.2.tgz", - "integrity": "sha512-DLi1Hgowqy6ygOUIuOhtI5RLdph9VpQSP6Ct3qbOY/y0dSTBjTvpIXgbK3J0XSO9feBRrhjcQAMkzOVD/KkuDA==", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mvt-to-geojson/-/mvt-to-geojson-0.0.5.tgz", + "integrity": "sha512-IG4bJJP9nxY/9LenWBY0yi5SEv9a1G06trP5b4Tg01LCrzB32US/FO9bygJr7c+ZVbt175AzBwJiiawaxfLTbA==", "dependencies": { "@types/geojson": "^7946.0.14", "pbf": "^3.2.1" @@ -16003,9 +16003,9 @@ "license": "MIT" }, "node_modules/panoramax-js": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.1.7.tgz", - "integrity": "sha512-G5m0cs+kODGyxmuLI06m+BcQecey2d6zhk+bajUMTkeXqvIIxP+CUS6NKnmN0WyIntd4q91gdGJeOR0Q2I/jeg==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.6.tgz", + "integrity": "sha512-CRdXwh91H6chd1PYptG070ukx+S6IkVaUeQVG91ySevoJoCYOuBT65qkMhRo49X2um1nGcs9UqolW90R57875g==", "dependencies": { "@ogcapi-js/features": "^1.1.1", "@ogcapi-js/shared": "^1.1.1", @@ -30241,9 +30241,9 @@ "version": "1.0.0" }, "mvt-to-geojson": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/mvt-to-geojson/-/mvt-to-geojson-0.0.2.tgz", - "integrity": "sha512-DLi1Hgowqy6ygOUIuOhtI5RLdph9VpQSP6Ct3qbOY/y0dSTBjTvpIXgbK3J0XSO9feBRrhjcQAMkzOVD/KkuDA==", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mvt-to-geojson/-/mvt-to-geojson-0.0.5.tgz", + "integrity": "sha512-IG4bJJP9nxY/9LenWBY0yi5SEv9a1G06trP5b4Tg01LCrzB32US/FO9bygJr7c+ZVbt175AzBwJiiawaxfLTbA==", "requires": { "@types/geojson": "^7946.0.14", "pbf": "^3.2.1" @@ -32074,9 +32074,9 @@ "version": "1.0.0" }, "panoramax-js": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.1.7.tgz", - "integrity": "sha512-G5m0cs+kODGyxmuLI06m+BcQecey2d6zhk+bajUMTkeXqvIIxP+CUS6NKnmN0WyIntd4q91gdGJeOR0Q2I/jeg==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.6.tgz", + "integrity": "sha512-CRdXwh91H6chd1PYptG070ukx+S6IkVaUeQVG91ySevoJoCYOuBT65qkMhRo49X2um1nGcs9UqolW90R57875g==", "requires": { "@ogcapi-js/features": "^1.1.1", "@ogcapi-js/shared": "^1.1.1", diff --git a/package.json b/package.json index 2f161e592..7137590a0 100644 --- a/package.json +++ b/package.json @@ -200,13 +200,13 @@ "maplibre-gl": "^4.1.1", "marked": "^12.0.2", "monaco-editor": "^0.46.0", - "mvt-to-geojson": "^0.0.2", + "mvt-to-geojson": "^0.0.5", "name-suggestion-index": "^6.0.20240422", "npm": "^10.7.0", "opening_hours": "^3.6.0", "osm-auth": "^2.5.0", "osmtogeojson": "^3.0.0-beta.5", - "panoramax-js": "^0.1.7", + "panoramax-js": "^0.3.6", "panzoom": "^9.4.3", "papaparse": "^5.3.1", "pg": "^8.11.3", diff --git a/src/Logic/BBox.ts b/src/Logic/BBox.ts index b54928418..5f98cb726 100644 --- a/src/Logic/BBox.ts +++ b/src/Logic/BBox.ts @@ -249,6 +249,13 @@ export class BBox { ] } + toLngLatFlat(): [number, number, number, number] { + return [ + this.minLon, this.minLat, + this.maxLon, this.maxLat, + ] + } + public asGeojsonCached() { if (this["geojsonCache"] === undefined) { this["geojsonCache"] = this.asGeoJson({}) diff --git a/src/Logic/FeatureSource/Sources/MvtSource.ts b/src/Logic/FeatureSource/Sources/MvtSource.ts index b65595016..6342fea90 100644 --- a/src/Logic/FeatureSource/Sources/MvtSource.ts +++ b/src/Logic/FeatureSource/Sources/MvtSource.ts @@ -63,7 +63,28 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature return } const buffer = await result.arrayBuffer() - const features = await MvtToGeojson.fromBuffer(buffer, this.x, this.y, this.z) + const features = MvtToGeojson.fromBuffer(buffer, this.x, this.y, this.z) + for (const feature of features) { + const properties = feature.properties + if(!properties["osm_type"]){ + continue + } + let type: string = "node" + switch (properties["osm_type"]) { + case "N": + type = "node" + break + case "W": + type = "way" + break + case "R": + type = "relation" + break + } + properties["id"] = type + "/" + properties["osm_id"] + delete properties["osm_id"] + delete properties["osm_type"] + } this._features.setData(features) } catch (e) { console.error("Could not download MVT " + this._url + " tile due to", e) diff --git a/src/Logic/GeoOperations.ts b/src/Logic/GeoOperations.ts index 7c3dabb6c..ab3795182 100644 --- a/src/Logic/GeoOperations.ts +++ b/src/Logic/GeoOperations.ts @@ -92,6 +92,13 @@ export class GeoOperations { return turf.distance(lonlat0, lonlat1, { units: "meters" }) } + /** + * Starting on `from`, travels `distance` meters in the direction of the `bearing` (default: 90) + */ + static destination(from: Coord | [number,number],distance: number, bearing: number = 90): [number,number]{ + return <[number,number]> turf.destination(from, distance, bearing, {units: "meters"}).geometry.coordinates + } + static convexHull(featureCollection, options: { concavity?: number }) { return turf.convex(featureCollection, options) } diff --git a/src/Logic/ImageProviders/ImageProvider.ts b/src/Logic/ImageProviders/ImageProvider.ts index 50878a94a..4588297c8 100644 --- a/src/Logic/ImageProviders/ImageProvider.ts +++ b/src/Logic/ImageProviders/ImageProvider.ts @@ -17,7 +17,8 @@ export interface ProvidedImage { */ rotation?: number lat?: number, - lon?: number + lon?: number, + host?: string } export default abstract class ImageProvider { @@ -25,7 +26,7 @@ export default abstract class ImageProvider { public abstract readonly name: string - public abstract SourceIcon(id?: string, location?: { lon: number; lat: number }): BaseUIElement + public abstract SourceIcon(img?: {id: string, url: string, host?: string}, location?: { lon: number; lat: number }): BaseUIElement /** diff --git a/src/Logic/ImageProviders/Mapillary.ts b/src/Logic/ImageProviders/Mapillary.ts index 5a26e31c2..2cc370f32 100644 --- a/src/Logic/ImageProviders/Mapillary.ts +++ b/src/Logic/ImageProviders/Mapillary.ts @@ -118,13 +118,14 @@ export class Mapillary extends ImageProvider { } SourceIcon( - id: string, + img: {id: string, url: string}, location?: { lon: number lat: number } ): BaseUIElement { let url: string = undefined + const id = img.id if (id) { url = Mapillary.createLink(location, 16, "" + id) } diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index 9536facb6..0fc4da425 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -7,6 +7,9 @@ import { LicenseInfo } from "./LicenseInfo" import { GeoOperations } from "../GeoOperations" import Constants from "../../Models/Constants" import { Store, Stores, UIEventSource } from "../UIEventSource" +import SvelteUIElement from "../../UI/Base/SvelteUIElement" +import Panoramax_bw from "../../assets/svg/Panoramax_bw.svelte" +import Link from "../../UI/Base/Link" export default class PanoramaxImageProvider extends ImageProvider { @@ -14,13 +17,18 @@ export default class PanoramaxImageProvider extends ImageProvider { public static readonly singleton = new PanoramaxImageProvider() private static readonly xyz = new PanoramaxXYZ() private static defaultPanoramax = new AuthorizedPanoramax(Constants.panoramax.url, Constants.panoramax.token) + public defaultKeyPrefixes: string[] = ["panoramax"] public readonly name: string = "panoramax" private static knownMeta: Record = {} - public SourceIcon(id?: string, location?: { lon: number; lat: number; }): BaseUIElement { - return undefined + public SourceIcon(img?: { id: string, url: string, host?: string }, location?: { lon: number; lat: number; }): BaseUIElement { + const p = new Panoramax(img.host) + return new Link(new SvelteUIElement(Panoramax_bw), p.createViewLink({ + imageId: img?.id, + location + }), true) } public addKnownMeta(meta: ImageData) { @@ -35,7 +43,7 @@ export default class PanoramaxImageProvider extends ImageProvider { private async getInfoFromMapComplete(id: string): Promise<{ data: ImageData, url: string }> { const sequence = "6e702976-580b-419c-8fb3-cf7bd364e6f8" // We always reuse this sequence const url = `https://panoramax.mapcomplete.org/` - const data = await PanoramaxImageProvider.defaultPanoramax.imageInfo(sequence, id) + const data = await PanoramaxImageProvider.defaultPanoramax.imageInfo(id, sequence) return { url, data } } @@ -67,10 +75,14 @@ export default class PanoramaxImageProvider extends ImageProvider { } const [lon, lat] = GeoOperations.centerpointCoordinates(meta) + const hd = meta.properties + console.log(">>>",meta) + // const hdUrl = new URL(hd) return { id: meta.id, url: makeAbsolute(meta.assets.sd.href), url_hd: makeAbsolute(meta.assets.hd.href), + host: meta["links"].find(l => l.rel === "root")?.href, lon, lat, key: "panoramax", provider: this, @@ -103,8 +115,9 @@ export default class PanoramaxImageProvider extends ImageProvider { } return undefined } + public async ExtractUrls(key: string, value: string): Promise { - if(!Panoramax.isId(value)){ + if (!Panoramax.isId(value)) { return undefined } return [await this.getInfoFor(value).then(r => this.featureToImage(r))] diff --git a/src/Logic/Web/NearbyImagesSearch.ts b/src/Logic/Web/NearbyImagesSearch.ts index 49853485d..bf9f571f4 100644 --- a/src/Logic/Web/NearbyImagesSearch.ts +++ b/src/Logic/Web/NearbyImagesSearch.ts @@ -10,6 +10,7 @@ import { Point } from "geojson" import MvtSource from "../FeatureSource/Sources/MvtSource" import AllImageProviders from "../ImageProviders/AllImageProviders" import { Imgur } from "../ImageProviders/Imgur" +import { Panoramax, PanoramaxXYZ } from "panoramax-js/dist" interface ImageFetcher { /** @@ -102,7 +103,7 @@ class P4CImageFetcher implements ImageFetcher { { mindate: new Date().getTime() - maxAgeSeconds, towardscenter: false, - } + }, ) } catch (e) { console.log("P4C image fetcher failed with", e) @@ -163,6 +164,55 @@ class ImagesInLoadedDataFetcher implements ImageFetcher { } } +class ImagesFromPanoramaxFetcher implements ImageFetcher { + private readonly _radius: number + private readonly _panoramax: Panoramax + name: string = "panoramax" + + constructor(url?: string, radius: number = 100) { + this._radius = radius + if (url) { + + this._panoramax = new Panoramax(url) + } else { + this._panoramax = new PanoramaxXYZ() + } + } + + + public async fetchImages(lat: number, lon: number): Promise { + + const bboxObj = new BBox([ + GeoOperations.destination([lon, lat], this._radius * Math.sqrt(2), -45), + GeoOperations.destination([lon, lat], this._radius * Math.sqrt(2), 135), + ]) + const bbox: [number, number, number, number] = bboxObj.toLngLatFlat() + const images = await this._panoramax.search({ bbox, limit: 1000 }) + + return images.map(i => { + const [lng, lat] = i.geometry.coordinates + return ({ + pictureUrl: i.assets.sd.href, + coordinates: { lng, lat }, + + provider: "panoramax", + direction: i.properties["view:azimuth"], + osmTags: { + "panoramax": i.id, + }, + thumbUrl: i.assets.thumb.href, + date: new Date(i.properties.datetime).getTime(), + license: i.properties["geovisio:license"], + author: i.providers.at(-1).name, + detailsUrl: i.id, + details: { + isSpherical: i.properties["exif"]["Xmp.GPano.ProjectionType"] === "equirectangular", + }, + }) + }) + } +} + class ImagesFromCacheServerFetcher implements ImageFetcher { private readonly _searchRadius: number public readonly name = "fromCacheServer" @@ -186,7 +236,7 @@ class ImagesFromCacheServerFetcher implements ImageFetcher { async fetchImagesForType( targetlat: number, targetlon: number, - type: "lines" | "pois" | "polygons" + type: "lines" | "pois" | "polygons", ): Promise { const { x, y, z } = Tiles.embedded_tile(targetlat, targetlon, 14) @@ -203,7 +253,7 @@ class ImagesFromCacheServerFetcher implements ImageFetcher { }), x, y, - z + z, ) await src.updateAsync() return src.features.data @@ -360,6 +410,8 @@ export class CombinedFetcher { this.sources = [ new ImagesInLoadedDataFetcher(indexedFeatures, radius), new ImagesFromCacheServerFetcher(radius), + new ImagesFromPanoramaxFetcher(), + new ImagesFromPanoramaxFetcher(Constants.panoramax.url), new MapillaryFetcher({ panoramas: "no", max_images: 25, @@ -375,7 +427,7 @@ export class CombinedFetcher { lat: number, lon: number, state: UIEventSource>, - sink: UIEventSource + sink: UIEventSource, ): Promise { try { const pics = await source.fetchImages(lat, lon) @@ -408,7 +460,7 @@ export class CombinedFetcher { public getImagesAround( lon: number, - lat: number + lat: number, ): { images: Store state: Store> diff --git a/src/UI/BigComponents/MenuDrawer.svelte b/src/UI/BigComponents/MenuDrawer.svelte index 989e19ebe..8e95253db 100644 --- a/src/UI/BigComponents/MenuDrawer.svelte +++ b/src/UI/BigComponents/MenuDrawer.svelte @@ -48,6 +48,7 @@ import Copyright from "../../assets/svg/Copyright.svelte" import Pencil from "../../assets/svg/Pencil.svelte" import Squares2x2 from "@babeard/svelte-heroicons/mini/Squares2x2" + import PanoramaxLink from "./PanoramaxLink.svelte" export let state: ThemeViewState let userdetails = state.osmConnection.userDetails @@ -229,6 +230,7 @@ + diff --git a/src/UI/BigComponents/PanoramaxLink.svelte b/src/UI/BigComponents/PanoramaxLink.svelte new file mode 100644 index 000000000..058674c06 --- /dev/null +++ b/src/UI/BigComponents/PanoramaxLink.svelte @@ -0,0 +1,40 @@ + + + + + {#if large} +
+ + +
+ {:else} + + {/if} +
diff --git a/src/UI/Image/ImageAttribution.svelte b/src/UI/Image/ImageAttribution.svelte index 9594c87d8..46bb525fc 100644 --- a/src/UI/Image/ImageAttribution.svelte +++ b/src/UI/Image/ImageAttribution.svelte @@ -16,7 +16,7 @@ let license: Store = UIEventSource.FromPromise( image.provider?.DownloadAttribution(image) ) - let icon = image.provider?.SourceIcon(image.id) + let icon = image.provider?.SourceIcon(image) {#if $license !== undefined} diff --git a/src/UI/Image/NearbyImages.svelte b/src/UI/Image/NearbyImages.svelte index d70dc2ba5..ca9f3ceb8 100644 --- a/src/UI/Image/NearbyImages.svelte +++ b/src/UI/Image/NearbyImages.svelte @@ -130,7 +130,7 @@ for (const f of features) { bbox = bbox.unionWith(BBox.get(f)) } - mapProperties.maxbounds.set(bbox.pad(1.1)) + mapProperties.maxbounds.set(bbox.pad(4)) }) ) diff --git a/src/Utils.ts b/src/Utils.ts index b2b7f879f..421c6c5cf 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1488,7 +1488,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be if (!element) { return } - console.log("Scrolling into view:", element) // Is the element completely in the view? const parentRect = Utils.findParentWithScrolling(element)?.getBoundingClientRect() if (!parentRect) { diff --git a/src/assets/svg/Circle.svelte b/src/assets/svg/Circle.svelte index f1926471a..510ddce25 100644 --- a/src/assets/svg/Circle.svelte +++ b/src/assets/svg/Circle.svelte @@ -1,4 +1,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/svg/Panoramax.svelte b/src/assets/svg/Panoramax.svelte new file mode 100644 index 000000000..6a20baf15 --- /dev/null +++ b/src/assets/svg/Panoramax.svelte @@ -0,0 +1,4 @@ + + image/svg+xml \ No newline at end of file diff --git a/src/assets/svg/Panoramax_bw.svelte b/src/assets/svg/Panoramax_bw.svelte new file mode 100644 index 000000000..71900d7f0 --- /dev/null +++ b/src/assets/svg/Panoramax_bw.svelte @@ -0,0 +1,4 @@ + + image/svg+xml \ No newline at end of file From 494fbaa320f18f34f7c2f7b95a24d1adc93f2926 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 30 Sep 2024 01:20:00 +0200 Subject: [PATCH 6/6] chore(release): 0.46.12 --- CHANGELOG.md | 18 ++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5099f6d7..95cc447ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.46.12](https://github.com/USERNAME/REPOSITORY_NAME/compare/v0.46.11...v0.46.12) (2024-09-29) + + +### Features + +* more integrations of panoramax: add panoramax to nearby images view, add to drawer, add icon, see [#1451](https://github.com/pietervdvn/MapComplete/issues/1451) ([5fd1d75](https://github.com/USERNAME/REPOSITORY_NAME/commits5fd1d75efb0fbd49cac3eb5f7a37104887f92f10)) + + +### Bug Fixes + +* add panoramax.xyz to CSP list ([89d31f5](https://github.com/USERNAME/REPOSITORY_NAME/commits89d31f54bcd9f7f2bf62a663d67aa5bf1308307a)) +* load external panoramax images ([df9c44a](https://github.com/USERNAME/REPOSITORY_NAME/commitsdf9c44aaed7b2670134e8f9128343022e92c6e8d)) + + +### Theme improvements + +* **drinking water:** fix [#2184](https://github.com/pietervdvn/MapComplete/issues/2184) ([308d2ba](https://github.com/USERNAME/REPOSITORY_NAME/commits308d2ba4c62cc66d9c30cd1536549411c0abcb88)) + ### [0.46.11](https://github.com/USERNAME/REPOSITORY_NAME/compare/v0.46.10...v0.46.11) (2024-09-28) diff --git a/package-lock.json b/package-lock.json index ef5e1ba5d..e5dc330ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mapcomplete", - "version": "0.46.11", + "version": "0.46.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mapcomplete", - "version": "0.46.11", + "version": "0.46.12", "license": "GPL-3.0-or-later", "dependencies": { "@comunica/core": "^3.0.1", diff --git a/package.json b/package.json index 7137590a0..0742d2f40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapcomplete", - "version": "0.46.11", + "version": "0.46.12", "repository": "https://github.com/pietervdvn/MapComplete", "description": "A small website to edit OSM easily", "bugs": "https://github.com/pietervdvn/MapComplete/issues",