Configuring Data Sources

Configuring Data Sources

Every map module must provide a valid map data source configuration, which is used to setup any data requests and/or styling options for the module's data when rendered on an interactive map.

The map data source can be one of the following types supported by the SDK:

TypeCodeDescription
TileSourcetileUsed to display raster map tiles, such as Xweather Raster Maps (opens in a new tab) imagery. More info
VectorSourcevectorUsed to display vector data on a map, such as markers, polygons and polylines. Data can be provided in a variety of formats. More info
GeoJsonSourcegeojsonSimilar to VectorSource except that the data must be provided in a valid GeoJSON format. More info
TextSourcetextA subclass of VectorSource that renders text markers on the map based on the source's configuration. Text values can also be animated across time. More info

Creating a TileSource

A tile map data source is responsible for requesting and rendering Xweather Raster Maps imagery and can be customized to include one or more supported Maps layers (opens in a new tab) along with modifiers (opens in a new tab) and other effects.

To use a tile source for your module's map data source, you just need to provide the MapRequest instance configured with your desired options. Assign this request instance to your source configuration's service property, which will be used to generate the proper URLs when requesting tile imagery on the map:

source() {
    const request = this.account.map()
        .layers('radar');
 
    return {
        type: 'tile',
        data: {
            service: request
        }
    };
}

Creating a VectorSource

Setting up a vector map data source is more involved because you need to provide the data or data request from which to fetch the source's data and the styling of elements when rendered on the map.

Provide one of the following options on your vector source's data configuration options:

PropertyTypeDescription
itemsanyA static object consisting of the data to use for the source.
urlstringA remote URL string to request data from for the source.
serviceAWFRequestAn AWFRequest instance configured to request data from the Xweather Weather API for the source.

The following data configuration would render a static dataset to the map:

source() {
    return {
        type: 'vector',
        data: {
            items: [{
                geography: {
                    latitude: 50.7309,
                    longitude: 7.6098,
                    altitude: 1653.54,
                    direction: 256.8
                },
                aircraft: {
                    regNumber: 'N570UP',
                    icaoCode: '',
                    icao24: '',
                    iataCode: ''
                },
                status: 'en-route'
            },{
                geography: {
                    latitude: 33.0373,
                    longitude: -96.8151,
                    altitude: 3261.36,
                    direction: 227.85
                },
                aircraft: {
                    regNumber: 'N146UP',
                    icaoCode: 'A306',
                    icao24: 'A0BACA',
                    iataCode: 'A306'
                },
                status: 'en-route'
            }]
        }
    };
}

However, your vector source can also request data from a remote URL:

source() {
    return {
        type: 'vector',
        data: {
            url: 'https://www.mydomain.com/flights?airline=UPS&status=en-route'
        }
    };
}

Or return an AWFRequest instance to load data from the Weather Data API:

source() {
    const request = this.account.api()
        .endpoint('earthquakes')
        .sort('mag:-1');
 
    return {
        type: 'vector',
        data: {
            service: request,
            properties: {
                id: 'report.id',
                timestamp: 'report.timestamp'
            }
        }
    };
}

Instead of manually creating an instance of ApiRequest, you can alternatively configure the request options with the desired API endpoint (along with an action and/or parameters if applicable). When using this method, the ApiRequest will automatically be created by the vector source:

source() {
    return {
        type: 'vector',
        data: {
            request: {
                endpoint: 'earthquakes',
                params: {
                    sort: 'mag:-1'
                }
            },
            properties: {
                id: 'report.id',
                timestamp: 'report.timestamp'
            }
        }
    };
}

Mapping Data Properties

With your data or request configured, you'll want to configure the source to map certain properties a vector source uses or requires with the properties from your dataset. You would need to configure these using the data.properties object in your source's configuration. The following property mappings are supported:

PropertyDescription
idThe property keypath to use for an element's identifier.
rootThe property keypath to the root node containing the array of elements to render on the map. If not provided, the root node of the data is assumed to be an array of objects to render.
categoryThe property keypath to use for an element's category or grouping, if any.
timestampThe property keypath to use for an element's date and time.
valueThe property keypath that contains the data's value for each element. This can also be a function that receives the model's data and returns the value to use in case additional formatting needs to occur on the value before rendering.
pointsThe property keypath that contains the point/coordinate information. This value can also be an array of keypaths, in which case the points will be merged when rendering the source's points on a map.
pathThe property keypath to use for the element's coordinate path that defines the polyline or polygon geometry. If the value of this property is an array, then multiple paths will be rendered for the model.

The following would setup the necessary data property mappings for the remote URL example above:

source() {
    return {
        type: 'vector',
        data: {
            url: 'https://www.mydomain.com/flights?airline=UPS&status=en-route',
            properties: {
                id: 'flightID',
                category: 'status.code'
            },
            coordinate: (data) => {
                const { latitude, longitude } = data;
                return { lat: latitude, lon: longitude };
            }
        }
    };
}

You'll also notice that the above example provides a function for data.coordinate. This allows you to convert a model's coordinate values from the datasource into the proper coordinate format required by the SDK. There is also a data.geometry property you can define using a function to format path information as required by the SDK for polylines and polygons.

The following properties can be defined on your data configuration to format points and paths as needed. They will receive a single parameter, which is the data object associated with a single model:

PropertyDescription
coordinateA function that formats and returns the geographical coordinate based on the model object.
geometryA function that formats and returns the shape's GeoJSON geometry based on the model object.

Styling Vector Elements

The last set of options you need to configure for your vector source is the styling to use when rendering the source's data on the map. You can customize the styling for markers, polylines and polygons, which are set using the style configuration for your source.

The following is a complete vector source example, including custom styling options and support for returning a different remote data URL based on the layer's active parameters:

source() {
    return {
        type: 'vector',
        refresh: 60,
        data: {
            url: (params) => {
                return 'https://www.mydomain.com/flights?airline=' + (params.filter || '') + '&status=en-route&';
            },
            properties: {
                id: 'flightID',
                category: 'status.code'
            },
            coordinate: (data) => {
                const { latitude, longitude } = data;
                return { lat: latitude, lon: longitude };
            }
        },
        style: {
            marker: (data) => {
                const angle = data.heading;
                return {
                    className: 'flights',
                    svg: {
                        shape: {
                            type: 'path',
                            fill: {
                                color: '#000000'
                            },
                            path: 'M497.25,357v-51l-204-127.5V38.25C293.25,17.85,275.4,0,255,0c-20.4,0-38.25,17.85-38.25,38.25V178.5L12.75,306v51 l204-63.75V433.5l-51,38.25V510L255,484.5l89.25,25.5v-38.25l-51-38.25V293.25L497.25,357z',
                            transform: 'rotate(' + angle + ',255,255)'
                        },
                        viewBox: '0 0 510 510'
                    },
                    size: [24, 24]
                };
            }
        }
    };
}

Refer to the layer styling documentation for more information and examples of custom styling.

Creating a GeoJsonSource

Setting up a GeoJSON source is similar setting up a vector source except that your data must be provided in a valid GeoJSON format. When rendering a GeoJSON source on the map, the geometries used in the GeoJSON data determines what is rendered on the map:

GeometryMap Object
Point, MultiPointRendered as a marker or series of markers.
LineString, MultiLineStringRendered as a polyline or series of polylines.
Polygon, MultiPolygonRendered as a closed polygon or series of polygons.

You can then style the elements in the same way you would a vector source. All data defined under each feature's properties object will be associated with the map element and will be provided on add, remove and click events.

The following example uses a static GeoJSON dataset for the GeoJSON source and configures a custom style for the various map elements that will get rendered:

source() {
    return {
        type: 'geojson',
        data: {
            items: {
                type: 'FeatureCollection',
                features: [{
                    geometry: {
                        type: 'Point',
                        coordinates: [
                            -104.9998241,
                            39.7471494
                        ]
                    },
                    type: 'Feature',
                    properties: {
                        popupContent: 'This is a B-Cycle Station. Come pick up a bike and pay by the hour. What a deal!'
                    },
                    id: 51
                },{
                    type: 'Feature',
                    geometry: {
                        type: 'LineString',
                        coordinates: [
                            [-104.99820470809937, 39.74979664004068],
                            [-104.98689651489258, 39.741052354709055]
                        ]
                    },
                    properties: {
                        popupContent: 'This is a free bus line that will take you across downtown.',
                        underConstruction: false
                    },
                    id: 3
                }]
            }
        },
        style: {
            marker: (data) => {
                return {
                    svg: {
                        shape: {
                            type: 'circle',
                            fill: {
                                color: '#ff0000'
                            },
                            stroke: {
                                color: '#ffffff',
                                width: 2
                            }
                        }
                    },
                    size: [16, 16]
                };
            },
            polygon: {
                fill: {
                    color: '#ff0000',
                    opacity: 0.7
                }
            },
            polyline: {
                stroke: {
                    color: '#ff0000',
                    width: 3
                }
            }
        }
    };
}

Creating a TextSource

A text source is also similar to a vector source except that it will only render data as markers on the map. You will typically only want to use a text source for point data that changes over time, such as observation or forecast information.

The data configuration for a text source is the same as that for a vector source, meaning you'll need to set up your request and data property mapping in order for your data to be rendered properly on the map. The primary difference with a text source is that you must provide the desired key path for the value property you want to display for each model.

The following configuration would render current temperatures in degrees Fahrenheit from an array of station observations:

source() {
    return {
        type: 'text',
        data: {
            request: {
                endpoint: 'observations',
                parameters: {
                    filter: 'allstations'
                }
            },
            properties: {
                timestamp: 'ob.timestamp',
                value: 'ob.tempF'
            }
        }
    };
}

If your data source contains different values across time, such as current temperatures or wind speeds from observations, each model object in your dataset can provide an array of values to display across time which is then displayed while animating the map.

For instance, the following text source configuration would display the observed temperature across time when animating. Notice that the source uses the observations/archive (opens in a new tab) endpoint instead of the observations (opens in a new tab) endpoint since data will need to be requested for a time range for the animation:

source() {
    return {
        type: 'text',
        data: {
            request: {
                endpoint: 'observations/archive',
                parameters: {
                    filter: 'allstations'
                }
            },
            properties: {
                timestamp: 'periods.ob.timestamp',
                value: 'periods.ob.tempF'
            }
        }
    };
}

The data for the entire time range would be requested when an animation is started and the data would then need to return an array of value objects, where each object provides both the value to display and the timestamp for that value. This information would still need to be properly mapped to your text source using the data.properties object in your source's configuration options.