MapsGL Android - Custom Data Sources
Data sources are responsible for loading and providing the necessary data for a map layer. Because sources are created and cached separately from their layers, one source can back multiple layers with different styles. A layer must reference a source id for that data to be drawn. Sources are configured with a source descriptor (a concrete type of SourceDescriptor) and registered on the map with MapController.addSource — in this SDK, descriptors live under com.xweather.mapsgl.sources.source.spec (Kotlin can also import the same types via com.xweather.mapsgl.sources typealiases).
The following source descriptors are supported. See the linked API reference for fields and defaults:
| Descriptor | Description |
|---|---|
ImageSourceDescriptor | Raster tile sources for image-based layers. API docs. |
EncodedSourceDescriptor | Encoded raster tile sources (binary / weather pipeline). API docs. |
VectorSourceDescriptor | Vector tiles (Mapbox Vector Tile format). API docs. |
GeoJSONSourceDescriptor | GeoJSON-backed sources (remote URL or in-memory data on the runtime source). API docs. |
Adding Data Sources
Register a source that matches the data you want to draw before adding layers that reference it.
Vector tiles (Kotlin)
Tile URLs are strings (not java.net.URL on the descriptor). Templates support {z}, {x}, {y}, and {s} (subdomain index). When authenticator on the descriptor is null, MapController.addSource supplies WeatherService.authenticator from the controller so path templates such as [CLIENT_ID]_[CLIENT_SECRET] or {accessKey} are expanded on each request.
import com.xweather.mapsgl.map.MapController
import com.xweather.mapsgl.sources.source.spec.VectorSourceDescriptor
val alertsSource = VectorSourceDescriptor(id = "alerts").apply {
url =
"https://maps{s}.aerisapi.com/[CLIENT_ID]_[CLIENT_SECRET]/alerts/{z}/{x}/{y}/0.pbf"
}
val source = controller.addSource(alertsSource)VectorSourceDescriptor is used because the data is MVT. The id is the source id layers will reference.
To limit fetch zoom, set minZoom / maxZoom on the descriptor (floats):
val alertsSource = VectorSourceDescriptor(id = "alerts").apply {
url =
"https://maps{s}.aerisapi.com/[CLIENT_ID]_[CLIENT_SECRET]/alerts/{z}/{x}/{y}/0.pbf"
minZoom = 4f
maxZoom = 8f
}
val source = controller.addSource(alertsSource)To change the tile URL template after the source exists, cast the runtime source to VectorTileSource and assign tileURL (from the shared tile base type):
// `source` is the DataSource returned from addSource
(source as? com.xweather.mapsgl.sources.VectorTileSource)?.let {
it.tileURL =
"https://maps{s}.aerisapi.com/[CLIENT_ID]_[CLIENT_SECRET]/alerts/{z}/{x}/{y}/20220506111000.pbf"
}
// or fetch by id
(controller.getSource("alerts") as? com.xweather.mapsgl.sources.VectorTileSource)?.let {
it.tileURL =
"https://maps{s}.aerisapi.com/[CLIENT_ID]_[CLIENT_SECRET]/alerts/{z}/{x}/{y}/20220506111000.pbf"
}Raster (ImageSourceDescriptor / ImageTileSource), vector (VectorSourceDescriptor / VectorTileSource), and encoded (EncodedSourceDescriptor / XweatherEncodedTileSource) follow the same general pattern: configure the descriptor, call addSource, then optionally adjust tileURL on the concrete tile source.
GeoJSON
GeoJSONSourceDescriptor carries URL-related fields used when the map creates a GeoJSONSource. For a remote dataset (example: earthquakes API with format=geojson):
import com.xweather.mapsgl.sources.source.spec.GeoJSONSourceDescriptor
val earthquakesSource = GeoJSONSourceDescriptor(id = "earthquakes").apply {
url =
"https://data.api.xweather.com/earthquakes/search?query=mag:1&limit=200&format=geojson&client_id=[CLIENT_ID]&client_secret=[CLIENT_SECRET]"
}
val source = controller.addSource(earthquakesSource)The descriptor does not currently expose an inline JSON property. For static GeoJSON, register the source then assign a Mapbox FeatureCollection on the runtime object:
import com.mapbox.geojson.FeatureCollection
import com.xweather.mapsgl.sources.GeoJSONSource
import com.xweather.mapsgl.sources.source.spec.GeoJSONSourceDescriptor
val 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
}
}
}]
}
""".trimIndent()
controller.addSource(GeoJSONSourceDescriptor(id = "earthquakes"))
val geoSource = controller.getSource("earthquakes") as GeoJSONSource
geoSource.data = FeatureCollection.fromJson(geoJSONString)To update URL or in-memory data later, mutate the GeoJSONSource instance (there are no setURL / setData helpers; use url / data properties):
import com.mapbox.geojson.FeatureCollection
import com.xweather.mapsgl.sources.GeoJSONSource
val geoSource = controller.getSource("earthquakes") as GeoJSONSource
geoSource.url =
"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]"
val 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
}
}
}]
}
""".trimIndent()
geoSource.data = FeatureCollection.fromJson(geoJSONString)addSource returns DataSource (common supertype). Use safe casts as shown when you need tile- or GeoJSON-specific APIs.
After sources are registered, add layers that reference each source id.
controller.addSource(GeoJSONSourceDescriptor(id = "earthquakes"))
val geoSource = controller.getSource("earthquakes") as GeoJSONSource
geoSource.data = FeatureCollection.fromJson(geoJSONString)
controller.addLayer(
CircleLayerDescriptor(
id = "earthquake-circles",
source = "earthquakes",
sourceLayer = null,
),
beforeID = null,
)Removing Data Sources
Pass the same source id you used when adding the source:
controller.removeSource("earthquakes")If that id is registered, it is removed from the controller and listeners are notified; associated caches are dropped as part of source teardown.