UNPKG

terriajs

Version:

Geospatial data visualization platform.

1,076 lines 60 kB
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>JSDoc: Source: Models/TableCatalogItem.js</title>

    <script src="scripts/prettify/prettify.js"> </script>
    <script src="scripts/prettify/lang-css.js"> </script>
    <!--[if lt IE 9]>
      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>

<body>

<div id="main">

    <h1 class="page-title">Source: Models/TableCatalogItem.js</h1>

    



    
    <section>
        <article>
            <pre class="prettyprint source linenums"><code>'use strict';

/*global require*/
var clone = require('terriajs-cesium/Source/Core/clone');
var defaultValue = require('terriajs-cesium/Source/Core/defaultValue');
var defined = require('terriajs-cesium/Source/Core/defined');
var defineProperties = require('terriajs-cesium/Source/Core/defineProperties');
var DeveloperError = require('terriajs-cesium/Source/Core/DeveloperError');
var freezeObject = require('terriajs-cesium/Source/Core/freezeObject');
var ImagerySplitDirection = require('terriajs-cesium/Source/Scene/ImagerySplitDirection');
var JulianDate = require('terriajs-cesium/Source/Core/JulianDate');
var knockout = require('terriajs-cesium/Source/ThirdParty/knockout');
var when = require('terriajs-cesium/Source/ThirdParty/when');
var TimeIntervalCollection = require('terriajs-cesium/Source/Core/TimeIntervalCollection');

var CatalogItem = require('./CatalogItem');
var ChartData = require('../Charts/ChartData');
var inherit = require('../Core/inherit');
var overrideProperty = require('../Core/overrideProperty');
var Polling = require('./Polling');
var RegionMapping = require('./RegionMapping');
var standardCssColors = require('../Core/standardCssColors');
var TableDataSource = require('../Models/TableDataSource');
var TableStyle = require('../Models/TableStyle');
var TerriaError = require('../Core/TerriaError');
var VarType = require('../Map/VarType');

var DEFAULT_ID_COLUMN = 'id';

/**
 * An abstract {@link CatalogItem} representing tabular data.
 * Extend this class for csv or other data by providing two critical functions:
 * _load and (optionally) startPolling.
 * You can also override concepts for greater control over the display.
 *
 * @alias TableCatalogItem
 * @constructor
 * @extends CatalogItem
 * @abstract
 *
 * @param {Terria} terria The Terria instance.
 * @param {String} [url] The URL from which to retrieve the data.
 * @param {Object} [options] Initial values.
 * @param {TableStyle} [options.tableStyle] An initial table style can be supplied if desired.
 */
var TableCatalogItem = function(terria, url, options) {
    CatalogItem.call(this, terria);

    options = defaultValue(options, defaultValue.EMPTY_OBJECT);

    this._tableStructure = undefined;
    // Handle tableStyle as any of: undefined, a regular object, or a TableStyle object. Convert all to TableStyle objects.
    if (defined(options.tableStyle)) {
        if (options.tableStyle instanceof TableStyle) {
            this._tableStyle = options.tableStyle;
        } else {
            this._tableStyle = new TableStyle(options.tableStyle);
        }
    } else {
        this._tableStyle = new TableStyle();  // Start with one so defaultSerializers.tableStyle will work.
    }
    this._dataSource = undefined;
    this._regionMapping = undefined;
    this._rectangle = undefined;
    this._pollTimeout = undefined; // Used internally to store the polling timeout id.

    this.url = url;

    /**
     * Gets or sets the data, represented as a binary Blob, a string, or a Promise for one of those things.
     * If this property is set, {@link CatalogItem#url} is ignored.
     * This property is observable.
     * @type {Blob|String|Promise}
     */
    this.data = undefined;

    /**
     * Gets or sets the URL from which the {@link TableCatalogItem#data} was obtained.  This is informational; it is not
     * used.  This propery is observable.
     * @type {String}
     */
    this.dataSourceUrl = undefined;

    /**
     * Gets or sets the opacity (alpha) of the data item, where 0.0 is fully transparent and 1.0 is
     * fully opaque.  This property is observable.
     * @type {Number}
     * @default 0.8
     */
    this.opacity = 0.8;

    /**
     * Keeps the layer on top of all other imagery layers.  This property is observable.
     * @type {Boolean}
     * @default false
     */
    this.keepOnTop = false;

    /**
     * Gets or sets polling information, such as the number of seconds between polls, and what url to poll.
     * @type {Polling}
     * @default undefined
     */
    this.polling = new Polling();

    /**
     * Should any warnings like failures in region mapping be displayed to the user?
     * @type {Boolean}
     * @default true
     */
    this.showWarnings = true;

    /**
     * Gets or sets the array of color strings used for chart lines.
     * TODO: make this customizable, eg. use colormap / colorPalette.
     * @type {String[]}
     */
    this.colors = standardCssColors.modifiedBrewer8ClassSet2;

    /**
     * Gets or sets the column identifiers (names or indices), so we can identify individual features
     * within a table with a time column, or across multiple polled lat/lon files.
     * Eg. ['lat', 'lon'] for immobile features, or ['identifier'] if a unique identifier is provided
     * (where these are column names in the table; column numbers work as well).
     * For region-mapped files, the region identifier is used instead.
     * For non-spatial files, the x-column is used instead.
     * @type {String[]}
     * @default undefined
     */
    this.idColumns = options.idColumns;

    /**
     * Gets or sets a value indicating whether the rows correspond to "sampled" data.
     * This only makes a difference if there is a time column and idColumns.
     * In this case, if isSampled is true, then feature position, color and size are interpolated
     * to produce smooth animation of the features over time.
     * If isSampled is false, then times are treated as the start of periods, so that
     * feature positions, color and size are kept constant from one time until the next,
     * then change suddenly.
     * Color and size are never interpolated when they are drawn from a text column.
     * @type {Boolean}
     * @default true
     */
    this.isSampled = defaultValue(options.isSampled, true);

    /**
     * Gets or sets which side of the splitter (if present) to display this imagery layer on.  Defaults to both sides.
     * Note that this only applies to region-mapped tables. This property is observable.
     * @type {ImagerySplitDirection}
     */
    this.splitDirection = ImagerySplitDirection.NONE;  // NONE means show on both sides of the splitter, if there is one.

    knockout.track(this, ['data', 'dataSourceUrl', 'opacity', 'keepOnTop', 'showWarnings', '_tableStructure', '_dataSource', '_regionMapping', 'splitDirection']);

    knockout.getObservable(this, 'opacity').subscribe(function(newValue) {
        if (defined(this._regionMapping) &amp;&amp; defined(this._regionMapping.updateOpacity)) {
            this._regionMapping.updateOpacity(newValue);
            this.terria.currentViewer.notifyRepaintRequired();
        }
    }, this);

    knockout.defineProperty(this, 'concepts', {
        get: function() {
            if (defined(this._tableStructure)) {
                return [this._tableStructure];
            } else {
                return [];
            }
        }
    });

    /**
     * Gets the tableStyle object.
     * This needs to be a property on the object (not the prototype), so that updateFromJson sees it.
     * @type {Object}
     */
    knockout.defineProperty(this, 'tableStyle', {
        get: function() {
            return this._tableStyle;
        }
    });

    /**
     * Gets an id which is different if the view of the data is different. Defaults to undefined.
     * For a csv file with a fixed TableStructure, undefined is fine.
     * However, if the underlying table changes depending on user selection (eg. for SDMX-JSON or SOS),
     * then the same feature may show different information.
     * If it is time-varying, the feature info panel will show a preview chart of the values over time.
     * This id is used when that chart is expanded, so that it can be opened into a different item, and
     * not override an earlier expanded chart of the same feature (but different data).
     * @type {Object}
     */
    knockout.defineProperty(this, 'dataViewId', {
        get: function() {
            return;
        }
    });

    /**
     * Gets javascript dates describing the discrete datetimes (or intervals) available for this item.
     * By declaring this as a knockout defined property, it is cached.
     * @member {Date[]} An array of discrete dates or intervals available for this item, or [] if none.
     * @memberOf ImageryLayerCatalogItem.prototype
     */
    knockout.defineProperty(this, 'availableDates', {
        get: function() {
            if (defined(this.intervals)) {
                const datetimes = [];
                // Only show the start of each interval. If only time instants were given, this is the instant.
                for (let i = 0; i &lt; this.intervals.length; i++) {
                    datetimes.push(JulianDate.toDate(this.intervals.get(i).start, 3));
                }
                return datetimes;
            }
            return [];
        }
    }, this);

    /**
     * Gets the TimeIntervalCollection containing all the table's intervals.
     * @type {TimeIntervalCollection}
     */
    knockout.defineProperty(this, 'intervals', {
        get: function() {
            if (defined(this.tableStructure) &amp;&amp; defined(this.tableStructure.activeTimeColumn) &amp;&amp; defined(this.tableStructure.activeTimeColumn.timeIntervals)) {
                return this.tableStructure.activeTimeColumn.timeIntervals.reduce(function(intervals, interval) {
                    if (defined(interval)) {
                        intervals.addInterval(interval);
                    }
                    return intervals;
                }, new TimeIntervalCollection());
            }
        }
    });

    overrideProperty(this, 'clock', {
        get: function() {
            var timeColumn = this.timeColumn;
            if (this.isMappable &amp;&amp; defined(timeColumn)) {
                return timeColumn.clock;
            }
        }
    });

    overrideProperty(this, 'legendUrl', {
        get: function() {
            if (defined(this._dataSource)) {
                return this._dataSource.legendUrl;
            } else if (defined(this._regionMapping)) {
                return this._regionMapping.legendUrl;
            }
        }
    });

    overrideProperty(this, 'rectangle', {
        get: function() {
            // Override the extent using this.rectangle, otherwise falls back the datasource's extent (with a small margin).
            if (defined(this._rectangle)) {
                return this._rectangle;
            }
            var rect;
            if (defined(this._dataSource)) {
                rect = this._dataSource.extent;
            } else if (defined(this._regionMapping)) {
                rect = this._regionMapping.extent;
            }
            return addMarginToRectangle(rect, 0.08);
        },
        set: function(rect) {
            this._rectangle = rect;
        }
    });

    overrideProperty(this, 'dataUrl', {
        // item.dataUrl returns a URI which downloads the table as a csv.
        get: function() {
            if (defined(this._dataUrl)) {
                return this._dataUrl;
            }
            // Even if the file only exists locally, we recreate it as a data URI, since there may have been geocoding or other processing.
            if (defined(this._tableStructure)) {
                return this._tableStructure.toDataUri();
            }
        },
        set: function(value) {
            this._dataUrl = value;
        }
    });

    overrideProperty(this, 'dataUrlType', {
        get: function() {
            if (defined(this._dataUrlType)) {
                return this._dataUrlType;
            }
            if (defined(this._tableStructure)) {
                return 'data-uri';
            }
        },
        set: function(value) {
            this._dataUrlType = value;
        }
    });

    knockout.getObservable(this, 'splitDirection').subscribe(function() {
        this.terria.currentViewer.updateItemForSplitter(this);
    }, this);
};

inherit(CatalogItem, TableCatalogItem);

function addMarginToRectangle(rect, marginFraction) {
    if (defined(rect)) {
        var heightMargin = rect.height * marginFraction;
        var widthMargin = rect.width * marginFraction;
        rect.north = Math.min(Math.PI / 2, rect.north + heightMargin);
        rect.south = Math.max(-Math.PI / 2, rect.south - heightMargin);
        rect.east = Math.min(Math.PI, rect.east + widthMargin);
        rect.west = Math.max(-Math.PI, rect.west - widthMargin);
    }
    return rect;
}

defineProperties(TableCatalogItem.prototype, {
    /**
     * Gets the active time column, if it exists.
     * @memberOf TableCatalogItem.prototype
     * @type {TableColumn}
     */
    timeColumn: {
        get: function() {
            return this._tableStructure &amp;&amp; this._tableStructure.activeTimeColumn;
        }
    },

    /**
     * Gets the x-axis column, if it exists (ie. if this is a chart).
     * @memberOf TableCatalogItem.prototype
     * @type {TableColumn}
     */
    xAxis: {
        get: function() {
            if (!this.isMappable &amp;&amp; this._tableStructure) {
                if (defined(this._tableStyle.xAxis)) {
                    return this._tableStructure.getColumnWithNameOrId(this._tableStyle.xAxis);
                }
                return this.timeColumn || this._tableStructure.columnsByType[VarType.SCALAR][0];
            }
        }
    },

    /**
     * Gets a value indicating whether this data source, when enabled, can be reordered with respect to other data sources.
     * Data sources that cannot be reordered are typically displayed above reorderable data sources.
     * @memberOf TableCatalogItem.prototype
     * @type {Boolean}
     */
    supportsReordering: {
        get: function() {
            return defined(this._regionMapping) &amp;&amp; defined(this._regionMapping.regionDetails) &amp;&amp; !this.keepOnTop;
        }
    },

    /**
     * Gets a value indicating whether the opacity of this data source can be changed.
     * @memberOf ImageryLayerCatalogItem.prototype
     * @type {Boolean}
     */
    supportsOpacity: {
        get: function() {
            return defined(this._regionMapping) &amp;&amp; defined(this._regionMapping.regionDetails);
        }
    },

    /**
     * Gets a value indicating whether this layer can be split so that it is
     * only shown on the left or right side of the screen.
     * @memberOf TableCatalogItem.prototype
     */
    supportsSplitting : {
        get : function() {
            return defined(this._regionMapping) &amp;&amp; defined(this._regionMapping.regionDetails);
        }
    },

    /**
     * Gets the table structure associated with this catalog item.
     * @memberOf TableCatalogItem.prototype
     * @type {TableStructure}
     */
    tableStructure: {
        get: function() {
            return this._tableStructure;
        }
    },

    /**
     * Gets the data source associated with this catalog item.
     * @memberOf TableCatalogItem.prototype
     * @type {DataSource}
     */
    dataSource: {
        get: function() {
            return this._dataSource;
        }
    },

    /**
     * Gets the region mapping associated with this catalog item.
     * @memberOf TableCatalogItem.prototype
     * @type {RegionMapping}
     */
    regionMapping: {
        get: function() {
            return this._regionMapping;
        }
    },

    /**
     * Gets the Cesium or Leaflet imagery layer object associated with this data source.
     * Used in region mapping only.
     * This property is undefined if the data source is not enabled.
     * @memberOf TableCatalogItem.prototype
     * @type {Object}
     */
    imageryLayer: {
        get: function() {
            return this._regionMapping &amp;&amp; this._regionMapping.imageryLayer;
        }
    },

    /**
     * Gets the set of names of the properties to be serialized for this object when {@link CatalogMember#serializeToJson} is called
     * for a share link.
     * @memberOf ImageryLayerCatalogItem.prototype
     * @type {String[]}
     */
    propertiesForSharing: {
        get: function() {
            return TableCatalogItem.defaultPropertiesForSharing;
        }
    },

    /**
     * Gets the set of functions used to update individual properties in {@link CatalogMember#updateFromJson}.
     * When a property name in the returned object literal matches the name of a property on this instance, the value
     * will be called as a function and passed a reference to this instance, a reference to the source JSON object
     * literal, and the name of the property.
     * @memberOf TableCatalogItem.prototype
     * @type {Object}
     */
    updaters: {
        get: function() {
            return TableCatalogItem.defaultUpdaters;
        }
    },

    /**
     * Gets the set of functions used to serialize individual properties in {@link CatalogMember#serializeToJson}.
     * When a property name on the model matches the name of a property in the serializers object literal,
     * the value will be called as a function and passed a reference to the model, a reference to the destination
     * JSON object literal, and the name of the property.
     * @memberOf TableCatalogItem.prototype
     * @type {Object}
     */
    serializers: {
        get: function() {
            return TableCatalogItem.defaultSerializers;
        }
    }
});

TableCatalogItem.defaultUpdaters = clone(CatalogItem.defaultUpdaters);

TableCatalogItem.defaultUpdaters.tableStyle = function(item, json, propertyName, options) {
    return item._tableStyle.updateFromJson(json[propertyName], options);
};

TableCatalogItem.defaultUpdaters.polling = function(item, json, propertyName, options) {
    return item[propertyName].updateFromJson(json[propertyName], options);
};

TableCatalogItem.defaultUpdaters.concepts = function() {
    // Don't update from JSON.
};

TableCatalogItem.defaultUpdaters.dataViewId = function() {
    // Don't update from JSON.
};

TableCatalogItem.defaultUpdaters.availableDates = function() {
    // Do not update/serialize availableDates.
};

TableCatalogItem.defaultUpdaters.intervals = function() {
    // Don't update from JSON.
};

freezeObject(TableCatalogItem.defaultUpdaters);

TableCatalogItem.defaultSerializers = clone(CatalogItem.defaultSerializers);

TableCatalogItem.defaultSerializers.tableStyle = function(item, json, propertyName, options) {
    json[propertyName] = item[propertyName].serializeToJson(options);
    // Add the currently active variable to the tableStyle (if any) so it starts with the right one.
    if (defined(item._tableStructure)) {
        var activeItems = item._tableStructure.activeItems;
        json[propertyName].dataVariable = activeItems[0] &amp;&amp; activeItems[0].name;
    }
};

TableCatalogItem.defaultSerializers.polling = function(item, json, propertyName, options) {
    json[propertyName] = item[propertyName].serializeToJson(options);
};

TableCatalogItem.defaultSerializers.legendUrl = function() {
    // Don't serialize, because legends are generated, and sticking an image embedded in a URL is a terrible idea.
};

TableCatalogItem.defaultSerializers.concepts = function() {
    // Don't serialize.
};

TableCatalogItem.defaultSerializers.dataViewId = function() {
    // Don't serialize.
};

TableCatalogItem.defaultSerializers.dataUrl = function(item, json) {
    // Only serialize this if it was set directly; if it is a data URI containing a representation of the whole table, ignore it.
    // ie. set it to item._dataUrl, not item.dataUrl.
    json.dataUrl = item._dataUrl;
};

TableCatalogItem.defaultSerializers.clock = function() {
    // Don't serialize. Clock is not part of propertiesForSharing, but it would be shared if this is user-added data.
    // See SharePopupViewModel.prototype._addUserAddedCatalog.
};

TableCatalogItem.defaultSerializers.availableDates = function() {
    // Do not update/serialize availableDates.
};

TableCatalogItem.defaultSerializers.intervals = function() {
    // Don't serialize.
};

freezeObject(TableCatalogItem.defaultSerializers);

/**
 * Gets or sets the default set of properties that are serialized when serializing a {@link CatalogItem}-derived object
 * for a share link.
 * @type {String[]}
 */
TableCatalogItem.defaultPropertiesForSharing = clone(CatalogItem.defaultPropertiesForSharing);
TableCatalogItem.defaultPropertiesForSharing.push('keepOnTop');
TableCatalogItem.defaultPropertiesForSharing.push('opacity');
TableCatalogItem.defaultPropertiesForSharing.push('tableStyle');
freezeObject(TableCatalogItem.defaultPropertiesForSharing);


TableCatalogItem.prototype._getValuesThatInfluenceLoad = function() {
    return [this.url, this.data];
};

/**
 * Updates tableStructure for the tableStyle, by looking at tableStyle.columns
 * and applying units, type, active and name.
 * If the data was loaded from a csv file, CsvCatalogItem's loadTableFromCsv
 * will already have taken care of this.
 * This function is needed if the data came directly from a TableStructure.
 *
 * @param  {TableStyle} tableStyle The table style.
 * @param  {TableStructure} tableStructure The table structure to update.
 */
TableCatalogItem.applyTableStyleColumnsToStructure = function(tableStyle, tableStructure) {
    if (defined(tableStyle.columns)) {
        for (const nameOrIndex in tableStyle.columns) {
            if (tableStyle.columns.hasOwnProperty(nameOrIndex)) {
                const columnStyle = tableStyle.columns[nameOrIndex];
                const column = tableStructure.getColumnWithNameIdOrIndex(nameOrIndex);
                if (defined(column)) {
                    if (defined(columnStyle.units)) {
                        column.units = columnStyle.units;
                    }
                    if (defined(columnStyle.type)) {
                        column.type = columnStyle.type;
                    }
                    if (defined(columnStyle.active)) {
                        column.isActive = columnStyle.active;
                    }
                    if (defined(columnStyle.name)) {
                        column.name = columnStyle.name;
                    }
                }
            }
        }
    }
};

/**
 * Given a TableStructure, determine what sort of table it is. Prepare:
 *  - TableDataSource if it has latitude and longitude
 *  - RegionMapping if it has a region column
 *  - nothing for non-geospatial data (just use the TableStructure directly).
 * @param  {TableStructure} tableStructure
 * @return {Promise} Returns a promise that resolves to true if it is a recognised format.
 */
TableCatalogItem.prototype.initializeFromTableStructure = function(tableStructure) {
    var item = this;
    var tableStyle = item._tableStyle;

    setDefaultIdColumns(item, tableStructure);
    tableStructure.setActiveTimeColumn(tableStyle.timeColumn);
    item._tableStructure = tableStructure;

    function makeChartable() {
        tableStructure.name = ''; // No need to show the section title 'Display Variables' in Now Viewing.
        tableStructure.allowMultiple = true;
        item.activateColumnFromTableStyle();
        item.setChartable();
        item.startPolling();
    }
    if (!item.isMappable) {
        makeChartable();
        return;
    }

    // Does the csv have addresses we can translate to long and lat?
    if (!tableStructure.hasLatitudeAndLongitude &amp;&amp; tableStructure.hasAddress &amp;&amp; defined(item.terria.batchGeocoder)) {
        var addressGeocoder = item.terria.batchGeocoder;
        return addressGeocoder.bulkConvertAddresses(tableStructure, item.terria.corsProxy).then(function(addressGeocoderData) {
            var timeTaken = JulianDate.secondsDifference(JulianDate.now(), addressGeocoderData.startTime);
            var developerMessage = "Bulk geocode of " + addressGeocoderData.numberOfAddressesConverted + " addresses took "
                + timeTaken.toFixed(2) + " seconds, which is "
                + (addressGeocoderData.numberOfAddressesConverted / timeTaken).toFixed(2)
                + " addresses/s, or " + (timeTaken / addressGeocoderData.numberOfAddressesConverted).toFixed(2)
                + " s/address.\n";
            console.log(developerMessage);
            var missingAddressesMessage = "";
            if (addressGeocoderData.missingAddresses.length > 0 || addressGeocoderData.nullAddresses > 0) {
                if (addressGeocoderData.missingAddresses.length > 0) {
                    missingAddressesMessage = "\nThe CSV contains addresses, but " + addressGeocoderData.missingAddresses.length
                        + " can\'t be located on the map:\n"
                        + addressGeocoderData.missingAddresses.join(", ") + ".&lt;br/>&lt;br/>";
                }
                if (addressGeocoderData.nullAddresses > 0) {
                    missingAddressesMessage += addressGeocoderData.nullAddresses + " addresses are missing from the CSV.";
                }
                item.terria.error.raiseEvent(new TerriaError({
                    sender: this,
                    title: 'Bulk Geocoder Information',
                    message: missingAddressesMessage
                }));
            }
            return createDataSourceForLatLong(item, tableStructure);
        })
            .otherwise(function(e) {
                item.terria.error.raiseEvent(new TerriaError({
                    sender: this,
                    title: 'Bulk Geocoder Error',
                    message: "Unable to map addresses to lat-long coordinates, as an error occurred while retrieving address coordinates. Please check your internet connection or try again later."
                }));
                console.log("Unable to map addresses to lat-long coordinates.", e);
            });
    }
    if (tableStructure.hasLatitudeAndLongitude) {
        return createDataSourceForLatLong(item, tableStructure);
    }
    var regionMapping = new RegionMapping(item, tableStructure, item._tableStyle);
    // Return a promise which resolves once we've set up region mapping, if any.
    return regionMapping.loadRegionDetails().then(function(regionDetails) {
        if (regionDetails) {
            // Save the region mapping to item._regionMapping.
            item._regionMapping = regionMapping;
            item._regionMapping.changedEvent.addEventListener(dataChanged.bind(null, item), item);
            // Set the first region column to have type VarType.REGION.
            item._regionMapping.setRegionColumnType();
            // Activate a column. This needed to wait until we had a regionMapping, so it can trigger the legendHelper build.
            item.activateColumnFromTableStyle();
            // This needed to wait until we know which column is the region.
            ensureActiveColumn(tableStructure, tableStyle);
            item.startPolling();
            return when(true);
        } else {
            // Non-geospatial data.
            makeChartable();
            return when(true);
        }
    });
};

function setDefaultIdColumns(item, tableStructure) {
    // This just checks there is a time column - not that it's one we would actually activate.
    // Would be better to do the full check.
    // Note we check if === undefined explicitly so the user can set to null to prevent this default.
    if (item.idColumns === undefined &amp;&amp; !defined(tableStructure.idColumnNames) &amp;&amp;
        defined(tableStructure.columnsByType[VarType.TIME].length > 0) &amp;&amp;
        tableStructure.getColumnNames().indexOf(DEFAULT_ID_COLUMN) >= 0) {
        item.idColumns = [DEFAULT_ID_COLUMN];
        tableStructure.idColumnNames = item.idColumns;
    }
}

/**
 * Creates a datasource based on tableStructure provided and adds it to item. Suitable for TableStructures that contain
 * lat-lon columns.
 *
 * @param {TableCatalogItem} item Item that tableDataSource is created for.
 * @param {TableStructure} tableStructure TableStructure to use in creating datasource.
 * @return {Promise}
 * @private
 */
function createDataSourceForLatLong(item, tableStructure) {
    // Create the TableDataSource and save it to item._dataSource.
    item._dataSource = new TableDataSource(item.terria, tableStructure, item._tableStyle, item.name, (item.polling.seconds > 0));
    item._dataSource.changedEvent.addEventListener(dataChanged.bind(null, item), item);
    // Activate a column. This needed to wait until we had a dataSource, so it can trigger the legendHelper build.
    item.activateColumnFromTableStyle();
    ensureActiveColumn(tableStructure, item._tableStyle);
    item.startPolling();
    return when(true); // We're done - nothing to wait for.
}

TableCatalogItem.prototype.setChartable = function() {
    var tableStructure = this._tableStructure;
    tableStructure.allowMultiple = true;
    tableStructure.requireSomeActive = false;
    this.isMappable = false;
    if (!defined(tableStructure.getColorCallback)) {
        tableStructure.getColorCallback = this.getNextColor.bind(this);
    }
    tableStructure.toggleActiveCallback = this.disableIncompatibleTableColumns.bind(this);
    // Only let the user choose for the y-axis from the scalar, alt, lon and lat columns. Also hide the x-axis.
    var xAxis = this.xAxis;
    tableStructure.columns.forEach(function(column) {
        if ([VarType.ALT, VarType.LON, VarType.LAT].indexOf(column.type) >= 0) {
            // Revert these column types back to scalars for charts, so eg. a column named "height" can be charted.
            column.type = VarType.SCALAR;
        }
        column.isVisible = (column !== xAxis) &amp;&amp; (column.type === VarType.SCALAR);
    });
    ensureActiveColumnForNonSpatial(this);
    // If this item is shown and enabled, ensure it is in the catalog's chartable items, so the ChartPanel can pick it up.
    addToChartableItemsIfNotMappable(this);
};

// An event listened triggered whenever the dataSource or regionMapping changes.
// Used to know when to redraw the display.
function dataChanged(item) {
    item.terria.currentViewer.notifyRepaintRequired();
}

function ensureActiveColumn(tableStructure, tableStyle) {
    // Find and activate the first SCALAR or ENUM column, if no columns are active, unless the tableStyle sets dataVariable to null.
    if (tableStyle.dataVariable === null) {
        // We still need to trigger an active column change to update TableDataSource and RegionMapping, so toggle one twice.
        tableStructure.columns[0].toggleActive();
        tableStructure.columns[0].toggleActive();
        return;
    }
    if (tableStructure.activeItems.length === 0) {
        var suitableColumns = tableStructure.columns.filter(col => col.type === VarType.SCALAR)
            .concat(tableStructure.columns.filter(col => col.type === VarType.ENUM));
        if (suitableColumns.length > 0) {
            // Look for the first non-trivial column
            // (where a trivial ENUM column has too few or too many unique values,
            // and a trivial SCALAR column has min === max.)
            for (var i = 0; i &lt; suitableColumns.length; i++) {
                var column = suitableColumns[i];
                if (column.isEnum) {
                    var numberOfUniqueValues = column.uniqueValues.length;
                    if (numberOfUniqueValues > 2 &amp;&amp;
                        numberOfUniqueValues &lt; 20 &amp;&amp;
                        numberOfUniqueValues &lt; column.values.length * 0.8) {
                        column.toggleActive();
                        return;
                    }
                } else {
                    if (column.minimumValue &lt; column.maximumValue) {
                        column.toggleActive();
                        return;
                    }
                }
            }
            // If it can't find any non-trivial columns, just use the first enum or scalar.
            suitableColumns[0].toggleActive();
        } else {
            // There are no suitable columns.
            // We need to trigger an active column change to update TableDataSource and RegionMapping, so toggle one twice.
            tableStructure.columns[0].toggleActive();
            tableStructure.columns[0].toggleActive();
        }
    }
}

/**
 * Set the color (for charts) on the active columns. Assumes the table's getColorCallback has been set.
 */
TableCatalogItem.prototype.setColorOnActiveColumns = function() {
    var tableStructure = this._tableStructure;
    tableStructure.columns.filter(column => column.isActive &amp;&amp; !defined(column.color)).forEach((column) => {
        column.color = tableStructure.getColorCallback(tableStructure.getColumnIndex(column.id));
    });
};


function ensureActiveColumnForNonSpatial(item) {
    // If it is not mappable, and has no time column, then the first scalar column will be treated as the x-variable, so choose the second one.
    var tableStructure = item._tableStructure;
    if (tableStructure.activeItems.length === 0) {
        var suitableColumns = tableStructure.columnsByType[VarType.SCALAR];
        if ((suitableColumns.length > 1) &amp;&amp; (tableStructure.columnsByType[VarType.TIME].length === 0)) {
            suitableColumns[1].toggleActive();
        } else if (suitableColumns.length > 0) {
            suitableColumns[0].toggleActive();
        }
    } else {
        // There's already an active column, but it may not have a color set yet.
        item.setColorOnActiveColumns();
    }
}

/**
 * Activates the column specified in the table style's "dataVariable" parameter, if any.
 */
TableCatalogItem.prototype.activateColumnFromTableStyle = function() {
    var tableStyle = this._tableStyle;
    if (defined(tableStyle) &amp;&amp; defined(tableStyle.dataVariable)) {
        var columnToActivate = this._tableStructure.getColumnWithNameOrId(tableStyle.dataVariable);
        if (columnToActivate) {
            columnToActivate.toggleActive();
        }
    }
};

/**
 * Your derived class should implement its own version of startPolling, if it is allowed.
 * No return value.
 */
TableCatalogItem.prototype.startPolling = function() {
    const polling = this.polling;
    if (defined(polling.seconds) &amp;&amp; polling.seconds > 0) {
        throw new DeveloperError('Polling is not available on this dataset.');
    }
};

/**
 * Your derived class must implement _load.
 * @returns {Promise} A promise that resolves when the load is complete, or undefined if the function is already loaded.
 */
TableCatalogItem.prototype._load = function() {
    throw new DeveloperError('_load must be implemented in the derived class.');
};

function addToChartableItemsIfNotMappable(item) {
    // If this is not mappable, assume it is chartable - add it to the chartable items array,
    // And then handle incompatible x-axes on existing chartable items.
    if (item.isEnabled &amp;&amp; item.isShown &amp;&amp; !item.isMappable &amp;&amp; item.terria.catalog.chartableItems.indexOf(item) &lt; 0) {
        item.terria.catalog.chartableItems.push(item);
        item.disableIncompatibleTableColumns();
        item.setColorOnActiveColumns();
    }
}

function removeFromChartableItems(item) {
    var indexInChartableItems = item.terria.catalog.chartableItems.indexOf(item);
    if (indexInChartableItems >= 0) {
        item.terria.catalog.chartableItems.splice(indexInChartableItems, 1);
    }
}

TableCatalogItem.prototype._enable = function(layerIndex) {
    if (defined(this._regionMapping)) {
        this._regionMapping.enable(layerIndex);
    }
    addToChartableItemsIfNotMappable(this);
    this.terria.currentViewer.updateItemForSplitter(this);
};

TableCatalogItem.prototype._disable = function() {
    if (defined(this._regionMapping)) {
        this._regionMapping.disable();
    }
    removeFromChartableItems(this);
};

TableCatalogItem.prototype._show = function() {
    if (defined(this._dataSource)) {
        var dataSources = this.terria.dataSources;
        if (dataSources.contains(this._dataSource)) {
            if(console &amp;&amp; console.log){
                console.log(new Error('This data source is already shown.'));
            }
            return;
        }
        dataSources.add(this._dataSource);
    }
    if (defined(this._regionMapping)) {
        this._regionMapping.show();
    }
    addToChartableItemsIfNotMappable(this);
};

TableCatalogItem.prototype._hide = function() {
    if (defined(this._dataSource)) {
        var dataSources = this.terria.dataSources;
        if (!dataSources.contains(this._dataSource)) {
            throw new DeveloperError('This data source is not shown.');
        }
        dataSources.remove(this._dataSource, false);
    }
    if (defined(this._regionMapping)) {
        this._regionMapping.hide();
    }
    removeFromChartableItems(this);
};

/**
 * Finds the next unused color for a chart line.
 * @return {String} A string description of the color.
 */
TableCatalogItem.prototype.getNextColor = function() {
    var catalog = this._terria.catalog;
    if (!defined(catalog)) {
        return;
    }
    if (!defined(this.colors) || this.colors.length === 0) {
        return;
    }
    if (!this.isEnabled) {  // So that previewed charts don't get assigned the next color (which could clash, since it won't be in the list yet).
        return;
    }
    var colors = this.colors.slice();
    // Get all the colors in use (as nested array).
    var colorsUsed = catalog.chartableItems.map(function(item) {
        return item.tableStructure.columns.map(function(column) { return column.color; }).filter(function(color) { return defined(color); });
    });
    // Flatten it.
    colorsUsed = colorsUsed.reduce(function(a, b) { return a.concat(b); }, []);
    // Remove the colors in use from the full list.
    for (var index = 0; index &lt; colorsUsed.length; index++) {
        var fullColorsIndex = colors.indexOf(colorsUsed[index]);
        if (fullColorsIndex > -1) {
            colors.splice(fullColorsIndex, 1);
        }
        if (colors.length === 0) {
            colors = this.colors.slice();  // Keep cycling through the colors when they're all used.
        }
    }
    return colors[0];
};

/**
 * Returns a {@link ChartData} object for the TableCatalogItem.  See ChartPanel.jsx for an example.
 *
 * @returns {ChartData}
 */
TableCatalogItem.prototype.chartData = function() {
    const item = this;
    const xColumn = item.xAxis;
    const yColumns = item.tableStructure.columnsByType[VarType.SCALAR].filter(column => column.isActive);
    if (yColumns.length > 0) {
        const yColumnNumbers = yColumns.map(yColumn => item.tableStructure.columns.indexOf(yColumn));
        const pointArrays = item.tableStructure.toPointArrays(xColumn, yColumns);
        return pointArrays.map((points, index) =>
            new ChartData(points, {
                id: item.uniqueId + '-' + yColumnNumbers[index],
                name: yColumns[index].name,
                categoryName: item.name,
                units: yColumns[index].units,
                color: yColumns[index].color || this.colors[0],
                yAxisMin: yColumns[index].yAxisMin,
                yAxisMax: yColumns[index].yAxisMax
            })
        );
    }
};

/**
 * Finds any other table structures that do not have the same xColumn type, and disable their columns.
 * @private
 */
TableCatalogItem.prototype.disableIncompatibleTableColumns = function() {
    if (this.isEnabled &amp;&amp; this.isShown) {
        var tableStructure = this._tableStructure;
        var xColumn = this.xAxis;

        this._terria.catalog.chartableItems.forEach(otherItem => {
            if (otherItem.tableStructure !== tableStructure) {
                if (otherItem.xAxis.type !== xColumn.type) {
                    // Deactivate the other table's columns, which will remove its chart.
                    otherItem.tableStructure.columns.forEach(column => { column.isActive = false; });
                }
            }
        });
    }
};

TableCatalogItem.prototype.showOnSeparateMap = function(globeOrMap) {
    var dataSource = this._dataSource;
    var removeRegionMapping;

    if (defined(this._regionMapping)) {
        removeRegionMapping = this._regionMapping.showOnSeparateMap(globeOrMap);
    }

    if (defined(dataSource)) {
        globeOrMap.addDataSource({
            dataSource: dataSource
        });
    }

    return function() {
        if (defined(removeRegionMapping)) {
            removeRegionMapping();
        }
        if (defined(dataSource)) {
            globeOrMap.removeDataSource({
                dataSource: dataSource
            });
        }
    };
};

module.exports = TableCatalogItem;
</code></pre>
        </article>
    </section>




</div>

<nav>
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="AbsCode.html">AbsCode</a></li><li><a href="AbsConcept.html">AbsConcept</a></li><li><a href="AbsDataset.html">AbsDataset</a></li><li><a href="AbsIttCatalogGroup.html">AbsIttCatalogGroup</a></li><li><a href="AbsIttCatalogItem.html">AbsIttCatalogItem</a></li><li><a href="AddressGeocoder.html">AddressGeocoder</a></li><li><a href="ArcGisCatalogGroup.html">ArcGisCatalogGroup</a></li><li><a href="ArcGisFeatureServerCatalogGroup.html">ArcGisFeatureServerCatalogGroup</a></li><li><a href="ArcGisFeatureServerCatalogItem.html">ArcGisFeatureServerCatalogItem</a></li><li><a href="ArcGisMapServerCatalogGroup.html">ArcGisMapServerCatalogGroup</a></li><li><a href="ArcGisMapServerCatalogItem.html">ArcGisMapServerCatalogItem</a></li><li><a href="AugmentedVirtuality.html">AugmentedVirtuality</a></li><li><a href="BingMapsCatalogItem.html">BingMapsCatalogItem</a></li><li><a href="BooleanParameter.html">BooleanParameter</a></li><li><a href="BulkAddressGeocoderResult.html">BulkAddressGeocoderResult</a></li><li><a href="CameraView.html">CameraView</a></li><li><a href="Catalog.html">Catalog</a></li><li><a href="CatalogFunction.html">CatalogFunction</a></li><li><a href="CatalogGroup.html">CatalogGroup</a></li><li><a href="CatalogItem.html">CatalogItem</a></li><li><a href="CatalogMember.html">CatalogMember</a></li><li><a href="Cesium.html">Cesium</a></li><li><a href="Cesium3DTilesCatalogItem.html">Cesium3DTilesCatalogItem</a></li><li><a href="CesiumDragPoints.html">CesiumDragPoints</a></li><li><a href="CesiumTerrainCatalogItem.html">CesiumTerrainCatalogItem</a></li><li><a href="CkanCatalogGroup.html">CkanCatalogGroup</a></li><li><a href="CkanCatalogItem.html">CkanCatalogItem</a></li><li><a href="Clock.html">Clock</a></li><li><a href="CompositeCatalogItem.html">CompositeCatalogItem</a></li><li><a href="Concept.html">Concept</a></li><li><a href="CorsProxy.html">CorsProxy</a></li><li><a href="CsvCatalogItem.html">CsvCatalogItem</a></li><li><a href="CswCatalogGroup.html">CswCatalogGroup</a></li><li><a href="CustomComponentType.html">CustomComponentType</a></li><li><a href="CzmlCatalogItem.html">CzmlCatalogItem</a></li><li><a href="DataSourceCatalogItem.html">DataSourceCatalogItem</a></li><li><a href="DateTimeParameter.html">DateTimeParameter</a></li><li><a href="DisplayVariablesConcept.html">DisplayVariablesConcept</a></li><li><a href="EnumerationParameter.html">EnumerationParameter</a></li><li><a href="Feature.html">Feature</a></li><li><a href="FunctionParameter.html">FunctionParameter</a></li><li><a href="GeoJsonCatalogItem.html">GeoJsonCatalogItem</a></li><li><a href="GlobeOrMap.html">GlobeOrMap</a></li><li><a href="GnafAddressGeocoder.html">GnafAddressGeocoder</a></li><li><a href="GnafApi.html">GnafApi</a></li><li><a href="GnafSearchProviderViewModel.html">GnafSearchProviderViewModel</a></li><li><a href="GpxCatalogItem.html">GpxCatalogItem</a></li><li><a href="HelpScreen.html">HelpScreen</a></li><li><a href="HelpSequence.html">HelpSequence</a></li><li><a href="HelpSequences.html">HelpSequences</a></li><li><a href="HelpViewState.html">HelpViewState</a></li><li><a href="ImageryLayerCatalogItem____.html">ImageryLayerCatalogItem</a></li><li><a href="IonImageryCatalogItem.html">IonImageryCatalogItem</a></li><li><a href="KmlCatalogItem.html">KmlCatalogItem</a></li><li><a href="Leaflet.html">Leaflet</a></li><li><a href="LeafletDataSourceDisplay.html">LeafletDataSourceDisplay</a></li><li><a href="LeafletDragPoints.html">LeafletDragPoints</a></li><li><a href="LeafletGeomVisualizer.html">LeafletGeomVisualizer</a></li><li><a href="LegendHelper.html">LegendHelper</a></li><li><a href="LegendUrl.html">LegendUrl</a></li><li><a href="LineParameter.html">LineParameter</a></li><li><a href="MagdaCatalogItem.html">MagdaCatalogItem</a></li><li><a href="MapboxMapCatalogItem.html">MapboxMapCatalogItem</a></li><li><a href="MapInteractionMode.html">MapInteractionMode</a></li><li><a href="Metadata.html">Metadata</a></li><li><a href="MetadataItem.html">MetadataItem</a></li><li><a href="module.html#.exports">exports</a></li><li><a href="OgrCatalogItem.html">OgrCatalogItem</a></li><li><a href="OpenStreetMapCatalogItem.html">OpenStreetMapCatalogItem</a></li><li><a href="PlacesLikeMeCatalogfunction.html">PlacesLikeMeCatalogfunction</a></li><li><a href="PointParameter.html">PointParameter</a></li><li><a href="Polling.html">Polling</a></li><li><a href="PolygonParameter.html">PolygonParameter</a></li><li><a href="RectangleParameter.html">RectangleParameter</a></li><li><a href="RegionDataParameter.html">RegionDataParameter</a></li><li><a href="RegionMapping.html">RegionMapping</a></li><li><a href="RegionParameter.html">RegionParameter</a></li><li><a href="RegionProvider.html">RegionProvider</a></li><li><a href="RegionProviderList.html">RegionProviderList</a></li><li><a href="RegionTypeParameter.html">RegionTypeParameter</a></li><li><a href="ResultPendingCatalogItem.html">ResultPendingCatalogItem</a></li><li><a href="SdmxJsonCatalogItem.html">SdmxJsonCatalogItem</a></li><li><a href="SensorObservationServiceCatalogItem.html">SensorObservationServiceCatalogItem</a></li><li><a href="SocrataCatalogGroup.html">SocrataCatalogGroup</a></li><li><a href="SpatialDetailingCatalogFunction.html">SpatialDetailingCatalogFunction</a></li><li><a href="StringParameter.html">StringParameter</a></li><li><a href="SummaryConcept.html">SummaryConcept</a></li><li><a href="TableCatalogItem.html">TableCatalogItem</a></li><li><a href="TableColumn.html">TableColumn</a></li><li><a href="TableColumnStyle.html">TableColumnStyle</a></li><li><a href="TableDataSource.html">TableDataSource</a></li><li><a href="TableStructure.html">TableStructure</a></li><li><a href="TableStyle.html">TableStyle</a></li><li><a href="TerrainCatalogItem.html">TerrainCatalogItem</a></li><li><a href="Terria.html">Terria</a></li><li><a href="TerriaError.html">TerriaError</a></li><li><a href="TerriaJsonCatalogFunction.html">TerriaJsonCatalogFunction</a></li><li><a href="TimeSeriesStack.html">TimeSeriesStack</a></li><li><a href="UrlTemplateCatalogItem.html">UrlTemplateCatalogItem</a></li><li><a href="UrthecastCatalogGroup.html">UrthecastCatalogGroup</a></li><li><a href="UrthecastServerCatalogItem.html">UrthecastServerCatalogItem</a></li><li><a href="UserDrawing.html">UserDrawing</a></li><li><a href="VariableConcept.html">VariableConcept</a></li><li><a href="ViewerModes..html">ViewerModes.</a></li><li><a href="WebFeatureServiceCatalogGroup.html">WebFeatureServiceCatalogGroup</a></li><li><a href="WebFeatureServiceCatalogItem.html">WebFeatureServiceCatalogItem</a></li><li><a href="WebMapServiceCatalogGroup.html">WebMapServiceCatalogGroup</a></li><li><a href="WebMapServiceCatalogItem.html">WebMapServiceCatalogItem</a></li><li><a href="WebMapTileServiceCatalogGroup.html">WebMapTileServiceCatalogGroup</a></li><li><a href="WebMapTileServiceCatalogItem.html">WebMapTileServiceCatalogItem</a></li><li><a href="WebProcessingServiceCatalogFunction.html">WebProcessingServiceCatalogFunction</a></li><li><a href="WebProcessingServiceCatalogGroup.html">WebProcessingServiceCatalogGroup</a></li><li><a href="WebProcessingServiceCatalogItem.html">WebProcessingServiceCatalogItem</a></li><li><a href="WfsFeaturesCatalogGroup.html">WfsFeaturesCatalogGroup</a></li><li><a href="WhyAmISpecialCatalogFunction.html">WhyAmISpecialCatalogFunction</a></li></ul><h3>Global</h3><ul><li><a href="global.html#_bumpyTerrainProvider">_bumpyTerrainProvider</a></li><li><a href="global.html#_terrain">_terrain</a></li><li><a href="global.html#activeTimeColumnNameIdOrIndex">activeTimeColumnNameIdOrIndex</a></li><li><a href="global.html#addBoundingBox">addBoundingBox</a></li><li><a href="global.html#addMarker">addMarker</a></li><li><a href="global.html#addUserCatalogMember">addUserCatalogMember</a></li><li><a href="global.html#allFeaturesAvailablePromise">allFeaturesAvailablePromise</a></li><li><a href="global.html#allShareKeys">allShareKeys</a></li><li><a href="global.html#arrayProduct">arrayProduct</a></li><li><a href="global.html#barHeightMax">barHeightMax</a></li><li><a href="global.html#barHeightMin">barHeightMin</a></li><li><a href="global.html#barLeft">barLeft</a></li><li><a href="global.html#barTop">barTop</a></li><li><a href="global.html#buildEmptyAccumulator">buildEmptyAccumulator</a></li><li><a href="global.html#buildRequestData">buildRequestData</a></li><li><a href="global.html#buildShareLink">buildShareLink</a></li><li><a href="global.html#buildShortShareLink">buildShortShareLink</a></li><li><a href="global.html#calculateFinishDatesFromStartDates">calculateFinishDatesFromStartDates</a></li><li><a href="global.html#canShorten">canShorten</a></li><li><a href="global.html#categoryName">categoryName</a></li><li><a href="global.html#ChartData">ChartData</a></li><li><a href="global.html#color">color</a></li><li><a href="global.html#ColorMap">ColorMap</a></li><li><a href="global.html#combineData">combineData</a></li><li><a href="global.html#combineFilters">combineFilters</a></li><li><a href="global.html#combineRepeated">combineRepeated</a></li><li><a href="global.html#combineValueArrays">combineValueArrays</a></li><li><a href="global.html#computeRingWindingOrder">computeRingWindingOrder</a></li><li><a href="global.html#computeScreenSpacePosition">computeScreenSpacePosition</a></li><li><a href="global.html#config">config</a></li><li><a href="global.html#containsAny">containsAny</a></li><li><a href="global.html#convertLuceneHit">convertLuceneHit</a></li><li><a href="global.html#convertToDates">convertToDates</a></li><li><a href="global.html#correctEntityHeight">correctEntityHeight</a></li><li><a href="global.html#createCatalogItemFromFileOrUrl">createCatalogItemFromFileOrUrl</a></li><li><a href="global.html#createCatalogItemFromUrl">createCatalogItemFromUrl</a></li><li><a href="global.html#createCatalogMemberFromType">createCatalogMemberFromType</a></li><li><a href="global.html#createLeafletCredit">createLeafletCredit</a></li><li><a href="global.html#createParameterFromType">createParameterFromType</a></li><li><a href="global.html#createRegexDeserializer">createRegexDeserializer</a></li><li><a href="global.html#createRegexSerializer">createRegexSerializer</a></li><li><a href="global.html#cssClass">cssClass</a></li><li><a href="global.html#CustomComponents">CustomComponents</a></li><li><a href="global.html#deIndexWithDescendants">deIndexWithDescendants</a></li><li><a href="global.html#Description">Description</a></li><li><a href="global.html#direction">direction</a></li><li><a href="global.html#disposeSubscription">disposeSubscription</a></li><li><a href="global.html#EarthGravityModel1996">EarthGravityModel1996</a></li><li><a href="global.html#error">error</a></li><li><a href="global.html#extendLoad">extendLoad</a></li><li><a href="global.html#extent">extent</a></li><li><a href="global.html#featureClicked">featureClicked</a></li><li><a href="global.html#featureDataToGeoJson">featureDataToGeoJson</a></li><li><a href="global.html#featureMousedown">featureMousedown</a></li><li><a href="global.html#features">features</a></li><li><a href="global.html#findKeyForGroupElement">findKeyForGroupElement</a></li><li><a href="global.html#flattenCatalog">flattenCatalog</a></li><li><a href="global.html#formatDate">formatDate</a></li><li><a href="global.html#formatDateTime">formatDateTime</a></li><li><a href="global.html#formatNumberForLocale">formatNumberForLocale</a></li><li><a href="global.html#formatPropertyValue">formatPropertyValue</a></li><li><a href="global.html#formatTime">formatTime</a></li><li><a href="global.html#getAncestors">getAncestors</a></li><li><a href="global.html#getColumnOptions">getColumnOptions</a></li><li><a href="global.html#getColumnWithNameIdOrIndex">getColumnWithNameIdOrIndex</a></li><li><a href="global.html#getDataUriFormat">getDataUriFormat</a></li><li><a href="global.html#getGroupChildren">getGroupChildren</a></li><li><a href="global.html#getShareData">getShareData</a></li><li><a href="global.html#getTemporalFiltersContext">getTemporalFiltersContext</a></li><li><a href="global.html#getUniqueValues">getUniqueValues</a></li><li><a href="global.html#gmlToGeoJson">gmlToGeoJson</a></li><li><a href="global.html#gradientColorMap">gradientColorMap</a></li><li><a href="global.html#hasAddress">hasAddress</a></li><li><a href="global.html#hasChildren">hasChildren</a></li><li><a href="global.html#hasLatitudeAndLongitude">hasLatitudeAndLongitude</a></li><li><a href="global.html#hostInDomains">hostInDomains</a></li><li><a href="global.html#id">id</a></li><li><a href="global.html#infoWithoutSources">infoWithoutSources</a></li><li><a href="global.html#isBrowserCompatible">isBrowserCompatible</a></li><li><a href="global.html#isCommonMobilePlatform">isCommonMobilePlatform</a></li><li><a href="global.html#isLoading">isLoading</a></li><li><a href="global.html#isVisible">isVisible</a></li><li><a href="global.html#itemHeight">itemHeight</a></li><li><a href="global.html#itemHeightMin">itemHeightMin</a></li><li><a href="global.html#items">items</a></li><li><a href="global.html#itemSpacing">itemSpacing</a></li><li><a href="global.html#itemWidth">itemWidth</a></li><li><a href="global.html#Legend">Legend</a></li><li><a href="global.html#legendUrl">legendUrl</a></li><li><a href="global.html#map">map</a></li><li><a href="global.html#markdownToHtml">markdownToHtml</a></li><li><a href="global.html#markerVisible">markerVisible</a></li><li><a href="global.html#name">name</a></li><li><a href="global.html#NowViewing">NowViewing</a></li><li><a href="global.html#overrideProperty">overrideProperty</a></li><li><a href="global.html#pad">pad</a></li><li><a href="global.html#parseCustomHtmlToReact">parseCustomHtmlToReact</a></li><li><a href="global.html#parseCustomMarkdownToReact">parseCustomMarkdownToReact</a></li><li><a href="global.html#PickedFeatures">PickedFeatures</a></li><li><a href="global.html#pickPosition">pickPosition</a></li><li><a href="global.html#point">point</a></li><li><a href="global.html#points">points</a></li><li><a href="global.html#position">position</a></li><li><a href="global.html#prettifyCoordinates">prettifyCoordinates</a></li><li><a href="global.html#prettifyProjection">prettifyProjection</a></li><li><a href="global.html#printWindow">printWindow</a></li><li><a href="global.html#processAddress">processAddress</a></li><li><a href="global.html#Proj4Definitions">Proj4Definitions</a></li><li><a href="global.html#propertyGetTimeValues">propertyGetTimeValues</a></li><li><a href="global.html#readJson">readJson</a></li><li><a href="global.html#rectangle">rectangle</a></li><li><a href="global.html#rectangleToLatLngBounds">rectangleToLatLngBounds</a></li><li><a href="global.html#RegionDataValue">RegionDataValue</a></li><li><a href="global.html#regionDetails">regionDetails</a></li><li><a href="global.html#registerCustomComponentTypes">registerCustomComponentTypes</a></li><li><a href="global.html#rememberRejections">rememberRejections</a></li><li><a href="global.html#removeMarker">removeMarker</a></li><li><a href="global.html#replaceUnderscores">replaceUnderscores</a></li><li><a href="global.html#sanitiseAddressNumber">sanitiseAddressNumber</a></li><li><a href="global.html#selectBaseMap">selectBaseMap</a></li><li><a href="global.html#serializeToJson">serializeToJson</a></li><li><a href="global.html#ServerConfig">ServerConfig</a></li><li><a href="global.html#setClockCurrentTime">setClockCurrentTime</a></li><li><a href="global.html#shareKeyIndex">shareKeyIndex</a></li><li><a href="global.html#shouldBeUpdated">shouldBeUpdated</a></li><li><a href="global.html#showSelection">showSelection</a></li><li><a href="global.html#sortByFirst">sortByFirst</a></li><li><a href="global.html#sortedIndices">sortedIndices</a></li><li><a href="global.html#splitIntoBatches">splitIntoBatches</a></li><li><a href="global.html#supportsIntervals">supportsIntervals</a></li><li><a href="global.html#supportsWebGL">supportsWebGL</a></li><li><a href="global.html#TerriaViewer">TerriaViewer</a></li><li><a href="global.html#Title">Title</a></li><li><a href="global.html#toArrayOfRows">toArrayOfRows</a></li><li><a href="global.html#Tooltip">Tooltip</a></li><li><a href="global.html#triggerResize">triggerResize</a></li><li><a href="global.html#unionRectangleArray">unionRectangleArray</a></li><li><a href="global.html#unionRectangles">unionRectangles</a></li><li><a href="global.html#units">units</a></li><li><a href="global.html#up">up</a></li><li><a href="global.html#updateApplicationOnHashChange">updateApplicationOnHashChange</a></li><li><a href="global.html#updateFromJson">updateFromJson</a></li><li><a href="global.html#updateRectangleFromRegion">updateRectangleFromRegion</a></li><li><a href="global.html#variableNameLeft">variableNameLeft</a></li><li><a href="global.html#variableNameTop">variableNameTop</a></li><li><a href="global.html#ViewerMode">ViewerMode</a></li><li><a href="global.html#width">width</a></li><li><a href="global.html#yAxisMax">yAxisMax</a></li><li><a href="global.html#yAxisMin">yAxisMin</a></li></ul>
</nav>

<br class="clear">

<footer>
    Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Fri Sep 21 2018 12:26:18 GMT+1000 (AUS Eastern Standard Time)
</footer>

<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>