package org.osmdroid.bonuspack.location; import android.graphics.Bitmap; import android.util.Log; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.osmdroid.bonuspack.utils.BonusPackHelper; import org.osmdroid.util.BoundingBox; import org.osmdroid.util.GeoPoint; import java.net.URLEncoder; import java.util.ArrayList; /** * POI Provider using Nominatim service. <br> * @see <a href="http://wiki.openstreetmap.org/wiki/Nominatim">Nominatim Reference</a> * @see <a href="http://open.mapquestapi.com/nominatim/">Nominatim at MapQuest Open</a> * * @author M.Kergall */ public class NominatimPOIProvider { /* As the doc lacks a lot of features, source code may help: https://trac.openstreetmap.org/browser/applications/utils/nominatim/website/search.php * featuretype= to select on feature type (country, city, state, settlement)<br> * format=jsonv2 to get a place_rank<br> * offset= to offset the result ?... <br> * polygon=1 to get the border of the poi as a polygon<br> * nearlat & nearlon = ???<br> * routewidth. routewidth/69 and routewidth/30 ???<br> */ public static final String NOMINATIM_POI_SERVICE = "http://nominatim.openstreetmap.org/"; public static final String MAPQUEST_POI_SERVICE = "http://open.mapquestapi.com/nominatim/v1/"; protected String mService; protected String mUserAgent; public NominatimPOIProvider(String userAgent){ mService = NOMINATIM_POI_SERVICE; mUserAgent = userAgent; } public void setService(String serviceUrl){ mService = serviceUrl; } private StringBuilder getCommonUrl(String facility, int maxResults){ StringBuilder urlString = new StringBuilder(mService); urlString.append("search?"); urlString.append("format=json"); urlString.append("&q=["+URLEncoder.encode(facility)+"]"); urlString.append("&limit="+maxResults); urlString.append("&bounded=1"); return urlString; } private String getUrlInside(BoundingBox bb, String type, int maxResults){ StringBuilder urlString = getCommonUrl(type, maxResults); urlString.append("&viewbox="+bb.getLonWest()+"," +bb.getLatNorth()+"," +bb.getLonEast()+"," +bb.getLatSouth()); return urlString.toString(); } private String getUrlCloseTo(GeoPoint p, String type, int maxResults, double maxDistance){ BoundingBox bb = new BoundingBox(p.getLatitude()+maxDistance, p.getLongitude()+maxDistance, p.getLatitude()-maxDistance, p.getLongitude()-maxDistance); return getUrlInside(bb, type, maxResults); } /** Low-level API to get a list of POI. * @param url full URL request. Assumes * @return the list of POI, of null if technical issue. */ public ArrayList<POI> getThem(String url){ Log.d(BonusPackHelper.LOG_TAG, "NominatimPOIProvider:get:"+url); String jString = BonusPackHelper.requestStringFromUrl(url, mUserAgent); if (jString == null) { Log.e(BonusPackHelper.LOG_TAG, "NominatimPOIProvider: request failed."); return null; } try { JSONArray jPlaceIds = new JSONArray(jString); int n = jPlaceIds.length(); ArrayList<POI> pois = new ArrayList<POI>(n); Bitmap thumbnail = null; for (int i=0; i<n; i++){ JSONObject jPlace = jPlaceIds.getJSONObject(i); POI poi = new POI(POI.POI_SERVICE_NOMINATIM); poi.mId = jPlace.optLong("osm_id"); poi.mLocation = new GeoPoint(jPlace.getDouble("lat"), jPlace.getDouble("lon")); poi.mCategory = jPlace.optString("class"); poi.mType = jPlace.getString("type"); poi.mDescription = jPlace.optString("display_name"); poi.mThumbnailPath = jPlace.optString("icon", null); if (i==0 && poi.mThumbnailPath != null) { //first POI, and we have a thumbnail: load it thumbnail = BonusPackHelper.loadBitmap(poi.mThumbnailPath); } poi.mThumbnail = thumbnail; pois.add(poi); } return pois; } catch (JSONException e) { e.printStackTrace(); return null; } } /** * @param position * @param facility a Nominatim facility. * Note that Nominatim facility search uses Special Phrases instead of OSM tags. * See http://wiki.openstreetmap.org/wiki/Nominatim/Special_Phrases * and http://wiki.openstreetmap.org/wiki/Nominatim/Special_Phrases/EN * or http://code.google.com/p/osmbonuspack/source/browse/trunk/OSMNavigator/res/values/poi_tags.xml * @param maxResults the maximum number of POI returned. * Note that in any case, Nominatim will have an absolute maximum of 50. * @param maxDistance to the position, in degrees. * Note that it is used to build a bounding box around the position, not a circle. * @return the list of POI close to position, null if technical issue. */ public ArrayList<POI> getPOICloseTo(GeoPoint position, String facility, int maxResults, double maxDistance){ String url = getUrlCloseTo(position, facility, maxResults, maxDistance); return getThem(url); } /** * @param boundingBox * @param facility Nominatim facility * @param maxResults * @return list of POIs inside the bounding box, null if technical issue. */ public ArrayList<POI> getPOIInside(BoundingBox boundingBox, String facility, int maxResults){ String url = getUrlInside(boundingBox, facility, maxResults); return getThem(url); } /** * @param path * Warning: a very long path may cause a failure due to the url to be too long. * Using a simplified route may help * @param facility Nominatim feature * @param maxResults * @param maxWidth to the path. Certainly not in degrees. Probably in km. * @return list of POIs along the path, null if technical issue. * @see org.osmdroid.bonuspack.routing.Road#getRouteLow for simplifying a route path */ public ArrayList<POI> getPOIAlong(ArrayList<GeoPoint> path, String facility, int maxResults, double maxWidth){ StringBuilder url = getCommonUrl(facility, maxResults); url.append("&routewidth="+maxWidth); url.append("&route="); boolean isFirst = true; for (GeoPoint p:path){ if (isFirst) isFirst = false; else url.append(","); String lat = Double.toString(p.getLatitude()); lat = lat.substring(0, Math.min(lat.length(), 7)); String lon = Double.toString(p.getLongitude()); lon = lon.substring(0, Math.min(lon.length(), 7)); url.append(lat+","+lon); //limit the url length as much as possible, as post method is not supported. } return getThem(url.toString()); } }