Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show large geojson file in android google maps

I am trying to show a geojson layer to Google Map. The code is as follows. The geojson file is stored locally in my raw folder. The file has 286 features (around 15MB). Hence, reading this file and displaying it is consuming more time. Initially, I was getting out of memory error, which is removed by setting large heap as true in the manifest file. How can I load this file quickly (currently, its taking a minute or more)? I am wondering if there is some other efficient method to do this. After this, I will also have other tasks of getting features and displaying some properties.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
    mapFragment.getMapAsync(this);
}

@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(myanmar, 5.25f));
    new MyAsyncTask().execute();
}

public class MyAsyncTask extends AsyncTask <Void, Void, Void> {

    ProgressDialog pd;
    GeoJsonLayer layer;

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        pd = new ProgressDialog(MapsActivity.this);
        pd.setMessage("Loading Data");
        pd.show();
    }

    @Override
    protected Void doInBackground(Void... voids) {
        try {
            layer = new GeoJsonLayer(mMap, R.raw.myanmar, getApplicationContext());
        } catch (Exception e) {
            Log.d("Error is : ", e.toString());
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        pd.cancel();
        layer.addLayerToMap();
     }
}

A very small portion of my geojson file is as follows:

{
 "type": "FeatureCollection",
 "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
 "features": [
{ "type": "Feature", "properties": { "ID_3": 193, "NAME_3": "Chaung-U" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 95.348327636718807, 21.878610610961914 ], [ 95.297210693359432, 21.860000610351676 ], [ 95.286926269531307, 21.853612899780387 ], [ 95.276092529296989, 21.850000381469727 ], [ 95.265823364257926, 21.84832954406744 ], [ 95.257217407226676, 21.846940994262695 ] ] ] ] } },
{ "type": "Feature", "properties": { "ID_3": 199, "NAME_3": "Myaung" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 95.376281738281193, 21.708541870117301 ], [ 95.375259399414119, 21.703050613403434 ], [ 95.370529174804801, 21.681390762329102 ], [ 95.367752075195313, 21.664720535278434 ], [ 95.366699218750114, 21.658369064331055 ], [ 95.362762451171875, 21.649999618530273 ] ] ] ] } }
]}

I try to divide the file into multiple small file units and run separate async tasks in parallel, each processing a separate file:

for (int i = 1; i < 3; i++) {
        new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,i);
}

AsyncTask:

public class MyAsyncTask extends AsyncTask <Integer, Void, GeoJsonLayer> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected GeoJsonLayer doInBackground(Integer... integers) {
        int i = integers[0];
        try {
            GeoJsonLayer layer = null;
            switch (i) {
                case 1:
                    layer = new GeoJsonLayer(mMap, R.raw.small_1,
                            getApplicationContext());
                    break;
                case 2:
                    layer = new GeoJsonLayer(mMap, R.raw.small_2,
                            getApplicationContext());
                    break;
                //other cases
            }
            for (GeoJsonFeature feature : layer.getFeatures()) {
                geoJsonFeatures.put(Integer.parseInt(feature.getProperty("ID_3")), feature);
            }
            return layer;
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    protected void onPostExecute(GeoJsonLayer layer) {
        super.onPostExecute(layer);
        layer.addLayerToMap();
    }
}

Is this suggested? Doing so, the time is reduced as compared to earlier, but still not yet fast considering the user experience factor.

like image 260
Sujal Avatar asked Feb 16 '17 06:02

Sujal


2 Answers

Finally, solved the issue by reducing the size of the geojson file. Used http://mapshaper.org/ , an online tool, that provides you with the options to reduce the size, yet preserving the shape. Used it to obtain simplified file (nearly 300KB) and bang, the loading completes within few seconds. Hope this helps someone facing the same issue.

like image 178
Sujal Avatar answered Nov 18 '22 19:11

Sujal


I had put code for how to deserialise geojson code. But i have never tried Google Map before. Maybe you might come up with another question ?

Below i show you how to deserialised your huge geojson file with GSON Streaming.

GeoJSON.java

public class GeoJSON {

    @SerializedName("type")
    private String type;
    @SerializedName("features")
    private List<Features> features;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }


    public List<Features> getFeatures() {
        return features;
    }

    public void setFeatures(List<Features> features) {
        this.features = features;
    }
}

Features.java

public class Features {

    @SerializedName("type")
    private String type;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

Convert json to pojo class

private void readGeoJSON() {
        try {
            InputStream is = getAssets().open("geo.json");

            JsonReader reader = new JsonReader(new InputStreamReader(is, "UTF-8"));
            Gson gson = new GsonBuilder().create();

            GeoJSON geoJSON = new GeoJSON();
            ArrayList<Features> features = new ArrayList<>();

            reader.beginObject();
            while (reader.hasNext()){
                String key = reader.nextName();
                if(key.equals("type")){
                    geoJSON.setType(reader.nextString());
                }else if(key.equals("features")){
                    // read array
                    reader.beginArray();
                    while (reader.hasNext()) {
                        Features o = gson.fromJson(reader, Features.class);
                        features.add(o);
                    }
                    reader.endArray();
                }else{
                    reader.skipValue(); //avoid some unhandle events
                }

                geoJSON.setFeatures(features);
            }

            reader.endObject();
            reader.close();


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

With this approach we are loading all the objects to memory but one by one and not the whole file at once.

Assume i store geo.json file in asset folder. This is how you can deserialised your local geo.json and i had tested the code, it worked. You may need to add in for other fields. Hope it might solve your memory overhead issue.

like image 45
Tixeon Avatar answered Nov 18 '22 20:11

Tixeon