Back to project page mycitybikes-android.
The source code is released under:
GNU General Public License
If you think the Android project mycitybikes-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.mycitybikes.android; /* w ww . j a v a 2 s . c om*/ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.CDATASection; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import android.content.Context; import android.content.res.AssetManager; import android.util.Log; import com.google.android.maps.GeoPoint; import com.mycitybikes.android.model.City; import com.mycitybikes.android.model.StationInfoBuilder; import com.mycitybikes.android.model.StationLocation; import com.mycitybikes.android.model.StationStatus; import com.mycitybikes.android.util.AndroidUtils; import com.mycitybikes.android.util.Utils; /** * Class to perform ClearChannel specific bike station operations. We only * support Oslo so far. * * @author jerome */ public class ClearChannel { public static void loadOsloBikeLocations(Context context, List<StationLocation> stationLocations) { try { AssetManager assets = context.getAssets(); InputStream is = assets.open("oslo2.xml"); loadStationLocationsAsset(is, stationLocations, Constants.CITY_OSLO); } catch (Exception e) { Log.e(Constants.TAG, "Failed to load Oslo bike station locations: " + e.getMessage(), e); } } public static void loadStockholmBikeLocations(Context context, List<StationLocation> stationLocations) { try { AssetManager assets = context.getAssets(); InputStream is = assets.open("stockholm.xml"); loadStationLocationsAsset(is, stationLocations, Constants.CITY_STOCKHOLM); } catch (Exception e) { Log.e(Constants.TAG, "Failed to load Stokholm bike station locations: " + e.getMessage(), e); } } static void loadStationLocationsAsset(InputStream is, List<StationLocation> stationLocations, City city) throws IOException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; Document dom; try { db = dbf.newDocumentBuilder(); dom = db.parse(is); } catch (Exception e) { throw new IllegalStateException("Unexpected parsing issue.", e); } Node stationsNode = dom.getDocumentElement(); if (!"stations".equals(stationsNode.getNodeName())) { throw new IllegalArgumentException("Unexpected XML:" + stationsNode.getNodeName()); } Integer id = null; String description = null; Double latitude = null; Double longitude = null; NodeList stationsChildren = stationsNode.getChildNodes(); for (int i = 0; i < stationsChildren.getLength(); i++) { Node stationNode = stationsChildren.item(i); if (stationNode.getNodeType() != Element.ELEMENT_NODE) { continue; } if (!"station".equals(stationNode.getNodeName())) { throw new IllegalArgumentException("Unexpected XML:" + stationNode.getNodeName()); } NodeList stationChildren = stationNode.getChildNodes(); for (int j = 0; j < stationChildren.getLength(); j++) { Node child = stationChildren.item(j); if (child.getNodeType() != Element.ELEMENT_NODE) { continue; } if ("description".equals(child.getNodeName())) { description = child.getFirstChild().getNodeValue().trim(); } else if ("longitude".equals(child.getNodeName())) { longitude = new Double(child.getFirstChild().getNodeValue()); } else if ("latitude".equals(child.getNodeName())) { latitude = new Double(child.getFirstChild().getNodeValue()); } else if ("id".equals(child.getNodeName())) { id = new Integer(child.getFirstChild().getNodeValue()); } else { throw new IllegalArgumentException( "Unexpected format of the XML station status " + child.getNodeName()); } } final StationLocation stationLocation = new StationLocation(id, city, description, longitude, latitude); stationLocation.setStationInfoBuilder(new StationInfoBuilder() { @Override public String buildStationInfo() { return ClearChannel.getStationInfo(stationLocation); } }); stationLocations.add(stationLocation); Log.v(Constants.TAG, "loaded stationLocation: " + stationLocation); } } public static StationStatus readBikeStationStatus(String httpUrl) { try { return parseStatus(Utils.readContent(httpUrl, 5000)); } catch (Exception e) { throw new RuntimeException( "Unable to read/parse status information", e); } } static StationStatus parseStatus(InputStream content) throws IOException { String s = Utils.parseISToString(content); s = s.substring(s.indexOf("<string")); int start = s.indexOf(">") + 1; int end = s.indexOf("</", start); String encodedContent = s.substring(start, end); String decodedContent = decode(encodedContent); Log.v(Constants.TAG, "decodedContent: " + decodedContent); InputStream is; try { is = new java.io.ByteArrayInputStream(decodedContent .getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new IllegalStateException("Unexpected issue.", e); } return parseBikeStationStatusDOM(is); } /* * private static BikeStationStatus * parseBikeStationStatusXstream(InputStream is) { XStream xstream = new * XStream(new DomDriver()); xstream.alias("station", * BikeStationStatus.class); xstream.aliasAttribute(BikeStationStatus.class, * "readyBikes", "ready_bikes"); * xstream.aliasAttribute(BikeStationStatus.class, "emptyLocks", * "empty_locks"); // xstream.addImplicitCollection(WikiItems.class, * "entries"); * * BikeStationStatus bikeStationStatus = (BikeStationStatus) * xstream.fromXML(is); return bikeStationStatus; } */ private static StationStatus parseBikeStationStatusDOM(InputStream is) { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; Document dom; try { db = dbf.newDocumentBuilder(); dom = db.parse(is); } catch (Exception e) { throw new IllegalStateException("Unexpected parsing issue.", e); } Node stationNode = dom.getFirstChild(); if (!"station".equals(stationNode.getNodeName())) { throw new IllegalArgumentException("Unexpected XML:" + stationNode.getNodeName()); } StationStatus bikeStationStatus = new StationStatus(); NodeList stationChildren = stationNode.getChildNodes(); for (int i = 0; i < stationChildren.getLength(); i++) { Node child = stationChildren.item(i); if ("description".equals(child.getNodeName())) { String description = child.getFirstChild().getNodeValue(); int splitIndex = description.indexOf('-'); if (splitIndex != -1) { description = description.substring(splitIndex + 1); } // bikeStationStatus.setDescription(description); } else if ("longitute"/* sic... */.equals(child.getNodeName())) { /* bikeStationStatus.setLongitude(new Double(child.getFirstChild() .getNodeValue())); */ } else if ("latitude".equals(child.getNodeName())) { /* bikeStationStatus.setLatitude(new Double(child.getFirstChild() .getNodeValue())); */ } else if ("online".equals(child.getNodeName())) { bikeStationStatus.setOnline("1".equals(child.getFirstChild() .getNodeValue())); } else if ("empty_locks".equals(child.getNodeName())) { bikeStationStatus.setEmptyLocks(new Integer(child .getFirstChild().getNodeValue())); } else if ("ready_bikes".equals(child.getNodeName())) { bikeStationStatus.setReadyBikes(new Integer(child .getFirstChild().getNodeValue())); } else { throw new IllegalArgumentException( "Unexpected format of the XML station status " + child.getNodeName()); } } return bikeStationStatus; } public static String getStationInfo(StationLocation stationLocation) { if (stationLocation.getCity().equals("Oslo")) { return getOsloStationInfo(stationLocation); } else if (stationLocation.getCity().equals("Stockholm")) { return stationLocation.getDescription() + "\n\n(no live data for Stockholm)"; } else { throw new IllegalStateException("" + stationLocation); } } public static String getOsloStationInfo(StationLocation stationLocation) { String result; try { StationStatus status = readBikeStationStatus(stationLocation.getId()); result = formatStationInfo(stationLocation, status); } catch (Exception e) { result = "Error: station information not available"; } return result; } private static String formatStationInfo(StationLocation stationLocation, StationStatus status) { String result; if (!status.isOnline()) { result = stationLocation.getDescription() + "\n\n(no station information)"; } else { result = stationLocation.getDescription() + "\n\n" + status.getReadyBikes() + " bike(s)\n" + status.getEmptyLocks() + " slot(s)"; } return result; } public static StationStatus readBikeStationStatus(int stationId) { return ClearChannel .readBikeStationStatus("http://smartbikeportal.clearchannel.no/public/mobapp/maq.asmx/getRack?id=" + stationId); } private static String decode(String content) { content = content.replaceAll("<", "<"); content = content.replaceAll(">", ">"); return content; } public static void loadBarcelonaBikeLocations(Context context, List<StationLocation> stationLocations) { try { loadBikeLocationsAndStatusFromKmlInPage(context, stationLocations, "http://www.bicing.com/localizaciones/localizaciones.php", Constants.CITY_BARCELONA); } catch (Exception e) { Log.e(Constants.TAG, "Failed to load Barcelona bike station locations: " + e.getMessage(), e); } } public static void loadBikeLocationsAndStatusFromKmlInPage(Context context, List<StationLocation> stationLocations, String httpUrl, City city) { try { InputStream is = Utils.readContent(httpUrl, 5000); loadBikeLocationsAndStatusFromKmlInPage(stationLocations, is, city); } catch (Exception e) { Log.e(Constants.TAG, "Failed to load " + city + " bike station locations: " + e.getMessage(), e); } } static void loadBikeLocationsAndStatusFromKmlInPage( List<StationLocation> stationLocations, InputStream is, City city) throws UnsupportedEncodingException { String kml = extractKMLFromHtml(is); Log.v(Constants.TAG, "Extracted KML: " + kml); InputStream is2 = new ByteArrayInputStream(kml.getBytes("UTF-8")); parseKml(is2, stationLocations, city); } static void parseKml(InputStream is, List<StationLocation> stationLocations, City city) { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; Document dom; try { db = dbf.newDocumentBuilder(); dom = db.parse(is); } catch (Exception e) { throw new IllegalStateException("Unexpected parsing issue.", e); } Node kmlNode = dom.getDocumentElement(); if (!"kml".equals(kmlNode.getNodeName())) { throw new IllegalArgumentException("Unexpected XML:" + kmlNode.getNodeName()); } NodeList placemarks = dom.getElementsByTagName("Placemark"); for (int i = 0; i < placemarks.getLength(); i++) { Integer id = null; String description = null; GeoPoint geoPoint = null; Node placemarkNode = placemarks.item(i); final StationStatus bikeStationStatus = new StationStatus(); // FIXME find out how offline is supported for Barcelona and the like bikeStationStatus.setOnline(true); NodeList stationChildren = placemarkNode.getChildNodes(); for (int j = 0; j < stationChildren.getLength(); j++) { Node child = stationChildren.item(j); if (child.getNodeType() != Element.ELEMENT_NODE) { continue; } if ("description".equals(child.getNodeName())) { CDATASection section = (CDATASection) child.getFirstChild(); String data = section.getData(); String[] parsedData = parseKmlDescription(data); if (parsedData == null) { Log.e(Constants.TAG, "couldn't extract address from data:" + data); } else { id = new Integer(parsedData[0]); description = parsedData[1]; bikeStationStatus.setReadyBikes(new Integer(parsedData[2])); bikeStationStatus.setEmptyLocks(new Integer(parsedData[3])); } } else if ("Point".equals(child.getNodeName())) { String coordinates = child.getFirstChild().getFirstChild().getNodeValue().trim(); geoPoint = parseKmlCoordinates(coordinates); if (geoPoint == null) { Log.e(Constants.TAG, "couldn't extract coordinates from:" + coordinates); } } } if (description == null || geoPoint == null) { Log.e(Constants.TAG, "couldn't find station location information. Skipping..."); continue; } final StationLocation stationLocation = new StationLocation(id, city, description, geoPoint); stationLocation.setStationInfoBuilder(new StationInfoBuilder() { @Override public String buildStationInfo() { return formatStationInfo(stationLocation, bikeStationStatus); } }); stationLocations.add(stationLocation); Log.v(Constants.TAG, "loaded stationLocation: " + stationLocation); } } /** * Return an array with [id,description,readyBikes,emptyLocks], or null if couldn't be parsed * @param descriptionData * @return */ static String[] parseKmlDescription(String descriptionData) { Pattern p = Pattern.compile("<div[^>]*><div[^>]*>(.*) - (.*)</div><div[^>]*>.*</div><div[^>]*>([0-9]+).*>([0-9]+).*</div></div>"); Matcher m = p.matcher(descriptionData); if (m.matches() && m.groupCount() == 4) { return new String[] {m.group(1), m.group(2), m.group(3), m.group(4) }; } return null; } static GeoPoint parseKmlCoordinates(String coordinates) { Pattern p = Pattern.compile("([0-9]+[\\.,][0-9]+)\\??,([0-9]+[\\.,][0-9]+),(.*)"); Matcher m = p.matcher(coordinates); if (m.matches() && m.groupCount() == 3) { double longitude = new Double(m.group(1).replace(",", ".")); double latitude = new Double(m.group(2).replace(",", ".")); //double altitude = new Double(m.group(3)); return AndroidUtils.buildGeoPoint(latitude, longitude); } return null; } static String extractKMLFromHtml(InputStream is) { String line = null; BufferedReader r = null; try { r = new BufferedReader(new InputStreamReader(is, "ISO-8859-1")); while ((line = r.readLine()) != null) { int start = -1; if ((start = line.indexOf("<?xml version=\"1.0\" encoding=\"UTF-8\"?><kml")) >= 0) { int end = line.indexOf("</kml>"); if (end == -1 || end < start) { throw new IllegalStateException("Unexpected HTML format. </kml> not found on same line as <kml>"); } if (end < start) { throw new IllegalStateException("Unexpected HTML format. </kml> before <kml>"); } return line.substring(start, end + "</kml>".length()); } } } catch (IOException e) { throw new RuntimeException("Unable to extract KML", e); } finally { if (r != null) { try { r.close(); } catch (IOException e) { throw new RuntimeException("Unable to close InputStream", e); } } } Log.v(Constants.TAG, "kml: " + line); return line; } public static void loadWashingtonBikeLocations(Context applicationContext, List<StationLocation> stationLocations) { try { parseWashington(Utils.readContent("https://www.smartbikedc.com/smartbike_locations.asp", 5000), stationLocations, Constants.CITY_WASHINGTON_DC); } catch (Exception e) { Log.e(Constants.TAG, "Failed to load Washington DC bike station locations: " + e.getMessage(), e); } } static void parseWashington(InputStream is, List<StationLocation> stationLocations, City city) { String line = null; BufferedReader r = null; try { r = new BufferedReader(new InputStreamReader(is, "UTF-8")); while ((line = r.readLine()) != null) { if (line.startsWith("//****")) { line = r.readLine(); line = r.readLine(); break; } } if (line == null) { Log.e(Constants.TAG, "Couldn't find start delimeter to parse Washington HTML"); return; } Integer id = null; String description = null; Double longitude = null; Double latitude = null; Integer emptyLocks = null; Integer readyBikes = null; do { Log.v(Constants.TAG, "to parse: " + line); if (line.startsWith("//")) { id = new Integer(line.substring(2, line.indexOf('.')).trim()); } else if (line.startsWith("var point")) { Pattern p = Pattern.compile(".*\\((.*),(.*)\\).*"); Matcher m = p.matcher(line); if (m.matches() && m.groupCount() == 2) { longitude = new Double(m.group(1)); latitude = new Double(m.group(2)); } else { Log.e(Constants.TAG, "Couldn't parse geo position from:" + line); } } else if (line.startsWith("address")) { description = line.substring("address = \"".length(), line.length() - 2).replaceAll("<br />", "\n"); } else if (line.startsWith("html = ")) { Pattern p = Pattern.compile(".*<font color=red>([0-9]*)</font>.*Slots: ([0-9]*)<br>.*"); Matcher m = p.matcher(line); if (m.matches() && m.groupCount() == 2) { readyBikes = new Integer(m.group(1)); emptyLocks = new Integer(m.group(2)); } else { Log.e(Constants.TAG, "Couldn't parse station statuses from:" + line); } } else if (line.startsWith("addMarker")) { if (id == null || description == null || latitude == null || emptyLocks == null || readyBikes == null) { Log.e(Constants.TAG, "Missing data in a Whashington station."); break; } final StationStatus bikeStationStatus = new StationStatus(); bikeStationStatus.setEmptyLocks(emptyLocks); bikeStationStatus.setReadyBikes(readyBikes); bikeStationStatus.setOnline(true); final StationLocation stationLocation = new StationLocation(id, city, description, longitude, latitude); stationLocation.setStationInfoBuilder(new StationInfoBuilder() { @Override public String buildStationInfo() { return formatStationInfo(stationLocation, bikeStationStatus); } }); Log.v(Constants.TAG, "loaded stationLocation: " + stationLocation); stationLocations.add(stationLocation); id = null; description = null; longitude = null; latitude = null; emptyLocks = null; readyBikes = null; } line = r.readLine(); if (line == null) { break; } if (line.matches(" *\\}.*")) { break; } } while (true); } catch (IOException e) { throw new RuntimeException("Unable to extract KML", e); } finally { if (r != null) { try { r.close(); } catch (IOException e) { throw new RuntimeException("Unable to close InputStream", e); } } } } }