Permalink
Browse files

Add support for Paris,France. Disabled until we find out a way to spe…

…ed things up. The MapOverlay doesn't like having so many items it seems :)
  • Loading branch information...
1 parent 72c06aa commit dd470326a83d64fbfad024c1afea57df8f94d25a @lacostej committed Jun 18, 2009
View
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mycitybikes.android"
- android:versionCode="2"
- android:versionName="1.0.1">
+ android:versionCode="3"
+ android:versionName="1.0.2">
<uses-sdk android:minSdkVersion="3"></uses-sdk>
<application android:icon="@drawable/icon" android:label="@string/app_name">
@@ -0,0 +1,189 @@
+package com.mycitybikes.android;
+
+import java.io.InputStream;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.mycitybikes.android.model.BikeStationStatus;
+import com.mycitybikes.android.model.StationLocation;
+import com.mycitybikes.android.util.Utils;
+
+public class JCDecaux {
+ public static void loadParisBikeLocations(Context context,
+ List<StationLocation> stationLocations) {
+ try {
+ InputStream is = Utils.readContent(
+ "http://www.velib.paris.fr/service/carto", 5000);
+ loadParisBikeLocations(is, stationLocations, "Paris", "France");
+ } catch (Exception e) {
+ Log.e(Constants.TAG,
+ "Failed to load Paris bike station locations: "
+ + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ static void loadParisBikeLocations(InputStream is,
+ List<StationLocation> stationLocations, String city, String country) {
+
+ 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);
+ }
+
+ NodeList markers;
+ markers = dom.getElementsByTagName("marker");
+ for (int j = 0; j < markers.getLength(); j++) {
+ Node markerNode = markers.item(j);
+ if (!"marker".equals(markerNode.getNodeName())) {
+ throw new IllegalArgumentException("Unexpected XML:"
+ + markerNode.getNodeName());
+ }
+
+ Integer id = null;
+ String description = null;
+ Double latitude = null;
+ Double longitude = null;
+
+ // FIXME refactor model as to attach status to station
+ BikeStationStatus bikeStationStatus = new BikeStationStatus();
+
+ NamedNodeMap markerAttributes = markerNode.getAttributes();
+ Node n;
+ // markerAttributes.getNamedItem("name");
+
+ n = markerAttributes.getNamedItem("number");
+ id = new Integer(n.getNodeValue());
+
+ n = markerAttributes.getNamedItem("address");
+ // description = child.getNodeValue();
+
+ n = markerAttributes.getNamedItem("fullAddress");
+ description = n.getNodeValue();
+
+ n = markerAttributes.getNamedItem("lat");
+ latitude = new Double(n.getNodeValue());
+
+ n = markerAttributes.getNamedItem("lng");
+ longitude = new Double(n.getNodeValue());
+
+ n = markerAttributes.getNamedItem("open");
+ bikeStationStatus.setOnline("1".equals(n.getNodeValue()));
+
+ // markerAttributes.getNamedItem("bonus");
+
+ StationLocation stationLocation = new StationLocation(id, city,
+ country, description, longitude, latitude);
+ stationLocations.add(stationLocation);
+ Log.v(Constants.TAG, "loaded stationLocation: " + stationLocation);
+ }
+ }
+
+ static BikeStationStatus readBikeStationStatus(int id) {
+ try {
+ InputStream is = Utils.readContent(
+ "http://www.velib.paris.fr/service/stationdetails/" + id,
+ 5000);
+ return parseStatus(is);
+ } catch (RuntimeException e) {
+ Log.e(Constants.TAG,
+ "Failed to load Paris bike station locations: "
+ + e.getMessage());
+ throw e;
+ }
+ }
+
+ static BikeStationStatus parseStatus(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.getDocumentElement();
+ if (!"station".equals(stationNode.getNodeName())) {
+ throw new IllegalArgumentException("Unexpected XML:"
+ + stationNode.getNodeName());
+ }
+ BikeStationStatus bikeStationStatus = new BikeStationStatus();
+ NodeList stationChildren = stationNode.getChildNodes();
+ for (int i = 0; i < stationChildren.getLength(); i++) {
+ Node child = stationChildren.item(i);
+ if (child.getNodeType() != Element.ELEMENT_NODE) {
+ continue;
+ }
+ if ("available".equals(child.getNodeName())) {
+ if (child.getFirstChild() == null) {
+ // system offline
+ bikeStationStatus.setOnline(false);
+ break;
+ }
+ bikeStationStatus.setOnline(true);
+ }
+ if ("available".equals(child.getNodeName())) {
+ bikeStationStatus.setReadyBikes(new Integer((child
+ .getFirstChild().getNodeValue())));
+ } else if ("free".equals(child.getNodeName())) {
+ bikeStationStatus.setEmptyLocks(new Integer(child
+ .getFirstChild().getNodeValue()));
+
+ // FIXME treat total and ticket
+ } else if ("total".equals(child.getNodeName())) {
+ } else if ("ticket".equals(child.getNodeName())) {
+ } else {
+ throw new IllegalArgumentException(
+ "Unexpected format of the XML station status "
+ + child.getNodeName());
+ }
+ }
+ return bikeStationStatus;
+ }
+
+ // FIXME refactor this code with ClearChannel
+ public static String getStationInfo(StationLocation stationLocation) {
+ if (stationLocation.getCity().equals("Paris")) {
+ return getParisStationInfo(stationLocation.getId());
+ } else {
+ throw new IllegalStateException("" + stationLocation);
+ }
+ }
+
+ public static String getParisStationInfo(int stationIndex) {
+ String result;
+ try {
+ BikeStationStatus status = readBikeStationStatus(stationIndex);
+ if (!status.isOnline()) {
+ result = status.getDescription()
+ + "\n\n(no station information)";
+ } else {
+ result = status.getDescription() + "\n\n"
+ + status.getReadyBikes() + " bike(s)\n"
+ + status.getEmptyLocks() + " slot(s)";
+ }
+ } catch (Exception e) {
+ result = "Error: station information not available";
+ }
+ return result;
+ }
+
+}
@@ -102,6 +102,11 @@ public void onCreate(Bundle savedInstanceState) {
stationLocations);
ClearChannel.loadStockholmBikeLocations(getApplicationContext(),
stationLocations);
+ // Disabled until we find a way to speed up things. 1000+ overlay items don't cut it...
+ /*
+ JCDecaux.loadParisBikeLocations(getApplicationContext(),
+ stationLocations);
+ */
}
private void animateMapToMyLocation() {
@@ -16,6 +16,7 @@
import com.google.android.maps.OverlayItem;
import com.mycitybikes.android.ClearChannel;
import com.mycitybikes.android.Constants;
+import com.mycitybikes.android.JCDecaux;
import com.mycitybikes.android.model.StationLocation;
import com.mycitybikes.android.util.AndroidUtils;
@@ -92,6 +93,9 @@ public void actUponTapLocation(int overlayIndex, String prefixMessage) {
|| stationLocation.getCity().equals("Stockholm")) {
text = prefixMessage
+ ClearChannel.getStationInfo(stationLocation);
+ } else if (stationLocation.getCity().equals("Paris")) {
+ text = prefixMessage
+ + JCDecaux.getStationInfo(stationLocation);
} else {
throw new IllegalStateException("Unsupported location "
+ stationLocation);
@@ -0,0 +1,105 @@
+package com.mycitybikes.android;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import com.mycitybikes.android.JCDecaux;
+import com.mycitybikes.android.model.BikeStationStatus;
+import com.mycitybikes.android.model.StationLocation;
+
+public class JCDecauxTest extends AndroidTestCase {
+
+ /*
+ * private final String OSLO_CSV = "#index,lat,lng,id\n" +
+ * "0,59.92786125852981,10.709009170532226,1\n" +
+ * "1,59.92805479800621,10.708515644073486,2\n";
+ */
+
+ private final String PARIS_CARTO_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<carto>\n"
+ + " <markers>\n"
+ + " <marker name=\"20020 - PYRENEES RENOUVIER\"\n"
+ + " number=\"20020\"\n"
+ + " address=\"183 RUE DES PYRENEES -\"\n"
+ + " fullAddress=\"183 RUE DES PYRENEES - 75020 PARIS\"\n"
+ + " lat=\"48.8610789316\"\n"
+ + " lng=\"2.40036207675\"\n"
+ + " open=\"1\"\n"
+ + " bonus=\"1\" />\n"
+ + " <marker name=\"20019 - DAVOUT SERPOLLET\"\n"
+ + " number=\"20019\"\n"
+ + " address=\"1 RUE SERPOLLET -\"\n"
+ + " fullAddress=\"1 RUE SERPOLLET - 75020 PARIS\"\n"
+ + " lat=\"48.8605974067\"\n"
+ + " lng=\"2.40950128611\"\n"
+ + " open=\"1\"\n"
+ + " bonus=\"1\" />\n"
+ + " </markers>\n"
+ + " <arrondissements>\n"
+ + " <arrondissement number=\"\"\n"
+ + " minLat=\"27.1409733745\"\n"
+ + " minLng=\"-3.40456062425\"\n"
+ + " maxLat=\"50.4549656869\"\n"
+ + " maxLng=\"2.45307201488\" />\n"
+ + " </arrondissements>\n" + "</carto>\n";
+
+ private final String PARIS_STATION_STATUS = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<station>\n"
+ + " <available>24</available>\n"
+ + " <free>2</free>\n"
+ + " <total>26</total>\n"
+ + " <ticket>1</ticket>\n" + "</station>\n";
+
+ private final String PARIS_OFFLINE_STATION_STATUS = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<station>\n"
+ + " <available/>\n"
+ + " <free/>\n"
+ + " <total/>\n" + " <ticket/>\n" + "</station>\n";
+
+ public void testLoadParisBikeLocations() throws IOException {
+ List<StationLocation> bikeLocations = new ArrayList<StationLocation>();
+ InputStream is = getStringInputStream(PARIS_CARTO_XML);
+ JCDecaux.loadParisBikeLocations(is, bikeLocations, "Paris", "France");
+
+ assertEquals(2, bikeLocations.size());
+ assertEquals(20020, bikeLocations.get(0).getId());
+ assertEquals(48.8610789316, bikeLocations.get(0).getLatitude());
+ assertEquals(2.40036207675, bikeLocations.get(0).getLongitude());
+ assertEquals("183 RUE DES PYRENEES - 75020 PARIS", bikeLocations.get(0)
+ .getDescription());
+ }
+
+ private ByteArrayInputStream getStringInputStream(String string)
+ throws UnsupportedEncodingException {
+ return new ByteArrayInputStream(string.getBytes("UTF-8"));
+ }
+
+ public void testParseParisStatus() throws IOException {
+ BikeStationStatus status = JCDecaux
+ .parseStatus(getStringInputStream(PARIS_STATION_STATUS));
+ assertEquals(true, status.isOnline());
+ assertEquals(2, status.getEmptyLocks());
+ assertEquals(24, status.getReadyBikes());
+ // assertEquals(26, status.getTotalSlots());
+ }
+
+ public void testParseOfflineParisStatus() throws IOException {
+ BikeStationStatus status = JCDecaux
+ .parseStatus(getStringInputStream(PARIS_OFFLINE_STATION_STATUS));
+ assertEquals(false, status.isOnline());
+ }
+
+ public void testFetchLiveParisStatus() {
+ int id = 4027;
+ BikeStationStatus status = JCDecaux.readBikeStationStatus(id);
+ Log.i(Constants.TAG, "Fetch Paris status for station #" + id + ": "
+ + status.toString());
+ }
+}

0 comments on commit dd47032

Please sign in to comment.