Load GeoJSON from a URL
This tutorial shows how to load GeoJSON data from a remote URL and display it on your MapMetrics Android map — useful for loading dynamic datasets, APIs, and external data feeds.
Prerequisites
- Completed the Getting Started Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Load GeoJSON from URL
Load and display a remote GeoJSON file:
kotlin
import android.graphics.Color
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.MapMetricsMap
import org.maplibre.android.maps.Style
import org.maplibre.android.style.layers.CircleLayer
import org.maplibre.android.style.layers.PropertyFactory.*
import org.maplibre.android.style.sources.GeoJsonSource
import java.net.URI
class GeoJsonUrlActivity : AppCompatActivity() {
private lateinit var mapView: MapView
private lateinit var map: MapMetricsMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync { mapMetricsMap ->
map = mapMetricsMap
map.setStyle(
Style.Builder().fromUri(
"https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY"
)
) { style ->
loadGeoJsonFromUrl(style)
}
}
}
private fun loadGeoJsonFromUrl(style: Style) {
// Load GeoJSON directly from a URL
val geoJsonUrl = "https://gateway.mapmetrics.org/assets/earthquakes.geojson"
style.addSource(
GeoJsonSource("remote-data", URI(geoJsonUrl))
)
// Visualize as colored circles
style.addLayer(
CircleLayer("data-layer", "remote-data")
.withProperties(
circleRadius(6f),
circleColor(Color.parseColor("#FF6B35")),
circleStrokeColor(Color.WHITE),
circleStrokeWidth(1f)
)
)
map.cameraPosition = CameraPosition.Builder()
.target(LatLng(20.0, 0.0))
.zoom(2.0)
.build()
}
override fun onStart() { super.onStart(); mapView.onStart() }
override fun onResume() { super.onResume(); mapView.onResume() }
override fun onPause() { super.onPause(); mapView.onPause() }
override fun onStop() { super.onStop(); mapView.onStop() }
override fun onDestroy() { super.onDestroy(); mapView.onDestroy() }
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
mapView.onSaveInstanceState(outState)
}
}Load from an API with Coroutines
Fetch JSON from a REST API, then display:
kotlin
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.maplibre.geojson.FeatureCollection
import java.net.HttpURLConnection
import java.net.URL
private fun loadFromApi(style: Style) {
// Add empty source first
val source = GeoJsonSource("api-data")
style.addSource(source)
// Add layer
style.addLayer(
CircleLayer("api-layer", "api-data")
.withProperties(
circleRadius(5f),
circleColor(Color.parseColor("#4285F4")),
circleStrokeColor(Color.WHITE),
circleStrokeWidth(1f)
)
)
// Fetch data asynchronously
lifecycleScope.launch {
val geoJson = withContext(Dispatchers.IO) {
fetchGeoJson("https://api.example.com/data.geojson")
}
geoJson?.let {
val featureCollection = FeatureCollection.fromJson(it)
source.setGeoJson(featureCollection)
}
}
}
private fun fetchGeoJson(urlString: String): String? {
return try {
val url = URL(urlString)
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "GET"
connection.connectTimeout = 10000
connection.readTimeout = 10000
if (connection.responseCode == HttpURLConnection.HTTP_OK) {
connection.inputStream.bufferedReader().readText()
} else null
} catch (e: Exception) {
e.printStackTrace()
null
}
}Load from Local Assets
Load a GeoJSON file bundled in assets/:
kotlin
private fun loadFromAssets(style: Style) {
// Read from assets/data/places.geojson
val json = assets.open("data/places.geojson")
.bufferedReader()
.readText()
val featureCollection = FeatureCollection.fromJson(json)
style.addSource(GeoJsonSource("local-data", featureCollection))
style.addLayer(
CircleLayer("local-layer", "local-data")
.withProperties(
circleRadius(6f),
circleColor(Color.parseColor("#34A853"))
)
)
}Refresh Data Periodically
Update GeoJSON data on a timer:
kotlin
import android.os.Handler
import android.os.Looper
private val refreshHandler = Handler(Looper.getMainLooper())
private val refreshInterval = 30000L // 30 seconds
private fun startAutoRefresh(style: Style) {
val source = style.getSource("remote-data") as? GeoJsonSource ?: return
val refreshRunnable = object : Runnable {
override fun run() {
lifecycleScope.launch {
val json = withContext(Dispatchers.IO) {
fetchGeoJson("https://api.example.com/live-data.geojson")
}
json?.let {
source.setGeoJson(FeatureCollection.fromJson(it))
}
}
refreshHandler.postDelayed(this, refreshInterval)
}
}
refreshHandler.postDelayed(refreshRunnable, refreshInterval)
}
override fun onDestroy() {
refreshHandler.removeCallbacksAndMessages(null)
super.onDestroy()
mapView.onDestroy()
}GeoJSON Source Options
| Constructor | Description |
|---|---|
GeoJsonSource(id, URI) | Load from URL (auto-fetches) |
GeoJsonSource(id, FeatureCollection) | Load from parsed data |
GeoJsonSource(id, String) | Load from raw JSON string |
GeoJsonSource(id, Geometry) | Load single geometry |
GeoJsonSource(id, URI, GeoJsonOptions) | URL with clustering options |
Next Steps
- GeoJSON Guide — GeoJSON fundamentals
- Multiple Sources — Combining multiple data sources
- Filter Features — Filter loaded data
Tip: GeoJsonSource(id, URI) handles network fetching automatically on a background thread. For simple static URLs, this is the easiest approach. Use the coroutine method when you need custom headers, authentication, or error handling.