forked from MapComplete/MapComplete
Merge branch 'master' into develop
This commit is contained in:
commit
25880ed7d3
16 changed files with 111 additions and 63 deletions
|
@ -2,6 +2,14 @@
|
|||
|
||||
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.51.7](https://source.mapcomplete.org/MapComplete/MapComplete/compare/v0.51.6...v0.51.7) (2025-05-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix error reporting ([913add4](https://source.mapcomplete.org/MapComplete/MapComplete/commits/913add4295216f45098cf7f8a98bcc263f7a7d93))
|
||||
* license info in 'nearby images' now works for mapillary, add bbox search for panoramax ([7f5544c](https://source.mapcomplete.org/MapComplete/MapComplete/commits/7f5544c1e52aac146fa7313f7b9b7335649f55d2))
|
||||
|
||||
### [0.51.6](https://source.mapcomplete.org/MapComplete/MapComplete/compare/v0.51.5...v0.51.6) (2025-05-08)
|
||||
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"_first_user_id:=get(feat)('comments')[0].uid",
|
||||
"_is_import_note:=(() => {const lines = feat.properties['_first_comment'].split('\\n'); const matchesMapCompleteURL = lines.map(l => l.match(\".*https://mapcomplete.\\(osm.be|org\\)/\\([a-zA-Z_-]+\\)\\(.html\\).*#import\")); const matchedIndexes = matchesMapCompleteURL.map((doesMatch, i) => [doesMatch !== null, i]).filter(v => v[0]).map(v => v[1]); return matchedIndexes[0] })()"
|
||||
],
|
||||
"isShown": "_total_comments>0",
|
||||
"isShown": "comments!=[]",
|
||||
"minzoom": 7,
|
||||
"title": {
|
||||
"render": {
|
||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "mapcomplete",
|
||||
"version": "0.51.6",
|
||||
"version": "0.51.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mapcomplete",
|
||||
"version": "0.51.6",
|
||||
"version": "0.51.7",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mapcomplete",
|
||||
"version": "0.51.6",
|
||||
"version": "0.51.7",
|
||||
"repository": "https://source.mapcomplete.org/MapComplete/MapComplete",
|
||||
"description": "A small website to edit OSM easily",
|
||||
"bugs": "hhttps://source.mapcomplete.org/MapComplete/MapComplete/issues",
|
||||
|
|
|
@ -8,7 +8,7 @@ export class LicenseInfo {
|
|||
copyrighted?: boolean = false
|
||||
credit?: string = ""
|
||||
description?: string = ""
|
||||
informationLocation?: URL = undefined
|
||||
informationLocation?: URL | string = undefined
|
||||
date?: Date
|
||||
views?: number
|
||||
}
|
||||
|
|
|
@ -192,7 +192,12 @@ export class Mapillary extends ImageProvider {
|
|||
license.license = "CC BY-SA 4.0"
|
||||
// license.license = "Creative Commons Attribution-ShareAlike 4.0 International License";
|
||||
license.attributionRequired = true
|
||||
license.date = new Date(response["captured_at"])
|
||||
const date = response["captured_at"]
|
||||
try {
|
||||
license.date = new Date(date)
|
||||
} catch (e) {
|
||||
console.warn("Could not parse captured_at date from mapillary image. The date is:", date)
|
||||
}
|
||||
return license
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ export class Changes {
|
|||
featureSwitchIsTesting?: Store<boolean>
|
||||
}
|
||||
osmConnection: OsmConnection
|
||||
reportError?: (error: string) => void
|
||||
reportError?: ((message: string | Error | XMLHttpRequest, extramessage?: string) => void),
|
||||
featureProperties?: FeaturePropertiesStore
|
||||
historicalUserLocations?: FeatureSource<Feature<Point, GeoLocationPointProperties>>
|
||||
allElements?: IndexedFeatureSource
|
||||
|
@ -75,7 +75,7 @@ export class Changes {
|
|||
}
|
||||
this.state = state
|
||||
this.backend = state.osmConnection.Backend()
|
||||
this._reportError = state.reportError
|
||||
this._reportError = (msg, err) => state.reportError(msg, err)
|
||||
this._changesetHandler = new ChangesetHandler(
|
||||
state.featureSwitches?.featureSwitchIsTesting ?? new ImmutableStore(false),
|
||||
state.osmConnection,
|
||||
|
@ -669,19 +669,24 @@ export class Changes {
|
|||
const createdIds = new Set(
|
||||
pending.filter((cd) => cd.changes !== undefined).map((cd) => cd.id)
|
||||
)
|
||||
pending.forEach((c) => {
|
||||
if (c.id < 0) {
|
||||
if (createdIds.has(c.id)) {
|
||||
for (const c of pending) {
|
||||
let id = c.id
|
||||
const newId = this._changesetHandler._remappings.get(c.type + "/" + c.id)
|
||||
if (newId) {
|
||||
id = Number(newId.split("/")[1])
|
||||
}
|
||||
if (id < 0) {
|
||||
if (createdIds.has(id)) {
|
||||
toUpload.push(c)
|
||||
} else {
|
||||
this._reportError(
|
||||
`Got an orphaned change. The 'creation'-change description for ${c.type}/${c.id} got lost. Permanently dropping this change:` +
|
||||
`Got an orphaned change. The 'creation'-change description for ${c.type}/${id} got lost. Permanently dropping this change:` +
|
||||
JSON.stringify(c)
|
||||
)
|
||||
}
|
||||
return
|
||||
continue
|
||||
}
|
||||
const matchFound = !!objects.find((o) => o.id === c.id && o.type === c.type)
|
||||
const matchFound = !!objects.find((o) => o.id === id && o.type === c.type)
|
||||
if (matchFound) {
|
||||
toUpload.push(c)
|
||||
} else {
|
||||
|
@ -689,12 +694,12 @@ export class Changes {
|
|||
"Refusing change about " +
|
||||
c.type +
|
||||
"/" +
|
||||
c.id +
|
||||
id +
|
||||
" as not in the objects. No internet?"
|
||||
)
|
||||
refused.push(c)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return { refused, toUpload }
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ export class ChangesetHandler {
|
|||
private readonly backend: string
|
||||
|
||||
/**
|
||||
* Contains previously rewritten IDs
|
||||
* Contains previously rewritten IDs, e.g. {"node/-1" --> "node/123456"}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
public readonly _remappings = new Map<string, string>()
|
||||
|
|
|
@ -9,6 +9,7 @@ import { Utils } from "../../Utils"
|
|||
import { Point } from "geojson"
|
||||
import { Imgur } from "../ImageProviders/Imgur"
|
||||
import { ImageData, Panoramax, PanoramaxXYZ } from "panoramax-js/dist"
|
||||
import { Mapillary } from "../ImageProviders/Mapillary"
|
||||
|
||||
interface ImageFetcher {
|
||||
/**
|
||||
|
@ -222,6 +223,11 @@ class ImagesFromPanoramaxFetcher implements ImageFetcher {
|
|||
const promises: Promise<ImageData[]>[] = []
|
||||
const maxRadius = this._radius
|
||||
let prevRadius = 0
|
||||
|
||||
const nearby = this._panoramax.search({
|
||||
bbox: new BBox([[lon, lat]]).pad(0.001).toLngLatFlat()
|
||||
})
|
||||
promises.push(nearby) // We do a nearby search with bbox, see https://source.mapcomplete.org/MapComplete/MapComplete/issues/2384
|
||||
for (const radiusSetting of radiusSettings) {
|
||||
const promise = this._panoramax.search({
|
||||
place: [lon, lat],
|
||||
|
@ -265,7 +271,7 @@ class MapillaryFetcher implements ImageFetcher {
|
|||
async fetchImages(lat: number, lon: number): Promise<P4CPicture[]> {
|
||||
const boundingBox = new BBox([[lon, lat]]).padAbsolute(0.003)
|
||||
let url =
|
||||
"https://graph.mapillary.com/images?fields=geometry,computed_geometry,creator,id,thumb_256_url,thumb_original_url,compass_angle&bbox=" +
|
||||
"https://graph.mapillary.com/images?fields=geometry,computed_geometry,creator,id,captured_at,thumb_256_url,thumb_original_url,compass_angle&bbox=" +
|
||||
[
|
||||
boundingBox.getWest(),
|
||||
boundingBox.getSouth(),
|
||||
|
@ -293,13 +299,14 @@ class MapillaryFetcher implements ImageFetcher {
|
|||
const response = await Utils.downloadJson<{
|
||||
data: {
|
||||
id: string
|
||||
creator: string
|
||||
creator: { username: string }
|
||||
geometry: Point
|
||||
computed_geometry: Point
|
||||
is_pano: boolean
|
||||
thumb_256_url: string
|
||||
thumb_original_url: string
|
||||
compass_angle: number
|
||||
captured_at: number
|
||||
}[]
|
||||
}>(url)
|
||||
const pics: P4CPicture[] = []
|
||||
|
@ -308,6 +315,7 @@ class MapillaryFetcher implements ImageFetcher {
|
|||
if (img.thumb_original_url === undefined) {
|
||||
continue
|
||||
}
|
||||
const [lon, lat] = img.computed_geometry.coordinates
|
||||
pics.push({
|
||||
pictureUrl: img.thumb_original_url,
|
||||
provider: "Mapillary",
|
||||
|
@ -319,6 +327,12 @@ class MapillaryFetcher implements ImageFetcher {
|
|||
details: {
|
||||
isSpherical: this._panoramas === "only",
|
||||
},
|
||||
|
||||
detailsUrl: Mapillary.singleton.visitUrl(img, { lon, lat }),
|
||||
date: img.captured_at,
|
||||
license: "CC-BY-SA",
|
||||
author: img.creator.username,
|
||||
direction: img.compass_angle
|
||||
})
|
||||
}
|
||||
return pics
|
||||
|
@ -367,7 +381,6 @@ export class CombinedFetcher {
|
|||
): Promise<void> {
|
||||
try {
|
||||
const pics = await source.fetchImages(lat, lon)
|
||||
console.log(source.name, "==>>", pics)
|
||||
state.data[source.name] = "done"
|
||||
state.ping()
|
||||
|
||||
|
|
|
@ -246,5 +246,5 @@ export interface TagRenderingConfigJson {
|
|||
* Note: if the theme already has a layer with this ID, the value is ignored
|
||||
* group: hidden
|
||||
*/
|
||||
requiredLayers: { id: string; minzoom?: number }[]
|
||||
requiredLayers?: { id: string; minzoom?: number }[]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import { NewGeometryFromChangesFeatureSource } from "../../Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource"
|
||||
import {
|
||||
NewGeometryFromChangesFeatureSource
|
||||
} from "../../Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource"
|
||||
import { WithLayoutSourceState } from "./WithLayoutSourceState"
|
||||
import ThemeConfig from "../ThemeConfig/ThemeConfig"
|
||||
import { Utils } from "../../Utils"
|
||||
|
@ -18,9 +20,7 @@ import { Map as MlMap } from "maplibre-gl"
|
|||
import FilteringFeatureSource from "../../Logic/FeatureSource/Sources/FilteringFeatureSource"
|
||||
import ShowDataLayer from "../../UI/Map/ShowDataLayer"
|
||||
import SelectedElementTagsUpdater from "../../Logic/Actors/SelectedElementTagsUpdater"
|
||||
import NoElementsInViewDetector, {
|
||||
FeatureViewState,
|
||||
} from "../../Logic/Actors/NoElementsInViewDetector"
|
||||
import NoElementsInViewDetector, { FeatureViewState } from "../../Logic/Actors/NoElementsInViewDetector"
|
||||
|
||||
export class WithChangesState extends WithLayoutSourceState {
|
||||
readonly changes: Changes
|
||||
|
@ -43,7 +43,7 @@ export class WithChangesState extends WithLayoutSourceState {
|
|||
osmConnection: this.osmConnection,
|
||||
featureProperties: this.featureProperties,
|
||||
historicalUserLocations: this.historicalUserLocations,
|
||||
reportError: this.reportError,
|
||||
reportError: (err, msg) => this.reportError(err, msg)
|
||||
},
|
||||
theme?.isLeftRightSensitive() ?? false
|
||||
)
|
||||
|
@ -104,16 +104,6 @@ export class WithChangesState extends WithLayoutSourceState {
|
|||
return
|
||||
}
|
||||
const isTesting = this.featureSwitchIsTesting?.data
|
||||
console.log(
|
||||
isTesting
|
||||
? ">>> _Not_ reporting error to report server as testmode is on"
|
||||
: ">>> Reporting error to",
|
||||
Constants.ErrorReportServer,
|
||||
message
|
||||
)
|
||||
if (isTesting) {
|
||||
return
|
||||
}
|
||||
|
||||
if ("" + message === "[object XMLHttpRequest]") {
|
||||
const req = <XMLHttpRequest>message
|
||||
|
@ -137,25 +127,35 @@ export class WithChangesState extends WithLayoutSourceState {
|
|||
}
|
||||
|
||||
const stacktrace: string = new Error().stack
|
||||
|
||||
try {
|
||||
const err = {
|
||||
stacktrace,
|
||||
message: "" + message,
|
||||
theme: this.theme?.id,
|
||||
version: Constants.vNumber,
|
||||
language: this.userRelatedState.language.data,
|
||||
username: this.osmConnection.userDetails.data?.name,
|
||||
userid: this.osmConnection.userDetails.data?.uid,
|
||||
pendingChanges: this.changes.pendingChanges.data,
|
||||
previousChanges: this.changes.allChanges.data,
|
||||
changeRewrites: Utils.MapToObj(this.changes._changesetHandler._remappings)
|
||||
}
|
||||
console.trace(
|
||||
isTesting
|
||||
? ">>> _Not_ reporting error to report server as testmode is on"
|
||||
: ">>> Reporting error to",
|
||||
Constants.ErrorReportServer,
|
||||
message, err
|
||||
)
|
||||
if (isTesting) {
|
||||
return
|
||||
}
|
||||
await fetch(Constants.ErrorReportServer, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
stacktrace,
|
||||
message: "" + message,
|
||||
theme: this.theme.id,
|
||||
version: Constants.vNumber,
|
||||
language: this.userRelatedState.language.data,
|
||||
username: this.osmConnection.userDetails.data?.name,
|
||||
userid: this.osmConnection.userDetails.data?.uid,
|
||||
pendingChanges: this.changes.pendingChanges.data,
|
||||
previousChanges: this.changes.allChanges.data,
|
||||
changeRewrites: Utils.MapToObj(this.changes._changesetHandler._remappings),
|
||||
}),
|
||||
body: JSON.stringify(err)
|
||||
})
|
||||
} catch (e) {
|
||||
console.error("Could not upload an error report")
|
||||
console.error("Could not upload an error report", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ export class WithImageState extends WithGuiState implements SpecialVisualization
|
|||
this.osmConnection,
|
||||
this.changes,
|
||||
this.geolocation.geolocationState.currentGPSLocation,
|
||||
this.reportError
|
||||
(err, msg) => this.reportError(err, msg)
|
||||
)
|
||||
const longAgo = new Date()
|
||||
longAgo.setTime(new Date().getTime() - 5 * 365 * 24 * 60 * 60 * 1000)
|
||||
|
|
|
@ -41,7 +41,10 @@ export class WithSelectedElementState extends UserMapFeatureswitchState {
|
|||
const [osm_type, osm_id] = selected.properties.id.split("/")
|
||||
const [lon, lat] = GeoOperations.centerpointCoordinates(selected)
|
||||
const layer = this.theme.getMatchingLayer(selected.properties)
|
||||
if (!layer.isNormal()) {
|
||||
if (!layer) {
|
||||
return
|
||||
}
|
||||
if (!layer?.isNormal()) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -37,18 +37,29 @@
|
|||
}
|
||||
})
|
||||
const t = Translations.t.image.nearby
|
||||
|
||||
let date: Date
|
||||
if (image.date) {
|
||||
try {
|
||||
date = new Date(image.date)
|
||||
} catch (e) {
|
||||
console.warn("Could not parse image date", image.date, "for", image.detailsUrl)
|
||||
}
|
||||
}
|
||||
|
||||
let license: LicenseInfo = {
|
||||
artist: image.author,
|
||||
license: image.license,
|
||||
date: new Date(image.date),
|
||||
informationLocation: image.detailsUrl,
|
||||
informationLocation: (image.detailsUrl ?? image.pictureUrl ?? image.thumbUrl),
|
||||
date
|
||||
}
|
||||
console.log(">>> trying to create license info based on", image, license)
|
||||
let providedImage: ProvidedImage = {
|
||||
url: image.thumbUrl ?? image.pictureUrl,
|
||||
url_hd: image.pictureUrl,
|
||||
key: undefined,
|
||||
provider: AllImageProviders.byName(image.provider),
|
||||
date: new Date(image.date),
|
||||
date,
|
||||
id: Object.values(image.osmTags)[0],
|
||||
isSpherical: image.details.isSpherical,
|
||||
license,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"
|
||||
import LinkableImage from "./LinkableImage.svelte"
|
||||
import type { Feature, Point } from "geojson"
|
||||
import type { Feature, Geometry, Point } from "geojson"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
|
||||
|
@ -25,7 +25,7 @@
|
|||
import { BBox } from "../../Logic/BBox"
|
||||
import PanoramaxLink from "../BigComponents/PanoramaxLink.svelte"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import type { PanoramaView } from "../../Logic/ImageProviders/ImageProvider"
|
||||
import type { HotspotProperties, PanoramaView } from "../../Logic/ImageProviders/ImageProvider"
|
||||
|
||||
export let tags: UIEventSource<OsmTags>
|
||||
export let state: SpecialVisualizationState
|
||||
|
@ -52,6 +52,7 @@
|
|||
[loadedImages]
|
||||
)
|
||||
|
||||
// Panorama-views get a geojson feature to browse around
|
||||
let asFeatures = result.map((p4cs) =>
|
||||
p4cs.map(
|
||||
(p4c) =>
|
||||
|
@ -147,25 +148,27 @@
|
|||
highlighted.set(feature.properties.id)
|
||||
},
|
||||
})
|
||||
let nearbyFeatures: Store<Feature[]> = asFeatures.map((nearbyPoints) => {
|
||||
|
||||
let nearbyFeatures: Store<Feature<Geometry, HotspotProperties>[]> = asFeatures.map((nearbyPoints) => {
|
||||
return [
|
||||
{
|
||||
type: "Feature",
|
||||
geometry: { type: "Point", coordinates: GeoOperations.centerpointCoordinates(feature) },
|
||||
properties: {
|
||||
properties: <HotspotProperties>{
|
||||
name: layer.title?.GetRenderValue(feature.properties).Subs(feature.properties).txt,
|
||||
focus: true,
|
||||
},
|
||||
},
|
||||
...nearbyPoints
|
||||
.filter((p) => p.properties.spherical === "yes")
|
||||
.filter((p) => p.properties["spherical"] === "yes")
|
||||
.map((f) => ({
|
||||
...f,
|
||||
properties: {
|
||||
properties: <HotspotProperties>{
|
||||
name: "Nearby panorama",
|
||||
pitch: "auto",
|
||||
type: "scene",
|
||||
gotoPanorama: f,
|
||||
focus: false
|
||||
},
|
||||
})),
|
||||
]
|
||||
|
|
|
@ -3,7 +3,7 @@ import {
|
|||
DataDrivenPropertyValueSpecification,
|
||||
LayerSpecification,
|
||||
Map as MlMap,
|
||||
SymbolLayerSpecification,
|
||||
SymbolLayerSpecification
|
||||
} from "maplibre-gl"
|
||||
import Locale from "../i18n/Locale"
|
||||
import { Utils } from "../../Utils"
|
||||
|
@ -1752,7 +1752,6 @@ export class ProtomapsLanguageSupport {
|
|||
}
|
||||
const newExpressionF = ProtomapsLanguageSupport.expressions[layer.id]
|
||||
if (!newExpressionF) {
|
||||
console.log(">>> No function found for", layer.id)
|
||||
return
|
||||
}
|
||||
const newExpression = newExpressionF(language)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue