Feat: more integrations of panoramax: add panoramax to nearby images view, add to drawer, add icon, see #1451
This commit is contained in:
parent
d079f8379b
commit
5fd1d75efb
24 changed files with 617 additions and 33 deletions
|
@ -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({})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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<string, { data: ImageData, time: Date }> = {}
|
||||
|
||||
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 <ProvidedImage>{
|
||||
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<ProvidedImage[]> {
|
||||
if(!Panoramax.isId(value)){
|
||||
if (!Panoramax.isId(value)) {
|
||||
return undefined
|
||||
}
|
||||
return [await this.getInfoFor(value).then(r => this.featureToImage(<any>r))]
|
||||
|
|
|
@ -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<P4CPicture[]> {
|
||||
|
||||
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<P4CPicture[]> {
|
||||
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<Record<string, "loading" | "done" | "error">>,
|
||||
sink: UIEventSource<P4CPicture[]>
|
||||
sink: UIEventSource<P4CPicture[]>,
|
||||
): Promise<void> {
|
||||
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<P4CPicture[]>
|
||||
state: Store<Record<string, "loading" | "done" | "error">>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue