package com.google.maps.android.geojson;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.Polyline;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
/**
* A class that allows the developer to import GeoJSON data, style it and interact with the
* imported data.
*
* To create a new GeoJsonLayer from a resource stored locally
* {@code GeoJsonLayer layer = new GeoJsonLayer(getMap(), R.raw.resource,
* getApplicationContext());}
*
* To render the imported GeoJSON data onto the layer
* {@code layer.addLayerToMap();}
*
* To remove the rendered data from the layer
* {@code layer.removeLayerFromMap();}
*/
public class GeoJsonLayer {
private final GeoJsonRenderer mRenderer;
private LatLngBounds mBoundingBox;
/**
* Creates a new GeoJsonLayer object. Default styles are applied to the GeoJsonFeature objects.
*
* @param map map where the layer is to be rendered
* @param geoJsonFile GeoJSON data to add to the layer
*/
public GeoJsonLayer(GoogleMap map, JSONObject geoJsonFile) {
if (geoJsonFile == null) {
throw new IllegalArgumentException("GeoJSON file cannot be null");
}
mBoundingBox = null;
GeoJsonParser parser = new GeoJsonParser(geoJsonFile);
// Assign GeoJSON bounding box for FeatureCollection
mBoundingBox = parser.getBoundingBox();
HashMap<GeoJsonFeature, Object> geoJsonFeatures = new HashMap<GeoJsonFeature, Object>();
for (GeoJsonFeature feature : parser.getFeatures()) {
geoJsonFeatures.put(feature, null);
}
mRenderer = new GeoJsonRenderer(map, geoJsonFeatures);
}
/**
* Creates a new GeoJsonLayer object. Default styles are applied to the GeoJsonFeature objects.
*
* @param map map where the layer is to be rendered
* @param resourceId GeoJSON file to add to the layer
* @param context context of the application, required to open the GeoJSON file
* @throws IOException if the file cannot be open for read
* @throws JSONException if the JSON file has invalid syntax and cannot be parsed successfully
*/
public GeoJsonLayer(GoogleMap map, int resourceId, Context context)
throws IOException, JSONException {
this(map, createJsonFileObject(context.getResources().openRawResource(resourceId)));
}
/**
* Sets a single click listener for the entire GoogleMap object, that will be called
* with the corresponding GeoJsonFeature object when an object on the map (Polygon,
* Marker, Polyline) is clicked.
*
* Note that if multiple GeoJsonLayer objects are bound to a GoogleMap object, calling
* setOnFeatureClickListener on one will override the listener defined on the other. In
* that case, you must define each of the GoogleMap click listeners manually
* (OnPolygonClickListener, OnMarkerClickListener, OnPolylineClickListener), and then
* use the GeoJsonLayer.getFeature(mapObject) method on each GeoJsonLayer instance to
* determine if the given mapObject belongs to the layer.
*
* @param listener Listener providing the onFeatureClick method to call.
*/
public void setOnFeatureClickListener(final GeoJsonOnFeatureClickListener listener) {
GoogleMap map = getMap();
map.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
@Override
public void onPolygonClick(Polygon polygon) {
listener.onFeatureClick(getFeature(polygon));
}
});
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
listener.onFeatureClick(getFeature(marker));
return false;
}
});
map.setOnPolylineClickListener(new GoogleMap.OnPolylineClickListener() {
@Override
public void onPolylineClick(Polyline polyline) {
listener.onFeatureClick(getFeature(polyline));
}
});
}
/**
* Callback interface for when a GeoJsonLayer's map object is clicked.
*/
public interface GeoJsonOnFeatureClickListener {
void onFeatureClick(GeoJsonFeature feature);
}
/**
* Retrieves a corresponding GeoJsonFeature instance for the given Polygon
* Allows maps with multiple layers to determine which layer the Polygon
* belongs to.
*
* @param polygon Polygon
* @return GeoJsonFeature for the given polygon
*/
public GeoJsonFeature getFeature(Polygon polygon) {
return mRenderer.getFeature(polygon);
}
/**
* Retrieves a corresponding GeoJsonFeature instance for the given Polyline
* Allows maps with multiple layers to determine which layer the Polyline
* belongs to.
*
* @param polyline Polyline
* @return GeoJsonFeature for the given polyline
*/
public GeoJsonFeature getFeature(Polyline polyline) {
return mRenderer.getFeature(polyline);
}
/**
* Retrieves a corresponding GeoJsonFeature instance for the given Marker
* Allows maps with multiple layers to determine which layer the Marker
* belongs to.
*
* @param marker Marker
* @return GeoJsonFeature for the given marker
*/
public GeoJsonFeature getFeature(Marker marker) {
return mRenderer.getFeature(marker);
}
/**
* Takes a character input stream and converts it into a JSONObject
*
* @param stream character input stream representing the GeoJSON file
* @return JSONObject with the GeoJSON data
* @throws IOException if the file cannot be opened for read
* @throws JSONException if the JSON file has poor structure
*/
private static JSONObject createJsonFileObject(InputStream stream)
throws IOException, JSONException {
String line;
StringBuilder result = new StringBuilder();
// Reads from stream
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
try {
// Read each line of the GeoJSON file into a string
while ((line = reader.readLine()) != null) {
result.append(line);
}
} finally {
reader.close();
}
// Converts the result string into a JSONObject
return new JSONObject(result.toString());
}
/**
* Gets an iterable of all GeoJsonFeature elements that have been added to the layer
*
* @return iterable of GeoJsonFeature elements
*/
public Iterable<GeoJsonFeature> getFeatures() {
return mRenderer.getFeatures();
}
/**
* Adds all the GeoJsonFeature objects parsed from the given GeoJSON data onto the map
*/
public void addLayerToMap() {
mRenderer.addLayerToMap();
}
/**
* Adds a GeoJsonFeature to the layer. If the point, linestring or polygon style is set to
* null, the relevant default styles are applied.
*
* @param feature GeoJsonFeature to add to the layer
*/
public void addFeature(GeoJsonFeature feature) {
if (feature == null) {
throw new IllegalArgumentException("Feature cannot be null");
}
mRenderer.addFeature(feature);
}
/**
* Removes the given GeoJsonFeature from the layer
*
* @param feature feature to remove
*/
public void removeFeature(GeoJsonFeature feature) {
if (feature == null) {
throw new IllegalArgumentException("Feature cannot be null");
}
mRenderer.removeFeature(feature);
}
/**
* Gets the map on which the layer is rendered
*
* @return map on which the layer is rendered
*/
public GoogleMap getMap() {
return mRenderer.getMap();
}
/**
* Renders the layer on the given map. The layer on the current map is removed and
* added to the given map.
*
* @param map to render the layer on, if null the layer is cleared from the current map
*/
public void setMap(GoogleMap map) {
mRenderer.setMap(map);
}
/**
* Removes all GeoJsonFeatures on the layer from the map
*/
public void removeLayerFromMap() {
mRenderer.removeLayerFromMap();
}
/**
* Get whether the layer is on the map
*
* @return true if the layer is on the map, false otherwise
*/
public boolean isLayerOnMap() {
return mRenderer.isLayerOnMap();
}
/**
* Gets the default style used to render GeoJsonPoints. Any changes to this style will be
* reflected in the features that use it.
*
* @return default style used to render GeoJsonPoints
*/
public GeoJsonPointStyle getDefaultPointStyle() {
return mRenderer.getDefaultPointStyle();
}
/**
* Gets the default style used to render GeoJsonLineStrings. Any changes to this style will be
* reflected in the features that use it.
*
* @return default style used to render GeoJsonLineStrings
*/
public GeoJsonLineStringStyle getDefaultLineStringStyle() {
return mRenderer.getDefaultLineStringStyle();
}
/**
* Gets the default style used to render GeoJsonPolygons. Any changes to this style will be
* reflected in the features that use it.
*
* @return default style used to render GeoJsonPolygons
*/
public GeoJsonPolygonStyle getDefaultPolygonStyle() {
return mRenderer.getDefaultPolygonStyle();
}
/**
* Gets the LatLngBounds containing the coordinates of the bounding box for the
* FeatureCollection. If the FeatureCollection did not have a bounding box or if the GeoJSON
* file did not contain a FeatureCollection then null will be returned.
*
* @return LatLngBounds containing bounding box of FeatureCollection, null if no bounding box
*/
public LatLngBounds getBoundingBox() {
return mBoundingBox;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Collection{");
sb.append("\n Bounding box=").append(mBoundingBox);
sb.append("\n}\n");
return sb.toString();
}
}