Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <html lang='en-GB' charset='utf-8'>

<head>
    <title>Agrometeorological Indicators</title>

    <!-- CSS -->
    <link rel='stylesheet' href='https://js.arcgis.com/4.18/esri/themes/light/main.css'>

    <!-- JS Libraries-->
    <script src='https://js.arcgis.com/4.18/'></script>
</head>

<body>

    <div id='frame'>
       
        <div id='mapDiv' name='mapDiv'> </div>

        <div id='bottomDiv' class='esri-widget' title='Press play to animate the time slider or drag to select a specific year'>
            <p><b>Press Play to animate the time slider or Drag to select the year</b><br> Observed 1961-2018, modelled 2019-2080 (ensemble mean)</p>
            <div id='sliderDiv' class='esri-widget'>
                <div id='playButton' class='esri-widget esri-widget--button toggle-button '>
                    <div>
                        <span class='toggle-button-icon esri-icon-play' aria-label='play icon'></span> Play
                    </div>
                    <div>
                        <span class='toggle-button-icon esri-icon-pause' aria-label='pause icon'></span> Pause
                    </div>
                </div>
                <div id='yearSlider' class='esri-widget'></div>
            </div>
        </div>
</body>

</html>
              
            
!

CSS

              
                html,
body {
    padding: 0;
    margin: 0;
    width: 100%;
    height: 100%;
}

#frame {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

#mapDiv {
    position: absolute;
    right: 0;
    left: 0;
    top: 0;
    bottom: 150px;
}


/******** bottomDiv********/

#bottomDiv {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    max-height: 150px;
    padding: 0 40px;
}

#sliderDiv {
    display: flex;
    padding: 20px 0;
}

#playButton {
    margin: 5px 10px;
    padding: 0 20px;
}

#yearSlider {
    padding-top: 10px;
}

.toggle-button {
    display: flex;
}

.toggle-button.toggled .toggle-button-icon {
    color: #cc1b1b;
}

.toggle-button .toggle-button-icon {
    color: #1bcc1b;
}

.toggle-button> :nth-child(2) {
    display: none;
}

.toggle-button.toggled> :nth-child(1) {
    display: none;
}

.toggle-button.toggled> :nth-child(2) {
    display: block;
}

/******** Misc ********/

.esri-popup__footer {
    display: none;
}

.esri-expand__content--expanded {
    overflow-y: auto;
    width: 25vw;
    height: auto;
    max-height: 26vw !important;
}

.esri-slider__label {
    font-weight: 999;
    font-size: 2em;
    transform: translate(0px, -.2em);
}

.esri-view:focus,
.esri-view-root:focus,
.esri-view .esri-view-surface:focus {
    outline: -webkit-focus-ring-color none !important;
}

.esri-view .esri-view-surface--inset-outline:focus::after {
    outline: 0 !important;
}

              
            
!

JS

              
                'use strict'; 
require([
    'esri/Map',
    'esri/Basemap',
    'esri/geometry/Point',
    'esri/views/MapView',
    'esri/widgets/ScaleBar',
    'esri/widgets/Slider',
    'esri/widgets/Home',
    'esri/widgets/Legend',
    'esri/layers/ImageryLayer',
    'esri/layers/support/RasterFunction',
    'esri/layers/support/MosaicRule',
    'esri/layers/support/DimensionalDefinition',
    'esri/renderers/RasterStretchRenderer',
    'esri/tasks/support/AlgorithmicColorRamp',
    'esri/tasks/support/MultipartColorRamp'
], (Map,
    Basemap,
    Point,
    MapView,
    ScaleBar,
    Slider,
    Home,
    Legend,
    ImageryLayer,
    RasterFunction,
    MosaicRule,
    DimensionalDefinition,
    RasterStretchRenderer,
    AlgorithmicColorRamp,
    MultipartColorRamp) => {

    // new basemap definition 
    const basemap = new Basemap({
        portalItem: {
            id: '54140d826fe34135abb3b60c157170dc' // os_open_greyscale_no_labels
        }
    });

    // create map
    const map = new Map({
        basemap: basemap,
        layers: []
    });

    // create 2D view for the Map
    const view = new MapView({
        container: 'mapDiv',
        map: map,
        center: new Point({ x: 200000, y: 785000, spatialReference: 27700 }), // reprojected to allow OS basemap
        zoom: 8
    });

    // create renderer for countOfDay
    const colorRamp1 = new AlgorithmicColorRamp({
        algorithm: 'lab-lch',
        fromColor: '#006837',
        toColor: '#1A9850'
    });
    const colorRamp2 = new AlgorithmicColorRamp({
        algorithm: 'lab-lch',
        fromColor: '#1A9850',
        toColor: '#66BD63'
    });
    const colorRamp3 = new AlgorithmicColorRamp({
        algorithm: 'lab-lch',
        fromColor: '#66BD63',
        toColor: '#A6D96A'
    });
    const colorRamp4 = new AlgorithmicColorRamp({
        algorithm: 'lab-lch',
        fromColor: '#A6D96A',
        toColor: '#D9EF8B'
    });
    const colorRamp5 = new AlgorithmicColorRamp({
        algorithm: 'lab-lch',
        fromColor: '#D9EF8B',
        toColor: '#FFFFBF'
    });
    const colorRamp6 = new AlgorithmicColorRamp({
        algorithm: 'lab-lch',
        fromColor: '#FFFFBF',
        toColor: '#FEE08B'
    });
    const colorRamp7 = new AlgorithmicColorRamp({
        algorithm: 'lab-lch',
        fromColor: '#FEE08B',
        toColor: '#FDAE61'
    });
    const colorRamp8 = new AlgorithmicColorRamp({
        algorithm: 'lab-lch',
        fromColor: '#FDAE61',
        toColor: '#F46D43'
    });
    const colorRamp9 = new AlgorithmicColorRamp({
        algorithm: 'lab-lch',
        fromColor: '#F46D43',
        toColor: '#D73027'
    });
    const colorRamp10 = new AlgorithmicColorRamp({
        algorithm: 'lab-lch',
        fromColor: '#D73027',
        toColor: '#A50026'
    });

    const combineColorRamp = new MultipartColorRamp({
        colorRamps: [colorRamp1, colorRamp2, colorRamp3, colorRamp4, colorRamp5, colorRamp6,
            colorRamp7, colorRamp8, colorRamp9, colorRamp10
        ]
    });

    const countOfDayRenderer = new RasterStretchRenderer({
        colorRamp: combineColorRamp, //flowerColorRamp
        stretchType: 'min-max',
        //statistics: [
        //  [1, 60, 5, 5]
        //   ] // min, max, avg, stddev
    });

    /******************************
     * Layer rules
     * ****************************/
    // create server-defined raster function
    let serviceRasterFunction = new RasterFunction({
        functionName: 'plantheatstress_count'
    });

    // set initial variable and dimension on mosaic dataset
    const yearDefinition = new DimensionalDefinition({
        variableName: 'plantheatstress_count',
        dimensionName: 'Year',
        values: [1961],
        isSlice: true
    });

    // create mosaicRule and set multidimensionalDefinition
    let mosaicRule = new MosaicRule({
        multidimensionalDefinition: [yearDefinition]
    });

    // Set up popup template
    let indicatorLayerPopupTemplate = {
        title: '',
        content: '<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> when the maximum temperature is greater than 25\u00B0C',
        fieldInfos: [{
            fieldName: 'Raster.ItemPixelValue',
            format: {
                places: 0,
                digitSeparator: true
            }
        }]
    };
    // remove dockability
    view.popup = {
        dockOptions: {
            buttonEnabled: false
        }
    };

    //function to popupTemplate if open
    const closePopup = () => {
        if (view.popup.visible) {
            view.popup.close()
        }
    };

    // create and add imagery layer to view
    const indicatorLayer = new ImageryLayer({
        title: [], //The legend automatically updates when a layer's renderer, opacity, or title is changed
        url: 'https://druid.hutton.ac.uk/arcgis/rest/services/Agmet/agrometIndicators/ImageServer',
        mosaicRule: mosaicRule,
        renderer: countOfDayRenderer,
        renderingRule: serviceRasterFunction,
        popupTemplate: indicatorLayerPopupTemplate,
        opacity: 0.9
    });
    map.add(indicatorLayer);

    /******************************
     * programmatically make selectors 
     *******************************/
    const selectorExpression = [
        [`accumulatedfrost_degreedays`, `Accumulated Frost (degree days)`],
        [`airfrost_count`, `Air Frost (count of days)`],
        [`cold_spell_n`, `Cold Spell (count of days)`],
        [`dry_count`, `Dry Count (count of days)`],
        [`dry_spell_n`, `Dry Spell (count of days)`],
        [`end_growingseason`, `End of Growing Season (day of year)`],
        [`first_airfrost_doy`, `First Airfrost (day of year)`],
        [`first_grassfrost_doy`, `First Grassfrost (day of year)`],
        [`grassfrost_count`, `Grassfrost Count (count of days)`],
        [`growing_degreedays`, `Growing (degree days)`],
        [`growing_season`, `Growing Season (count of days)`],
        [`growseason_length`, `Grow Season Length (count of days)`],
        [`growseason_range`, `Grow Season Range (count of days)`],
        [`heating_degreedays`, `Heating (degree days)`],
        [`heatwave_n`, `Heatwave (count of days)`],
        [`last_airfrost_doy`, `Last Airfrost (day of year)`],
        [`last_grassfrost_doy`, `Last Grassfrost (day of year)`],
        [`p_intensity`, `P Intensity (index)`],
        [`p_seasonality`, `P Seasonality (index)`],
        [`personheatstress_count`, `Person Heat Stress (count of days)`],
        [`plantheatstress_count`, `Plant Heat Stress (count of days)`],
        [`start_fieldops_doy`, `Start FieldOps (day of year)`],
        [`start_grow_doy`, `Start Grow (day of year)`],
        [`tempgrowingperiod_length`, `Temp Growing Period (count of days)`],
        [`thermaltime_sum`, `Thermal Time (degree days)`],
        [`wet_count`, `Wet Count (count of days)`],
        [`wet_spell_n`, `Wet Spell (count of days)`],
        [`wettestweek_doy`, `Wettest Week (day of year)`],
        [`wettestweek_mm`, `Wettest Week (mm)`]
    ];

    // selectDivs configs
    const selectDiv = document.createElement('div');
    const selectDivs = [selectDiv] //, selectDivRight]

    selectDivs.forEach(element => {
        element.setAttribute('id', 'selectDiv');
        element.setAttribute('class', 'esri-widget');
        element.setAttribute('style', 'padding: 0 10px 10px 10px;background-color:white;');
        element.setAttribute('title', `Select Agrometeorological Indicator to display on the map`) // tooltip
    });

    selectDiv.innerHTML = '<p>Select Agrometeorological Indicator:<p>';

    const selectFilter = document.createElement('select');
    const selectFilters = [selectFilter] //, selectFilterRight];

    selectFilters.forEach(element => {
        element.setAttribute('id', 'selectFilter');
        element.setAttribute('class', 'esri-widget');
        element.setAttribute('style', 'width: 280px;')
    })

    selectDiv.appendChild(selectFilter);

    // make options and add labels for each 
    selectorExpression.forEach(element => {
        let option = document.createElement('option');
        option.value = element[0];
        option.innerHTML = element[1];
        selectFilter.appendChild(option);
    });

    // make plantheatstress_count selected 
    selectFilter.value = 'plantheatstress_count';

    // add selectDivs to view
    view.ui.add(selectDiv, 'top-left');

    /******************************
     * selectorDiv configs
     * ****************************/
    //listen to change events on indicatorSelect and change multidimensional variable
    selectFilter.addEventListener('change', () => {
        const chosenIndicator = selectFilter.value;
        changeIndicator(chosenIndicator);
        changeDescriptors(chosenIndicator);
        stopAnimation();
    });

    function changeIndicator(chosenIndicator) {
        closePopup();

        // change mosaicRule of layer as clone and reassign
        const mosaicRuleClone = indicatorLayer.mosaicRule.clone(); // makes clone of layer's mosaicRule
        const indicatorVariable = mosaicRuleClone.multidimensionalDefinition[0];
        indicatorVariable.values = yearSlider.get('values');
        indicatorVariable.variableName = chosenIndicator;
        mosaicRuleClone.multidimensionalDefinition = [indicatorVariable];
        indicatorLayer.mosaicRule = mosaicRuleClone;

        // change renderingRule (raster function) of layer as clone and reassign 
        const renderingRuleClone = indicatorLayer.renderingRule.clone();
        renderingRuleClone.functionName = chosenIndicator;
        indicatorLayer.renderingRule = renderingRuleClone;
    };

    /************************************
     * Year Slider
     *************************************/
    const yearSlider = new Slider({
        container: 'yearSlider',
        min: 1961,
        max: 2080,
        values: [1961],
        precision: 0,
        snapOnClickEnabled: true,
        visibleElements: {
            labels: true,
            rangeLabels: true
        },
        // steps: 119,
        tickConfigs: [{
            // mode: 'position',
            // values: [1970, 1980, 1990, 2000, 2010, 2020, 2030, 2040, 2050, 2060, 2070],
            mode: 'count',
            values: 119,
            labelsVisible: false,
        }]
    });

    // when the user changes the yearSlider's value, change the year to reflect data
    yearSlider.on(['thumb-change', 'thumb-drag'], event => {
        closePopup();
        stopAnimation();
        updateYearDef(event.value);
    });

    // read all other values when year updates
    function updateYearDef() {
        const mosaicRuleClone = indicatorLayer.mosaicRule.clone(); // makes clone of layer's mosaicRule
        const yearVariable = mosaicRuleClone.multidimensionalDefinition[0];
        yearVariable.values = yearSlider.get('values');
        mosaicRuleClone.multidimensionalDefinition = [yearVariable];
        indicatorLayer.mosaicRule = mosaicRuleClone;
    };

    // set var for play button 
    const playButton = document.getElementById('playButton');

    // Toggle animation on/off when user
    // clicks on the play button
    playButton.addEventListener('click', () => {
        closePopup();
        if (playButton.classList.contains('toggled')) {
            stopAnimation();
        } else {
            startAnimation();
        }
    });
    let timerId = 0;

    // Starts the animation that cycle through the years
    function startAnimation() {
        stopAnimation();
        timerId = setInterval(() => {
                let year = yearSlider.values[0];

                // check if year has loaded on map
                if (indicatorLayer.mosaicRule.multidimensionalDefinition[0].values[0] == year && indicatorLayer.loaded) {
                    year += 1;
                    if (year > yearSlider.max) {
                        year = yearSlider.min;
                    }
                    yearSlider.values = [year];
                    updateYearDef(year);
                }

            }, 700) // speed of playback, milliseconds
        playButton.classList.add('toggled');
    };

    // Stops the animation
    function stopAnimation() {
        if (!timerId) {
            return;
        }
        clearTimeout(timerId);
        timerId = 0;
        playButton.classList.remove('toggled');
    };


    /******************************
     * Small ui widgets
     *******************************/
    // moves the zoom widget to other corner
    view.ui.move('zoom', 'bottom-left');

    // create  and add scale bar to view
    const scaleBar = new ScaleBar({
        view: view,
        unit: 'dual' // The scale bar displays both metric and non-metric units.
    });

    // create and add home button to view 
    const home = new Home({
        view: view
    });

    // add elements in order
    view.ui.add(home, 'bottom-left');
    view.ui.add(scaleBar, 'bottom-right')

    // create and add legend to view 
    const legend = new Legend({
        view: view,
        layerInfos: [{
            layer: indicatorLayer,
            title: [`Plant Heat Stress: count of days when Tmax > 25\u00B0C`]
        }],
        title: 'Click anywhere on the map to find out the data value for that pixel'
    });

    view.ui.add(legend, 'top-left');

    //add tooltip to legend
    let legendTooltip = () => {
        let legendClass = document.getElementsByClassName('esri-legend')[0];
        legendClass.setAttribute('title', `Click anywhere on the map to get the data's pixel value`)
    };
    setTimeout(legendTooltip, 1000);

    /******************************
     * Lengthy Configs
     *******************************/
    // change title of layer for Legend display
    // change innerHTML of hideaway div
    // change popup contents
    function changeDescriptors(chosenIndicator) {
        const hideaway = document.getElementById("hideaway");

        const HTML30yrmaps = `
        <div id='imgGrid'><div><p>1961-1990 Average</p><img src='img/${chosenIndicator}_1961-1990_observed_scotland.png'></div>
        <div><p>1991-2018 Average</p><img src='img/${chosenIndicator}_1991-2018_observed_scotland.png'></div>
        <div><p>2019-2050 Average</p><img src='img/${chosenIndicator}_2019-2050_ensemblemean_scotland.png'></div>
        <div><p>2051-2080 Average</p><img src='img/${chosenIndicator}_2051-2080_ensemblemean_scotland.png'></div></div>`

        const popupTemplateClone = indicatorLayer.popupTemplate.clone();
        let popupCloneContent = popupTemplateClone.content;

        switch (chosenIndicator) {
            case 'accumulatedfrost_degreedays':
                indicatorLayer.title = accumulatedfrost_legend
                popupCloneContent = accumulatedfrost_popup
                break;
            case 'airfrost_count':
                indicatorLayer.title = airfrostcount_legend
                popupCloneContent = airfrostcount_popup
                break;
            case 'cold_spell_n':
                indicatorLayer.title = coldspell_legend
                popupCloneContent = coldspell_popup
                break;
            case 'dry_count':
                indicatorLayer.title = drycount_legend
                popupCloneContent = drycount_popup
                break;
            case 'dry_spell_n':
                indicatorLayer.title = dryspell_legend
                popupCloneContent = dryspell_popup
                break;
            case 'end_growingseason':
                indicatorLayer.title = endgrowing_legend
                popupCloneContent = endgrowing_popup
                break;
            case 'first_airfrost_doy':
                indicatorLayer.title = firstairfrost_legend
                popupCloneContent = firstairfrost_popup
                break;
            case 'first_grassfrost_doy':
                indicatorLayer.title = firstgrassfrost_legend
                popupCloneContent = firstgrassfrost_popup
                break;
            case 'grassfrost_count':
                indicatorLayer.title = grassfrostcount_legend
                popupCloneContent = grassfrostcount_popup
                break;
            case 'growing_degreedays':
                indicatorLayer.title = growingdd_legend
                popupCloneContent = growingdd_popup
                break;
            case 'growing_season':
                indicatorLayer.title = growingseason_legend
                popupCloneContent = growingseason_popup
                break;
            case 'growseason_length':
                indicatorLayer.title = growseasonlength_legend
                popupCloneContent = growseasonlength_popup
                break;
            case 'growseason_range':
                indicatorLayer.title = growseasonrange_legend
                popupCloneContent = growseasonrange_popup
                break;
            case 'heating_degreedays':
                indicatorLayer.title = heatingdd_legend
                popupCloneContent = heatingdd_popup
                break;
            case 'heatwave_n':
                indicatorLayer.title = heatwave_legend
                popupCloneContent = heatwave_popup
                break;
            case 'last_airfrost_doy':
                indicatorLayer.title = lastairfrost_legend
                popupCloneContent = lastairfrost_popup
                break;
            case 'last_grassfrost_doy':
                indicatorLayer.title = lastgrassfrost_legend
                popupCloneContent = lastgrassfrost_popup
                break;
            case 'p_intensity':
                indicatorLayer.title = pintensity_legend
                popupCloneContent = pintensity_popup
                break;
            case 'p_seasonality':
                indicatorLayer.title = pseasonality_legend
                popupCloneContent = pseasonality_popup
                break;
            case 'personheatstress_count':
                indicatorLayer.title = personheatstress_legend
                popupCloneContent = personheatstress_popup
                break;
            case 'plantheatstress_count':
                indicatorLayer.title = plantheatstress_legend
                popupCloneContent = plantheatstress_popup
                break;
            case 'start_fieldops_doy':
                indicatorLayer.title = startfieldops_legend
                popupCloneContent = startfieldops_popup
                break;
            case 'start_grow_doy':
                indicatorLayer.title = startgrowdoy_legend
                popupCloneContent = startgrowdoy_popup
                break;
            case 'tempgrowingperiod_length':
                indicatorLayer.title = tempgrowingperiod_legend
                popupCloneContent = tempgrowingperiod_popup
                break;
            case 'thermaltime_sum':
                indicatorLayer.title = thermaltime_legend
                popupCloneContent = thermaltime_popup
                break;
            case 'wet_count':
                indicatorLayer.title = wetcount_legend
                popupCloneContent = wetcount_popup
                break;
            case 'wet_spell_n':
                indicatorLayer.title = wetspell_legend
                popupCloneContent = wetspell_popup
                break;
            case 'wettestweek_doy':
                indicatorLayer.title = wettestweekdoy_legend
                popupCloneContent = wettestweekdoy_popup
                break;
            case 'wettestweek_mm':
                indicatorLayer.title = wettestweekmm_legend
                popupCloneContent = wettestweekmm_popup
                break;
        };
        popupTemplateClone.content = popupCloneContent;
        indicatorLayer.popupTemplate = popupTemplateClone;;
    };


    const accumulatedfrost_legend = `Accumulated Frost (degree days): sum of degree days where Tmin < 0\u00B0C`,
        accumulatedfrost_popup = `<b>{Raster.ItemPixelValue}</b> degree days in <b>{Year}</b> when the minimum temperature is less than 0\u00B0C`,

        airfrostcount_legend = `Air Frost (count of days): count of days when Tmin < 0\u00B0C`,
        airfrostcount_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> when the minimum temperature is less than 0\u00B0C`,

        coldspell_legend = `Cold Spell (count of days): Max count of consecutive days when Tmax < avgTmax (baseline year) - 3\u00B0C (min 6 days)`,
        coldspell_popup = `<b>{Raster.ItemPixelValue}</b> consecutive days in <b>{Year}</b> when the maximum temperature is less than the average maximum temperature in a baseline year minus 3\u00B0C`,

        drycount_legend = `Dry Count (count of days): count of days when P < 0.2 mm`,
        drycount_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> when precipitation is less than 0.2 mm `,

        dryspell_legend = `Dry Spell (count of days): max consecutive count P < 0.2 mm`,
        dryspell_popup = `<b>{Raster.ItemPixelValue}</b> consecutive days in <b>{Year}</b> when precipitation is less than 0.2 mm`,

        endgrowing_legend = `End of Growing Season (day of year): day when 5 consecutive days Tavg < 5.6\u00B0C from 1 July`,
        endgrowing_popup = `<b>{Raster.ItemPixelValue}</b>th day of the year (out of 365) in <b>{Year}</b> when average temperature for five consecutive days is less than 5.6\u00B0C from 1 July`,

        firstairfrost_legend = `First Airfrost (day of year): first day when Tmin < 0\u00B0C from 1 July`,
        firstairfrost_popup = `<b>{Raster.ItemPixelValue}</b>th day of the year (out of 365) in <b>{Year}</b> when the minimum temperature is less than 0\u00B0C from 1 July`,

        firstgrassfrost_legend = `First Grassfrost (day of year): first day when Tmin < 5\u00B0C from 1 July`,
        firstgrassfrost_popup = `<b>{Raster.ItemPixelValue}</b>th day of the year (out of 365) in <b>{Year}</b> when the minimum temperature is less than 5\u00B0C from 1 July`,

        grassfrostcount_legend = `Grassfrost (count of days): count of days when Tmin < 5\u00B0C`,
        grassfrostcount_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> when the minimum temperature is less than 5\u00B0C`,

        growingdd_legend = `Growing (degree days): sum Tavg > 5.6\u00B0C`,
        growingdd_popup = `<b>{Raster.ItemPixelValue}</b> degree days in <b>{Year}</b> when the average temperature is greater than 5.6\u00B0C`,

        growingseason_legend = `Growing Season (count of days): beginning when the temperature on five consecutive days exceeds 5\u00B0C, and ending when the temperature on five consecutive days is below 5\u00B0C`,
        growingseason_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> when the temperature on five consecutive days exceeds 5\u00B0C, and ending when the temperature on five consecutive days is below 5\u00B0C`,

        growseasonlength_legend = `Grow Season Length (count of days): days when Tavg > 5.6\u00B0C between start and end of growing season`,
        growseasonlength_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> when the average temperature is greater than 5.6\u00B0C between the start and the end of growing season`,

        growseasonrange_legend = `Grow Season Range (count of days): days between start and end of growing season`,
        growseasonrange_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> calculated from the count of days between the start and end of the growing season`,

        heatingdd_legend = `Heating (degree days): Sum of 15.5\u00B0C minus Tavg where Tavg < 15.5\u00B0C`,
        heatingdd_popup = `<b>{Raster.ItemPixelValue}</b> degree days in <b>{Year}</b> representing the sum of 15.5\u00B0C minus the average temperature where the average temperature is less than 15.5\u00B0C`,

        heatwave_legend = `Heatwave (count of days): Max count of consecutive days when Tmax > avgTmax (baseline year) + 3\u00B0C (min 6 days)`,
        heatwave_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> when the maximum temperature is greater than the average temperature in a baseline year plus 3\u00B0C`,

        lastairfrost_legend = `Last Airfrost (day of year): last day when Tmin < 0\u00B0C before 1 July`,
        lastairfrost_popup = `<b>{Raster.ItemPixelValue}</b>th day of the year (out of 365) in <b>{Year}</b> when the minimum temperature is less than 0\u00B0C before 1 July`,

        lastgrassfrost_legend = `Last Grassfrost (day of year): last day when Tmin < 5\u00B0C before 1 July`,
        lastgrassfrost_popup = `<b>{Raster.ItemPixelValue}</b>th day of the year (out of 365) in <b>{Year}</b> when the minimum temperature is less than 5\u00B0C before 1 July`,

        pintensity_legend = `P Intensity (index): P > 0.2 / count days P > 0.2mm`,
        pintensity_popup = `<b>{Raster.ItemPixelValue}</b>: index of precipitation intensity in <b>{Year}</b>`,

        pseasonality_legend = `P Seasonality (index): S = winter P - summer P / annual total P`,
        pseasonality_popup = `<b>{Raster.ItemPixelValue}</b>: index of precipitation seasonality in <b>{Year}</b>`,

        personheatstress_legend = `Person Heat Stress (count of days): count of days when Tmax > 32\u00B0C`,
        personheatstress_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> when the maximum temperature is greater than 32\u00B0C`,

        plantheatstress_legend = `Plant Heat Stress (count of days): count of days when Tmax > 25\u00B0C`,
        plantheatstress_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> when the maximum temperature is greater than 25\u00B0C`,

        startfieldops_legend = `Start FieldOps (day of year): day when Tavg from 1 Jan > 200\u00B0C`,
        startfieldops_popup = `<b>{Raster.ItemPixelValue}</b>th day of the year (out of 365) in <b>{Year}</b> when the sum of the daily average temperatures from 1 Jan is greater than 200\u00B0C`,

        startgrowdoy_legend = `Start Grow (day of year): day when 5 consecutve days Tavg > 5.6\u00B0C`,
        startgrowdoy_popup = `<b>{Raster.ItemPixelValue}</b>th day of the year (out of 365) in <b>{Year}</b> when five consecutive days have an average temperature greater than 5.6\u00B0C`,

        tempgrowingperiod_legend = `Temp Growing Period (count of days): count of days between average 5 day temp > 5\u00B0C and average 5 day temp < 5\u00B0C where average daily temp greater than 5\u00B0C`,
        tempgrowingperiod_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> between when the average five-day temperature is greater than 5\u00B0C and when the average five-day temperature is less than 5\u00B0C`,

        thermaltime_legend = `Thermal Time (degree days): sum of day degrees for period from 5th of 5 day period where Tavg greater than 5\u00B0C to end point where Tavg less than 5\u00B0C`,
        thermaltime_popup = `<b>{Raster.ItemPixelValue}</b> degree days in <b>{Year}</b> for the period from fifth day of a five-day period where the average temperater is greater than 5\u00B0C to the end point when the average temperature is less than 5\u00B0C`,

        wetcount_legend = `Wet Count (count of days): days when P >= 0.2 mm`,
        wetcount_popup = `<b>{Raster.ItemPixelValue}</b> total wet days in <b>{Year}</b> when precipitation is greater than or equal to 0.2 mm`,

        wetspell_legend = `Wet Spell (count of days): max consecutive count P > 0.2 mm`,
        wetspell_popup = `<b>{Raster.ItemPixelValue}</b> total days in <b>{Year}</b> when the maximum consecutive count of precipitation is greater than 0.2 mm`,

        wettestweekdoy_legend = `Wettest Week (day of year): mid-week date when maximum 7d value of P occurs`,
        wettestweekdoy_popup = `<b>{Raster.ItemPixelValue}</b>th day of the year (out of 365) in <b>{Year}</b> when the maximum seven-day value of precipitation occurs`,

        wettestweekmm_legend = `Wettest Week (mm): Maximum amount of P (7 consecutive days)`,
        wettestweekmm_popup = `<b>{Raster.ItemPixelValue}</b> mm of precipitation in <b>{Year}</b> calculated from the maximum amount of precipitation in seven consecutive days`

});
              
            
!
999px

Console