| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  | import {Utils} from "../../../Utils"; | 
					
						
							|  |  |  | import * as OsmToGeoJson from "osmtogeojson"; | 
					
						
							|  |  |  | import StaticFeatureSource from "../Sources/StaticFeatureSource"; | 
					
						
							|  |  |  | import PerLayerFeatureSourceSplitter from "../PerLayerFeatureSourceSplitter"; | 
					
						
							|  |  |  | import {UIEventSource} from "../../UIEventSource"; | 
					
						
							|  |  |  | import FilteredLayer from "../../../Models/FilteredLayer"; | 
					
						
							|  |  |  | import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; | 
					
						
							|  |  |  | import {Tiles} from "../../../Models/TileRange"; | 
					
						
							|  |  |  | import {BBox} from "../../BBox"; | 
					
						
							|  |  |  | import {OsmConnection} from "../../Osm/OsmConnection"; | 
					
						
							| 
									
										
										
										
											2021-10-07 18:58:45 +02:00
										 |  |  | import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"; | 
					
						
							|  |  |  | import {Or} from "../../Tags/Or"; | 
					
						
							|  |  |  | import {TagsFilter} from "../../Tags/TagsFilter"; | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-15 02:44:11 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * If a tile is needed (requested via the UIEventSource in the constructor), will download the appropriate tile and pass it via 'handleTile' | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  | export default class OsmFeatureSource { | 
					
						
							|  |  |  |     public readonly isRunning: UIEventSource<boolean> = new UIEventSource<boolean>(false) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |     public readonly downloadedTiles = new Set<number>() | 
					
						
							|  |  |  |     public rawDataHandlers: ((osmJson: any, tileId: number) => void)[] = [] | 
					
						
							|  |  |  |     private readonly _backend: string; | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |     private readonly filteredLayers: UIEventSource<FilteredLayer[]>; | 
					
						
							|  |  |  |     private readonly handleTile: (fs: (FeatureSourceForLayer & Tiled)) => void; | 
					
						
							|  |  |  |     private isActive: UIEventSource<boolean>; | 
					
						
							|  |  |  |     private options: { | 
					
						
							|  |  |  |         handleTile: (tile: FeatureSourceForLayer & Tiled) => void; | 
					
						
							|  |  |  |         isActive: UIEventSource<boolean>, | 
					
						
							|  |  |  |         neededTiles: UIEventSource<number[]>, | 
					
						
							|  |  |  |         state: { | 
					
						
							|  |  |  |             readonly osmConnection: OsmConnection; | 
					
						
							| 
									
										
										
										
											2021-09-30 04:13:23 +02:00
										 |  |  |         }, | 
					
						
							|  |  |  |         markTileVisited?: (tileId: number) => void | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2021-10-07 18:58:45 +02:00
										 |  |  |     private readonly allowedTags: TagsFilter; | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     constructor(options: { | 
					
						
							|  |  |  |         handleTile: (tile: FeatureSourceForLayer & Tiled) => void; | 
					
						
							|  |  |  |         isActive: UIEventSource<boolean>, | 
					
						
							|  |  |  |         neededTiles: UIEventSource<number[]>, | 
					
						
							|  |  |  |         state: { | 
					
						
							|  |  |  |             readonly filteredLayers: UIEventSource<FilteredLayer[]>; | 
					
						
							|  |  |  |             readonly osmConnection: OsmConnection; | 
					
						
							| 
									
										
										
										
											2021-10-07 18:58:45 +02:00
										 |  |  |             readonly layoutToUse: LayoutConfig | 
					
						
							| 
									
										
										
										
											2021-09-30 04:13:23 +02:00
										 |  |  |         }, | 
					
						
							|  |  |  |         markTileVisited?: (tileId: number) => void | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |     }) { | 
					
						
							|  |  |  |         this.options = options; | 
					
						
							|  |  |  |         this._backend = options.state.osmConnection._oauth_config.url; | 
					
						
							|  |  |  |         this.filteredLayers = options.state.filteredLayers.map(layers => layers.filter(layer => layer.layerDef.source.geojsonSource === undefined)) | 
					
						
							|  |  |  |         this.handleTile = options.handleTile | 
					
						
							|  |  |  |         this.isActive = options.isActive | 
					
						
							|  |  |  |         const self = this | 
					
						
							|  |  |  |         options.neededTiles.addCallbackAndRunD(neededTiles => { | 
					
						
							|  |  |  |             if (options.isActive?.data === false) { | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |             neededTiles = neededTiles.filter(tile => !self.downloadedTiles.has(tile)) | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             if (neededTiles.length == 0) { | 
					
						
							| 
									
										
										
										
											2021-10-10 23:38:09 +02:00
										 |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |             self.isRunning.setData(true) | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 for (const neededTile of neededTiles) { | 
					
						
							|  |  |  |                     self.downloadedTiles.add(neededTile) | 
					
						
							| 
									
										
										
										
											2021-10-08 15:11:20 +02:00
										 |  |  |                     self.LoadTile(...Tiles.tile_from_index(neededTile)).then(_ => { | 
					
						
							| 
									
										
										
										
											2021-12-13 02:05:34 +01:00
										 |  |  |                         console.debug("Tile ", Tiles.tile_from_index(neededTile).join("/"), "loaded from OSM") | 
					
						
							| 
									
										
										
										
											2021-10-08 15:11:20 +02:00
										 |  |  |                     }) | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } catch (e) { | 
					
						
							|  |  |  |                 console.error(e) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             } finally { | 
					
						
							| 
									
										
										
										
											2021-10-08 15:11:20 +02:00
										 |  |  |                 self.isRunning.setData(false) | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2021-10-08 15:11:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 18:58:45 +02:00
										 |  |  |         const neededLayers = options.state.layoutToUse.layers | 
					
						
							| 
									
										
										
										
											2021-10-08 15:11:20 +02:00
										 |  |  |             .filter(layer => !layer.doNotDownload) | 
					
						
							| 
									
										
										
										
											2021-10-07 18:58:45 +02:00
										 |  |  |             .filter(layer => layer.source.geojsonSource === undefined || layer.source.isOsmCacheLayer) | 
					
						
							|  |  |  |         this.allowedTags = new Or(neededLayers.map(l => l.source.osmTags)) | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private async LoadTile(z, x, y): Promise<void> { | 
					
						
							| 
									
										
										
										
											2021-09-28 18:00:44 +02:00
										 |  |  |         if (z > 20) { | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |             throw "This is an absurd high zoom level" | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const bbox = BBox.fromTile(z, x, y) | 
					
						
							|  |  |  |         const url = `${this._backend}/api/0.6/map?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}` | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-31 02:08:39 +01:00
										 |  |  |             const osmJson = await Utils.downloadJson(url) | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |             try { | 
					
						
							| 
									
										
										
										
											2021-11-03 00:44:53 +01:00
										 |  |  |                 console.debug("Got tile", z, x, y, "from the osm api") | 
					
						
							| 
									
										
										
										
											2021-10-31 02:08:39 +01:00
										 |  |  |                 this.rawDataHandlers.forEach(handler => handler(osmJson, Tiles.tile_index(z, x, y))) | 
					
						
							|  |  |  |                 const geojson = OsmToGeoJson.default(osmJson, | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |                     // @ts-ignore
 | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                         flatProperties: true | 
					
						
							|  |  |  |                     }); | 
					
						
							| 
									
										
										
										
											2021-10-08 15:11:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 18:58:45 +02:00
										 |  |  |                 // The geojson contains _all_ features at the given location
 | 
					
						
							|  |  |  |                 // We only keep what is needed
 | 
					
						
							| 
									
										
										
										
											2021-10-08 15:11:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 18:58:45 +02:00
										 |  |  |                 geojson.features = geojson.features.filter(feature => this.allowedTags.matchesProperties(feature.properties)) | 
					
						
							| 
									
										
										
										
											2021-12-17 19:28:05 +01:00
										 |  |  |                 geojson.features.forEach(f => { | 
					
						
							|  |  |  |                     f.properties["_backend"] = this._backend | 
					
						
							|  |  |  |                 }) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-08 15:11:20 +02:00
										 |  |  |                 const index = Tiles.tile_index(z, x, y); | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |                 new PerLayerFeatureSourceSplitter(this.filteredLayers, | 
					
						
							|  |  |  |                     this.handleTile, | 
					
						
							|  |  |  |                     new StaticFeatureSource(geojson.features, false), | 
					
						
							|  |  |  |                     { | 
					
						
							| 
									
										
										
										
											2021-10-08 15:11:20 +02:00
										 |  |  |                         tileIndex: index | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |                     } | 
					
						
							|  |  |  |                 ); | 
					
						
							| 
									
										
										
										
											2021-10-08 15:11:20 +02:00
										 |  |  |                 if (this.options.markTileVisited) { | 
					
						
							| 
									
										
										
										
											2021-09-30 04:13:23 +02:00
										 |  |  |                     this.options.markTileVisited(index) | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |             } catch (e) { | 
					
						
							|  |  |  |                 console.error("Weird error: ", e) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } catch (e) { | 
					
						
							|  |  |  |             console.error("Could not download tile", z, x, y, "due to", e, "; retrying with smaller bounds") | 
					
						
							|  |  |  |             if (e === "rate limited") { | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             await this.LoadTile(z + 1, x * 2, y * 2) | 
					
						
							|  |  |  |             await this.LoadTile(z + 1, 1 + x * 2, y * 2) | 
					
						
							|  |  |  |             await this.LoadTile(z + 1, x * 2, 1 + y * 2) | 
					
						
							|  |  |  |             await this.LoadTile(z + 1, 1 + x * 2, 1 + y * 2) | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |