Show a GeoJson layer on Google Maps/OSM/Mapbox with Android SDK

The GeoJson format

GeoJson is a relatively new open standard format (GeoJSON as RFC 7946were released in August 2016) designed to represent simple geographical features. It’s simple, human-readable and based on JSON.

Working with GeoJson layers is extremely simple, you can even use an online editor to create, edit and download new layers. But what about viewing GeoJson files on your Android device?

Let’s see how to render GeoJson layers on the top of an existing map. In this example, we’ll use 3 technologies: Google Maps, Open Street Map and Rambox.

Google Maps

Maps offers GeoJson support directly in the Android SDK, it’s in fact the easiest service to use: implementing the feature is just one code line away.

I’ll assume you’ve already added Google Maps dependence in your build.gradle and already created an Activity with a Google Maps layer in it (read here how to get started).

You can create a new GeoJson Layer with:

GeoJsonLayer layer = new GeoJsonLayer(map, geoJsonData);

where map is the map you want to render the layer on and geoJsonData a JSONObject containing the data.

You can even open directly a .geojson file using:

GeoJsonLayer layer = new GeoJsonLayer(map, R.raw.geoJsonFile,
context);

Then, you just have to call this method to pass it to a map:

layer.addLayerToMap();

Here you can read more about GeoJson support on Google Maps and how to customize elements and style.

Open Street Map

Again, I’m assuming you added osmdroid as dependence, declared all the needed permissions in Manifest and created a proper Activity with a map layer (read here).

Adding a geojson layer here is a bit more difficult. I suggest you to use Osm Bonus pack library, that adds useful object to a standard osm implementation:

Add the jitpack repo in your top level build.gradle:

repositories { 
     ...
     maven { url "https://jitpack.io" }
}

Then in your app level build.gradle add the dependency:

dependencies {
    ...
    compile 'com.github.MKergall:osmbonuspack:6.3'
}

You need to create a KmlDocument object and then parse the geojson string with it.

KmlDocument kmlDocument = new KmlDocument();
kmlDocument.parseGeoJSON(geoJsonString);

Where of course geoJsonString is a string containing your GeoJson.

Then you can customize some aspects of the layer like the style of the marker or the lines:

Drawable defaultMarker = getResources().getDrawable(R.drawable.marker_default);
Bitmap defaultBitmap = ((BitmapDrawable) defaultMarker).getBitmap();
Style defaultStyle = new Style(defaultBitmap, 0x901010AA, 5f, 0x20AA1010);
FolderOverlay geoJsonOverlay = (FolderOverlay) kmlDocument.mKmlRoot.buildOverlay(map, defaultStyle, null, kmlDocument);

Here we’re applying the default values. After that, apply the overlay to the map and call invalidate() to refresh and see the new changes.

map.getOverlays().add(geoJsonOverlay);
map.invalidate();

MapBox

Here the situation is a bit more tricky. MapBox doesn’t have a single method to import data from GeoJson, or at least, that’s what I found.

It used to have it though: there’s a class on a MapBox legacy SDK for Android with a method createUIObjectsFromGeoJSONObjects(). Sadly they removed it…

Anyway, in your MapBox Activity (read how to get started here), in your onMapReady method add:

GeoJsonSource source = new GeoJsonSource("geojson", geoJsonString);
mapboxMap.addSource(source);
mapboxMap.addLayer(new LineLayer("geojson", "geojson"));

where geoJsonString is the string with your GeoJson data. The method addLayer will generate and add a new LineLayer that will take all features from the GeoJson file (like lines and polygons) and render them as lines on the map.

Now we may want also to render markers that are defined in GeoJson as Points. To do so, we must parse all GeoJson collections in a FeatureCollection object and for each feature that is a Point, add manually a marker on the map, taking the latitude and longitude coordinates.

FeatureCollection featureCollection = FeatureCollection.fromJson(geoJsonString);
List<Feature> features = featureCollection.getFeatures();
for (Feature f : features) {
if (f.getGeometry() instanceof Point) {
    Position coordinates = (Position) 
    f.getGeometry().getCoordinates();
    map.addMarker(
        new MarkerViewOptions().position(new 
         LatLng(coordinates.getLatitude(),
         coordinates.getLongitude()))
         );
    }
}

So this will display both lines and markers correctly on the map.

You may notice that the LineLayer is aquick hack to quickly draw GeoJson features as lines. You’re free to repeat the second method: for each feature in the list, manually add the corresponding item using MapBox’s SDK.

Convert .geojson into String

All the methods we’ve seen before take as parameter a String with geojson data in it. Use those two methods to convert the content of a .geojson file at a specific URI to a String.

private static String convertStreamToString(InputStream is) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
            sb.append(line).append("n");
        }
        reader.close();
        return sb.toString();
}
public static String getStringFromFile(Uri fileUri, Context context) throws Exception {
        InputStream fin = context.getContentResolver().openInputStream(fileUri);
        String ret = convertStreamToString(fin);
        fin.close();
        return ret;
}

Introducing GeoJson Viewer

Based on this code, we decided to make an Open Source app to simplify the process of viewing geojson files on Android directly on a map.

GeoJson Viewer allows you to pick a .geojson file on your device, select a map (Google Map/OpenStreetMap/MapBox) and see the GeoJson layer in it.

The full source code is available on GitHub while the app is downloadable for free on Google Play and shared under Apache 2.0 Licence.