10

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.

Sujal
  • 1,447
  • 19
  • 34
  • 1
    can you look into GSON library from goole which has streaming capabilities to load large amount json without affecting memory too much . https://github.com/google/gson . quick example can be found here https://www.mkyong.com/java/gson-streaming-to-read-and-write-json/ – Tixeon Feb 16 '17 at 06:43
  • @Sanny I looked into using Gson (https://sites.google.com/site/gson/gson-user-guide) but I am having trouble parsing my geojson file. I have added a sample of my geojson file in my question. Also i am not sure how can i show this on android google maps after deserialization. – Sujal Feb 17 '17 at 04:36

3 Answers3

5

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.

Sujal
  • 1,447
  • 19
  • 34
  • i have very large geojson, then crash when rendering in both ios and android – famfamfam Sep 12 '18 at 05:16
  • Hi. the data is loading from server and need to convert to geojson format, so I dont know how to resize it. Can you take a look for a format here: https://pastebin.com/VxSYqvum – famfamfam Sep 12 '18 at 06:20
  • In that case, maybe your server could send you the optimum features to be displayed in the map, so that you don't have to resize it. For creating the geojson, you could try https://github.com/cocoahero/android-geojson – Sujal Sep 12 '18 at 09:31
1

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.

Tixeon
  • 930
  • 1
  • 12
  • 27
  • I have used another way as mentioned in the edits in the question. I will try your deserialization code too. @Sanny – Sujal Feb 17 '17 at 15:28
0

You should use json streaming either with Gson or Jackson.For details have a look at Jackson Vs. Gson, Streaming JSON Parsing Performance Test: Jackson vs. GSON or JSON.simple vs GSON vs Jackson vs JSONP

Community
  • 1
  • 1
Haris Qurashi
  • 2,104
  • 1
  • 13
  • 28
  • @harris Could you please tell me if the way Im using async task (edits in question) is good way? What would be the efficient way for my case? Maybe deserialization would require additional processing for showing in the map. – Sujal Feb 17 '17 at 15:33
  • can u provide way to use in swift 4? Thanks – famfamfam Sep 12 '18 at 06:23