package com.flickr4java.flickr.places;
import com.flickr4java.flickr.FlickrException;
import com.flickr4java.flickr.Response;
import com.flickr4java.flickr.Transport;
import com.flickr4java.flickr.photos.SearchParameters;
import com.flickr4java.flickr.tags.Tag;
import com.flickr4java.flickr.util.StringUtilities;
import com.flickr4java.flickr.util.XMLUtilities;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Lookup Flickr Places.
* <p>
*
* Announcement on places from yahoo:
* <p>
*
* <PRE>
* From: kellan <kellan@yahoo-inc.com>
* Date: Fri, 11 Jan 2008 15:57:59 -0800
* Subject: [yws-flickr] Flickr and "Place IDs"
*
* At Flickr we've got a really big database that lists a significant
* percentage of the places that exist in the world, and a few that don't.
* When you geotag a photo we try to identify the "place" (neighborhood,
* village, city, county, state, or country) where the photo was taken. And
* we assign that photo a "place ID".
*
* A place ID is a globally unique identifier for a place on Earth. A city
* has a place ID, so do counties, states, and countries. Even some
* neighborhoods and landmarks have them, though Flickr isn't currently
* tracking those. And we're starting to expose these place IDs around Flickr.
*
* ### Place IDs and flickr.photos.search()
*
* The Flickr API method flickr.photos.search() now accepts place_id as an
* argument. Along with all of the other parameters you can
* search on you can now scope your search to a given place. Historically
* you've been able to pass bounding boxes to the API, but calculating the
* right bounding box for a city is tricky, and you can get noise and bad
* results around the edge. Now you can pass a single non-ambiguous string
* and get photos geotagged in San Francisco, CA, or Ohio, or Beijing.
* (kH8dLOubBZRvX_YZ, LtkqzVqbApjAbJxv, and wpK7URqbAJnWB90W respectively)
*
* The documentation has been updated at:
* http://www.flickr.com/services/api/flickr.photos.search.html
*
* ### Sources of Place IDs
*
* Place IDs are now returned from a number of source:
* flickr.photos.getInfo will return place IDs for geotagged photos
* available as a microformat on the appropriate Places page
* flickr.places.resolvePlaceURL, and flickr.places.resolvePlaceId are
* available for round tripping Flickr Places URLs.
*
* http://www.flickr.com/services/api/flickr.photos.getInfo.html
* http://www.flickr.com/services/api/flickr.places.resolvePlaceURL.html
* http://www.flickr.com/services/api/flickr.places.resolvePlaceId.html
*
* ### More Place IDs
*
* Right now you can also place IDs in the places URL, and pass them to the
* map like so:
*
* http://flickr.com/places/wpK7URqbAJnWB90W
* http://flickr.com/map?place_id=kH8dLOubBZRvX_YZ
*
* ### Place IDs elsewhere
*
* The especially eagle-eyed among you might recognize Place IDs. Upcoming
* has been quietly using them for months to uniquely identify their metros.
*
* See events from San Francisco at:
* http://upcoming.yahoo.com/place/kH8dLOubBZRvX_YZ
*
* See photos from San Francisco at: http://flickr.com/places/kH8dLOubBZRvX_YZ
*
* Additionally Yahoo's skunkworks project FireEagle will also support
* place IDs.
*
* And yes, there is more work to do, but we're exciting about this as a start.
*
* Thanks,
* -kellan
* </PRE>
*
* @author mago
* @version $Id: PlacesInterface.java,v 1.10 2011/07/02 19:47:35 x-mago Exp $
*/
public class PlacesInterface {
private static final String METHOD_FIND = "flickr.places.find";
private static final String METHOD_FIND_BY_LATLON = "flickr.places.findByLatLon";
private static final String METHOD_RESOLVE_PLACE_ID = "flickr.places.resolvePlaceId";
private static final String METHOD_RESOLVE_PLACE_URL = "flickr.places.resolvePlaceURL";
private static final String METHOD_GET_CHILDREN_WITH_PHOTOS_PUBLIC = "flickr.places.getChildrenWithPhotosPublic";
private static final String METHOD_GET_INFO = "flickr.places.getInfo";
private static final String METHOD_GET_INFO_BY_URL = "flickr.places.getInfoByUrl";
private static final String METHOD_GET_PLACETYPES = "flickr.places.getPlaceTypes";
private static final String METHOD_GET_SHAPEHISTORY = "flickr.places.getShapeHistory";
private static final String METHOD_GET_TOP_PLACES_LIST = "flickr.places.getTopPlacesList";
private static final String METHOD_PLACES_FOR_BOUNDINGBOX = "flickr.places.placesForBoundingBox";
private static final String METHOD_PLACES_FOR_CONTACTS = "flickr.places.placesForContacts";
private static final String METHOD_PLACES_FOR_TAGS = "flickr.places.placesForTags";
private static final String METHOD_PLACES_FOR_USER = "flickr.places.placesForUser";
private static final String METHOD_TAGS_FOR_PLACE = "flickr.places.tagsForPlace";
private final String apiKey;
private final String sharedSecret;
private final Transport transportAPI;
public PlacesInterface(String apiKey, String sharedSecret, Transport transportAPI) {
this.apiKey = apiKey;
this.sharedSecret = sharedSecret;
this.transportAPI = transportAPI;
}
/**
* Return a list of place IDs for a query string.
*
* The flickr.places.find method is not a geocoder. It will round "up" to the nearest place type to which place IDs apply. For example, if you pass it a
* street level address it will return the city that contains the address rather than the street, or building, itself.
*
* <p>
* This method does not require authentication.
* </p>
*
* @param query
* @return PlacesList
* @throws FlickrException
*/
public PlacesList<Place> find(String query) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
PlacesList<Place> placesList = new PlacesList<Place>();
parameters.put("method", METHOD_FIND);
parameters.put("query", query);
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element placesElement = response.getPayload();
NodeList placesNodes = placesElement.getElementsByTagName("place");
placesList.setPage("1");
placesList.setPages("1");
placesList.setPerPage("" + placesNodes.getLength());
placesList.setTotal("" + placesNodes.getLength());
for (int i = 0; i < placesNodes.getLength(); i++) {
Element placeElement = (Element) placesNodes.item(i);
placesList.add(parsePlace(placeElement));
}
return placesList;
}
/**
* Return a place ID for a latitude, longitude and accuracy triple.
* <p>
*
* The flickr.places.findByLatLon method is not meant to be a (reverse) geocoder in the traditional sense. It is designed to allow users to find photos for
* "places" and will round up to the nearest place type to which corresponding place IDs apply.
* <p>
*
* For example, if you pass it a street level coordinate it will return the city that contains the point rather than the street, or building, itself.
* <p>
*
* It will also truncate latitudes and longitudes to three decimal points.
* <p>
*
* The gory details :
*
* This is (most of) the same magic that is performed when you geotag one of your photos on the site itself. We know that at the neighbourhood level this
* can get messy and not always return the correct location.
* <p>
*
* At the city level things are much better but there may still be some gotchas floating around. Sometimes it's as simple as a bug and other times it is an
* issue of two competing ideas of where a place "is".
* <p>
*
* This comes with the territory and we are eager to identify and wherever possible fix the problems so when you see something that looks wrong please be
* gentle :-)
* <p>
*
* (Reports of incorrect places sent to mailing list will not be ignored but it would be better if you could use the forums for that sort of thing.)
* <p>
*
* Also, as we do on the site if we can not identify a location for a point as a specific accuracy we pop up the stack and try again. For example, if we
* can't find a city for a given set of coordinates we try instead to locate the state.
* <p>
*
* As mentioned above, this method is not designed to serve as a general purpose (reverse) geocoder which is partly reflected by the truncated lat/long
* coordinates.
* <p>
*
* If you think that three decimal points are the cause of wonky results locating photos for places, we are happy to investigate but until then it should be
* All Good (tm).
*
* <p>
* This method does not require authentication.
* </p>
*
* @param latitude
* The latitude whose valid range is -90 to 90. Anything more than 4 decimal places will be truncated.
* @param longitude
* The longitude whose valid range is -180 to 180. Anything more than 4 decimal places will be truncated.
* @param accuracy
* @return A PlacesList
* @throws FlickrException
*/
public PlacesList<Place> findByLatLon(double latitude, double longitude, int accuracy) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
PlacesList<Place> placesList = new PlacesList<Place>();
parameters.put("method", METHOD_FIND_BY_LATLON);
parameters.put("lat", "" + Double.toString(latitude));
parameters.put("lon", "" + Double.toString(longitude));
parameters.put("accuracy", "" + Integer.toString(accuracy));
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element placesElement = response.getPayload();
NodeList placesNodes = placesElement.getElementsByTagName("place");
placesList.setPage("1");
placesList.setPages("1");
placesList.setPerPage("" + placesNodes.getLength());
placesList.setTotal("" + placesNodes.getLength());
for (int i = 0; i < placesNodes.getLength(); i++) {
Element placeElement = (Element) placesNodes.item(i);
placesList.add(parsePlace(placeElement));
}
return placesList;
}
/**
* <p>
* Return a list of locations with public photos that are parented by a Where on Earth (WOE) or Places ID.
* </p>
*
* <p>
* This method does not require authentication.
* </p>
*
* @param placeId
* A Flickr Places ID. Can be null. (While optional, you must pass either a valid Places ID or a WOE ID.)
* @param woeId
* A Where On Earth (WOE) ID. Can be null. (While optional, you must pass either a valid Places ID or a WOE ID.)
* @return List of Places
* @throws FlickrException
*/
public PlacesList<Place> getChildrenWithPhotosPublic(String placeId, String woeId) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
PlacesList<Place> placesList = new PlacesList<Place>();
parameters.put("method", METHOD_GET_CHILDREN_WITH_PHOTOS_PUBLIC);
if (placeId != null) {
parameters.put("place_id", placeId);
}
if (woeId != null) {
parameters.put("woe_id", woeId);
}
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element placesElement = response.getPayload();
NodeList placesNodes = placesElement.getElementsByTagName("place");
placesList.setPage("1");
placesList.setPages("1");
placesList.setPerPage("" + placesNodes.getLength());
placesList.setTotal("" + placesNodes.getLength());
for (int i = 0; i < placesNodes.getLength(); i++) {
Element placeElement = (Element) placesNodes.item(i);
placesList.add(parsePlace(placeElement));
}
return placesList;
}
/**
* Get informations about a place.
*
* <p>
* This method does not require authentication.
* </p>
*
* @param placeId
* A Flickr Places ID. Optional, can be null. (While optional, you must pass either a valid Places ID or a WOE ID.)
* @param woeId
* A Where On Earth (WOE) ID. Optional, can be null. (While optional, you must pass either a valid Places ID or a WOE ID.)
* @return A Location
* @throws FlickrException
*/
public Location getInfo(String placeId, String woeId) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("method", METHOD_GET_INFO);
if (placeId != null) {
parameters.put("place_id", placeId);
}
if (woeId != null) {
parameters.put("woe_id", woeId);
}
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element locationElement = response.getPayload();
return parseLocation(locationElement);
}
/**
* Lookup information about a place, by its flickr.com/places URL.
*
* <p>
* This method does not require authentication.
* </p>
*
* @param url
* @return A Location
* @throws FlickrException
*/
public Location getInfoByUrl(String url) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("method", METHOD_GET_INFO_BY_URL);
parameters.put("url", url);
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element locationElement = response.getPayload();
return parseLocation(locationElement);
}
/**
* Fetches a list of available place types for Flickr.
*
* <p>
* This method does not require authentication.
* </p>
*
* @return A list of placetypes
* @throws FlickrException
*/
public ArrayList<PlaceType> getPlaceTypes() throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("method", METHOD_GET_PLACETYPES);
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
ArrayList<PlaceType> placeTypeList = new ArrayList<PlaceType>();
Element placeTypeElement = response.getPayload();
NodeList placeTypeNodes = placeTypeElement.getElementsByTagName("place_type");
for (int i = 0; i < placeTypeNodes.getLength(); i++) {
placeTypeElement = (Element) placeTypeNodes.item(i);
PlaceType placeType = new PlaceType();
placeType.setPlaceTypeId(placeTypeElement.getAttribute("id"));
placeType.setPlaceTypeName(XMLUtilities.getValue(placeTypeElement));
placeTypeList.add(placeType);
}
return placeTypeList;
}
/**
* Return an historical list of all the shape data generated for a Places or Where on Earth (WOE) ID.
* <p>
*
* <p>
* This method does not require authentication.
* </p>
*
* <p>
* Not working. As it was not possible to find any results. Not even the ones, that have been described in the announcement of this feature.
* </p>
*
* @param placeId
* A Flickr Places ID. Optional, can be null.
* @param woeId
* A Where On Earth (WOE) ID. Optional, can be null.
* @return A list of shapes
* @throws FlickrException
*/
public ShapeDataList<ShapeData> getShapeHistory(String placeId, String woeId) throws FlickrException {
ShapeDataList<ShapeData> shapeList = new ShapeDataList<ShapeData>();
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("method", METHOD_GET_SHAPEHISTORY);
if (placeId != null) {
parameters.put("place_id", placeId);
}
if (woeId != null) {
parameters.put("woe_id", woeId);
}
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element shapeElements = response.getPayload();
shapeList.setTotal(Integer.parseInt(shapeElements.getAttribute("total")));
shapeList.setWoeId(shapeElements.getAttribute("woe_id"));
shapeList.setPlaceId(shapeElements.getAttribute("place_id"));
shapeList.setPlaceType(shapeElements.getAttribute("place_type"));
shapeList.setPlaceTypeId(Integer.parseInt(shapeElements.getAttribute("place_type_id")));
NodeList shapeNodes = shapeElements.getElementsByTagName("shape");
for (int i = 0; i < shapeNodes.getLength(); i++) {
Element shapeElement = (Element) shapeNodes.item(i);
ShapeData data = new ShapeData();
data.setAlpha(Double.parseDouble(shapeElement.getAttribute("alpha")));
data.setCountEdges(Integer.parseInt(shapeElement.getAttribute("count_edges")));
data.setCountPoints(Integer.parseInt(shapeElement.getAttribute("count_points")));
data.setCreated(shapeElement.getAttribute("created"));
data.setIsDonutHole("1".equals(shapeElement.getAttribute("is_donuthole")));
data.setHasDonuthole("1".equals(shapeElement.getAttribute("has_donuthole")));
Element polyElement = XMLUtilities.getChild(shapeElement, "polylines");
data.setPolyline(XMLUtilities.getChildValue(polyElement, "polyline"));
Element urlElement = XMLUtilities.getChild(shapeElement, "urls");
data.setShapefile(XMLUtilities.getChildValue(urlElement, "shapefile"));
shapeList.add(data);
}
return shapeList;
}
/**
* Return the top 100 most geotagged places for a day.
*
* <p>
* This method does not require authentication.
* </p>
*
* @param placeType
* @param date
* Optional, can be null. The default is yesterday.
* @param placeId
* A Flickr Places ID. Optional, can be null.
* @param woeId
* A Where On Earth (WOE) ID. Optional, can be null.
* @return PlacesList
* @throws FlickrException
*/
public PlacesList<Place> getTopPlacesList(int placeType, Date date, String placeId, String woeId) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
PlacesList<Place> placesList = new PlacesList<Place>();
parameters.put("method", METHOD_GET_TOP_PLACES_LIST);
parameters.put("place_type", intPlaceTypeToString(placeType));
if (placeId != null) {
parameters.put("place_id", placeId);
}
if (woeId != null) {
parameters.put("woe_id", woeId);
}
if (date != null) {
parameters.put("date", ((DateFormat) SearchParameters.DATE_FORMATS.get()).format(date));
}
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element placesElement = response.getPayload();
NodeList placesNodes = placesElement.getElementsByTagName("place");
placesList.setPage("1");
placesList.setPages("1");
placesList.setPerPage("" + placesNodes.getLength());
placesList.setTotal("" + placesNodes.getLength());
for (int i = 0; i < placesNodes.getLength(); i++) {
Element placeElement = (Element) placesNodes.item(i);
placesList.add(parsePlace(placeElement));
}
return placesList;
}
/**
* Return all the locations of a matching place type for a bounding box.
* <p>
*
* The maximum allowable size of a bounding box (the distance between the SW and NE corners) is governed by the place type you are requesting. Allowable
* sizes are as follows:
* <ul>
* <li>neighbourhood: 3km (1.8mi)</li>
* <li>locality: 7km (4.3mi)</li>
* <li>county: 50km (31mi)</li>
* <li>region: 200km (124mi)</li>
* <li>country: 500km (310mi)</li>
* <li>continent: 1500km (932mi)</li>
* </ul>
*
* <p>
* This method does not require authentication.
* </p>
*
* @param bbox
* @param placeType
* @return A PlacesList
* @throws FlickrException
*/
public PlacesList<Place> placesForBoundingBox(int placeType, String bbox) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
PlacesList<Place> placesList = new PlacesList<Place>();
parameters.put("method", METHOD_PLACES_FOR_BOUNDINGBOX);
parameters.put("place_type", intPlaceTypeToString(placeType));
parameters.put("bbox", bbox);
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element placesElement = response.getPayload();
NodeList placesNodes = placesElement.getElementsByTagName("place");
placesList.setPage("1");
placesList.setPages("1");
placesList.setPerPage("" + placesNodes.getLength());
placesList.setTotal("" + placesNodes.getLength());
placesList.setBBox(placesElement.getAttribute("bbox"));
placesList.setPlaceType(placesElement.getAttribute("place_type"));
for (int i = 0; i < placesNodes.getLength(); i++) {
Element placeElement = (Element) placesNodes.item(i);
placesList.add(parsePlace(placeElement));
}
return placesList;
}
/**
* Return a list of the top 100 unique places clustered by a given placetype for a user's contacts.
*
* @param placeType
* Use Type-constants at {@link Place}
* @param placeId
* A Flickr Places ID. Optional, can be null.
* @param woeId
* A Where On Earth (WOE) ID. Optional, can be null.
* @param threshold
* The minimum number of photos that a place type must have to be included. If the number of photos is lowered then the parent place type for
* that place will be used. Optional, can be null.
* @param contacts
* Search your contacts. Either 'all' or 'ff' for just friends and family. (Optional, default is all)
* @return A PlacesList
* @throws FlickrException
*/
public PlacesList<Place> placesForContacts(int placeType, String placeId, String woeId, String threshold, String contacts) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
PlacesList<Place> placesList = new PlacesList<Place>();
parameters.put("method", METHOD_PLACES_FOR_CONTACTS);
parameters.put("place_type", intPlaceTypeToString(placeType));
if (placeId != null) {
parameters.put("place_id", placeId);
}
if (woeId != null) {
parameters.put("woe_id", woeId);
}
if (threshold != null) {
parameters.put("threshold", threshold);
}
if (contacts != null) {
parameters.put("contacts", contacts);
}
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element placesElement = response.getPayload();
NodeList placesNodes = placesElement.getElementsByTagName("place");
placesList.setPage("1");
placesList.setPages("1");
placesList.setPerPage("" + placesNodes.getLength());
placesList.setTotal("" + placesNodes.getLength());
for (int i = 0; i < placesNodes.getLength(); i++) {
Element placeElement = (Element) placesNodes.item(i);
placesList.add(parsePlace(placeElement));
}
return placesList;
}
/**
* Return a list of the top 100 unique places clustered by a given placetype for set of tags or machine tags.
*
* <p>
* This method does not require authentication.
* </p>
*
* @param placeTypeId
* @param woeId
* A Where On Earth (WOE) ID. Optional, can be null. (While optional, you must pass either a valid Places ID or a WOE ID.)
* @param placeId
* A Flickr Places ID. Optional, can be null. (While optional, you must pass either a valid Places ID or a WOE ID.)
* @param threshold
* The minimum number of photos that a place type must have to be included. If the number of photos is lowered then the parent place type for
* that place will be used. Optional, can be null.
* @param tags
* A String-array of Tags. Photos with one or more of the tags listed will be returned. Optional, can be null.
* @param tagMode
* Either 'any' for an OR combination of tags, or 'all' for an AND combination. Defaults to 'any' if not specified. Optional, can be null.
* @param machineTags
* @param machineTagMode
* Either 'any' for an OR combination of tags, or 'all' for an AND combination. Defaults to 'any' if not specified. Optional, can be null.
* @param minUploadDate
* Optional, can be null.
* @param maxUploadDate
* Optional, can be null.
* @param minTakenDate
* Optional, can be null.
* @param maxTakenDate
* Optional, can be null.
* @return A PlacesList
* @throws FlickrException
*/
public PlacesList<Place> placesForTags(int placeTypeId, String woeId, String placeId, String threshold, String[] tags, String tagMode, String machineTags,
String machineTagMode, Date minUploadDate, Date maxUploadDate, Date minTakenDate, Date maxTakenDate) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
PlacesList<Place> placesList = new PlacesList<Place>();
parameters.put("method", METHOD_PLACES_FOR_TAGS);
parameters.put("place_type_id", Integer.toString(placeTypeId));
if (woeId != null) {
parameters.put("woe_id", woeId);
}
if (placeId != null) {
parameters.put("place_id", placeId);
}
if (threshold != null) {
parameters.put("threshold", threshold);
}
if (tags != null) {
parameters.put("tags", StringUtilities.join(tags, ","));
}
if (tagMode != null) {
parameters.put("tag_mode", tagMode);
}
if (machineTags != null) {
parameters.put("machine_tags", machineTags);
}
if (machineTagMode != null) {
parameters.put("machine_tag_mode", machineTagMode);
}
if (minUploadDate != null) {
parameters.put("min_upload_date", Long.toString(minUploadDate.getTime() / 1000L));
}
if (maxUploadDate != null) {
parameters.put("max_upload_date", Long.toString(maxUploadDate.getTime() / 1000L));
}
if (minTakenDate != null) {
parameters.put("min_taken_date", ((DateFormat) SearchParameters.MYSQL_DATE_FORMATS.get()).format(minTakenDate));
}
if (maxTakenDate != null) {
parameters.put("max_taken_date", ((DateFormat) SearchParameters.MYSQL_DATE_FORMATS.get()).format(maxTakenDate));
}
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element placesElement = response.getPayload();
NodeList placesNodes = placesElement.getElementsByTagName("place");
placesList.setPage("1");
placesList.setPages("1");
placesList.setPerPage("" + placesNodes.getLength());
placesList.setTotal("" + placesNodes.getLength());
for (int i = 0; i < placesNodes.getLength(); i++) {
Element placeElement = (Element) placesNodes.item(i);
placesList.add(parsePlace(placeElement));
}
return placesList;
}
/**
* Return a list of the top 100 unique places clustered by a given placetype for a user.
*
* @param placeType
* Use Type-constants at {@link Place}
* @param woeId
* A Where On Earth (WOE) ID. Optional, can be null.
* @param placeId
* A Flickr Places ID. Optional, can be null.
* @param threshold
* The minimum number of photos that a place type must have to be included. If the number of photos is lowered then the parent place type for
* that place will be used. Optional, can be null.
* @param minUploadDate
* Optional, can be null.
* @param maxUploadDate
* Optional, can be null.
* @param minTakenDate
* Optional, can be null.
* @param maxTakenDate
* Optional, can be null.
* @return A PlacesList
* @throws FlickrException
*/
public PlacesList<Place> placesForUser(int placeType, String woeId, String placeId, String threshold, Date minUploadDate, Date maxUploadDate,
Date minTakenDate, Date maxTakenDate) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
PlacesList<Place> placesList = new PlacesList<Place>();
parameters.put("method", METHOD_PLACES_FOR_USER);
parameters.put("place_type", intPlaceTypeToString(placeType));
if (placeId != null) {
parameters.put("place_id", placeId);
}
if (woeId != null) {
parameters.put("woe_id", woeId);
}
if (threshold != null) {
parameters.put("threshold", threshold);
}
if (minUploadDate != null) {
parameters.put("min_upload_date", Long.toString(minUploadDate.getTime() / 1000L));
}
if (maxUploadDate != null) {
parameters.put("max_upload_date", Long.toString(maxUploadDate.getTime() / 1000L));
}
if (minTakenDate != null) {
parameters.put("min_taken_date", ((DateFormat) SearchParameters.MYSQL_DATE_FORMATS.get()).format(minTakenDate));
}
if (maxTakenDate != null) {
parameters.put("max_taken_date", ((DateFormat) SearchParameters.MYSQL_DATE_FORMATS.get()).format(maxTakenDate));
}
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element placesElement = response.getPayload();
NodeList placesNodes = placesElement.getElementsByTagName("place");
placesList.setPage("1");
placesList.setPages("1");
placesList.setPerPage("" + placesNodes.getLength());
placesList.setTotal("" + placesNodes.getLength());
for (int i = 0; i < placesNodes.getLength(); i++) {
Element placeElement = (Element) placesNodes.item(i);
placesList.add(parsePlace(placeElement));
}
return placesList;
}
/**
* Find Flickr Places information by Place ID.
*
* @deprecated This method has been deprecated. It won't be removed but you should use {@link #getInfo(String, String)} instead.
* @param placeId
* @return A Location
* @throws FlickrException
*/
@Deprecated
public Location resolvePlaceId(String placeId) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("method", METHOD_RESOLVE_PLACE_ID);
parameters.put("place_id", placeId);
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element locationElement = response.getPayload();
return parseLocation(locationElement);
}
/**
* Find Flickr Places information by Place URL.
*
* <p>
* This method does not require authentication.
* </p>
*
* @deprecated This method has been deprecated. It won't be removed but you should use {@link PlacesInterface#getInfoByUrl(String)} instead.
* @param flickrPlacesUrl
* @return A Location
* @throws FlickrException
*/
@Deprecated
public Location resolvePlaceURL(String flickrPlacesUrl) throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("method", METHOD_RESOLVE_PLACE_URL);
parameters.put("url", flickrPlacesUrl);
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element locationElement = response.getPayload();
return parseLocation(locationElement);
}
/**
* Return a list of the top 100 unique tags for a Flickr Places or Where on Earth (WOE) ID.
*
* <p>
* This method does not require authentication.
* </p>
*
* @param woeId
* A Where On Earth (WOE) ID. Optional, can be null.
* @param placeId
* A Flickr Places ID. Optional, can be null.
* @param minUploadDate
* Optional, can be null.
* @param maxUploadDate
* Optional, can be null.
* @param minTakenDate
* Optional, can be null.
* @param maxTakenDate
* Optional, can be null.
* @return A list of Tags
* @throws FlickrException
*/
public ArrayList<Tag> tagsForPlace(String woeId, String placeId, Date minUploadDate, Date maxUploadDate, Date minTakenDate, Date maxTakenDate)
throws FlickrException {
Map<String, Object> parameters = new HashMap<String, Object>();
ArrayList<Tag> tagsList = new ArrayList<Tag>();
parameters.put("method", METHOD_TAGS_FOR_PLACE);
if (woeId != null) {
parameters.put("woe_id", woeId);
}
if (placeId != null) {
parameters.put("place_id", placeId);
}
if (minUploadDate != null) {
parameters.put("min_upload_date", Long.toString(minUploadDate.getTime() / 1000L));
}
if (maxUploadDate != null) {
parameters.put("max_upload_date", Long.toString(maxUploadDate.getTime() / 1000L));
}
if (minTakenDate != null) {
parameters.put("min_taken_date", ((DateFormat) SearchParameters.MYSQL_DATE_FORMATS.get()).format(minTakenDate));
}
if (maxTakenDate != null) {
parameters.put("max_taken_date", ((DateFormat) SearchParameters.MYSQL_DATE_FORMATS.get()).format(maxTakenDate));
}
Response response = transportAPI.get(transportAPI.getPath(), parameters, apiKey, sharedSecret);
if (response.isError()) {
throw new FlickrException(response.getErrorCode(), response.getErrorMessage());
}
Element tagsElement = response.getPayload();
NodeList tagsNodes = tagsElement.getElementsByTagName("tag");
for (int i = 0; i < tagsNodes.getLength(); i++) {
Element tagElement = (Element) tagsNodes.item(i);
Tag tag = new Tag();
tag.setCount(tagElement.getAttribute("count"));
tag.setValue(XMLUtilities.getValue(tagElement));
tagsList.add(tag);
}
return tagsList;
}
private Location parseLocation(Element locationElement) {
Location location = new Location();
Element localityElement = (Element) locationElement.getElementsByTagName("locality").item(0);
Element countyElement = (Element) locationElement.getElementsByTagName("county").item(0);
Element regionElement = (Element) locationElement.getElementsByTagName("region").item(0);
Element countryElement = (Element) locationElement.getElementsByTagName("country").item(0);
location.setPlaceId(locationElement.getAttribute("place_id"));
// location.setName(locationElement.getAttribute("name"));
location.setPlaceUrl(locationElement.getAttribute("place_url"));
location.setWoeId(locationElement.getAttribute("woeid"));
location.setLatitude(locationElement.getAttribute("latitude"));
location.setLongitude(locationElement.getAttribute("longitude"));
location.setTimezone(locationElement.getAttribute("timezone"));
location.setName(locationElement.getAttribute("name"));
location.setWoeName(locationElement.getAttribute("woe_name"));
location.setIsHasShapeData("1".equals(locationElement.getAttribute("has_shapedata")));
location.setPlaceType(stringPlaceTypeToInt(locationElement.getAttribute("place_type")));
location.setLocality(parseLocationPlace(localityElement, Place.TYPE_LOCALITY));
location.setCounty(parseLocationPlace(countyElement, Place.TYPE_COUNTY));
location.setRegion(parseLocationPlace(regionElement, Place.TYPE_REGION));
location.setCountry(parseLocationPlace(countryElement, Place.TYPE_COUNTRY));
return location;
}
private Place parseLocationPlace(Element element, int type) {
Place place = null;
if (element != null) {
place = new Place();
place.setName(XMLUtilities.getValue(element));
place.setPlaceId(element.getAttribute("place_id"));
place.setPlaceUrl(element.getAttribute("place_url"));
place.setWoeId(element.getAttribute("woeid"));
place.setLatitude(element.getAttribute("latitude"));
place.setLongitude(element.getAttribute("longitude"));
place.setPlaceType(type);
}
return place;
}
private Place parsePlace(Element placeElement) {
Place place = new Place();
place.setPlaceId(placeElement.getAttribute("place_id"));
place.setPlaceUrl(placeElement.getAttribute("place_url"));
place.setWoeId(placeElement.getAttribute("woeid"));
place.setLatitude(placeElement.getAttribute("latitude"));
place.setLongitude(placeElement.getAttribute("longitude"));
place.setPhotoCount(placeElement.getAttribute("photo_count"));
// String typeString = placeElement.getAttribute("place_type");
// Now the place-Id is directly available
place.setPlaceType(placeElement.getAttribute("place_type_id"));
// place.setPlaceType(stringPlaceTypeToInt(typeString));
place.setName(XMLUtilities.getValue(placeElement));
return place;
}
private int stringPlaceTypeToInt(String typeString) {
int placeType = 0;
if (typeString.equals("locality")) {
placeType = Place.TYPE_LOCALITY;
} else if (typeString.equals("county")) {
placeType = Place.TYPE_COUNTY;
} else if (typeString.equals("region")) {
placeType = Place.TYPE_REGION;
} else if (typeString.equals("country")) {
placeType = Place.TYPE_COUNTRY;
} else if (typeString.equals("continent")) {
placeType = Place.TYPE_CONTINENT;
} else if (typeString.equals("neighbourhood")) {
placeType = Place.TYPE_NEIGHBOURHOOD;
}
return placeType;
}
public String intPlaceTypeToString(int placeType) throws FlickrException {
String placeTypeStr = "";
if (placeType == Place.TYPE_COUNTRY) {
placeTypeStr = "country";
} else if (placeType == Place.TYPE_REGION) {
placeTypeStr = "region";
} else if (placeType == Place.TYPE_LOCALITY) {
placeTypeStr = "locality";
} else if (placeType == Place.TYPE_CONTINENT) {
placeTypeStr = "continent";
} else if (placeType == Place.TYPE_NEIGHBOURHOOD) {
placeTypeStr = "neighbourhood";
} else {
throw new FlickrException("33", "Not a valid place type");
}
return placeTypeStr;
}
}