| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  | import Script from "./Script" | 
					
						
							| 
									
										
										
										
											2025-01-27 03:51:21 +01:00
										 |  |  | import { CommunityResource } from "../src/Logic/Web/CommunityIndex" | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  | import { Utils } from "../src/Utils" | 
					
						
							|  |  |  | import { FeatureCollection, MultiPolygon, Polygon } from "geojson" | 
					
						
							| 
									
										
										
										
											2025-01-27 03:14:19 +01:00
										 |  |  | import { existsSync, mkdirSync, writeFileSync } from "fs" | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  | import { GeoOperations } from "../src/Logic/GeoOperations" | 
					
						
							|  |  |  | import { Tiles } from "../src/Models/TileRange" | 
					
						
							|  |  |  | import ScriptUtils from "./ScriptUtils" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DownloadCommunityIndex extends Script { | 
					
						
							|  |  |  |     constructor() { | 
					
						
							|  |  |  |         super("Updates the community index") | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     printHelp() { | 
					
						
							|  |  |  |         console.log("Arguments are:\noutputdirectory") | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private static targetZoomlevel: number = 6 | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |     private static upstreamUrl: string = | 
					
						
							|  |  |  |         "https://raw.githubusercontent.com/osmlab/osm-community-index/main/dist/" | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Prunes away unnecessary fields from a CommunityResource | 
					
						
							|  |  |  |      * @private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private static stripResource(r: Readonly<CommunityResource>): CommunityResource { | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             id: r.id, | 
					
						
							|  |  |  |             languageCodes: r.languageCodes, | 
					
						
							|  |  |  |             account: r.account, | 
					
						
							|  |  |  |             type: r.type, | 
					
						
							|  |  |  |             resolved: { | 
					
						
							|  |  |  |                 name: r.resolved.name, | 
					
						
							|  |  |  |                 description: r.resolved.description, | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |                 url: r.resolved.url, | 
					
						
							|  |  |  |             }, | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |     private static stripResourcesObj( | 
					
						
							|  |  |  |         resources: Readonly<Record<string, Readonly<CommunityResource>>> | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |         const stripped: Record<string, CommunityResource> = {} | 
					
						
							|  |  |  |         for (const k in resources) { | 
					
						
							| 
									
										
										
										
											2025-01-27 04:02:03 +01:00
										 |  |  |             const type = resources[k].type | 
					
						
							|  |  |  |             if (type === "twitter" || type === "facebook" || type === "x") { | 
					
						
							| 
									
										
										
										
											2025-01-27 03:47:50 +01:00
										 |  |  |                 // These channels are toxic nowadays - we simply omit them
 | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |             stripped[k] = DownloadCommunityIndex.stripResource(resources[k]) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return stripped | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public static async update(targetDirectory: string) { | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |         const data = await Utils.downloadJson< | 
					
						
							|  |  |  |             FeatureCollection< | 
					
						
							|  |  |  |                 Polygon | MultiPolygon, | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     resources: Record<string, CommunityResource> | 
					
						
							|  |  |  |                     nameEn: string | 
					
						
							|  |  |  |                     id: string | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             > | 
					
						
							|  |  |  |         >(DownloadCommunityIndex.upstreamUrl + "completeFeatureCollection.json") | 
					
						
							| 
									
										
										
										
											2025-01-27 03:14:19 +01:00
										 |  |  |         if (!existsSync(targetDirectory)) { | 
					
						
							|  |  |  |             mkdirSync(targetDirectory) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |         const features = data.features | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |         const global = features.find((f) => f.id === "Q2") | 
					
						
							|  |  |  |         const globalProperties = DownloadCommunityIndex.stripResourcesObj( | 
					
						
							|  |  |  |             global.properties.resources | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |         ) | 
					
						
							|  |  |  |         writeFileSync(targetDirectory + "/global.json", JSON.stringify(globalProperties), "utf8") | 
					
						
							|  |  |  |         console.log("Written global properties") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const types = new Set<string>() | 
					
						
							|  |  |  |         for (const f of features) { | 
					
						
							|  |  |  |             const res = f.properties.resources | 
					
						
							|  |  |  |             for (const k in res) { | 
					
						
							|  |  |  |                 types.add(res[k].type) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         for (const type of types) { | 
					
						
							|  |  |  |             const url = `${DownloadCommunityIndex.upstreamUrl}img/${type}.svg` | 
					
						
							|  |  |  |             await ScriptUtils.DownloadFileTo(url, `${targetDirectory}/${type}.svg`) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |         const local = features.filter((f) => f.id !== "Q2") | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |         const spread = GeoOperations.spreadIntoBboxes(local, DownloadCommunityIndex.targetZoomlevel) | 
					
						
							|  |  |  |         let written = 0 | 
					
						
							|  |  |  |         let skipped = 0 | 
					
						
							| 
									
										
										
										
											2025-01-27 03:14:19 +01:00
										 |  |  |         const writtenTilesOverview: Record<number, number[]> = {} | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |         writeFileSync( | 
					
						
							|  |  |  |             targetDirectory + "local.geojson", | 
					
						
							|  |  |  |             JSON.stringify({ type: "FeatureCollection", features: local }) | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |         for (const tileIndex of spread.keys()) { | 
					
						
							|  |  |  |             const features = spread.get(tileIndex) | 
					
						
							|  |  |  |             const clipped = GeoOperations.clipAllInBox(features, tileIndex) | 
					
						
							|  |  |  |             if (clipped.length === 0) { | 
					
						
							|  |  |  |                 skipped++ | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-01-27 04:00:51 +01:00
										 |  |  |             for (const f of clipped) { | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |                 f.properties.resources = DownloadCommunityIndex.stripResourcesObj( | 
					
						
							|  |  |  |                     f.properties.resources | 
					
						
							|  |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2025-01-27 04:00:51 +01:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |             const [z, x, y] = Tiles.tile_from_index(tileIndex) | 
					
						
							|  |  |  |             const path = `${targetDirectory}/tile_${z}_${x}_${y}.geojson` | 
					
						
							|  |  |  |             clipped.forEach((f) => { | 
					
						
							|  |  |  |                 delete f.bbox | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |             writeFileSync( | 
					
						
							|  |  |  |                 path, | 
					
						
							|  |  |  |                 JSON.stringify({ type: "FeatureCollection", features: clipped }), | 
					
						
							|  |  |  |                 "utf8" | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |             written++ | 
					
						
							| 
									
										
										
										
											2025-01-27 03:14:19 +01:00
										 |  |  |             let yList = writtenTilesOverview[x] | 
					
						
							|  |  |  |             if (!yList) { | 
					
						
							|  |  |  |                 yList = [] | 
					
						
							|  |  |  |                 writtenTilesOverview[x] = yList | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             yList.push(y) | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |             console.log(`Written tile ${path}`) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         console.log(`Created ${written} tiles, skipped ${skipped}`) | 
					
						
							| 
									
										
										
										
											2025-02-10 02:04:58 +01:00
										 |  |  |         writeFileSync( | 
					
						
							|  |  |  |             targetDirectory + "/tile_6_overview.json", | 
					
						
							|  |  |  |             JSON.stringify(writtenTilesOverview), | 
					
						
							|  |  |  |             "utf8" | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2025-01-27 03:20:07 +01:00
										 |  |  |         console.log("Created overview file") | 
					
						
							| 
									
										
										
										
											2025-01-27 02:32:19 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async main(args: string[]): Promise<void> { | 
					
						
							|  |  |  |         const path = args[0] | 
					
						
							|  |  |  |         if (!path) { | 
					
						
							|  |  |  |             this.printHelp() | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         await DownloadCommunityIndex.update(path) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | new DownloadCommunityIndex().run() |