summaryrefslogblamecommitdiff
path: root/app.mjs
blob: 666cb2a603db409fdacdec6b3bdb45df44557380 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11



                                                                  
                                                         

                                                 



                                  

                                                 


                                                

                                

                                                      




                                                                


                                                    











                                                             
                                                                                

                                                 
                                 
                          











                                                                


                                                    



















                                                                                





                                                                        











                                                                    




                                                                


                                                           

                                     


                                                           






















                                                                                






                                                                        











                                                                    






























































                                                                                      
             

                                                                      
                                                                

                                                           
  
 







                                      


                                              
                       

                     

                                                                                                                                     







                                                         
                                                    


                                                                            









                                                                                                             


         
#!/usr/bin/env node

const csUrl = "https://aeronav.faa.gov/upload_313-d/supplements/";
const tppUrl = "https://aeronav.faa.gov/upload_313-d/terminal/";
const ifrEnrouteUrl = "https://aeronav.faa.gov/enroute/";
const vfrUrl = "https://aeronav.faa.gov/visual/";
const output = ".";

import { JSDOM } from "jsdom";
import moment from "moment";
import fs from "node:fs/promises";
import decompress from "decompress";
import { waitForDebugger } from "node:inspector";

const csRegions = ["SW"];
const tppRegions = ["SW1", "SW2", "SW3", "SW4"];
const ifrEnrouteLow = [2, 3, 4];
const ifrEnrouteHigh = [];
const vfrSectional = ["San Francisco", "Los Angeles"];
const vfrTerminal = ["San Francisco", "Los Angeles"];

const getChartSupplementDiretory = async () => {
    const dom = await JSDOM.fromURL(csUrl, {});
    let chd = dom.window.document.querySelector("pre").children;
    const files = {};
    for (let region of csRegions) {
        files[region] = [];
        files[region].current = { date: moment(0) };
    }
    const pattern = /CS_([A-Z]+)_([0-9]{8}).pdf/;
    for (let i = 0; i < chd.length; i++) {
        if (chd[i] instanceof dom.window.HTMLAnchorElement) {
            const file = chd[i].textContent;
            const m = file.match(pattern);
            if (m) {
                const region = m[1];
                const date = moment(m[2], "YYYYMMDD");
                const url = `${csUrl}${file}`;
                if (files[region]) {
                    files[region].push({ url, date });
                    if (moment() >= date && date > files[region].current.date) {
                        files[region].current = {
                            url,
                            date,
                        };
                    }
                }
            }
        }
    }
    return files;
};

const getTerminalProcedurePublication = async () => {
    const dom = await JSDOM.fromURL(tppUrl, {});
    let chd = dom.window.document.querySelector("pre").children;
    const files = {};
    for (let region of tppRegions) {
        files[region] = [];
        files[region].current = { date: moment(0) };
    }
    const pattern = /([0-9]{4}-[0-9]{2}-[0-9]{2})/;
    for (let i = 0; i < chd.length; i++) {
        if (chd[i] instanceof dom.window.HTMLAnchorElement) {
            const folder = chd[i].textContent;
            const m = folder.match(pattern);
            if (m) {
                const dom = await JSDOM.fromURL(`${tppUrl}${m[1]}`, {});
                const date = moment(m[1], "YYYY-MM-DD");
                const items = dom.window.document.querySelector("pre").children;
                const pattern = /([A-Z1-9]+).pdf/;
                if (moment() >= date) {
                    for (let j = 0; j < items.length; j++) {
                        const file = items[j].textContent;
                        const m = file.match(pattern);
                        const url = `${tppUrl}/${folder}/${file}`;
                        if (m) {
                            const region = m[1];
                            if (files[region]) {
                                files[region].push({ url, date });
                                if (date > files[region].current.date) {
                                    files[region].current = {
                                        url,
                                        date,
                                    };
                                }
                            }
                        }
                    }
                } else {
                    console.log(`skipped ${folder}: in the future`);
                }
            }
        }
    }
    return files;
};

const getIFREnroute = async () => {
    const dom = await JSDOM.fromURL(ifrEnrouteUrl, {});
    let chd = dom.window.document.querySelector("pre").children;
    const files = {};
    for (const i of ifrEnrouteLow) {
        const region = `L${i.toFixed(0).padStart(2, "0")}`;
        files[region] = [];
        files[region].current = { date: moment(0) };
    }
    for (const i of ifrEnrouteHigh) {
        const region = `H${i.toFixed(0).padStart(2, "0")}`;
        files[region] = [];
        files[region].current = { date: moment(0) };
    }

    const pattern = /([0-9]{2}-[0-9]{2}-[0-9]{4})/;
    for (let i = 0; i < chd.length; i++) {
        if (chd[i] instanceof dom.window.HTMLAnchorElement) {
            const folder = chd[i].textContent;
            const m = folder.match(pattern);
            if (m) {
                const dom = await JSDOM.fromURL(`${ifrEnrouteUrl}${m[1]}`, {});
                const date = moment(m[1], "MM-DD-YYYY");
                const items = dom.window.document.querySelector("pre").children;
                const pattern = /ENR_([A-Z0-9]+).zip/;
                if (moment() >= date) {
                    for (let j = 0; j < items.length; j++) {
                        const file = items[j].textContent;
                        const m = file.match(pattern);
                        const url = `${ifrEnrouteUrl}/${folder}/${file}`;
                        if (m) {
                            const region = m[1];
                            const unzip = {};
                            unzip[`ENR_${region}.tif`] = ".tif";
                            if (files[region]) {
                                files[region].push({ url, date });
                                if (date > files[region].current.date) {
                                    files[region].current = {
                                        url,
                                        date,
                                        unzip,
                                    };
                                }
                            }
                        }
                    }
                } else {
                    console.log(`skipped ${folder}: in the future`);
                }
            }
        }
    }
    return files;
};

const getVFR = async () => {
    const secFiles = {};
    for (const i of vfrSectional) {
        const _region = i.replace(" ", "_");
        secFiles[_region] = [];
        secFiles[_region].current = { date: moment(0) };
    }
    const tacFiles = {};
    for (const i of vfrTerminal) {
        const _region = i.replace(" ", "_");
        tacFiles[_region] = [];
        tacFiles[_region].current = { date: moment(0) };
    }
    const dom = await JSDOM.fromURL(`${vfrUrl}`, {});
    let chd = dom.window.document.querySelector("pre").children;
    const pattern = /([0-9]{2}-[0-9]{2}-[0-9]{4})/;
    for (let i = 0; i < chd.length; i++) {
        if (chd[i] instanceof dom.window.HTMLAnchorElement) {
            const folder = chd[i].textContent;
            const m = folder.match(pattern);
            if (m) {
                const date = moment(m[1], "MM-DD-YYYY");
                if (moment() >= date) {
                    for (const region of vfrSectional) {
                        const unzip = {};
                        const _region = region.replace(" ", "_");
                        const url = `${vfrUrl}${m[1]}/sectional-files/${_region}.zip`;
                        unzip[`${region} SEC.tif`] = ".tif";
                        secFiles[_region].push({ url, date });
                        if (date > secFiles[_region].current.date) {
                            secFiles[_region].current = {
                                url,
                                date,
                                unzip,
                            };
                        }
                    }
                    for (const region of vfrTerminal) {
                        const unzip = {};
                        const _region = region.replace(" ", "_");
                        const url = `${vfrUrl}${m[1]}/tac-files/${_region}_TAC.zip`;
                        unzip[`${region} TAC.tif`] = ".tif";
                        unzip[`${region} FLY.tif`] = "-flyway.tif";
                        tacFiles[_region].push({ url, date });
                        if (date > tacFiles[_region].current.date) {
                            tacFiles[_region].current = {
                                url,
                                date,
                                unzip,
                            };
                        }
                    }
                } else {
                    console.log(`skipped ${folder}: in the future`);
                }
            }
        }
    }
    return { secFiles, tacFiles };
};

const vfrCharts = await getVFR();

const res = [
    { prefix: "cs", files: await getChartSupplementDiretory() },
    { prefix: "tpp", files: await getTerminalProcedurePublication() },
    { prefix: "ifr-enroute-low", files: await getIFREnroute() },
    { prefix: "vfr-sectional", files: vfrCharts.secFiles },
    { prefix: "vfr-terminal", files: vfrCharts.tacFiles },
];

const zip_path = `${output}/zips`;
const chart_path = `${output}/charts`;

try {
    await fs.mkdir(zip_path);
    await fs.mkdir(chart_path);
} catch (_) {}

for (let r of res) {
    for (let region of Object.keys(r.files)) {
        const cur = r.files[region].current;
        if (!cur.url) {
            continue;
        }
        const date = cur.date.format("YYYY-MM-DD");
        const file = `${cur.unzip ? zip_path : chart_path}/${r.prefix}-${region.toLowerCase()}-${date}.${cur.unzip ? "zip" : "pdf"}`;
        const fileExists = (path) =>
            fs.stat(path).then(
                () => true,
                () => false,
            );
        if (await fileExists(file)) {
            console.log(`file "${file}" already exists`);
        } else {
            console.log(`downloading "${cur.url}"`);
            await fetch(cur.url)
                .then((r) => r.arrayBuffer())
                .then((bytes) => fs.writeFile(file, new Uint8Array(bytes)));
        }
        if (cur.unzip) {
            console.log(`extracting "${file}"`);
            await decompress(file, "./", {
                filter: (f) => cur.unzip[f.path] != null,
                map: (f) => {
                    f.path = `${chart_path}/${r.prefix}-${region.toLowerCase()}-${date}${cur.unzip[f.path]}`;
                    return f;
                },
            });
        }
    }
}