Examples
Customize app info panel content

Create a Custom Info Panel Content View

Create a custom info panel content view associated with a custom map content source. The following example will setup a vector content source that loads airport observations and renders their latest flight rules as point markers on an interactive map. It then configures a custom airport content view to display within the application info panel when an airport’s marker is selected.

Custom interactive map data source example

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Xweather JavaScript SDK - Interactive Map App w/Custom Info Panel</title>
    <script defer src="https://cdn.aerisapi.com/sdk/js/latest/aerisweather.min.js"></script>
    <link rel="stylesheet" href="https://cdn.aerisapi.com/sdk/js/latest/aerisweather.css">
 
    <style>
    #app {
        font-family: 'Helvetica','Arial',sans-serif;
        height: 600px;
        margin: 30px auto;
        width: 1000px;
    }
    .awxjs__app__ui-panel-info-content .flightrule,
    .awxjs__app__ui-panel-info-content .flight-status {
        font-size: 16px;
        font-weight: bold;
    }
    .awxjs__app__ui-panel-info-content .flightrule .value,
    .awxjs__app__ui-panel-info-content .flight-status .value {
        font-size: 20px;
        font-weight: bold;
        padding-right: 35px;
        position: relative;
        text-align: right;
    }
    .awxjs__app__ui-panel-info-content .flightrule .indicator,
    .awxjs__app__ui-panel-info-content .flight-status .indicator {
        border: 2px solid #333;
        border-radius: 15px;
        height: 20px;
        margin-top: -12px;
        right: 0;
        position: absolute;
        top: 50%;
        width: 20px;
    }
    </style>
 
</head>
<body>
 
<div id="app"></div>
 
<script>
 
    // Flight rule-to-color mapping dictionary
    const colors = {
        flightrule: {
            VFR: '#1bbe08',
            MVFR: '#1c56cf',
            IFR: '#fa0708',
            LIFR: '#fb06ff'
        }
    };
 
    // Utility function that returns the color for a specific flight rule code
    function flightRuleColor(code) {
        code = (code || '').toUpperCase();
        return colors.flightrule || '#999999';
    }
 
    window.onload = () => {
 
        const aeris = new Xweather('CLIENT_ID', 'CLIENT_SECRET');
        const utils = aeris.utils;
 
        aeris.apps().then((apps) => {
            const app = new apps.InteractiveMapApp('#app', {
                map: {
                    strategy: 'leaflet',
                    zoom: 4,
                    layers: 'radar'
                },
                panels: {
                    layers: {
                        buttons: [{
                            id: 'radar',
                            value: 'radar:80',
                            title: 'Radar'
                        },{
                            id: 'satellite',
                            value: 'satellite:75',
                            title: 'Satellite'
                        },{
                            id: 'alerts',
                            value: 'alerts',
                            title: 'Alerts'
                        },{
                            id: 'airports',
                            value: 'airports',
                            title: 'Airports'
                        }]       
                    },
                    info: {
                        sections: {
 
                        },
                        views: {
 
                            // Create an airport-specific view for the info panel
                            airport: {
 
                                // Return the network request to load the necessary data required by all of the views
                                request: () => {
                                    const request = aeris.api();
                                    const forecastFields = 'timestamp,tempF,icon,weatherPrimary,windSpeedMPH,windSpeedMinMPH,windSpeedMaxMPH,windGustMPH,snowIN,precipIN'.split(',').map((key) => 'periods.' + key);
                                    request.addRequest(aeris.api().endpoint('forecasts').fields(forecastFields.join(',')).filter('3hr').limit(7));
                                    request.addRequest(aeris.api().endpoint('convective/outlook').action('contains'));
                                    request.addRequest(aeris.api().endpoint('lightning/summary').action('closest').radius('60mi').limit(100));
 
                                    return request;
                                },
 
                                // Setup the array of views to display in the info panel for an airport.
                                // Each view object's `renderer` can be a string to use a built-in view, or a function that receives the data
                                // configured by the `data` function and returns the HTML to display for that view
                                views: [{
                                    data: (data) => {
                                        if (!data) return null;
                                        data = data.observations || data;
                                        return data.ob;
                                    },
                                    renderer: (data) => {
                                        if (!data) return;
                                        return (`
                                            <div class="flightrule">
                                                <div class="awxjs__ui-cols align-center">
                                                    <div class="awxjs__ui-expand">Flight Rule</div>
                                                    <div class="awxjs__ui-expand value">${data.flightRule} <div class="indicator" style="background:${flightRuleColor(data.flightRule)};"></div></div>
                                                </div>
                                            </div>
                                        `);
                                    }
                                },{
                                    title: 'Impacts',
                                    renderer: 'hazards'
                                },{
                                    title: 'Observations',
                                    renderer: 'obs'
                                },{
                                    title: 'Short-Term Forecast',
                                    renderer: 'forecast'
                                }]
                            }
                        }
                    }
                }
            });
 
            // Add vector source showing airport flight rules as markers
            const ids = 'id:AYPY;id:BIKF;id:BKPR;id:CYEG;id:CYHZ;id:CYOW;id:CYQB;id:CYUL;id:CYVR;id:CYWG;id:CYXU;id:CYYC;id:CYYJ;id:CYYT;id:CYYZ;id:DAAG;id:DGAA;id:DNAA;id:DNMM;id:DTTA;id:EBBR;id:EBLG;id:EDDB;id:EDDC;id:EDDF;id:EDDG;id:EDDH;id:EDDK;id:EDDL;id:EDDM;id:EDDN;id:EDDP;id:EDDS;id:EDDT;id:EDDV;id:EDDW;id:EDLW;id:EDSB;id:EETN;id:EFHK;id:EGAA;id:EGAC;id:EGBB;id:EGCC;id:EGCN;id:EGFF;id:EGGD;id:EGGP;id:EGGW;id:EGHH;id:EGHI;id:EGKK;id:EGLC;id:EGLL;id:EGNM;id:EGNT;id:EGNX;id:EGPD;id:EGPF;id:EGPH;id:EGSH;id:EGSS;id:EGTE;id:EGUL;id:EGUN;id:EGVA;id:EGVN;id:EHAM;id:EHBK;id:EHEH;id:EICK;id:EIDW;id:EINN;id:EKBI;id:EKCH;id:EKYT;id:ELLX;id:ENBO;id:ENBR;id:ENGM;id:ENTC;id:ENVA;id:ENZV;id:EPGD;id:EPKK;id:EPKT;id:EPMO;id:EPPO;id:EPWA;id:EPWR;id:ESGG;id:ESMS;id:ESPA;id:ESSA;id:ETAR;id:EVRA;id:EYVI;id:FACT;id:FAGG;id:FAJS;id:FALE;id:GCLP;id:GCTS;id:GCXO;id:GMMN;id:GOOY;id:HAAB;id:HECA;id:HEGN;id:HKJK;id:HKMO;id:HLLT;id:HSSS;id:KABQ;id:KADW;id:KAFW;id:KAGS;id:KAMA;id:KATL;id:KAUS;id:KAVL;id:KBAB;id:KBAD;id:KBDL;id:KBFI;id:KBGR;id:KBHM;id:KBIL;id:KBLV;id:KBMI;id:KBNA;id:KBOI;id:KBOS;id:KBTR;id:KBUF;id:KBWI;id:KCAE;id:KCBM;id:KCHA;id:KCHS;id:KCID;id:KCLE;id:KCLT;id:KCMH;id:KCOF;id:KCOS;id:KCPR;id:KCRP;id:KCRW;id:KCVG;id:KCVS;id:KDAL;id:KDAY;id:KDBQ;id:KDCA;id:KDEN;id:KDFW;id:KDLF;id:KDLH;id:KDOV;id:KDSM;id:KDTW;id:KDYS;id:KEDW;id:KEND;id:KERI;id:KEWR;id:KFFO;id:KFLL;id:KFSM;id:KFTW;id:KFWA;id:KGEG;id:KGPT;id:KGRB;id:KGSB;id:KGSO;id:KGSP;id:KGUS;id:KHIB;id:KHMN;id:KHOU;id:KHSV;id:KHTS;id:KIAD;id:KIAH;id:KICT;id:KIND;id:KJAN;id:KJAX;id:KJFK;id:KJLN;id:KLAS;id:KLAX;id:KLBB;id:KLCK;id:KLEX;id:KLFI;id:KLFT;id:KLGA;id:KLIT;id:KLTS;id:KLUF;id:KMBS;id:KMCF;id:KMCI;id:KMCO;id:KMDW;id:KMEM;id:KMGE;id:KMGM;id:KMHT;id:KMIA;id:KMKE;id:KMLI;id:KMLU;id:KMOB;id:KMSN;id:KMSP;id:KMSY;id:KMUO;id:KMYR;id:KOAK;id:KOKC;id:KONT;id:KORD;id:KORF;id:KPAM;id:KPBI;id:KPDX;id:KPHF;id:KPHL;id:KPHX;id:KPIA;id:KPIT;id:KPWM;id:KRDU;id:KRFD;id:KRIC;id:KRND;id:KRNO;id:KROA;id:KROC;id:KRST;id:KRSW;id:KSAN;id:KSAT;id:KSAV;id:KSBN;id:KSDF;id:KSEA;id:KSFB;id:KSFO;id:KSGF;id:KSHV;id:KSJC;id:KSKA;id:KSLC;id:KSMF;id:KSNA;id:KSPI;id:KSPS;id:KSRQ;id:KSSC;id:KSTL;id:KSUS;id:KSUU;id:KSUX;id:KSYR;id:KSZL;id:KTCM;id:KTIK;id:KTLH;id:KTOL;id:KTPA;id:KTRI;id:KTUL;id:KTUS;id:KTYS;id:KVBG;id:KVPS;id:KWRB;id:LATI;id:LBBG;id:LBPD;id:LBSF;id:LBWN;id:LCLK;id:LCPH;id:LDZA;id:LEAL;id:LEBL;id:LEMD;id:LEMG;id:LEPA;id:LEST;id:LFBD;id:LFBO;id:LFBV;id:LFML;id:LFMN;id:LFOA;id:LFOT;id:LFPG;id:LFPO;id:LGAV;id:LGIR;id:LGTS;id:LHBP;id:LIBD;id:LICC;id:LICJ;id:LIEE;id:LIMC;id:LIME;id:LIMF;id:LIMJ;id:LIML;id:LIMZ;id:LIPE;id:LIPH;id:LIPX;id:LIPZ;id:LIRA;id:LIRF;id:LIRN;id:LIRP;id:LJLJ;id:LKPR;id:LLBG;id:LLOV;id:LMML;id:LOWW;id:LPFR;id:LPLA;id:LPPD;id:LPPR;id:LPPT;id:LQSA;id:LROP;id:LSGG;id:LSZH;id:LTAC;id:LTAF;id:LTAI;id:LTAJ;id:LTAZ;id:LTBA;id:LTBJ;id:LTBS;id:LTCE;id:LTCG;id:LTFC;id:LTFE;id:LTFJ;id:LXGB;id:LYBE;id:LYPG;id:LZIB;id:MDPC;id:MDSD;id:MKJP;id:MMAA;id:MMGL;id:MMHO;id:MMMX;id:MMMY;id:MMPR;id:MMSD;id:MMTJ;id:MMUN;id:MPTO;id:MUHA;id:MUVR;id:MWCR;id:MYNN;id:MZBZ;id:NCRG;id:NZAA;id:NZCH;id:NZWN;id:OBBI;id:OEDF;id:OEDR;id:OEJN;id:OEMA;id:OERK;id:OIIE;id:OIII;id:OIMM;id:OISS;id:OITT;id:OJAI;id:OKBK;id:OLBA;id:OMAA;id:OMDB;id:OMDW;id:OMSJ;id:OOMS;id:OPRN;id:OPST;id:ORBI;id:ORMM;id:OSAP;id:OSDI;id:OSLK;id:OTBD;id:PAFA;id:PANC;id:PGUM;id:PHNL;id:PR20;id:RCBS;id:RCKH;id:RCTP;id:RJAA;id:RJBB;id:RJCC;id:RJFF;id:RJFK;id:RJGG;id:RJNS;id:RJOO;id:RJTT;id:RJTY;id:RKJB;id:RKJK;id:RKPC;id:RKPK;id:RKSI;id:RKSO;id:RKSS;id:RKTU;id:ROAH;id:RODN;id:RPLC;id:RPLL;id:RPMD;id:RPVM;id:SAEZ;id:SBBE;id:SBBR;id:SBCF;id:SBCT;id:SBFL;id:SBGL;id:SBGR;id:SBSP;id:SBSV;id:SCEL;id:SELT;id:SEQM;id:SKBO;id:SPIM;id:SPZO;id:SUMU;id:TJSJ;id:TMJG;id:TNCM;id:UAAA;id:UACC;id:UAFM;id:UAKK;id:UBBB;id:UDYZ;id:UGTB;id:UKBB;id:UKCC;id:UKFF;id:UKHH;id:UKOO;id:ULLI;id:UMMS;id:UNKL;id:URSS;id:USSS;id:UTTT;id:UUBW;id:UUDD;id:UUEE;id:UUMU;id:UWUU;id:UWWW;id:VABB;id:VAGO;id:VCBI;id:VDPP;id:VDSR;id:VECC;id:VHHH;id:VIAR;id:VIDP;id:VISM;id:VMMC;id:VOBL;id:VOCI;id:VOCL;id:VOHS;id:VOMM;id:VOPN;id:VOTV;id:VRMM;id:VTBD;id:VTBS;id:VTCC;id:VVDN;id:VVNB;id:VVTS;id:VYMD;id:VYYY;id:WAAA;id:WADD;id:WARR;id:WBSB;id:WIII;id:WMKK;id:WSSS;id:YBBN;id:YMML;id:YPPH;id:YSCB;id:YSSY;id:ZBAA;id:ZBNY;id:ZBTJ;id:ZBYN;id:ZGGG;id:ZGHA;id:ZGKL;id:ZGNN;id:ZGSZ;id:ZHCC;id:ZHHH;id:ZJHK;id:ZJSY;id:ZLXY;id:ZPPP;id:ZSAM;id:ZSFZ;id:ZSHC;id:ZSJN;id:ZSNB;id:ZSNJ;id:ZSPD;id:ZSSS;id:ZSWZ;id:ZUCK;id:ZUGY;id:ZUUU;id:ZWWW;id:ZYHB;id:ZYTL;id:ZYTX';
            const airportsRequest = aeris.api()
                    .endpoint('observations')
                    .action('search')
                    .query(ids)
                    .fields('id,loc,ob,place')
                    .from('-2hours')
                    .limit(1000);
 
            let airportData;
            const airports = app.addSource('airports', 'vector', {
                refresh: 60,
                data: {
                    service: airportsRequest
                },
                style: {
                    marker: (data) => {
                        return {
                            svg: {
                                shape: {
                                    type: 'circle',
                                    fill: {
                                        color: flightRuleColor(utils.get(data, 'ob.flightRule'))
                                    },
                                    stroke: {
                                        color: '#ffffff',
                                        width: 2
                                    }
                                }
                            },
                            size: [16, 16]
                        };
                    }
                }
            });
            airports.on('data:load', (e) => {
                if (e.data) {
                    airportData = (e.data.results || []).reduce((prev, current) => {
                        prev[current.id] = current;
                        return prev;
                    }, {});
                }
            });
 
            // Show the info panel with the 'airport' content view when an airport marker
            // is selected on the map
            app.map.on('marker:click', (e) => {
                const data = e.data.data || {};
 
                // Get the source identifier (key) that triggered the click event and only show the
                // info panel if the source came from our custom airport data source
                const source = data.awxjs_source;
                if (source === 'airports') {
                    const id = data.id;
                    const place = data.place || {};
                    const name = utils.strings.toName(place.name);
 
                    // Show the info panel and then trigger the panel's content request while
                    // also passing in the cached observations for the airport to use in the content view
                    app.showInfo('airport', `${id} - ${name}`).load({ p: id }, { observations: airportData[id] });
                }
            });
 
        });
 
    };
 
</script>
 
</body>
</html>