package com.mapbox.mapboxsdk.util; import android.content.Context; import android.graphics.Paint; import android.text.TextUtils; import android.util.Log; import com.cocoahero.android.geojson.Feature; import com.cocoahero.android.geojson.FeatureCollection; import com.cocoahero.android.geojson.GeoJSON; import com.cocoahero.android.geojson.LineString; import com.cocoahero.android.geojson.MultiLineString; import com.cocoahero.android.geojson.MultiPoint; import com.cocoahero.android.geojson.MultiPolygon; import com.cocoahero.android.geojson.Point; import com.cocoahero.android.geojson.Polygon; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.overlay.Icon; import com.mapbox.mapboxsdk.overlay.Marker; import com.mapbox.mapboxsdk.overlay.PathOverlay; import com.mapbox.mapboxsdk.util.constants.UtilConstants; import org.json.JSONArray; import org.json.JSONException; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Locale; public class DataLoadingUtils { /** * Load GeoJSON from URL (in synchronous manner) and return GeoJSON FeatureCollection * @param url URL of GeoJSON data * @return Remote GeoJSON parsed into Library objects * @throws IOException * @throws JSONException */ public static FeatureCollection loadGeoJSONFromUrl(final String url) throws IOException, JSONException { if (TextUtils.isEmpty(url)) { throw new NullPointerException("No GeoJSON URL passed in."); } if (UtilConstants.DEBUGMODE) { Log.d(DataLoadingUtils.class.getCanonicalName(), "Mapbox SDK downloading GeoJSON URL: " + url); } InputStream is; if (url.toLowerCase(Locale.US).indexOf("http") == 0) { is = NetworkUtils.getHttpURLConnection(new URL(url)).getInputStream(); } else { is = new URL(url).openStream(); } BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8"))); String jsonText = readAll(rd); FeatureCollection parsed = (FeatureCollection) GeoJSON.parse(jsonText); if (UtilConstants.DEBUGMODE) { Log.d(DataLoadingUtils.class.getCanonicalName(), "Parsed GeoJSON with " + parsed.getFeatures().size() + " features."); } return parsed; } /** * Load GeoJSON from URL (in synchronous manner) and return GeoJSON FeatureCollection * @param context Application's Context * @param fileName Name of file in assets directory * @return Local GeoJSON file parsed into Library objects * @throws IOException * @throws JSONException */ public static FeatureCollection loadGeoJSONFromAssets(final Context context, final String fileName) throws IOException, JSONException { if (TextUtils.isEmpty(fileName)) { throw new NullPointerException("No GeoJSON File Name passed in."); } if (UtilConstants.DEBUGMODE) { Log.d(DataLoadingUtils.class.getCanonicalName(), "Mapbox SDK loading GeoJSON URL: " + fileName); } InputStream is = context.getAssets().open(fileName); BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8"))); String jsonText = readAll(rd); FeatureCollection parsed = (FeatureCollection) GeoJSON.parse(jsonText); if (UtilConstants.DEBUGMODE) { Log.d(DataLoadingUtils.class.getCanonicalName(), "Parsed GeoJSON with " + parsed.getFeatures().size() + " features."); } return parsed; } public static String readAll(Reader rd) throws IOException { StringBuilder sb = new StringBuilder(); int cp; while ((cp = rd.read()) != -1) { sb.append((char) cp); } return sb.toString(); } /** * Converts GeoJSON objects into Mapbox SDK UI Objects * @param featureCollection Parsed GeoJSON Objects * @param markerIcon Optional Icon to use for markers * @return Collection of Mapbox SDK UI Objects * @throws JSONException */ public static ArrayList<Object> createUIObjectsFromGeoJSONObjects(final FeatureCollection featureCollection, final Icon markerIcon) throws JSONException { ArrayList<Object> uiObjects = new ArrayList<Object>(); for (Feature f : featureCollection.getFeatures()) { // Parse Into UI Objections int j; if (f.getGeometry() instanceof Point) { JSONArray coordinates = (JSONArray) f.getGeometry().toJSON().get("coordinates"); double lon = (Double) coordinates.get(0); double lat = (Double) coordinates.get(1); Marker marker = new Marker(f.getProperties().optString("title"), f.getProperties().optString("description"), new LatLng(lat, lon)); if (markerIcon != null) { marker.setIcon(markerIcon); } uiObjects.add(marker); } else if (f.getGeometry() instanceof MultiPoint) { JSONArray points = (JSONArray) f.getGeometry().toJSON().get("coordinates"); for (j = 0; j < points.length(); j++) { JSONArray coordinates = (JSONArray) points.get(j); double lon = (Double) coordinates.get(0); double lat = (Double) coordinates.get(1); Marker marker = new Marker(f.getProperties().optString("title"), f.getProperties().optString("description"), new LatLng(lat, lon)); if (markerIcon != null) { marker.setIcon(markerIcon); } uiObjects.add(marker); } } else if (f.getGeometry() instanceof LineString) { PathOverlay path = new PathOverlay(); JSONArray points = (JSONArray) f.getGeometry().toJSON().get("coordinates"); JSONArray coordinates; for (j = 0; j < points.length(); j++) { coordinates = (JSONArray) points.get(j); double lon = (Double) coordinates.get(0); double lat = (Double) coordinates.get(1); path.addPoint(new LatLng(lat, lon)); } uiObjects.add(path); } else if (f.getGeometry() instanceof MultiLineString) { JSONArray lines = (JSONArray) f.getGeometry().toJSON().get("coordinates"); for (int k = 0; k < lines.length(); k++) { PathOverlay path = new PathOverlay(); JSONArray points = (JSONArray) lines.get(k); JSONArray coordinates; for (j = 0; j < points.length(); j++) { coordinates = (JSONArray) points.get(j); double lon = (Double) coordinates.get(0); double lat = (Double) coordinates.get(1); path.addPoint(new LatLng(lat, lon)); } uiObjects.add(path); } } else if (f.getGeometry() instanceof Polygon) { PathOverlay path = new PathOverlay(); path.getPaint().setStyle(Paint.Style.FILL); JSONArray points = (JSONArray) f.getGeometry().toJSON().get("coordinates"); for (int r = 0; r < points.length(); r++) { JSONArray ring = (JSONArray) points.get(r); JSONArray coordinates; // we re-wind inner rings of GeoJSON polygons in order // to render them as transparent in the canvas layer. // first ring should have windingOrder = true, // all others should have winding order == false if ((r == 0 && !windingOrder(ring)) || (r != 0 && windingOrder(ring))) { for (j = 0; j < ring.length(); j++) { coordinates = (JSONArray) ring.get(j); double lon = (Double) coordinates.get(0); double lat = (Double) coordinates.get(1); path.addPoint(new LatLng(lat, lon)); } } else { for (j = ring.length() - 1; j >= 0; j--) { coordinates = (JSONArray) ring.get(j); double lon = (Double) coordinates.get(0); double lat = (Double) coordinates.get(1); path.addPoint(new LatLng(lat, lon)); } } uiObjects.add(path); } } else if (f.getGeometry() instanceof MultiPolygon) { PathOverlay path = new PathOverlay(); path.getPaint().setStyle(Paint.Style.FILL); JSONArray polygons = (JSONArray) f.getGeometry().toJSON().get("coordinates"); for (int p = 0; p < polygons.length(); p++) { JSONArray points = (JSONArray) polygons.get(p); for (int r = 0; r < points.length(); r++) { JSONArray ring = (JSONArray) points.get(r); JSONArray coordinates; // we re-wind inner rings of GeoJSON polygons in order // to render them as transparent in the canvas layer. // first ring should have windingOrder = true, // all others should have winding order == false if ((r == 0 && !windingOrder(ring)) || (r != 0 && windingOrder(ring))) { for (j = 0; j < ring.length(); j++) { coordinates = (JSONArray) ring.get(j); double lon = (Double) coordinates.get(0); double lat = (Double) coordinates.get(1); path.addPoint(new LatLng(lat, lon)); } } else { for (j = ring.length() - 1; j >= 0; j--) { coordinates = (JSONArray) ring.get(j); double lon = (Double) coordinates.get(0); double lat = (Double) coordinates.get(1); path.addPoint(new LatLng(lat, lon)); } } uiObjects.add(path); } } } } return uiObjects; } private static boolean windingOrder(JSONArray ring) throws JSONException { float area = 0; if (ring.length() > 2) { for (int i = 0; i < ring.length() - 1; i++) { JSONArray p1 = (JSONArray) ring.get(i); JSONArray p2 = (JSONArray) ring.get(i + 1); area += rad((Double) p2.get(0) - (Double) p1.get(0)) * (2 + Math.sin( rad((Double) p1.get(1))) + Math.sin(rad((Double) p2.get(1)))); } } return area > 0; } private static double rad(double _) { return _ * Math.PI / 180f; } }