forked from MapComplete/MapComplete
Merge master
This commit is contained in:
commit
51fa48a01f
151 changed files with 16260 additions and 1872 deletions
|
|
@ -165,9 +165,12 @@ class MvtFeatureBuilder {
|
|||
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{
|
||||
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++
|
||||
|
|
@ -438,7 +441,7 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature
|
|||
}
|
||||
this._features.setData(features)
|
||||
} catch (e) {
|
||||
console.error("Could not download MVT "+this._url+" tile due to", e)
|
||||
console.error("Could not download MVT " + this._url + " tile due to", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export default class AllImageProviders {
|
|||
Mapillary.singleton,
|
||||
WikidataImageProvider.singleton,
|
||||
WikimediaImageProvider.singleton,
|
||||
AllImageProviders.genericImageProvider
|
||||
AllImageProviders.genericImageProvider,
|
||||
]
|
||||
public static apiUrls: string[] = [].concat(
|
||||
...AllImageProviders.ImageAttributionSource.map((src) => src.apiUrls())
|
||||
|
|
@ -52,15 +52,13 @@ export default class AllImageProviders {
|
|||
}
|
||||
|
||||
public static async selectBestProvider(key: string, value: string): Promise<ImageProvider> {
|
||||
|
||||
for (const imageProvider of AllImageProviders.ImageAttributionSource) {
|
||||
try{
|
||||
|
||||
const extracted = await Promise.all(await imageProvider.ExtractUrls(key, value))
|
||||
if(extracted?.length > 0){
|
||||
return imageProvider
|
||||
}
|
||||
}catch (e) {
|
||||
try {
|
||||
const extracted = await Promise.all(await imageProvider.ExtractUrls(key, value))
|
||||
if (extracted?.length > 0) {
|
||||
return imageProvider
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Provider gave an error while trying to determine a match:", e)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,9 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
"titles=" +
|
||||
filename +
|
||||
"&format=json&origin=*"
|
||||
const data = await Utils.downloadJsonCached<{query: {pages: {title: string, imageinfo: { extmetadata} []}[]}}>(url, 365 * 24 * 60 * 60)
|
||||
const data = await Utils.downloadJsonCached<{
|
||||
query: { pages: { title: string; imageinfo: { extmetadata }[] }[] }
|
||||
}>(url, 365 * 24 * 60 * 60)
|
||||
const licenseInfo = new LicenseInfo()
|
||||
const pageInfo = data.query.pages.at(-1)
|
||||
if (pageInfo === undefined) {
|
||||
|
|
|
|||
|
|
@ -576,7 +576,7 @@ export class Changes {
|
|||
if (createdIds.has(c.id)) {
|
||||
toUpload.push(c)
|
||||
} else {
|
||||
(this._reportError)(
|
||||
this._reportError(
|
||||
`Got an orphaned change. The 'creation'-change description for ${c.type}/${c.id} got lost. Permanently dropping this change:` +
|
||||
JSON.stringify(c)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ export class Geocoding {
|
|||
const b = bbox ?? BBox.global
|
||||
const url = `${
|
||||
Geocoding.host
|
||||
}search?format=json&limit=1&viewbox=${b.getEast()},${b.getNorth()},${b.getWest()},${b.getSouth()}&accept-language=${Locale.language.data}&q=${query}`
|
||||
}search?format=json&limit=1&viewbox=${b.getEast()},${b.getNorth()},${b.getWest()},${b.getSouth()}&accept-language=${
|
||||
Locale.language.data
|
||||
}&q=${query}`
|
||||
return Utils.downloadJson(url)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export abstract class OsmObject {
|
|||
// @ts-ignore
|
||||
this.type = type
|
||||
this.tags = {
|
||||
id: `${this.type}/${id}`
|
||||
id: `${this.type}/${id}`,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ export abstract class OsmObject {
|
|||
const allGeojsons = OsmToGeoJson(
|
||||
{ elements },
|
||||
{
|
||||
flatProperties: true
|
||||
flatProperties: true,
|
||||
}
|
||||
)
|
||||
const feature = allGeojsons.features.find(
|
||||
|
|
@ -120,7 +120,7 @@ export abstract class OsmObject {
|
|||
const blacklist = polygonFeature.polygon === "blacklist"
|
||||
result.set(key, {
|
||||
values: new Set<string>(polygonFeature.values),
|
||||
blacklist: blacklist
|
||||
blacklist: blacklist,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +151,9 @@ export abstract class OsmObject {
|
|||
}
|
||||
const v = this.tags[key]
|
||||
if (v !== "" && v !== undefined) {
|
||||
tags += ` <tag k="${Utils.EncodeXmlValue(key)}" v="${Utils.EncodeXmlValue(this.tags[key])}"/>
|
||||
tags += ` <tag k="${Utils.EncodeXmlValue(key)}" v="${Utils.EncodeXmlValue(
|
||||
this.tags[key]
|
||||
)}"/>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
|
@ -212,7 +214,7 @@ export class OsmNode extends OsmObject {
|
|||
ChangesetXML(changesetId: string, header?: string): string {
|
||||
const tags = this.TagsXML()
|
||||
return ` <node id="${this.id}" ${header ?? ""} ${
|
||||
changesetId ? " changeset=\"" + changesetId + "\" " : ""
|
||||
changesetId ? ' changeset="' + changesetId + '" ' : ""
|
||||
}${this.VersionXML()} lat="${this.lat}" lon="${this.lon}">
|
||||
${tags} </node>
|
||||
`
|
||||
|
|
@ -233,8 +235,8 @@ ${tags} </node>
|
|||
properties: this.tags,
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [this.lon, this.lat]
|
||||
}
|
||||
coordinates: [this.lon, this.lat],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -264,11 +266,11 @@ export class OsmWay extends OsmObject {
|
|||
const tags = this.TagsXML()
|
||||
let nds = ""
|
||||
for (const node in this.nodes) {
|
||||
nds += " <nd ref=\"" + this.nodes[node] + "\"/>\n"
|
||||
nds += ' <nd ref="' + this.nodes[node] + '"/>\n'
|
||||
}
|
||||
|
||||
return ` <way id="${this.id}" ${header ?? ""} ${
|
||||
changesetId ? "changeset=\"" + changesetId + "\" " : ""
|
||||
changesetId ? 'changeset="' + changesetId + '" ' : ""
|
||||
} ${this.VersionXML()}>
|
||||
${nds}${tags} </way>
|
||||
`
|
||||
|
|
@ -313,18 +315,18 @@ ${nds}${tags} </way>
|
|||
if (this.isPolygon()) {
|
||||
geometry = {
|
||||
type: "Polygon",
|
||||
coordinates: [coordinates]
|
||||
coordinates: [coordinates],
|
||||
}
|
||||
} else {
|
||||
geometry = {
|
||||
type: "LineString",
|
||||
coordinates: coordinates
|
||||
coordinates: coordinates,
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: "Feature",
|
||||
properties: <any>this.tags,
|
||||
geometry
|
||||
geometry,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,8 +54,14 @@ export class Overpass {
|
|||
return `${this._interpreterUrl}?data=${encodeURIComponent(query)}`
|
||||
}
|
||||
|
||||
private async ExecuteQuery(query: string): Promise<[FeatureCollection<Geometry, OsmTags>, Date]> {
|
||||
const json = await Utils.downloadJson<{elements: [], remark, osm3s: {timestamp_osm_base: string}}>(this.buildUrl(query))
|
||||
private async ExecuteQuery(
|
||||
query: string
|
||||
): Promise<[FeatureCollection<Geometry, OsmTags>, Date]> {
|
||||
const json = await Utils.downloadJson<{
|
||||
elements: []
|
||||
remark
|
||||
osm3s: { timestamp_osm_base: string }
|
||||
}>(this.buildUrl(query))
|
||||
|
||||
if (json.elements.length === 0 && json.remark !== undefined) {
|
||||
console.warn("Timeout or other runtime error while querying overpass", json.remark)
|
||||
|
|
|
|||
|
|
@ -194,11 +194,17 @@ export class And extends TagsFilter {
|
|||
* const expr = <And> TagUtils.Tag({and: ["sport=climbing", {or:["club~*", "office~*"]}]} )
|
||||
* expr.removePhraseConsideredKnown(new Tag("club","climbing"), false) // => expr
|
||||
*/
|
||||
removePhraseConsideredKnown(knownExpression: TagsFilter, value: boolean): (TagsFilterClosed & OptimizedTag) | boolean {
|
||||
removePhraseConsideredKnown(
|
||||
knownExpression: TagsFilter,
|
||||
value: boolean
|
||||
): (TagsFilterClosed & OptimizedTag) | boolean {
|
||||
const newAnds: TagsFilter[] = []
|
||||
for (const tag of this.and) {
|
||||
if (tag instanceof And) {
|
||||
throw "Optimize expressions before using removePhraseConsideredKnown. Found an AND in an AND: " + this.asHumanString()
|
||||
throw (
|
||||
"Optimize expressions before using removePhraseConsideredKnown. Found an AND in an AND: " +
|
||||
this.asHumanString()
|
||||
)
|
||||
}
|
||||
if (tag instanceof Or) {
|
||||
// Second try
|
||||
|
|
@ -449,9 +455,9 @@ export class And extends TagsFilter {
|
|||
} else {
|
||||
const newOrs: TagsFilterClosed[] = []
|
||||
for (const containedOr of containedOrs) {
|
||||
const elements: (FlatTag | (And & OptimizedTag))[] = TagTypes.safeOr( containedOr).filter(
|
||||
(candidate) => !commonValues.some((cv) => cv.shadows(candidate))
|
||||
)
|
||||
const elements: (FlatTag | (And & OptimizedTag))[] = TagTypes.safeOr(
|
||||
containedOr
|
||||
).filter((candidate) => !commonValues.some((cv) => cv.shadows(candidate)))
|
||||
if (elements.length > 0) {
|
||||
newOrs.push(Or.construct(elements))
|
||||
}
|
||||
|
|
@ -464,7 +470,7 @@ export class And extends TagsFilter {
|
|||
return false
|
||||
} else if (result === true) {
|
||||
// neutral element: skip
|
||||
}else if(result instanceof And) {
|
||||
} else if (result instanceof And) {
|
||||
newAnds.push(...TagTypes.safeAnd(result))
|
||||
} else {
|
||||
newAnds.push(result)
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ export default class ComparingTag extends TagsFilter {
|
|||
}
|
||||
|
||||
optimize(): (ComparingTag & OptimizedTag) | boolean {
|
||||
return <any> this
|
||||
return <any>this
|
||||
}
|
||||
|
||||
isNegative(): boolean {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export class Or extends TagsFilter {
|
|||
|
||||
public static construct(or: TagsFilter[]): TagsFilter
|
||||
public static construct<T extends TagsFilter>(or: [T]): T
|
||||
public static construct(or: ((And & OptimizedTag) | FlatTag)[]): (TagsFilterClosed & OptimizedTag)
|
||||
public static construct(or: ((And & OptimizedTag) | FlatTag)[]): TagsFilterClosed & OptimizedTag
|
||||
public static construct(or: TagsFilter[]): TagsFilter {
|
||||
if (or.length === 1) {
|
||||
return or[0]
|
||||
|
|
@ -63,7 +63,11 @@ export class Or extends TagsFilter {
|
|||
return choices
|
||||
}
|
||||
|
||||
asHumanString(linkToWiki: boolean = false, shorten: boolean = false, properties: Record<string, string> = {}) {
|
||||
asHumanString(
|
||||
linkToWiki: boolean = false,
|
||||
shorten: boolean = false,
|
||||
properties: Record<string, string> = {}
|
||||
) {
|
||||
return this.or
|
||||
.map((t) => {
|
||||
let e = t.asHumanString(linkToWiki, shorten, properties)
|
||||
|
|
@ -125,11 +129,17 @@ export class Or extends TagsFilter {
|
|||
* new Or([ new Tag("key","value") ]).removePhraseConsideredKnown(new Tag("key","value"), false) // => false
|
||||
* new Or([new RegexTag("x", "y", true),new RegexTag("c", "d")]).removePhraseConsideredKnown(new Tag("foo","bar"), false) // => new Or([new RegexTag("c", "d"), new RegexTag("x", "y", true)])
|
||||
*/
|
||||
removePhraseConsideredKnown(knownExpression: TagsFilter, value: boolean): (TagsFilterClosed & OptimizedTag) | boolean {
|
||||
removePhraseConsideredKnown(
|
||||
knownExpression: TagsFilter,
|
||||
value: boolean
|
||||
): (TagsFilterClosed & OptimizedTag) | boolean {
|
||||
const newOrs: TagsFilter[] = []
|
||||
for (const tag of this.or) {
|
||||
if (tag instanceof Or) {
|
||||
throw "Optimize expressions before using removePhraseConsideredKnown. Found an OR in an OR: " + this.asHumanString()
|
||||
throw (
|
||||
"Optimize expressions before using removePhraseConsideredKnown. Found an OR in an OR: " +
|
||||
this.asHumanString()
|
||||
)
|
||||
}
|
||||
if (tag instanceof And) {
|
||||
const r = tag.removePhraseConsideredKnown(knownExpression, value)
|
||||
|
|
@ -203,7 +213,9 @@ export class Or extends TagsFilter {
|
|||
// partition of all the ands
|
||||
containedAnds.push(tf)
|
||||
} else {
|
||||
newOrs.push(<(Tag | (And & OptimizedTag) | RegexTag | SubstitutingTag | ComparingTag)>tf)
|
||||
newOrs.push(
|
||||
<Tag | (And & OptimizedTag) | RegexTag | SubstitutingTag | ComparingTag>tf
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,13 +242,14 @@ export class Or extends TagsFilter {
|
|||
continue // clean up with the other known values
|
||||
}
|
||||
|
||||
if(cleaned instanceof Or){
|
||||
if (cleaned instanceof Or) {
|
||||
// An optimized 'or' should not contain 'ors', we can safely cast
|
||||
newOrs.push(...TagTypes.safeOr(cleaned))
|
||||
continue
|
||||
}
|
||||
|
||||
const noAnd: OptimizedTag & (Tag | RegexTag | SubstitutingTag | ComparingTag) = cleaned
|
||||
const noAnd: OptimizedTag &
|
||||
(Tag | RegexTag | SubstitutingTag | ComparingTag) = cleaned
|
||||
// the 'and' dissolved into a normal tag -> it has to be added to the newOrs
|
||||
newOrs.push(noAnd)
|
||||
dirty = true // rerun this algo later on
|
||||
|
|
@ -263,9 +276,9 @@ export class Or extends TagsFilter {
|
|||
} else {
|
||||
const newAnds: TagsFilterClosed[] = []
|
||||
for (const containedAnd of containedAnds) {
|
||||
const elements: (FlatTag | (Or & OptimizedTag))[] = TagTypes.safeAnd(containedAnd).filter(
|
||||
(candidate) => !commonValues.some((cv) => cv.shadows(candidate))
|
||||
)
|
||||
const elements: (FlatTag | (Or & OptimizedTag))[] = TagTypes.safeAnd(
|
||||
containedAnd
|
||||
).filter((candidate) => !commonValues.some((cv) => cv.shadows(candidate)))
|
||||
if (elements.length > 0) {
|
||||
newAnds.push(And.construct(elements))
|
||||
}
|
||||
|
|
@ -280,10 +293,9 @@ export class Or extends TagsFilter {
|
|||
return true
|
||||
} else if (result === false) {
|
||||
// neutral element: skip
|
||||
} else if(result instanceof Or){
|
||||
} else if (result instanceof Or) {
|
||||
newOrs.push(...TagTypes.safeOr(result))
|
||||
}else
|
||||
newOrs.push(result)
|
||||
} else newOrs.push(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -356,7 +356,7 @@ export class RegexTag extends TagsFilter {
|
|||
}
|
||||
|
||||
optimize(): (RegexTag & OptimizedTag) | boolean {
|
||||
return <any> this
|
||||
return <any>this
|
||||
}
|
||||
|
||||
isNegative(): boolean {
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export default class SubstitutingTag extends TagsFilter {
|
|||
}
|
||||
|
||||
optimize(): (SubstitutingTag & OptimizedTag) | boolean {
|
||||
return <any> this
|
||||
return <any>this
|
||||
}
|
||||
|
||||
isNegative(): boolean {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export class Tag extends TagsFilter {
|
|||
asOverpass(): string[] {
|
||||
if (this.value === "") {
|
||||
// NOT having this key
|
||||
return ["[!\"" + this.key + "\"]"]
|
||||
return ['[!"' + this.key + '"]']
|
||||
}
|
||||
return [`["${this.key}"="${this.value}"]`]
|
||||
}
|
||||
|
|
@ -141,7 +141,7 @@ export class Tag extends TagsFilter {
|
|||
return other.key === this.key && other.value === this.value
|
||||
}
|
||||
if (other instanceof RegexTag) {
|
||||
if(other.key === this.key || !other.invert){
|
||||
if (other.key === this.key || !other.invert) {
|
||||
return other.matchesProperties({ [this.key]: this.value })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ type Brand<B> = { [__is_optimized]: B }
|
|||
*/
|
||||
export type OptimizedTag = Brand<TagsFilter>
|
||||
|
||||
|
||||
export type UploadableTag = Tag | SubstitutingTag | And
|
||||
/**
|
||||
* Not nested
|
||||
|
|
@ -21,15 +20,12 @@ export type UploadableTag = Tag | SubstitutingTag | And
|
|||
export type FlatTag = Tag | RegexTag | SubstitutingTag | ComparingTag
|
||||
export type TagsFilterClosed = FlatTag | And | Or
|
||||
|
||||
|
||||
export class TagTypes {
|
||||
|
||||
static safeAnd(and: And & OptimizedTag): ((FlatTag | (Or & OptimizedTag)) & OptimizedTag)[]{
|
||||
return <any> and.and
|
||||
static safeAnd(and: And & OptimizedTag): ((FlatTag | (Or & OptimizedTag)) & OptimizedTag)[] {
|
||||
return <any>and.and
|
||||
}
|
||||
|
||||
static safeOr(or: Or & OptimizedTag): ((FlatTag | (And & OptimizedTag)) & OptimizedTag)[]{
|
||||
return <any> or.or
|
||||
static safeOr(or: Or & OptimizedTag): ((FlatTag | (And & OptimizedTag)) & OptimizedTag)[] {
|
||||
return <any>or.or
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -484,7 +484,10 @@ export class TagUtils {
|
|||
* regex.matchesProperties({maxspeed: "50 mph"}) // => true
|
||||
*/
|
||||
|
||||
public static Tag(json: TagConfigJson, context: string | ConversionContext = ""): TagsFilterClosed {
|
||||
public static Tag(
|
||||
json: TagConfigJson,
|
||||
context: string | ConversionContext = ""
|
||||
): TagsFilterClosed {
|
||||
try {
|
||||
const ctx = typeof context === "string" ? context : context.path.join(".")
|
||||
return this.ParseTagUnsafe(json, ctx)
|
||||
|
|
|
|||
|
|
@ -151,9 +151,9 @@ class ImagesInLoadedDataFetcher implements ImageFetcher {
|
|||
coordinates: { lng: centerpoint[0], lat: centerpoint[1] },
|
||||
provider: "OpenStreetMap",
|
||||
details: {
|
||||
isSpherical: false
|
||||
isSpherical: false,
|
||||
},
|
||||
osmTags: { image }
|
||||
osmTags: { image },
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
@ -173,46 +173,64 @@ class ImagesFromCacheServerFetcher implements ImageFetcher {
|
|||
}
|
||||
|
||||
async fetchImages(lat: number, lon: number): Promise<P4CPicture[]> {
|
||||
return (await Promise.all([
|
||||
this.fetchImagesForType(lat, lon, "lines"),
|
||||
this.fetchImagesForType(lat, lon, "pois"),
|
||||
this.fetchImagesForType(lat, lon, "polygons")
|
||||
|
||||
])).flatMap(x => x)
|
||||
return (
|
||||
await Promise.all([
|
||||
this.fetchImagesForType(lat, lon, "lines"),
|
||||
this.fetchImagesForType(lat, lon, "pois"),
|
||||
this.fetchImagesForType(lat, lon, "polygons"),
|
||||
])
|
||||
).flatMap((x) => x)
|
||||
}
|
||||
|
||||
async fetchImagesForType(targetlat: number, targetlon: number, type: "lines" | "pois" | "polygons"): Promise<P4CPicture[]> {
|
||||
async fetchImagesForType(
|
||||
targetlat: number,
|
||||
targetlon: number,
|
||||
type: "lines" | "pois" | "polygons"
|
||||
): Promise<P4CPicture[]> {
|
||||
const { x, y, z } = Tiles.embedded_tile(targetlat, targetlon, 14)
|
||||
|
||||
const url = this._serverUrl
|
||||
|
||||
async function getFeatures(x: number, y: number) {
|
||||
const src = new MvtSource(Utils.SubstituteKeys(url, {
|
||||
type, x, y, z, layer: "item_with_image"
|
||||
}), x, y, z)
|
||||
const src = new MvtSource(
|
||||
Utils.SubstituteKeys(url, {
|
||||
type,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
layer: "item_with_image",
|
||||
}),
|
||||
x,
|
||||
y,
|
||||
z
|
||||
)
|
||||
await src.updateAsync()
|
||||
return src.features.data
|
||||
}
|
||||
|
||||
const features = (await Promise.all([
|
||||
getFeatures(x, y),
|
||||
getFeatures(x, y + 1),
|
||||
getFeatures(x, y - 1),
|
||||
const features = (
|
||||
await Promise.all([
|
||||
getFeatures(x, y),
|
||||
getFeatures(x, y + 1),
|
||||
getFeatures(x, y - 1),
|
||||
|
||||
getFeatures(x + 1, y + 1),
|
||||
getFeatures(x + 1, y),
|
||||
getFeatures(x + 1, y - 1),
|
||||
getFeatures(x + 1, y + 1),
|
||||
getFeatures(x + 1, y),
|
||||
getFeatures(x + 1, y - 1),
|
||||
|
||||
getFeatures(x - 1, y - 1),
|
||||
getFeatures(x - 1, y),
|
||||
getFeatures(x - 1, y + 1)
|
||||
])).flatMap(x => x)
|
||||
getFeatures(x - 1, y - 1),
|
||||
getFeatures(x - 1, y),
|
||||
getFeatures(x - 1, y + 1),
|
||||
])
|
||||
).flatMap((x) => x)
|
||||
|
||||
const pics: P4CPicture[] = []
|
||||
for (const f of features) {
|
||||
|
||||
const [lng, lat] = GeoOperations.centerpointCoordinates(f)
|
||||
if (GeoOperations.distanceBetween([targetlon, targetlat], [lng, lat]) > this._searchRadius) {
|
||||
if (
|
||||
GeoOperations.distanceBetween([targetlon, targetlat], [lng, lat]) >
|
||||
this._searchRadius
|
||||
) {
|
||||
return []
|
||||
}
|
||||
for (let i = -1; i < 50; i++) {
|
||||
|
|
@ -235,13 +253,13 @@ class ImagesFromCacheServerFetcher implements ImageFetcher {
|
|||
pictureUrl: v,
|
||||
coordinates: { lat, lng },
|
||||
details: {
|
||||
isSpherical: false
|
||||
isSpherical: false,
|
||||
},
|
||||
osmTags: {
|
||||
image: v
|
||||
image: v,
|
||||
},
|
||||
thumbUrl: v,
|
||||
provider
|
||||
provider,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -299,12 +317,12 @@ class MapillaryFetcher implements ImageFetcher {
|
|||
|
||||
const response = await Utils.downloadJson<{
|
||||
data: {
|
||||
id: string,
|
||||
creator: string,
|
||||
computed_geometry: Point,
|
||||
is_pano: boolean,
|
||||
thumb_256_url: string,
|
||||
thumb_original_url: string,
|
||||
id: string
|
||||
creator: string
|
||||
computed_geometry: Point
|
||||
is_pano: boolean
|
||||
thumb_256_url: string
|
||||
thumb_original_url: string
|
||||
compass_angle: number
|
||||
}[]
|
||||
}>(url)
|
||||
|
|
@ -323,8 +341,8 @@ class MapillaryFetcher implements ImageFetcher {
|
|||
mapillary: img.id,
|
||||
},
|
||||
details: {
|
||||
isSpherical: img.is_pano
|
||||
}
|
||||
isSpherical: img.is_pano,
|
||||
},
|
||||
})
|
||||
}
|
||||
return pics
|
||||
|
|
@ -342,24 +360,28 @@ export class CombinedFetcher {
|
|||
new ImagesInLoadedDataFetcher(indexedFeatures, radius),
|
||||
new ImagesFromCacheServerFetcher(radius),
|
||||
new MapillaryFetcher({
|
||||
panoramas: "no",
|
||||
max_images: 25,
|
||||
start_captured_at : maxage
|
||||
}),
|
||||
new P4CImageFetcher("mapillary"),
|
||||
new P4CImageFetcher("wikicommons"),
|
||||
].map(f => new CachedFetcher(f))
|
||||
panoramas: "no",
|
||||
max_images: 25,
|
||||
start_captured_at: maxage,
|
||||
}),
|
||||
new P4CImageFetcher("mapillary"),
|
||||
new P4CImageFetcher("wikicommons"),
|
||||
].map((f) => new CachedFetcher(f))
|
||||
}
|
||||
|
||||
private async fetchImage(source: CachedFetcher, lat: number, lon: number, state: UIEventSource<Record<string, "loading" | "done" | "error">>, sink: UIEventSource<P4CPicture[]>): Promise<void> {
|
||||
private async fetchImage(
|
||||
source: CachedFetcher,
|
||||
lat: number,
|
||||
lon: number,
|
||||
state: UIEventSource<Record<string, "loading" | "done" | "error">>,
|
||||
sink: UIEventSource<P4CPicture[]>
|
||||
): Promise<void> {
|
||||
try {
|
||||
|
||||
const pics = await source.fetchImages(lat, lon)
|
||||
console.log(source.name, "==>>", pics)
|
||||
state.data[source.name] = "done"
|
||||
state.ping()
|
||||
|
||||
|
||||
if (sink.data === undefined) {
|
||||
sink.setData(pics)
|
||||
} else {
|
||||
|
|
@ -383,8 +405,11 @@ export class CombinedFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
public getImagesAround(lon: number, lat: number): {
|
||||
images: Store<P4CPicture[]>,
|
||||
public getImagesAround(
|
||||
lon: number,
|
||||
lat: number
|
||||
): {
|
||||
images: Store<P4CPicture[]>
|
||||
state: Store<Record<string, "loading" | "done" | "error">>
|
||||
} {
|
||||
const sink = new UIEventSource<P4CPicture[]>([])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue