package com.nutiteq.kml; import java.io.IOException; import java.util.Enumeration; import java.util.Vector; import javax.microedition.lcdui.Image; import org.json.me.JSONArray; import org.json.me.JSONException; import org.json.me.JSONObject; import com.java4ever.apime.io.GZIP; import com.nutiteq.cache.Cache; import com.nutiteq.components.ExtendedDataMap; import com.nutiteq.components.KmlPlace; import com.nutiteq.components.Line; import com.nutiteq.components.OnMapElement; import com.nutiteq.components.Place; import com.nutiteq.components.Polygon; import com.nutiteq.components.WgsPoint; import com.nutiteq.io.ResourceDataWaiter; import com.nutiteq.io.ResourceRequestor; import com.nutiteq.log.Log; /** * Creates KML Elements from JSON data. Specific for CloudMade geocoder * @author Jaak Laineste * */ public class JsonKmlReader implements ResourceRequestor, ResourceDataWaiter { private static Image defaultKmlIcon = Image.createImage(18, 18); private final KmlService service; private final KmlElementsWaiter servicesHandler; private final String serviceUrl; private int placeCount; private static final String DEFAULT_KML_ICON = "/images/def_kml.png"; public JsonKmlReader(final KmlElementsWaiter servicesHandler, final KmlService service, final String serviceUrl){ this(servicesHandler, service, serviceUrl, DEFAULT_KML_ICON); } public JsonKmlReader(final KmlElementsWaiter servicesHandler, final KmlService service, final String serviceUrl, final String defaultIcon) { this.servicesHandler = servicesHandler; this.service = service; this.serviceUrl = serviceUrl; try { defaultKmlIcon = Image.createImage(defaultIcon); } catch (final IOException e) { defaultKmlIcon = Image.createImage(18, 18); } } public String resourcePath() { return serviceUrl; } public void dataRetrieved(final byte[] data) { boolean guessPacked = false; if(data[0]==31 && data[1]==-117){ guessPacked=true; Log.debug("Guess response packed based on first 2 data bytes"); }else{ Log.debug("Response NOT packed based on first 2 data bytes"); } // Log.debug("KmlReader.read() response packed ? " + responsePacked + ". Bytes:"+data.length); final byte[] finalData = guessPacked ? GZIP.inflate(data) : data; // remove BOM (ie the first 3 bytes) if(finalData[0]==0xef && finalData[1]==0xbb && finalData[2]==0xbf){ System.arraycopy(finalData, 3, finalData, 0, finalData.length-3); } try { servicesHandler.addKmlPlaces(service, read(finalData, service.maxResults())); } catch (final IOException e) { Log.printStackTrace(e); notifyError(); } finally { } Log.debug("JsonKmlReader done, read places: "+placeCount); } public void notifyError() { //TODO jaanus : ignore? } protected KmlPlace[] read(final byte[] finalData, final int maxResults) throws IOException { final Vector places = new Vector(); // read JSON and create KmlPlace from it. String jsonString = new String(finalData,"UTF8"); ExtendedDataMap xdata = new ExtendedDataMap(); try { JSONObject root = new JSONObject(jsonString); int found = root.getInt("found"); // Log.debug(root.toString(2)); Log.debug("found "+found+" objects"); JSONArray features = root.getJSONArray("features"); int size = features.length(); for (int i=0;i<size;i++){ final Vector geomElements = new Vector(); JSONObject feature = (JSONObject) features.get(i); JSONObject location = feature.optJSONObject("location"); String country= new String(); String county = new String(); String city = new String(); if (location != null){ country = location.optString("country"); county = location.optString("county"); city = location.optString("city"); } JSONObject properties = feature.optJSONObject("properties"); String name = properties.optString("name"); JSONObject centroid = feature.optJSONObject("centroid"); JSONArray coordinates = centroid.optJSONArray("coordinates"); String latS = coordinates.getString(0); String lngS = coordinates.getString(1); double lat = Double.parseDouble(latS); double lng = Double.parseDouble(lngS); // read geometry JSONObject geometry = feature.optJSONObject("geometry"); String geomType = geometry.getString("type"); if (geomType.equals("POLYGON")){ JSONArray geomCoords = geometry.getJSONArray("coordinates"); Polygon polygon = new Polygon(getCoords(geomCoords)); geomElements.addElement(polygon); } if (geomType.equals("LINESTRING")){ JSONArray geomCoords = geometry.getJSONArray("coordinates"); Line polyline = new Line(getCoords(geomCoords)); geomElements.addElement(polyline); } if (geomType.equals("MULTIPOLYGON")){ JSONArray geomCoords = geometry.getJSONArray("coordinates"); int nPolygons = geomCoords.length(); for(int j=0;j<nPolygons;j++){ Polygon polygon = new Polygon(getCoords(geomCoords.getJSONArray(j))); geomElements.addElement(polygon); } } if (geomType.equals("MULTILINESTRING")){ JSONArray geomCoords = geometry.getJSONArray("coordinates"); int nLinestrings = geomCoords.length(); for(int j=0;j<nLinestrings;j++){ Line line = new Line(getCoords(geomCoords.getJSONArray(j))); geomElements.addElement(line); } } for (Enumeration e = properties.keys() ; e.hasMoreElements() ;) { String key = (String) e.nextElement(); String val = properties.getString(key); xdata.addPair(key, val); } xdata.addPair("country", country); xdata.addPair("county", county); xdata.addPair("city", city); // create KML object Place kmlPlace = new Place(0, name, defaultKmlIcon, new WgsPoint(lng,lat)); final OnMapElement[] elements = new OnMapElement[geomElements.size()]; geomElements.copyInto(elements); String desc = new String(""); if (location != null){ desc = "in "; if(!city.equals("")){ desc += city+", "; } if(!county.equals("")){ desc += county+", "; } if(!country.equals("")){ desc += country; } } kmlPlace.setOnMapElements(elements); KmlPlace place = new KmlPlace(kmlPlace, null, desc, null, null, xdata); places.addElement(place); } // for each feature found } catch (Exception e) { e.printStackTrace(); notifyError(); } // TODO now some dummy place for testing final KmlPlace[] result = new KmlPlace[places.size()]; places.copyInto(result); placeCount = places.size(); return result; } private WgsPoint[] getCoords(JSONArray geomCoords) { Vector coordsV = new Vector(); JSONArray geomCoords1=geomCoords; try { if (geomCoords.getJSONArray(0).optJSONArray(0) != null) { geomCoords1 = geomCoords.getJSONArray(0); // there is extra level int geomCoordsSize = geomCoords1.length(); for (int j = 0; j < geomCoordsSize; j++) { JSONArray geomCoordPair = geomCoords1.getJSONArray(j); String gLatS = geomCoordPair.getString(0); String gLngS = geomCoordPair.getString(1); double gLat = Double.parseDouble(gLatS); double gLng = Double.parseDouble(gLngS); WgsPoint point = new WgsPoint(gLng, gLat); coordsV.addElement(point); } } } catch (JSONException e) { Log.error("JSON parsing exception"); e.printStackTrace(); } WgsPoint[] coords = new WgsPoint[coordsV.size()]; coordsV.copyInto(coords); return coords; } public int getCachingLevel() { return Cache.CACHE_LEVEL_NONE; } }