/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * *********************************************************************************/ package totalcross.map; import totalcross.io.*; import totalcross.net.*; import totalcross.sys.*; import totalcross.util.*; /** Shows a Google Maps viewer on a separate screen. * Pressing back returns to the application. * * Internet connection is required. * * Currently works in Android and iOS only. * * @since TotalCross 1.3 */ public class GoogleMaps { /** Used in the flags argument of showRoute: shows the satellite photos. */ public static final int SHOW_SATELLITE_PHOTOS = 1; /** Used in the flags argument of showRoute: use waze to show the route of the current location to a target * address. Note that the destination address is NOT used. */ public static final int USE_WAZE = 2; /** An abstract class used to group an array of map items. */ public abstract static class MapItem { abstract void serialize(StringBuffer sb); } private static int toCoordI(double v) { return (int)(v * 1e6); } /** A map item that represents a circle. * <pre> GoogleMaps.Circle c = new GoogleMaps.Circle(); c.lat = -3.73243; c.lon = -38.483414; c.color = Color.BLUE; c.filled = false; c.rad = 70; * </pre> */ public static class Circle extends MapItem { /** Center of the circle */ public double lat, lon; /** The radius; if > 0, its computed as meters; if < 0, its computed as delta of the coordinates */ public double rad; /** Set if the item is filled or not */ public boolean filled; /** The item color. Alpha defaults to 255 if not specified. */ public int color; void serialize(StringBuffer sb) { sb.append("*C*,").append(toCoordI(lat)).append(",").append(toCoordI(lon)).append(",").append(rad).append(",").append(filled).append(",").append(color); } } /** A map item that represents a polygon with any shape. * <pre> GoogleMaps.Shape s1 = new GoogleMaps.Shape(); s1.color = Color.YELLOW | 0X88000000; s1.filled = true; s1.lats = new double[]{-3.73143,-3.73243,-3.73193}; s1.lons = new double[]{-38.483424, -38.483524, -38.483124}; * </pre> */ public static class Shape extends MapItem { /** The coordinates of the polygon */ public double[] lats, lons; /** Set if the item is filled or not */ public boolean filled; /** The item color. Alpha defaults to 255 if not specified. */ public int color; void serialize(StringBuffer sb) { sb.append("*S*,").append(lats.length).append(","); for (int i = 0; i < lats.length; i++) sb.append(toCoordI(lats[i])).append(",").append(toCoordI(lons[i])).append(","); sb.append(filled).append(",").append(color); } } /** A map item that represents a place in the map. It shows with a pin, and above it, a balloon with a text. * <pre> * </pre> */ public static class Place extends MapItem { /** The location of the place */ public double lat,lon; /** An optional caption of the place; shown in bold */ public String caption; /** The detail of the place. Use \n to split lines. Cannot be null. */ public String detail; /** The item's background color. Alpha defaults to 255 if not specified. */ public int backColor; /** The item caption's color. Alpha defaults to 255 if not specified. */ public int capColor; /** The item details' color. Alpha defaults to 255 if not specified. */ public int detColor; /** The item pin's color. Alpha defaults to 255 if not specified. */ public int pinColor; /** The percentage of the font based on the device's original font size. Defaults to 100. */ public int fontPerc = 100; void serialize(StringBuffer sb) { sb.append("*P*,\"").append(caption==null?null:caption.replace('"','\'')).append("\",\"").append(detail.replace('"','\'')) .append("\",").append(toCoordI(lat)).append(",").append(toCoordI(lon)).append(",").append(backColor) .append(",").append(capColor).append(",").append(detColor).append(",").append(pinColor).append(",").append(fontPerc); } } /** Shows the given address in a separate viewer. * * If you want to show your current location, you will have to turn * GPS on, get the coordinates and pass to this method. * * See the tc.samples.map.GoogleMaps sample. * * @param address The address (WITHOUT ACCENTUATION) to show (E.G.: "rua tonelero 10, copacabana, rio de janeiro, brasil", or "22030,brasil"). * The address is resolved and its latitude and logitude coordinates are retrieved. * Careful with the address given, be sure to make some tests before. To remove accentuation, you can use Convert.removeAccentuation. * * If you want to pass the lat/lon coordinates, pass them in the form: "@lat,lon" (E.G.: "@-22.966000,-43.185000"). * Note that, due to Android's restrictions, only the first 6 decimal digits in the coordinates are used. * * Passing an empty string will make the engine search for a valid "last known position". If there's no such position, * it will return false. * * @param showSatellitePhotos If true, an image of the satellite is combined with the map. May require more * bandwidth and internet resources. * * @return true if the map was shown, false otherwise. False is often returned when there's no internet connection. * @see Convert#removeAccentuation(String) */ public static boolean showAddress(String address, boolean showSatellitePhotos) { return true; } native static boolean showAddress4D(String address, boolean showSatellitePhotos); /** Shows the route between two points. The traversed points is a sequence of lat,lon coordinates comma-separated. * @deprecated * @see #showRoute(String, String, String, int) */ public static boolean showRoute(String addressI, String addressF, String traversedPoints, boolean showSatellitePhotos) throws NotInstalledException { return showRoute(addressI, addressF, traversedPoints, SHOW_SATELLITE_PHOTOS); } native static boolean showRoute4D(String addressI, String addressF, String traversedPoints, boolean showSatellitePhotos) throws NotInstalledException; /** Shows the route between two points. The traversed points is a sequence of lat,lon coordinates comma-separated. * If flags is USE_WAZE, the addressF is not used and can be null, and a NotInstalledException is thrown if WAZE is not installed. * * Note that you must remove accentuation from addressI and addressF; you may use Convert.removeAccentuation. * * @see Convert#removeAccentuation(String) */ public static boolean showRoute(String addressI, String addressF, String traversedPoints, int flags) throws NotInstalledException { return true; } native static boolean showRoute4D(String addressI, String addressF, String traversedPoints, int flags) throws NotInstalledException; /** Shows an array of MapItem elements in the map. The map is zommed in a way that all coordinates are visible. * See the Circle, Shape and Place map items. */ public static boolean showMap(MapItem[] items, boolean showSatellitePhotos) { StringBuffer sb = new StringBuffer(items.length * 100); sb.append("***"); items[0].serialize(sb); for (int i = 1; i < items.length; i++) { sb.append("|"); items[i].serialize(sb); } return showAddress(sb.toString(), showSatellitePhotos); } /** Returns the location after searching Google. Requires internet connection and waits up to 20 seconds for an answer. * Returns the lat/lon pair, or null if a bad response code was returned. */ public static double[] getLocation(String address) throws IOException, InvalidNumberException { // connect to map web service String url = "http://maps.googleapis.com/maps/api/geocode/xml?address=" + Convert.removeAccentuation(address).replace(" "," ").replace(' ','+') + "&sensor=false"; HttpStream hs = new HttpStream(new URI(url)); if (hs.badResponseCode) { Vm.debug("getLocation \""+url+"\" returned response code: "+hs.responseCode); return null; } ByteArrayStream bas = new ByteArrayStream(2048); bas.readFully(hs, 5, 2048); String s = new String(bas.getBuffer(), 0, bas.available()).toLowerCase(); if (s.contains("<location_type>approximate</location_type>")) { Vm.debug("getLocation \""+url+"\" returned a bad location from google maps"); return null; } int idx1 = s.indexOf("<lat>"), idx2 = s.indexOf("</lat>",idx1); int idx3 = s.indexOf("<lng>"), idx4 = s.indexOf("</lng>",idx1); if (idx1 == -1 || idx2 == -1 || idx3 == -1 || idx4 == -1) return null; String lat = s.substring(idx1+5,idx2); String lon = s.substring(idx3+5,idx4); return new double[]{Convert.toDouble(lat),Convert.toDouble(lon)}; } }