Skip to content

Commit 7d45f6b

Browse files
committed
feat: add support for FAA tiles
1 parent 4fb97ca commit 7d45f6b

File tree

1 file changed

+99
-19
lines changed

1 file changed

+99
-19
lines changed

packages/leaflet/src/lib/NavigraphTileLayer.ts

Lines changed: 99 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getApp, getDefaultAppDomain, Logger, NotInitializedError, Scope } from "@navigraph/app"
22
import { NavigraphAuth } from "@navigraph/auth"
3-
import { Coords, DoneCallback, TileLayer, TileLayerOptions } from "leaflet"
3+
import { Coords, DoneCallback, LatLngBounds, TileLayer, TileLayerOptions } from "leaflet"
44

55
enum NavigraphRasterSourceOption {
66
"IFR HIGH" = "ifr.hi",
@@ -9,21 +9,26 @@ enum NavigraphRasterSourceOption {
99
"WORLD" = "world",
1010
}
1111

12-
export type NavigraphRasterSource = keyof typeof NavigraphRasterSourceOption
13-
export type NavigraphRasterTheme = "DAY" | "NIGHT"
14-
15-
export interface PresetConfig {
16-
source: NavigraphRasterSource
17-
theme: NavigraphRasterTheme
18-
forceRetina?: boolean
12+
enum FAASourceOption {
13+
"VFR" = "VFR",
14+
"IFR HIGH" = "IFRH",
15+
"IFR LOW" = "IFRL",
1916
}
2017

21-
function getNavigraphTileURL(
22-
source: NavigraphRasterSource = "VFR",
23-
theme: NavigraphRasterTheme = "DAY",
24-
retina = false,
25-
) {
26-
return `https://enroute-bitmap.charts.api-v2.${getDefaultAppDomain()}/styles/${NavigraphRasterSourceOption[source]}.${theme.toLowerCase()}/{z}/{x}/{y}${retina ? "@2x" : "{r}"}.png` // prettier-ignore
18+
export type NavigraphRasterSource = keyof typeof NavigraphRasterSourceOption
19+
export type FAASource = keyof typeof FAASourceOption
20+
export type NavigraphTheme = "DAY" | "NIGHT"
21+
22+
export type PresetConfig = { theme?: NavigraphTheme; forceRetina?: boolean } & (
23+
| { type: "Navigraph"; source: NavigraphRasterSource }
24+
| { type: "FAA"; source: Extract<FAASource, "VFR">; withTAC?: boolean }
25+
| { type: "FAA"; source: Exclude<FAASource, "VFR"> }
26+
)
27+
28+
function getNavigraphTileURL(options: PresetConfig) {
29+
const { source, theme = "DAY" } = options
30+
const forceRetina = "forceRetina" in options ? options.forceRetina : false
31+
return `https://enroute-bitmap.charts.api-v2.${getDefaultAppDomain()}/styles/${NavigraphRasterSourceOption[source]}.${theme.toLowerCase()}/{z}/{x}/{y}${forceRetina ? "@2x" : "{r}"}.png` // prettier-ignore
2732
}
2833

2934
/**
@@ -43,12 +48,67 @@ export class NavigraphTileLayer extends TileLayer {
4348
/** Indicates whether map tiles failed to load due to authentication being invalid or missing. */
4449
private isMissingAuth = false
4550

51+
private maxFAAZoom = 12
52+
53+
private tacLayer = new TileLayer(`https://enroute.charts.api-v2.${getDefaultAppDomain()}/FAA/TAC/{z}/{x}/{y}.png`, {
54+
minZoom: 10,
55+
maxNativeZoom: 12,
56+
maxZoom: this.maxFAAZoom,
57+
tileSize: 512,
58+
zoomOffset: -1,
59+
className: "faa-vfr-tiles",
60+
})
61+
62+
private vfrLayer = new TileLayer(`https://enroute.charts.api-v2.${getDefaultAppDomain()}/FAA/VFR/{z}/{x}/{y}.png`, {
63+
minZoom: 1,
64+
maxNativeZoom: 11,
65+
maxZoom: this.maxFAAZoom,
66+
className: "faa-vfr-tiles",
67+
})
68+
69+
// prettier-ignore
70+
private ifrLowLayer = new TileLayer(`https://enroute.charts.api-v2.${getDefaultAppDomain()}/FAA/IFRL/{z}/{x}/{y}.png`, {
71+
minZoom: 1,
72+
maxNativeZoom: 10,
73+
maxZoom: this.maxFAAZoom,
74+
className: "faa-ifr-tiles"
75+
})
76+
77+
// prettier-ignore
78+
private ifrHighLayer = new TileLayer(`https://enroute.charts.api-v2.${getDefaultAppDomain()}/FAA/IFRH/{z}/{x}/{y}.png`, {
79+
minZoom: 1,
80+
maxNativeZoom: 9,
81+
maxZoom: this.maxFAAZoom,
82+
className: "faa-ifr-tiles"
83+
})
84+
85+
private styleElement = (() => {
86+
const style = document.createElement("style")
87+
style.innerHTML = `
88+
.night .faa-vfr-tiles img {
89+
filter: brightness(0.6);
90+
}
91+
92+
.night .faa-ifr-tiles img {
93+
filter: hue-rotate(180deg) invert(1);
94+
}
95+
`
96+
97+
return style
98+
})()
99+
46100
constructor(
47101
public auth: NavigraphAuth,
48-
public preset: PresetConfig = { source: "VFR", theme: "DAY" },
102+
public preset: PresetConfig = { type: "Navigraph", source: "VFR", theme: "DAY" },
49103
tileOptions?: TileLayerOptions,
50104
) {
51-
super(getNavigraphTileURL(preset.source, preset.theme, preset.forceRetina), tileOptions)
105+
super(getNavigraphTileURL(preset), tileOptions)
106+
107+
this.on("remove", () => document.head?.removeChild(this.styleElement))
108+
this.on("add", () => {
109+
document.head.appendChild(this.styleElement)
110+
this.setPreset(preset)
111+
})
52112

53113
const app = getApp()
54114

@@ -78,10 +138,30 @@ export class NavigraphTileLayer extends TileLayer {
78138
* navigraphLayer.setPreset({ source: "IFR HIGH", theme: "NIGHT" });
79139
* ```
80140
*/
81-
public setPreset(preset: Partial<PresetConfig>) {
82-
this.preset = { ...this.preset, ...preset }
83-
const newUrl = getNavigraphTileURL(this.preset.source, this.preset.theme, this.preset.forceRetina)
141+
public setPreset(preset: PresetConfig) {
142+
const newUrl = getNavigraphTileURL(preset)
84143
this.setUrl(newUrl)
144+
145+
this.toggleFAALayer(this.vfrLayer, preset.type === "FAA" && preset.source === "VFR")
146+
this.toggleFAALayer(this.tacLayer, preset.type === "FAA" && preset.source === "VFR" && (preset.withTAC ?? false))
147+
this.toggleFAALayer(this.ifrLowLayer, preset.type === "FAA" && preset.source === "IFR LOW")
148+
this.toggleFAALayer(this.ifrHighLayer, preset.type === "FAA" && preset.source === "IFR HIGH")
149+
150+
document.body.classList.toggle("night", preset.theme === "NIGHT")
151+
152+
Logger.debug("Changed map preset", preset)
153+
154+
this.preset = preset
155+
}
156+
157+
private toggleFAALayer(layer: TileLayer, visible: boolean) {
158+
if (visible && !this._map.hasLayer(layer)) {
159+
Logger.debug("Adding FAA layer", layer)
160+
layer.addTo(this._map)
161+
} else if (!visible && this._map.hasLayer(layer)) {
162+
Logger.debug("Removing FAA layer", layer)
163+
layer.remove()
164+
}
85165
}
86166

87167
protected createTile(coords: Coords, done: DoneCallback): HTMLElement {

0 commit comments

Comments
 (0)