|
| 1 | +import { Logger, getDefaultAppDomain } from "@navigraph/app"; |
| 2 | +import { NavigraphAuth } from "@navigraph/auth"; |
| 3 | +import { TileLayer, Coords, DoneCallback } from "leaflet"; |
| 4 | + |
| 5 | +export enum NavigraphRasterSource { |
| 6 | + "IFR HIGH" = "ifr.hi", |
| 7 | + "IFR LOW" = "ifr.lo", |
| 8 | + "VFR" = "vfr", |
| 9 | + "WORLD" = "world", |
| 10 | +} |
| 11 | + |
| 12 | +export type RasterTheme = "DAY" | "NIGHT"; |
| 13 | + |
| 14 | +function getNavigraphTileURL( |
| 15 | + source: keyof typeof NavigraphRasterSource = "VFR", |
| 16 | + theme: RasterTheme = "DAY", |
| 17 | + retina = false |
| 18 | +) { |
| 19 | + return `https://enroute-bitmap.charts.api-v2.${getDefaultAppDomain()}/styles/${NavigraphRasterSource[source]}.${theme.toLowerCase()}/{z}/{x}/{y}${retina ? "@2x" : "{r}"}.png` // prettier-ignore |
| 20 | +} |
| 21 | + |
| 22 | +export interface PresetConfig { |
| 23 | + source: keyof typeof NavigraphRasterSource; |
| 24 | + theme: RasterTheme; |
| 25 | + forceRetina?: boolean; |
| 26 | +} |
| 27 | + |
| 28 | +/** |
| 29 | + * A Leaflet tile layer that renders Navigraph enroute charts. |
| 30 | + * @example |
| 31 | + * ```ts |
| 32 | + * const navigraphLayer = new NavigraphTileLayer(auth, { source: "IFR HIGH", theme: "NIGHT" }); |
| 33 | + * navigraphLayer.addTo(map); |
| 34 | + * |
| 35 | + * navigraphLayer.setPreset({ source: "IFR LOW", theme: "DAY" }); |
| 36 | + * ``` |
| 37 | + */ |
| 38 | +export class NavigraphTileLayer extends TileLayer { |
| 39 | + /** A list of tiles that has failed to load since the last successful tile load. */ |
| 40 | + protected FAILED_TILES = new Set<string>(); |
| 41 | + |
| 42 | + /** Indicates whether map tiles failed to load due to authentication being invalid or missing. */ |
| 43 | + private isMissingAuth = false; |
| 44 | + |
| 45 | + constructor(public auth: NavigraphAuth, public preset: PresetConfig = { source: "VFR", theme: "DAY" }) { |
| 46 | + super(getNavigraphTileURL(preset.source, preset.theme, preset.forceRetina)); |
| 47 | + |
| 48 | + auth.onAuthStateChanged((user) => { |
| 49 | + if (this.isMissingAuth && user) { |
| 50 | + this.redraw(); |
| 51 | + this.isMissingAuth = false; |
| 52 | + } |
| 53 | + }); |
| 54 | + |
| 55 | + if (!this.auth.isInitialized()) { |
| 56 | + Logger.warning( |
| 57 | + "NavigraphLayer was created before Navigraph Auth was initialized. Tiles may fail to load until a user is signed in." |
| 58 | + ); |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + /** |
| 63 | + * Changes the preset that the map is rendering. Automatically rerenders the map. |
| 64 | + * @param preset The base style of the map tiles. |
| 65 | + * @param theme The color theme of the map tiles. |
| 66 | + * @example |
| 67 | + * ```ts |
| 68 | + * navigraphLayer.setPreset({ source: "IFR HIGH", theme: "NIGHT" }); |
| 69 | + * ``` |
| 70 | + */ |
| 71 | + public setPreset(preset: PresetConfig) { |
| 72 | + this.preset = preset; |
| 73 | + const newUrl = getNavigraphTileURL(preset.source, preset.theme, preset.forceRetina); |
| 74 | + this.setUrl(newUrl); |
| 75 | + } |
| 76 | + |
| 77 | + protected createTile(coords: Coords, done: DoneCallback): HTMLElement { |
| 78 | + const url = this.getTileUrl(coords); |
| 79 | + const img = document.createElement("img"); |
| 80 | + |
| 81 | + img.onerror = () => { |
| 82 | + Logger.debug("Failed to load tile!"); |
| 83 | + |
| 84 | + this.isMissingAuth = this.auth.getUser() === null; |
| 85 | + const tileHasFailedBefore = this.FAILED_TILES.has(url); |
| 86 | + |
| 87 | + if (tileHasFailedBefore || this.isMissingAuth) return; |
| 88 | + |
| 89 | + Logger.debug("Refreshing auth and tile..."); |
| 90 | + this.FAILED_TILES.add(url); |
| 91 | + this.auth |
| 92 | + .getUser(true) |
| 93 | + .then(() => (img.src = url)) |
| 94 | + .catch(() => (this.isMissingAuth = true)); |
| 95 | + }; |
| 96 | + |
| 97 | + img.onload = () => { |
| 98 | + done(undefined, img); |
| 99 | + this.FAILED_TILES.clear(); |
| 100 | + Logger.debug("Loaded tile successfully!"); |
| 101 | + }; |
| 102 | + |
| 103 | + img.src = url; |
| 104 | + |
| 105 | + return img; |
| 106 | + } |
| 107 | +} |
0 commit comments