forked from MapComplete/MapComplete
		
	Merge master
This commit is contained in:
		
						commit
						c939d8ea8e
					
				
					 352 changed files with 976 additions and 212534 deletions
				
			
		|  | @ -8,11 +8,13 @@ import { FeatureSource, WritableFeatureSource } from "../FeatureSource/FeatureSo | |||
| import { LocalStorageSource } from "../Web/LocalStorageSource" | ||||
| import { GeoOperations } from "../GeoOperations" | ||||
| import { OsmTags } from "../../Models/OsmFeature" | ||||
| import StaticFeatureSource, { WritableStaticFeatureSource } from "../FeatureSource/Sources/StaticFeatureSource" | ||||
| import StaticFeatureSource, { | ||||
|     WritableStaticFeatureSource, | ||||
| } from "../FeatureSource/Sources/StaticFeatureSource" | ||||
| import { MapProperties } from "../../Models/MapProperties" | ||||
| import { Orientation } from "../../Sensors/Orientation" | ||||
| 
 | ||||
| ("use strict") | ||||
| ;("use strict") | ||||
| /** | ||||
|  * The geolocation-handler takes a map-location and a geolocation state. | ||||
|  * It'll move the map as appropriate given the state of the geolocation-API | ||||
|  |  | |||
|  | @ -32,7 +32,6 @@ export default class NoElementsInViewDetector { | |||
|                     return "zoom-to-low" | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|                 for (const [layerName, source] of themeViewState.perLayerFiltered) { | ||||
|                     if (priviliged.has(layerName)) { | ||||
|                         continue | ||||
|  |  | |||
|  | @ -34,11 +34,15 @@ export default class SelectedElementTagsUpdater { | |||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     public static applyUpdate(latestTags: OsmTags, id: string, state: { | ||||
|         theme: ThemeConfig, | ||||
|         changes: Changes, | ||||
|         featureProperties: FeaturePropertiesStore | ||||
|     }) { | ||||
|     public static applyUpdate( | ||||
|         latestTags: OsmTags, | ||||
|         id: string, | ||||
|         state: { | ||||
|             theme: ThemeConfig | ||||
|             changes: Changes | ||||
|             featureProperties: FeaturePropertiesStore | ||||
|         } | ||||
|     ) { | ||||
|         try { | ||||
|             const leftRightSensitive = state.theme.isLeftRightSensitive() | ||||
| 
 | ||||
|  |  | |||
|  | @ -69,7 +69,7 @@ export default class DetermineTheme { | |||
|             tagRenderings: DetermineTheme.getSharedTagRenderings(), | ||||
|             tagRenderingOrder: DetermineTheme.getSharedTagRenderingOrder(), | ||||
|             sharedLayers: knownLayersDict, | ||||
|             publicLayers: new Set<string>() | ||||
|             publicLayers: new Set<string>(), | ||||
|         } | ||||
|         return convertState | ||||
|     } | ||||
|  |  | |||
|  | @ -84,10 +84,10 @@ export default class FavouritesFeatureSource extends StaticFeatureSource { | |||
|     private async updateFeature( | ||||
|         feature: Feature, | ||||
|         state: { | ||||
|             theme: ThemeConfig, | ||||
|             changes: Changes, | ||||
|             featureProperties: FeaturePropertiesStore, | ||||
|             osmObjectDownloader: OsmObjectDownloader, | ||||
|             theme: ThemeConfig | ||||
|             changes: Changes | ||||
|             featureProperties: FeaturePropertiesStore | ||||
|             osmObjectDownloader: OsmObjectDownloader | ||||
|         } | ||||
|     ) { | ||||
|         const id = feature.properties.id | ||||
|  |  | |||
|  | @ -32,7 +32,6 @@ export default class ThemeSource implements IndexedFeatureSource { | |||
|     public readonly featuresById: Store<Map<string, Feature>> | ||||
|     private readonly core: Store<ThemeSourceCore> | ||||
| 
 | ||||
| 
 | ||||
|     private readonly addedSources: FeatureSource[] = [] | ||||
|     private readonly addedItems: OsmFeature[] = [] | ||||
| 
 | ||||
|  | @ -48,14 +47,23 @@ export default class ThemeSource implements IndexedFeatureSource { | |||
|         const isLoading = new UIEventSource(true) | ||||
|         this.isLoading = isLoading | ||||
| 
 | ||||
|         const features = this.features = new UIEventSource<Feature[]>([]) | ||||
|         const featuresById = this.featuresById = new UIEventSource(new Map()) | ||||
|         this.core = mvtAvailableLayers.mapD(mvtAvailableLayers => { | ||||
|             const core = new ThemeSourceCore(layers, featureSwitches, mapProperties, backend, isDisplayed, mvtAvailableLayers, isLoading, fullNodeDatabaseSource) | ||||
|             this.addedSources.forEach(src => core.addSource(src)) | ||||
|             this.addedItems.forEach(item => core.addItem(item)) | ||||
|             core.features.addCallbackAndRun(data => features.set(data)) | ||||
|             core.featuresById.addCallbackAndRun(data => featuresById.set(data)) | ||||
|         const features = (this.features = new UIEventSource<Feature[]>([])) | ||||
|         const featuresById = (this.featuresById = new UIEventSource(new Map())) | ||||
|         this.core = mvtAvailableLayers.mapD((mvtAvailableLayers) => { | ||||
|             const core = new ThemeSourceCore( | ||||
|                 layers, | ||||
|                 featureSwitches, | ||||
|                 mapProperties, | ||||
|                 backend, | ||||
|                 isDisplayed, | ||||
|                 mvtAvailableLayers, | ||||
|                 isLoading, | ||||
|                 fullNodeDatabaseSource | ||||
|             ) | ||||
|             this.addedSources.forEach((src) => core.addSource(src)) | ||||
|             this.addedItems.forEach((item) => core.addItem(item)) | ||||
|             core.features.addCallbackAndRun((data) => features.set(data)) | ||||
|             core.featuresById.addCallbackAndRun((data) => featuresById.set(data)) | ||||
|             return core | ||||
|         }) | ||||
|     } | ||||
|  | @ -69,7 +77,6 @@ export default class ThemeSource implements IndexedFeatureSource { | |||
|         this.addedSources.push(source) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public addItem(obj: OsmFeature) { | ||||
|         this.core.data?.addItem(obj) | ||||
|         this.addedItems.push(obj) | ||||
|  | @ -82,7 +89,6 @@ export default class ThemeSource implements IndexedFeatureSource { | |||
|  * Note that special layers (with `source=null` will be ignored) | ||||
|  */ | ||||
| class ThemeSourceCore extends FeatureSourceMerger { | ||||
| 
 | ||||
|     /** | ||||
|      * This source is _only_ triggered when the data is downloaded for CSV export | ||||
|      * @private | ||||
|  | @ -116,7 +122,7 @@ class ThemeSourceCore extends FeatureSourceMerger { | |||
|                     mapProperties, | ||||
|                     { | ||||
|                         isActive: isDisplayed(layer.id), | ||||
|                         maxAge: layer.maxAgeOfCache | ||||
|                         maxAge: layer.maxAgeOfCache, | ||||
|                     } | ||||
|                 ) | ||||
|                 fromCache.set(layer.id, src) | ||||
|  | @ -169,11 +175,11 @@ class ThemeSourceCore extends FeatureSourceMerger { | |||
|                 overpassUrl: featureSwitches.overpassUrl, | ||||
|                 overpassTimeout: featureSwitches.overpassTimeout, | ||||
|                 overpassMaxZoom: new ImmutableStore(99), | ||||
|                 widenFactor: 0 | ||||
|                 widenFactor: 0, | ||||
|             }, | ||||
|             { | ||||
|                 ignoreZoom: true, | ||||
|                 isActive: new ImmutableStore(false) | ||||
|                 isActive: new ImmutableStore(false), | ||||
|             } | ||||
|         ) | ||||
| 
 | ||||
|  | @ -247,7 +253,7 @@ class ThemeSourceCore extends FeatureSourceMerger { | |||
|             backend, | ||||
|             isActive, | ||||
|             patchRelations: true, | ||||
|             fullNodeDatabase | ||||
|             fullNodeDatabase, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|  | @ -279,11 +285,11 @@ class ThemeSourceCore extends FeatureSourceMerger { | |||
|                 widenFactor: 1.5, | ||||
|                 overpassUrl: featureSwitches.overpassUrl, | ||||
|                 overpassTimeout: featureSwitches.overpassTimeout, | ||||
|                 overpassMaxZoom: featureSwitches.overpassMaxZoom | ||||
|                 overpassMaxZoom: featureSwitches.overpassMaxZoom, | ||||
|             }, | ||||
|             { | ||||
|                 padToTiles: zoom.map((zoom) => Math.min(15, zoom + 1)), | ||||
|                 isActive | ||||
|                 isActive, | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ export default class AllImageProviders { | |||
|         ...WikimediaImageProvider.commonsPrefixes, | ||||
|         ...Mapillary.valuePrefixes, | ||||
|         ...AllImageProviders.dontLoadFromPrefixes, | ||||
|         "Category:" | ||||
|         "Category:", | ||||
|     ]) | ||||
| 
 | ||||
|     private static imageAttributionSources: ImageProvider[] = [ | ||||
|  | @ -31,7 +31,7 @@ export default class AllImageProviders { | |||
|         WikidataImageProvider.singleton, | ||||
|         WikimediaImageProvider.singleton, | ||||
|         Panoramax.singleton, | ||||
|         AllImageProviders.genericImageProvider | ||||
|         AllImageProviders.genericImageProvider, | ||||
|     ] | ||||
|     public static apiUrls: string[] = [].concat( | ||||
|         ...AllImageProviders.imageAttributionSources.map((src) => src.apiUrls()) | ||||
|  | @ -44,7 +44,7 @@ export default class AllImageProviders { | |||
|         mapillary: Mapillary.singleton, | ||||
|         wikidata: WikidataImageProvider.singleton, | ||||
|         wikimedia: WikimediaImageProvider.singleton, | ||||
|         panoramax: Panoramax.singleton | ||||
|         panoramax: Panoramax.singleton, | ||||
|     } | ||||
| 
 | ||||
|     public static byName(name: string) { | ||||
|  | @ -77,7 +77,10 @@ export default class AllImageProviders { | |||
|      * | ||||
|      * | ||||
|      */ | ||||
|     public static estimateNumberOfImages(tags: Record<string, string>, prefixes: string[] = undefined): number { | ||||
|     public static estimateNumberOfImages( | ||||
|         tags: Record<string, string>, | ||||
|         prefixes: string[] = undefined | ||||
|     ): number { | ||||
|         let count = 0 | ||||
| 
 | ||||
|         const sources = [Imgur.singleton, | ||||
|  | @ -137,7 +140,7 @@ export default class AllImageProviders { | |||
|      */ | ||||
|     public static loadImagesFrom(urls: string[]): Store<ProvidedImage[]> { | ||||
|         const tags = { | ||||
|             id: urls.join(";") | ||||
|             id: urls.join(";"), | ||||
|         } | ||||
|         for (let i = 0; i < urls.length; i++) { | ||||
|             tags["image:" + i] = urls[i] | ||||
|  |  | |||
|  | @ -604,14 +604,16 @@ export class OsmConnection { | |||
|         if (this.fakeUser) { | ||||
|             return | ||||
|         } | ||||
|         this.FetchCapabilities().then(({ api, gpx }) => { | ||||
|             this.apiIsOnline.setData(api) | ||||
|             this.gpxServiceIsOnline.setData(gpx) | ||||
|         }).catch(err => { | ||||
|             console.log("Could not reach the api:", err) | ||||
|             this.apiIsOnline.set("unreachable") | ||||
|             this.gpxServiceIsOnline.set("unreachable") | ||||
|         }) | ||||
|         this.FetchCapabilities() | ||||
|             .then(({ api, gpx }) => { | ||||
|                 this.apiIsOnline.setData(api) | ||||
|                 this.gpxServiceIsOnline.setData(gpx) | ||||
|             }) | ||||
|             .catch((err) => { | ||||
|                 console.log("Could not reach the api:", err) | ||||
|                 this.apiIsOnline.set("unreachable") | ||||
|                 this.gpxServiceIsOnline.set("unreachable") | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     private readonly _userInfoCache: Record<number, OsmUserInfo> = {} | ||||
|  |  | |||
|  | @ -210,7 +210,7 @@ export class OsmPreferences { | |||
|      * @private | ||||
|      */ | ||||
|     private async getPreferencesDictDirectly(): Promise<Record<string, string>> { | ||||
|         if(!this.osmConnection.isLoggedIn.data){ | ||||
|         if (!this.osmConnection.isLoggedIn.data) { | ||||
|             return {} | ||||
|         } | ||||
|         return new Promise<Record<string, string>>((resolve, reject) => { | ||||
|  | @ -260,7 +260,7 @@ export class OsmPreferences { | |||
|      * | ||||
|      */ | ||||
|     private async uploadKvSplit(k: string, v: string) { | ||||
|         if(!this.osmConnection.isLoggedIn.data){ | ||||
|         if (!this.osmConnection.isLoggedIn.data) { | ||||
|             return | ||||
|         } | ||||
|         if (v === null || v === undefined || v === "" || v === "undefined" || v === "null") { | ||||
|  |  | |||
|  | @ -15,7 +15,6 @@ export default class OpenStreetMapIdSearch implements GeocodingProvider { | |||
|     } | ||||
|     private readonly _osmObjectDownloader: OsmObjectDownloader | ||||
| 
 | ||||
| 
 | ||||
|     constructor(osmObjectDownloader: OsmObjectDownloader) { | ||||
|         this._osmObjectDownloader = osmObjectDownloader | ||||
|     } | ||||
|  |  | |||
|  | @ -41,7 +41,7 @@ export default class SearchState { | |||
|             new OpenLocationCodeSearch(), | ||||
|             new OpenStreetMapIdSearch(state.osmObjectDownloader), | ||||
|             new PhotonSearch(true, 2), | ||||
|             new PhotonSearch() | ||||
|             new PhotonSearch(), | ||||
|             // new NominatimGeocoding(),
 | ||||
|         ] | ||||
| 
 | ||||
|  |  | |||
|  | @ -414,7 +414,7 @@ export default class UserRelatedState { | |||
|                 typeof window === "undefined" ? "no" : window.navigator.share ? "yes" : "no", | ||||
|             _iframe: Utils.isIframe ? "yes" : "no", | ||||
|         }) | ||||
|         if(!Utils.runningFromConsole){ | ||||
|         if (!Utils.runningFromConsole) { | ||||
|             amendedPrefs.data["_host"] = window.location.host | ||||
|             amendedPrefs.data["_path"] = window.location.pathname | ||||
|             amendedPrefs.data["_userAgent"] = navigator.userAgent | ||||
|  | @ -492,7 +492,7 @@ export default class UserRelatedState { | |||
|         }) | ||||
| 
 | ||||
|         const usersettingMetaTagging = new ThemeMetaTagging() | ||||
|         osmConnection.isLoggedIn.addCallbackAndRun(loggedIn => { | ||||
|         osmConnection.isLoggedIn.addCallbackAndRun((loggedIn) => { | ||||
|             amendedPrefs.data["_loggedIn"] = "" + loggedIn | ||||
|             amendedPrefs.ping() | ||||
|         }) | ||||
|  |  | |||
|  | @ -1,14 +1,42 @@ | |||
| import { Utils } from "../../Utils" | ||||
| /** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */ | ||||
| export class ThemeMetaTagging { | ||||
|    public static readonly themeName = "usersettings" | ||||
|     public static readonly themeName = "usersettings" | ||||
| 
 | ||||
|    public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) { | ||||
|       Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )  | ||||
|       Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )  | ||||
|       Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href   }) (feat)  )  | ||||
|       Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat)  )  | ||||
|       Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )  | ||||
|       feat.properties['__current_backgroun'] = 'initial_value' | ||||
|    } | ||||
| } | ||||
|     public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) { | ||||
|         Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () => | ||||
|             feat.properties._description | ||||
|                 .match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/) | ||||
|                 ?.at(1) | ||||
|         ) | ||||
|         Utils.AddLazyProperty( | ||||
|             feat.properties, | ||||
|             "_d", | ||||
|             () => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? "" | ||||
|         ) | ||||
|         Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () => | ||||
|             ((feat) => { | ||||
|                 const e = document.createElement("div") | ||||
|                 e.innerHTML = feat.properties._d | ||||
|                 return Array.from(e.getElementsByTagName("a")).filter( | ||||
|                     (a) => a.href.match(/mastodon|en.osm.town/) !== null | ||||
|                 )[0]?.href | ||||
|             })(feat) | ||||
|         ) | ||||
|         Utils.AddLazyProperty(feat.properties, "_mastodon_link", () => | ||||
|             ((feat) => { | ||||
|                 const e = document.createElement("div") | ||||
|                 e.innerHTML = feat.properties._d | ||||
|                 return Array.from(e.getElementsByTagName("a")).filter( | ||||
|                     (a) => a.getAttribute("rel")?.indexOf("me") >= 0 | ||||
|                 )[0]?.href | ||||
|             })(feat) | ||||
|         ) | ||||
|         Utils.AddLazyProperty( | ||||
|             feat.properties, | ||||
|             "_mastodon_candidate", | ||||
|             () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a | ||||
|         ) | ||||
|         feat.properties["__current_backgroun"] = "initial_value" | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ export interface NSIItem { | |||
|     } | ||||
|     readonly tags: Readonly<Record<string, string>> | ||||
|     fromTemplate?: boolean | ||||
|     ext? : string | ||||
|     ext?: string | ||||
| } | ||||
| 
 | ||||
| export default class NameSuggestionIndex { | ||||
|  | @ -214,9 +214,7 @@ export default class NameSuggestionIndex { | |||
|             for (const nsiItem of actualBrands) { | ||||
|                 const tags = nsiItem.tags | ||||
|                 const frequency = frequencies[nsiItem.displayName] | ||||
|                 const iconUrl = this.getIconExternalUrl(nsiItem, type) | ||||
|                 const hasIcon = iconUrl !== undefined | ||||
|                 const icon = hasIcon ? this.getIconUrl(nsiItem, type) : undefined | ||||
|                 const icon = this.getIconUrl(nsiItem) | ||||
|                 mappings.push({ | ||||
|                     if: new Tag(type, tags[type]), | ||||
|                     addExtraTags: Object.keys(tags) | ||||
|  | @ -274,7 +272,7 @@ export default class NameSuggestionIndex { | |||
|             const values = tags[osmKey] | ||||
|             for (const osmValue of values) { | ||||
|                 const suggestions = this.getSuggestionsForKV(type, osmKey, osmValue) | ||||
|                 if(!suggestions){ | ||||
|                 if (!suggestions) { | ||||
|                     console.warn("No suggestions found for", type, osmKey, osmValue) | ||||
|                     continue | ||||
|                 } | ||||
|  | @ -399,9 +397,14 @@ export default class NameSuggestionIndex { | |||
|         return logos?.facebook ?? logos?.wikidata | ||||
|     } | ||||
| 
 | ||||
|     public getIconUrl(nsiItem: NSIItem, type: string) { | ||||
|     public getIconUrl(nsiItem: NSIItem): string | undefined { | ||||
|         if (!nsiItem.ext) { | ||||
|             // No extension -> there is no logo
 | ||||
|             return undefined | ||||
|         } | ||||
|         return "./assets/data/nsi/logos/" + nsiItem.id + "." + nsiItem.ext | ||||
|     } | ||||
| 
 | ||||
|     private static readonly brandPrefix = ["name", "alt_name", "operator", "brand"] as const | ||||
| 
 | ||||
|     /** | ||||
|  |  | |||
|  | @ -6,8 +6,8 @@ import { UIEventSource } from "../UIEventSource" | |||
| 
 | ||||
| export default class ThemeViewStateHashActor { | ||||
|     private readonly _state: { | ||||
|         indexedFeatures: IndexedFeatureSource, | ||||
|         selectedElement: UIEventSource<Feature>, | ||||
|         indexedFeatures: IndexedFeatureSource | ||||
|         selectedElement: UIEventSource<Feature> | ||||
|         guistate: MenuState | ||||
|     } | ||||
|     private isUpdatingHash = false | ||||
|  | @ -22,7 +22,7 @@ export default class ThemeViewStateHashActor { | |||
|         "", | ||||
|         "The possible hashes are:", | ||||
|         "", | ||||
|         MenuState.pageNames.map((tab) => "`" + tab + "`").join(",") | ||||
|         MenuState.pageNames.map((tab) => "`" + tab + "`").join(","), | ||||
|     ] | ||||
| 
 | ||||
|     /** | ||||
|  | @ -35,9 +35,9 @@ export default class ThemeViewStateHashActor { | |||
|      * | ||||
|      */ | ||||
|     constructor(state: { | ||||
|         indexedFeatures: IndexedFeatureSource, | ||||
|         selectedElement: UIEventSource<Feature>, | ||||
|         guistate: MenuState, | ||||
|         indexedFeatures: IndexedFeatureSource | ||||
|         selectedElement: UIEventSource<Feature> | ||||
|         guistate: MenuState | ||||
|     }) { | ||||
|         this._state = state | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue