Managing Data Sources

Managing Map Data Sources

A weather map can display a variety of different weather data sources at once, such as radar, satellite, earthquakes and storm cells. You can complete control over which data sources appear on your weather map and can add or remove them at any time.

Data Source Types

There are three primary data types for a weather map: raster/tile, point and shape:

  • tile: A raster image or tile layer, such as radar or temperatures.
  • vector: Vector point, polyline and/or polygon data, such as storm reports and convective outlooks.
  • geojson: A vector layer that uses data provided in valid GeoJSON format.
  • text: Data values are rendered as text labels, such as observation elements like temperature or wind speeds.

Using Layer Codes

The interactive weather map has built-in support for automatically creating and managing the various data sources associated with supported layer codes. These layer codes correspond to those currently offered through Xweather Map (opens in a new tab)s.

However, point and shape layers are rendered using data from the Xweather Weather API (opens in a new tab) instead of Maps raster imagery which considerably reduces the number of map units consumed by your weather map. For example, stormcells and stormreports layers will use API data instead of Maps imagery on a weather map.

Data Source Types for Layer Codes

The following tables indicate which layer codes are rendered as point, shape or text layers using API data. All other layer codes will use Xweather Raster Maps raster layers:

LayerType
air-qualityvector
convectivevector
drought-monitorvector
earthquakesvector
firesvector
fire-outlookvector
observationstext
recordsvector
river-observationsvector
stormcellsvector
stormreportsvector
tropical-cyclonesvector

You can override the default data source type used for a particular layer code by providing a different value for type in the layer options when calling addLayer():

map.addLayer('stormcells', {
    type: 'tile'
});

The above would then render stormcells using the Maps raster tile layer instead of requesting data from the Xweather Weather API and rendering the result as markers. The downside to rendering point data as raster tiles is they will not be interactive, meaning you won't be able to select a marker to reveal the callout with additional details for that item.

Adding Layers

To add a layer to your map, just use addLayer():

map.addLayer('radar');

You can also add multiple layers at once using addLayers():

map.addLayers(['satellite','radar','stormreports']);

When adding layers with their code, you can also provide additional configuration options for the layer as the second parameter to addLayer(). This should be an object containing values for one or all of the following options depending on the type of layer you're adding (e.g. tile or vector).

Changing the Layer Type

For instance, if you wanted to show storm cells on the map using raster Maps layers instead of as a point data source, you can specify the type value to override the default source type to use:

map.addLayer('stormcells', {
    type: 'tile'
});

Adding Raster Layers

Raster layers are added to the map as tiles. By default, tile data is dependent upon the parent map's current timeline position and will update as the timeline is scrubbed or animated. However, you can specify a specific date/time or time offset when adding a layer by providing custom configuration options.

Raster Layer Options

OptionTypeDescription
typestringType of content source to use if supported by the layer. Supports tile, point or shape.
styleRasterStyleStyle configuration to use when rendering the layer on the map. Refer to the styling guide for more details and configuration options.
timeDate or numberThe timestamp to use for the source data, either as a Date or Epoch time in milliseconds. This value will be ignored if offset is also defined.
offsetnumber or stringTime offset to use for the layer's data. If an offset is defined, then the layer will only display data for that time interval regardless of the global timeline time interval and its animation will be disabled.
alwaysShowbooleanWhether this tile source should always be visible regardless of past or future state. Default value is false.
animationAnimationOptionsAnimation options for the layer. Not all options will be used for a specific layer since its animation is managed by the global map timeline.

Controlling Raster Layer Order

Some raster tile layers should always appear above or below other layers. When adding layers using addLayer() without a custom RasterStyle configuration, the layer will always be added on top of all other layers. However, you may want the layer to be inserted at a particular index or at the bottom of the stack.

To control the order in which the layer will be inserted, include a custom RasterStyle with your call to addLayer() that specifies the desired z-index of the layer. If the specified z-index value is higher than the total number of layers currently on the map, then the layer will be added to the top of the stack instead.

map.addLayer('satellite', {
    style: {
        zIndex: 1
    }
});

Some third-party mapping libraries allow you to insert layers relative to the map's own layers, such as Mapbox GL (opens in a new tab). If you're using one of these mapping strategies, you can also specify which layer your weather layer should be inserted under. For instance, the following would add the satellite layer underneath the Mapbox layer whose identifier is national_parks:

map.addLayer('satellite', {
    style: {
        belowLayer: 'national_park'
    }
});

You can also change a layer's stacking order after it's been added to the map by using either bringLayerToFront(), sendLayerToBack(), or setLayerOrder():

// sends the existing satellite layer behind all other weather layers
map.sendLayerToBack('satellite');
 
// brings the existing radar layer above all other weather layers
map.bringLayerToFront('radar');
 
// move the existing alerts layer to third position in the stack of existing weather layers
// z-index positions are zero-based, meaning `0` is the bottom position
map.setLayerOrder('alerts', 2);

Note that using any of the above methods for changing layer ordering will not also add the layer to the map if it does not exist. They only affect existing weather layers. You will still need to add the layer beforehand using addLayer() or the map's initial configuration.

Using Raster Maps Valid Times

By default, Xweather Raster Maps (opens in a new tab) will automatically redirect requests to the appropriate valid time for the dataset. However, you may need to access the valid times directly in your own applications, such as performing animations for exact valid times in the dataset.

The layerInfo instance on InteractiveMap provides a validTimes property that you can use for fetching the valid times associated with a specific Maps layer. This is just an instance of LayerTimes that provides two methods for fetching a layer's valid times: times() and timesInRange().

You can fetch the latest valid times for a layer:

map.layerInfo.validTimes.times('radar', 20).then((times) => {
    // do something with times...
});

Or, you can fetch the valid times within a specific date range:

const from = map.timeline.startDate();
const to = map.timeline.endDate();
map.layerInfo.validTimes.timesInRange('radar', from, to, 300).then((times) => {
    // do something with times...
});

If you want to ensure only valid times are using during a layer's animation, you can also force that layer's datasource animation to use a static set of times you provide:

const source = app.map.getSourceForLayer('radar');
if (source && source.animation) {
    const from = map.timeline.startDate();
    const to = map.timeline.endDate();
    
    // set the layer's animation intervals to exact valid times instead of auto-calculating
    app.map.layerInfo.validTimes.timesInRange('radar', from, to, 300).then((times) => {
        const intervals = 10;
        const every = Math.ceil(times.length / intervals);
        source.animation.setTimes(times, every);
    });
 
    // get the actual time for each interval during animation playback, such as displaying 
    // in the interface alongside the layer control
    source.animation.on('advance:image', (e) => {
        const { time } = e.data || {};
        console.log(new Date(time));
    });
}

Note, however, you will need to use the above code each time you need to update the layer's animation data, such as when map data refreshes or the global timeline range changes.

If you want to revert a layer's animation to use the default method of auto-calculating interval times, just set the animation times to a null value:

const source = app.map.getSourceForLayer('radar');
if (source && source.animation) {
    source.animation.setTimes(null);
}

Adding Vector Layers

Vector layers are added to the map as markers and/or polylines and polygons. By default, a vector layer will request data from its configured source based on the global timeline's date range. However, you can configure different behavior using the various layer data options described below.

Vector Layer Options

OptionTypeDescription
typestringType of content source to use, if supported by the layer. Supports tile, point or shape.
styleVectorStyleStyle configuration to use when rendering the layer on the map. Refer to the styling guide for more details and configuration options.
dataobjectProvides the data-related configuration options for the layer.
data.requestobjectRequest options for vector layers.
data.request.endpointstringAPI endpoint to use for the request.
data.request.actionstringAPI action to use for the request. Default value is within for point content sources and search for shape content sources.
data.request.parametersobjectDefines the request parameters to use when requesting data for the layer.
data.urlstring or FunctionThe URL string to request data from for the data source. Value may be a function that receives information about the map, such as current coordinate bounds, to format a URL string before returning.
data.itemsarrayAn array of records to display on the map. If this value is provided, then data will not be requested from a remote source.
data.propertiesobjectData key-path configuration for property values.
data.properties.idstringProperty key path to use for each object's identifier.
data.properties.rootstringKey path to the root of the node containing the array of data elements (e.g. features). If not provided, then the root node is assumed to contain the array of data elements.
data.properties.categorystringProperty key path to use for an object's category or grouping, if any.
data.properties.timestampstringDefines the key path of the property to use for a model object's time value.
data.properties.valuestringDefines the key path of the property to use for a model object's displayed value.
data.properties.pointsstring or string[]Property key path that contains the point/coordinate data for the layer. This value can also be an array of key paths, in which case the points will be combined when rendering the data source's points on the map.
data.properties.pathstringProperty key path to use for the object's coordinate path that defines the shape. If the value of this property is an array, then multiple paths will be rendered on the map for the model.
data.formatterFunctionAn optional formatter function that can be used to format layer data before being processed for rendering on the map. This function receives the entire dataset loaded for the layer and should return the formatted data to use when rendering map elements.
data.coordinateFunctionA function that returns the geographical coordinate based on the model object.
data.geometryFunctionA function that returns the shape's GeoJSON geometry based on the model object.
data.reversedCoordsbooleanA Boolean indicating whether the data's coordinate arrays are reversed from the GeoJSON standard (e.g. [lat, lon] instead of the default of [lon, lat]).
animationAnimationOptionsAnimation options for the layer. Not all options will be used for a specific layer since its animation is managed by the global map timeline.
refreshnumberData auto-update interval, in seconds. Default value is 0, which disables auto-updating.

Changing the Request Parameters

Since point, shape and text data sources request their data from the Xweather Weather API instead of rendering static raster imagery, it uses an ApiRequest object to setup and perform the request internally.

Each layer will setup the default request parameters to use for its request, but you can also override these default values when you add these layers to the map. Just provide your custom API request parameters as an object assigned to the request.parameters property of your layer options object:

map.addLayer('stormcells', {
    data: {
        request: {
            parameters: {
                filter: 'tornado'
            }
        }
    }
});

Note You will not be able to override certain API request parameters for point, shape and text data sources since they are controlled by the visible map bounds and timeline start and end range. These parameters include: from, to, p, and limit.

Adding Text Layers

For text layers, such as observations, you also need to provide the property key paths that correspond to the time and the value you want to display from the data returned by an API request. You specify these key paths using the time and value properties within the request.property object of your layer options object when adding a layer.

For instance, the following will display the current temperature as text using the value key path of periods.ob.tempF whose valid time is defined by the property at key path periods.ob.timestamp for each object in the API response:

map.addLayer('observations', {
    data: {
        properties: {
            timestamp: 'periods.ob.timestamp',
            value: 'periods.ob.tempF'
        }
    }
});

The time key path should always be the same value regardless of the value for value. To change the value that gets rendered to the map, just use a different key path for value:

map.addLayer('observations', {
    data: {
        properties: {
            timestamp: 'periods.ob.timestamp',
            value: 'periods.ob.windSpeedMPH'
        }
    }
});

The value property also accepts a callback function that can be used to format and return the value that gets rendered on the map instead of using a key path. This callback will receive the model's data as an argument, allowing you to access the complete model data when formatting the label's value for the map.

The following configuration would render a stormreports layer for only snow-related reports as text instead of points and outputs the snow total value to the map:

map.addLayer('stormreports', {
    type: 'text',
    data: {
        request: {
            parameters: {
                filter: 'snow'
            }
        },
        properties: {
            timestamp: 'report.timestamp',
            value: (data) => {
                const { report: { detail: { snowIN: val }}} = data;
                if (val) {
                    return val.toFixed(1);
                }
                return null;
            }
        }
    }
});

Removing Layers

Removing layers from the map is similar to adding them. To remove a layer from your map, just use removeLayer():

map.removeLayer('radar');

You can also remove multiple layers at once using removeLayers():

map.removeLayers(['satellite','stormreports']);