package org.osmdroid.location;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Locale;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.oscim.core.BoundingBox;
import org.oscim.core.GeoPoint;
import org.osmdroid.utils.BonusPackHelper;
import org.osmdroid.utils.HttpConnection;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
/**
* POI Provider using GeoNames services. Currently, "find Nearby Wikipedia" and
* "Wikipedia Articles in Bounding Box" services.
* @see "http://www.geonames.org"
* @author M.Kergall
*/
public class GeoNamesPOIProvider {
protected String mUserName;
/**
* @param account
* the registered "username" to give to GeoNames service.
* @see "http://www.geonames.org/login"
*/
public GeoNamesPOIProvider(String account) {
mUserName = account;
}
private String getUrlCloseTo(GeoPoint p, int maxResults, double maxDistance) {
StringBuffer url = new StringBuffer("http://api.geonames.org/findNearbyWikipediaJSON?");
url.append("lat=" + p.getLatitude());
url.append("&lng=" + p.getLongitude());
url.append("&maxRows=" + maxResults);
url.append("&radius=" + maxDistance); //km
url.append("&lang=" + Locale.getDefault().getLanguage());
url.append("&username=" + mUserName);
return url.toString();
}
private String getUrlInside(BoundingBox boundingBox, int maxResults) {
StringBuffer url = new StringBuffer("http://api.geonames.org/wikipediaBoundingBoxJSON?");
url.append("south=" + boundingBox.getMinLatitude());
url.append("&north=" + boundingBox.getMaxLatitude());
url.append("&west=" + boundingBox.getMinLongitude());
url.append("&east=" + boundingBox.getMaxLongitude());
url.append("&maxRows=" + maxResults);
url.append("&lang=" + Locale.getDefault().getLanguage());
url.append("&username=" + mUserName);
return url.toString();
}
/**
* @param fullUrl
* ...
* @return the list of POI
*/
public ArrayList<POI> getThem(String fullUrl) {
Log.d(BonusPackHelper.LOG_TAG, "GeoNamesPOIProvider:get:" + fullUrl);
String jString = BonusPackHelper.requestStringFromUrl(fullUrl);
if (jString == null) {
Log.e(BonusPackHelper.LOG_TAG, "GeoNamesPOIProvider: request failed.");
return null;
}
try {
JSONObject jRoot = new JSONObject(jString);
JSONArray jPlaceIds = jRoot.getJSONArray("geonames");
int n = jPlaceIds.length();
ArrayList<POI> pois = new ArrayList<POI>(n);
for (int i = 0; i < n; i++) {
JSONObject jPlace = jPlaceIds.getJSONObject(i);
POI poi = new POI(POI.POI_SERVICE_GEONAMES_WIKIPEDIA);
poi.location = new GeoPoint(jPlace.getDouble("lat"),
jPlace.getDouble("lng"));
poi.category = jPlace.optString("feature");
poi.type = jPlace.getString("title");
poi.description = jPlace.optString("summary");
poi.thumbnailPath = jPlace.optString("thumbnailImg", null);
/* This makes loading too long. Thumbnail loading will be done
* only when needed, with POI.getThumbnail() if
* (poi.mThumbnailPath != null){ poi.mThumbnail =
* BonusPackHelper.loadBitmap(poi.mThumbnailPath); } */
poi.url = jPlace.optString("wikipediaUrl", null);
if (poi.url != null)
poi.url = "http://" + poi.url;
poi.rank = jPlace.optInt("rank", 0);
//other attributes: distance?
pois.add(poi);
}
Log.d(BonusPackHelper.LOG_TAG, "done");
return pois;
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
//XML parsing seems 2 times slower than JSON parsing
public ArrayList<POI> getThemXML(String fullUrl) {
Log.d(BonusPackHelper.LOG_TAG, "GeoNamesPOIProvider:get:" + fullUrl);
HttpConnection connection = new HttpConnection();
connection.doGet(fullUrl);
InputStream stream = connection.getStream();
if (stream == null) {
return null;
}
GeoNamesXMLHandler handler = new GeoNamesXMLHandler();
try {
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(stream, handler);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
connection.close();
Log.d(BonusPackHelper.LOG_TAG, "done");
return handler.mPOIs;
}
/**
* @param position
* ...
* @param maxResults
* ...
* @param maxDistance
* ... in km. 20 km max for the free service.
* @return list of POI, Wikipedia entries close to the position. Null if
* technical issue.
*/
public ArrayList<POI> getPOICloseTo(GeoPoint position,
int maxResults, double maxDistance) {
String url = getUrlCloseTo(position, maxResults, maxDistance);
return getThem(url);
}
/**
* @param boundingBox
* ...
* @param maxResults
* ...
* @return list of POI, Wikipedia entries inside the bounding box. Null if
* technical issue.
*/
public ArrayList<POI> getPOIInside(BoundingBox boundingBox, int maxResults) {
String url = getUrlInside(boundingBox, maxResults);
return getThem(url);
}
}
class GeoNamesXMLHandler extends DefaultHandler {
private String mString;
double mLat, mLng;
POI mPOI;
ArrayList<POI> mPOIs;
public GeoNamesXMLHandler() {
mPOIs = new ArrayList<POI>();
}
@Override
public void startElement(String uri, String localName, String name,
Attributes attributes) {
if (localName.equals("entry")) {
mPOI = new POI(POI.POI_SERVICE_GEONAMES_WIKIPEDIA);
}
mString = new String();
}
@Override
public void characters(char[] ch, int start, int length) {
String chars = new String(ch, start, length);
mString = mString.concat(chars);
}
@Override
public void endElement(String uri, String localName, String name) {
if (localName.equals("lat")) {
mLat = Double.parseDouble(mString);
} else if (localName.equals("lng")) {
mLng = Double.parseDouble(mString);
} else if (localName.equals("feature")) {
mPOI.category = mString;
} else if (localName.equals("title")) {
mPOI.type = mString;
} else if (localName.equals("summary")) {
mPOI.description = mString;
} else if (localName.equals("thumbnailImg")) {
if (mString != null && !mString.equals(""))
mPOI.thumbnailPath = mString;
} else if (localName.equals("wikipediaUrl")) {
if (mString != null && !mString.equals(""))
mPOI.url = "http://" + mString;
} else if (localName.equals("rank")) {
mPOI.rank = Integer.parseInt(mString);
} else if (localName.equals("entry")) {
mPOI.location = new GeoPoint(mLat, mLng);
mPOIs.add(mPOI);
}
}
}