| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Fetches a geojson file somewhere and passes it along | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | import { UIEventSource } from "../../UIEventSource" | 
					
						
							|  |  |  | import FilteredLayer from "../../../Models/FilteredLayer" | 
					
						
							|  |  |  | import { Utils } from "../../../Utils" | 
					
						
							|  |  |  | import { FeatureSourceForLayer, Tiled } from "../FeatureSource" | 
					
						
							|  |  |  | import { Tiles } from "../../../Models/TileRange" | 
					
						
							|  |  |  | import { BBox } from "../../BBox" | 
					
						
							|  |  |  | import { GeoOperations } from "../../GeoOperations" | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     public readonly features: UIEventSource<{ feature: any; freshness: Date }[]> | 
					
						
							|  |  |  |     public readonly state = new UIEventSource<undefined | { error: string } | "loaded">(undefined) | 
					
						
							|  |  |  |     public readonly name | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |     public readonly isOsmCache: boolean | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     public readonly layer: FilteredLayer | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |     public readonly tileIndex | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     public readonly bbox | 
					
						
							|  |  |  |     private readonly seenids: Set<string> | 
					
						
							|  |  |  |     private readonly idKey?: string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public constructor( | 
					
						
							|  |  |  |         flayer: FilteredLayer, | 
					
						
							|  |  |  |         zxy?: [number, number, number] | BBox, | 
					
						
							|  |  |  |         options?: { | 
					
						
							|  |  |  |             featureIdBlacklist?: Set<string> | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |         if (flayer.layerDef.source.geojsonZoomLevel !== undefined && zxy === undefined) { | 
					
						
							|  |  |  |             throw "Dynamic layers are not supported. Use 'DynamicGeoJsonTileSource instead" | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         this.layer = flayer | 
					
						
							| 
									
										
										
										
											2022-02-11 03:57:39 +01:00
										 |  |  |         this.idKey = flayer.layerDef.source.idKey | 
					
						
							|  |  |  |         this.seenids = options?.featureIdBlacklist ?? new Set<string>() | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         let url = flayer.layerDef.source.geojsonSource.replace("{layer}", flayer.layerDef.id) | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |         if (zxy !== undefined) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             let tile_bbox: BBox | 
					
						
							| 
									
										
										
										
											2022-01-21 01:57:16 +01:00
										 |  |  |             if (zxy instanceof BBox) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 tile_bbox = zxy | 
					
						
							| 
									
										
										
										
											2022-01-21 01:57:16 +01:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 const [z, x, y] = zxy | 
					
						
							|  |  |  |                 tile_bbox = BBox.fromTile(z, x, y) | 
					
						
							| 
									
										
										
										
											2022-01-21 01:57:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 this.tileIndex = Tiles.tile_index(z, x, y) | 
					
						
							|  |  |  |                 this.bbox = BBox.fromTile(z, x, y) | 
					
						
							|  |  |  |                 url = url | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     .replace("{z}", "" + z) | 
					
						
							|  |  |  |                     .replace("{x}", "" + x) | 
					
						
							|  |  |  |                     .replace("{y}", "" + y) | 
					
						
							| 
									
										
										
										
											2022-01-21 01:57:16 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             let bounds: { minLat: number; maxLat: number; minLon: number; maxLon: number } = | 
					
						
							|  |  |  |                 tile_bbox | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             if (this.layer.layerDef.source.mercatorCrs) { | 
					
						
							| 
									
										
										
										
											2021-10-27 03:52:19 +02:00
										 |  |  |                 bounds = tile_bbox.toMercator() | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-01-21 01:57:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |             url = url | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 .replace("{y_min}", "" + bounds.minLat) | 
					
						
							|  |  |  |                 .replace("{y_max}", "" + bounds.maxLat) | 
					
						
							|  |  |  |                 .replace("{x_min}", "" + bounds.minLon) | 
					
						
							|  |  |  |                 .replace("{x_max}", "" + bounds.maxLon) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2021-09-26 17:36:39 +02:00
										 |  |  |             this.tileIndex = Tiles.tile_index(0, 0, 0) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             this.bbox = BBox.global | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         this.name = "GeoJsonSource of " + url | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         this.isOsmCache = flayer.layerDef.source.isOsmCacheLayer | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |         this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) | 
					
						
							|  |  |  |         this.LoadJSONFrom(url) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private LoadJSONFrom(url: string) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const eventSource = this.features | 
					
						
							|  |  |  |         const self = this | 
					
						
							| 
									
										
										
										
											2022-06-13 03:17:10 +02:00
										 |  |  |         Utils.downloadJsonCached(url, 60 * 60) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             .then((json) => { | 
					
						
							| 
									
										
										
										
											2022-03-24 03:11:29 +01:00
										 |  |  |                 self.state.setData("loaded") | 
					
						
							| 
									
										
										
										
											2022-07-09 21:41:33 +00:00
										 |  |  |                 // TODO: move somewhere else, just for testing
 | 
					
						
							|  |  |  |                 // Check for maproulette data
 | 
					
						
							| 
									
										
										
										
											2022-07-12 07:42:26 +00:00
										 |  |  |                 if (url.startsWith("https://maproulette.org/api/v2/tasks/box/")) { | 
					
						
							| 
									
										
										
										
											2022-07-09 21:41:33 +00:00
										 |  |  |                     console.log("MapRoulette data detected") | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     const data = json | 
					
						
							|  |  |  |                     let maprouletteFeatures: any[] = [] | 
					
						
							|  |  |  |                     data.forEach((element) => { | 
					
						
							| 
									
										
										
										
											2022-07-09 21:41:33 +00:00
										 |  |  |                         maprouletteFeatures.push({ | 
					
						
							|  |  |  |                             type: "Feature", | 
					
						
							|  |  |  |                             geometry: { | 
					
						
							|  |  |  |                                 type: "Point", | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                                 coordinates: [element.point.lng, element.point.lat], | 
					
						
							| 
									
										
										
										
											2022-07-09 21:41:33 +00:00
										 |  |  |                             }, | 
					
						
							|  |  |  |                             properties: { | 
					
						
							|  |  |  |                                 // Map all properties to the feature
 | 
					
						
							|  |  |  |                                 ...element, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                             }, | 
					
						
							|  |  |  |                         }) | 
					
						
							|  |  |  |                     }) | 
					
						
							|  |  |  |                     json.features = maprouletteFeatures | 
					
						
							| 
									
										
										
										
											2022-07-09 21:41:33 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |                 if (json.features === undefined || json.features === null) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     return | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if (self.layer.layerDef.source.mercatorCrs) { | 
					
						
							| 
									
										
										
										
											2021-10-27 03:52:19 +02:00
										 |  |  |                     json = GeoOperations.GeoJsonToWGS84(json) | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-01-21 01:57:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 const time = new Date() | 
					
						
							|  |  |  |                 const newFeatures: { feature: any; freshness: Date }[] = [] | 
					
						
							|  |  |  |                 let i = 0 | 
					
						
							|  |  |  |                 let skipped = 0 | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |                 for (const feature of json.features) { | 
					
						
							|  |  |  |                     const props = feature.properties | 
					
						
							|  |  |  |                     for (const key in props) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                         if (props[key] === null) { | 
					
						
							| 
									
										
										
										
											2022-02-14 13:52:18 +01:00
										 |  |  |                             delete props[key] | 
					
						
							|  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |                         if (typeof props[key] !== "string") { | 
					
						
							| 
									
										
										
										
											2021-09-29 17:48:15 +02:00
										 |  |  |                             // Make sure all the values are string, it crashes stuff otherwise
 | 
					
						
							| 
									
										
										
										
											2022-01-07 04:14:53 +01:00
										 |  |  |                             props[key] = JSON.stringify(props[key]) | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     if (self.idKey !== undefined) { | 
					
						
							| 
									
										
										
										
											2022-02-11 03:57:39 +01:00
										 |  |  |                         props.id = props[self.idKey] | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |                     if (props.id === undefined) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                         props.id = url + "/" + i | 
					
						
							|  |  |  |                         feature.id = url + "/" + i | 
					
						
							|  |  |  |                         i++ | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |                     } | 
					
						
							|  |  |  |                     if (self.seenids.has(props.id)) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                         skipped++ | 
					
						
							|  |  |  |                         continue | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |                     } | 
					
						
							|  |  |  |                     self.seenids.add(props.id) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     let freshness: Date = time | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |                     if (feature.properties["_last_edit:timestamp"] !== undefined) { | 
					
						
							|  |  |  |                         freshness = new Date(props["_last_edit:timestamp"]) | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     newFeatures.push({ feature: feature, freshness: freshness }) | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |                 if (newFeatures.length == 0) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     return | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 eventSource.setData(eventSource.data.concat(newFeatures)) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             }) | 
					
						
							|  |  |  |             .catch((msg) => { | 
					
						
							|  |  |  |                 console.debug("Could not load geojson layer", url, "due to", msg) | 
					
						
							|  |  |  |                 self.state.setData({ error: msg }) | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2021-09-20 17:14:55 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } |