Advanced Usage
Custom Sources

Custom Data Sources

Data sources are responsible for loading and providing the necessary data for a map layer. Since data sources are created and cached apart from their associated layer(s), a single data source can be used to render one or more layers with different styles. A layer must be associated with a data source in order for it to be rendered to the map. Data sources are configured and added to the map controller using the appropriate source descriptor, which is a type of source descriptor that provides the necessary configuration for the data source.

The following data source descriptors are supported. View their API documentation for more information on how to configure them and their available options:

DescriptorDescription
ImageSourceDescriptorA descriptor for raster tile sources used in image-based map layers. API docs.
EncodedSourceDescriptorA descriptor for encoded raster tile sources, often used with vector or binary data. API docs.
VectorSourceDescriptorA descriptor for vector tile sources in the Mapbox Vector Tile (opens in a new tab) format, which is commonly used for vector data layers. API docs.
GeoJSONSourceDescriptorA descriptor for GeoJSON data sources, which can contain a variety of geographic data structures. API docs.

Adding Data Sources

In order to render data layers on your map, you must first add the necessary data source that corresponds to the type of data you want to render.

For instance, you want to render a vector tile source, such as our weather alerts (opens in a new tab) dataset. You would first need to configure and add the source to the map controller:

do {
    var alertsSource = VectorSourceDescriptor(id: "alerts")
    alertsSource.url = URL(string: "https://maps{s}.aerisapi.com/[CLIENT_ID]_[CLIENT_SECRET]/alerts/{z}/{x}/{y}/0.pbf")
    let source = try controller.addSource(alertsSource)
} catch {
    print("Failed to add source: \(error)")
}

The above source configuration provides uses a VectorSourceDescriptor in this case because we're using vector tiles, and the url template string to use when requesting individual tiles. We also assign it a custom identifier as the first argument to addSource, which we're just setting to alerts in this example.

If your dataset only contained data tiles within a certain zoom range or you wanted to limit data requests, you can also provide that information in your configuration as well:

do {
    var alertsSource = VectorSourceDescriptor(id: "alerts")
    alertsSource.url = URL(string: "https://maps{s}.aerisapi.com/[CLIENT_ID]_[CLIENT_SECRET]/alerts/{z}/{x}/{y}/0.pbf")
    alertsSource.zoomRange = 4...8
    let source = try controller.addSource(alertsSource)
} catch {
    print("Failed to add source: \(error)")
}

Then if your application needs to update the tile URL template used for tile requests at a later time, you can use the setTileUrl method on your source instance (for TileSource subclasses, such as VectorTileSource):

// 'source' is the instance returned by `addSource` above
if let source = source as? VectorTileSource {
    source.setTileURL(URL(string: "https://maps{s}.aerisapi.com/[CLIENT_ID]_[CLIENT_SECRET]/alerts/{z}/{x}/{y}/202205061110.pbf"))
}
 
// or, use 'getSource' on your map controller to get the source instance and update its tile url
if let source = controller.getSource(id: "alerts") as? VectorTileSource {
    source.setTileURL(URL(string: "https://maps{s}.aerisapi.com/[CLIENT_ID]_[CLIENT_SECRET]/alerts/{z}/{x}/{y}/202205061110.pbf"))
}

Raster, vector and encoded data sources all work similar to the example above. If you want to render GeoJSON data, then its data source is configured slightly differently since we're not working with map tiles.

For instance, you can configure a GeoJSON source to request data from a remote URL, such as earthquake (opens in a new tab) data from our Weather API (opens in a new tab) and its geojson response format:

do {
    var earthquakesSource = GeoJSONSourceDescriptor(id: "earthquakes")
    earthquakesSource.url = URL(string: "https://data.api.xweather.com/earthquakes/search?query=mag:1&limit=200&format=geojson&client_id=[CLIENT_ID]&client_secret=[CLIENT_SECRET]")
    let source = try mapController.addSource(earthquakesSource)
    
} catch {
    print("Failed to add source: \(error)")
}

Or, you can set the static data directly:

let geoJSONString = """
{
   "type": "FeatureCollection",
   "features": [{
	   "type": "Feature",
	   "geometry": {
		   "type": "Point",
		   "coordinates": [
			   -117.7805,
			   38.1714
		   ]
	   },
	   "properties": {
		   "report": {
			   "id": "nn00838438",
			   "timestamp": 1651863279,
			   "dateTimeISO": "2022-05-06T11:54:39-07:00",
			   "updatedTimestamp": 1651863515,
			   "updatedDateTimeISO": "2022-05-06T11:58:35-07:00",
			   "mag": 1.7,
			   "type": "mini",
			   "depthKM": 1.7,
			   "depthMI": 1.06,
			   "region": "37 km SE of Mina, Nevada",
			   "location": "37 km SE of Mina, Nevada"
		   },
		   "loc": {
			   "long": -117.7805,
			   "lat": 38.1714
		   }
	   }
   }, {
	   "type": "Feature",
	   "geometry": {
		   "type": "Point",
		   "coordinates": [
			   -155.38716125488,
			   19.248332977295
		   ]
	   },
	   "properties": {
		   "report": {
			   "id": "hv73004612",
			   "timestamp": 1651863228,
			   "dateTimeISO": "2022-05-06T08:53:48-10:00",
			   "updatedTimestamp": 1651863409,
			   "updatedDateTimeISO": "2022-05-06T08:56:49-10:00",
			   "mag": 1.83000004,
			   "type": "mini",
			   "depthKM": 32.25,
			   "depthMI": 20.04,
			   "region": "10 km ENE of Pāhala, Hawaii",
			   "location": "10 km ENE of Pāhala, Hawaii"
		   },
		   "loc": {
			   "long": -155.38716125488,
			   "lat": 19.248332977295
		   }
	   }
   }]
}
"""
 
do {
    var earthquakesSource = GeoJSONSourceDescriptor(id: "earthquakes")
    earthquakesSource.data = .string(geoJSONString)
    let source = try mapController.addSource(earthquakesSource)
} catch {
    print("Failed to add source: \(error)")
}

And similar to tile-related data sources, you can also update either the GeoJSON url or data on your source after it has been added to your map as needed:

if let source = mapController.getSource(id: "earthquakes") as? MapsGLMaps.GeoJSONSource {
 
    // update the remote GeoJSON url
    source.setURL(URL(string: "https://data.api.xweather.com/earthquakes/search?query=mag:1&limit=200&from=-7days&to=now&format=geojson&client_id=[CLIENT_ID]&client_secret=[CLIENT_SECRET]"))
    
    // or, update the source data directly
    let geoJSONString = """
{
    "type": "FeatureCollection",
    "features": [{
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": [
                -117.7805,
                38.1714
            ]
        },
        "properties": {
            "report": {
                "id": "nn00838438",
                "timestamp": 1651863279,
                "dateTimeISO": "2022-05-06T11:54:39-07:00",
                "updatedTimestamp": 1651863515,
                "updatedDateTimeISO": "2022-05-06T11:58:35-07:00",
                "mag": 1.7,
                "type": "mini",
                "depthKM": 1.7,
                "depthMI": 1.06,
                "region": "37 km SE of Mina, Nevada",
                "location": "37 km SE of Mina, Nevada"
            },
            "loc": {
                "long": -117.7805,
                "lat": 38.1714
            }
        }
    }]
}
        """
    source.setData(.string(geoJSONString))
}

With your data sources configured and added to the map controller, you can now add your associated layers to render that data on your map. Refer to our getting started documentation on layers for more information about the supported layers, render styles and their configuration.

Removing Data Sources

Removing a data source from your map controller is simple, just provide the identifier you assigned the source when it was added to the map:

controller.removeSource(id: "alerts")

If the source exists on the map, then it will be removed and perform any necessary cache and object disposal.