/**
* GeoDress - A program for reverse geocoding
* Copyright (C) 2010 Stefan T.
*
* See COPYING for Details.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package geodress.model.reader;
import geodress.main.Logging;
import geodress.model.Address;
import geodress.model.Coordinate;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* A location reader that gets the address from Google Maps.
*
* @author Stefan T.
* @see "http://code.google.com/intl/en/apis/maps/documentation/services.html"
*/
public class GoogleMapsReader implements LocationReader {
/** Logger object */
private Logger logger = null;
/**
* standard Google Maps API key for <a
* href="http://geodress.sourceforge.net"
* >http://geodress.sourceforge.net</a>
*/
private final static String GOOGLE_MAPS_API_KEY = "ABQIAAAAz3DnkG-LcuBIXz2lfXNr_xRyL367N49j2iW21h-7GXG3qz_7-hRkguI3dEfa8B9XuirrjZ-ZD3Eg0Q";
/**
* Google maps API key, it is needed to use the Google Maps API
*
* @see "http://code.google.com/intl/en-US/apis/maps/faq.html#geocoder_limit"
*/
private String googleMapsKey;
/**
* Country code top-level domain that specifies the region bias for the Google Maps request
*
* @see "http://code.google.com/intl/en/apis/maps/documentation/geocoding/index.html#RegionCodes"
*/
private String googleRegion;
/**
* Initializes the reader and generates a random Google Maps key.
*/
public GoogleMapsReader() {
this(GOOGLE_MAPS_API_KEY);
}
/**
* Initializes the reader with a special Google Maps key.
*
* @param googleMapsKey
* the Google Maps API key
*/
public GoogleMapsReader(String googleMapsKey) {
logger = Logging.getLogger(this.getClass().getName());
setGoogleMapsKey(googleMapsKey);
setGoogleRegion("us");
}
/**
* @return the Google Maps key
*/
public String getGoogleMapsKey() {
return googleMapsKey;
}
/**
* @param googleMapsKey
* the Google Maps key to set
*/
public void setGoogleMapsKey(String googleMapsKey) {
this.googleMapsKey = googleMapsKey;
}
/**
* @return the region
*/
public String getGoogleRegion() {
return googleRegion;
}
/**
* @param googleRegion a region to set
*/
public void setGoogleRegion(String googleRegion) {
this.googleRegion = googleRegion;
}
/**
* @throws UnknownHostException
* @see geodress.model.reader.LocationReader#getAddress(geodress.model.Coordinate)
*/
@Override
public Address getAddress(Coordinate coordinates)
throws UnknownHostException {
URI GoogleMapsXml = null;
try {
GoogleMapsXml = new URI(
"http://maps.google.com/maps/geo?output=xml&q="
+ coordinates.getLatitude() + ","
+ coordinates.getLongitude() + "&key="
+ googleMapsKey + "®ion="+googleRegion+"&output=xml");
} catch (URISyntaxException urise) {
logger.log(Level.WARNING, "URI for Google Maps address retrieval",
urise);
}
try {
return getAddress(GoogleMapsXml);
} catch (SAXException saxe) {
logger.log(Level.WARNING,
"SAX error while reading Google Maps XML file", saxe);
} catch (UnknownHostException uhe) {
throw new UnknownHostException(
"error while retrieving Google Maps XML file "
+ "(maybe no Internet connection)");
} catch (IOException ioe) {
logger.log(Level.WARNING,
"error while reading Google Maps XML file", ioe);
}
return new Address();
}
/**
* Gets the address by an XML file from the Google Maps APIs.
*
* @param xmlFile
* the URI of the XML file with the data
* @return an Address object with the address
* @throws IOException
* if an I/O error occurs
* @throws SAXException
* if an SAX error occurs
* @throws UnknownHostException
* if the Google Maps XML file could not be retrieved (maybe no
* Internet connection)
*/
private Address getAddress(URI xmlFile) throws SAXException, IOException {
logger.log(Level.FINEST, "get address by Google XML from "
+ xmlFile.toString());
/* parse XML file */
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException pce) {
logger
.log(
Level.WARNING,
"parser for Google Maps XML file could not be created",
pce);
}
Document document = builder.parse(xmlFile.toString());
int statusCode = Integer.valueOf(getNode(
new LinkedList<String>(Arrays.asList("kml", "Response",
"Status", "code")), document).getTextContent());
logger.log(Level.FINEST, "status code of XML file: " + statusCode);
Node address = getNode(new LinkedList<String>(Arrays.asList("kml",
"Response", "Placemark", "address")), document);
if (address != null) {
logger.log(Level.FINEST, "address found: "
+ address.getTextContent());
return new Address(address.getTextContent().trim(), String
.valueOf(statusCode));
} else {
return new Address();
}
}
/**
* @see geodress.model.reader.LocationReader#toString()
*/
@Override
public String toString() {
return "Google Maps API";
}
/**
* Gets a special node in a XML document via DOM.
*
* @param path
* the path where the node is located, this is a list with the
* node (like a path)
* @param node
* the root of the path
* @return the wanted element
*/
private Node getNode(LinkedList<String> path, Node node) {
if (path.isEmpty()) {
return node;
} else {
NodeList ndList = node.getChildNodes();
for (int i = 0; i < ndList.getLength(); i++) {
if (ndList.item(i).getNodeName() == path.getFirst()) {
path.removeFirst();
return getNode(path, ndList.item(i));
}
}
}
return null;
}
}