it.tidalwave.geo.geocoding.geonamesprovider.GeoNamesProvider Maven / Gradle / Ivy
/***********************************************************************************************************************
*
* forceTen - open source geography
* Copyright (C) 2007-2012 by Tidalwave s.a.s. (http://www.tidalwave.it)
*
***********************************************************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
***********************************************************************************************************************
*
* WWW: http://forceten.tidalwave.it
* SCM: https://bitbucket.org/tidalwave/forceten-src
*
**********************************************************************************************************************/
package it.tidalwave.geo.geocoding.geonamesprovider;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.io.Serializable;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.openide.util.lookup.ServiceProvider;
import it.tidalwave.util.logging.Logger;
import it.tidalwave.util.Finder;
import it.tidalwave.util.NotFoundException;
import it.tidalwave.util.spi.SimpleFinderSupport;
import it.tidalwave.geo.Coordinate;
import it.tidalwave.geo.geocoding.GeoCoder;
import it.tidalwave.geo.geocoding.GeoCoderSupport;
import it.tidalwave.geo.geocoding.GeoCoderEntity;
import it.tidalwave.geo.geocoding.GeoCoderEntity.Type;
import it.tidalwave.geo.geocoding.GeoCoderEntity.FactSheet;
import it.tidalwave.geo.geocoding.GeoCoderEntity.FactSheet.Key;
abstract class GeoNamesFinderSupport extends SimpleFinderSupport
{
public GeoNamesFinderSupport (final @Nonnull String description,
final @Nonnegative int defaultMaxResults)
{
super(description);
maxResults = defaultMaxResults;
}
}
/***********************************************************************************************************************
*
* @author Fabrizio Giudici
* @version $Id$
*
**********************************************************************************************************************/
@edu.umd.cs.findbugs.annotations.SuppressWarnings("REC_CATCH_EXCEPTION")
@ServiceProvider(service=GeoCoder.class)
public class GeoNamesProvider extends GeoCoderSupport
{
private static final String CLASS = GeoNamesProvider.class.getName();
private static final Logger logger = Logger.getLogger(CLASS);
private GeoCoderEntity rootGeoEntity;
private static final XPathFactory xpathFactory = XPathFactory.newInstance();
private static final XPath xpath = xpathFactory.newXPath();
// private static final XPathExpression NAME;
// private static final XPathExpression GEONAME_ID;
/* package */ static final XPathExpression NAME2;
/* package */ static final XPathExpression GEONAME_ID2;
/* package */ static final XPathExpression FCODE;
/* package */ static final XPathExpression TIMEZONE;
/* package */ static final XPathExpression POPULATION;
/* package */ static final XPathExpression ELEVATION;
/* package */ static final XPathExpression ALTERNATE_NAME;
/* package */ static final XPathExpression CONTINENT_CODE;
/* package */ static final XPathExpression COUNTRY_CODE;
/* package */ static final XPathExpression ADMIN1_CODE;
/* package */ static final XPathExpression ADMIN2_CODE;
/* package */ static final XPathExpression LAT;
/* package */ static final XPathExpression LNG;
/* package */ static final XPathExpression GEONAME_SET;
// private static final XPathExpression SUBDIVISION_COUNTRY_CODE;
// private static final XPathExpression SUBDIVISION_COUNTRY_NAME;
// private static final XPathExpression SUBDIVISION_ADMIN_CODE;
// private static final XPathExpression SUBDIVISION_ADMIN_NAME;
public static final String URI_BASE = "http://sws.geonames.org/";
/* package */ static final Map TYPE_MAP = new HashMap();
private static final Map CODE_MAP = new HashMap();
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
static
{
try
{
TYPE_MAP.put("PLANET", Type.PLANET);
TYPE_MAP.put("CONT", Type.CONTINENT);
TYPE_MAP.put("PCLI", Type.COUNTRY);
TYPE_MAP.put("ADM1", Type.ADMIN_DIVISION_1);
TYPE_MAP.put("ADM2", Type.ADMIN_DIVISION_2);
TYPE_MAP.put("ADM3", Type.ADMIN_DIVISION_3);
TYPE_MAP.put("ADM4", Type.ADMIN_DIVISION_4);
TYPE_MAP.put("PPL", Type.INHABITED_PLACE);
// NAME = xpath.compile("//geonames/geoname/name");
// GEONAME_ID = xpath.compile("//geonames/geoname/geonameId");
NAME2 = xpath.compile("name");
GEONAME_ID2 = xpath.compile("geonameId");
LAT = xpath.compile("lat");
LNG = xpath.compile("lng");
FCODE = xpath.compile("fcode");
TIMEZONE = xpath.compile("timezone");
POPULATION = xpath.compile("population");
ELEVATION = xpath.compile("elevation");
ALTERNATE_NAME = xpath.compile("alternateName");
CONTINENT_CODE = xpath.compile("continentCode");
COUNTRY_CODE = xpath.compile("countryCode");
ADMIN1_CODE = xpath.compile("adminCode1");
ADMIN2_CODE = xpath.compile("adminCode2");
CODE_MAP.put("CONT", CONTINENT_CODE);
CODE_MAP.put("PCLI", COUNTRY_CODE);
CODE_MAP.put("ADM1", ADMIN1_CODE);
CODE_MAP.put("ADM2", ADMIN2_CODE);
GEONAME_SET = xpath.compile("//geonames/geoname");
// SUBDIVISION_COUNTRY_CODE = xpath.compile("//geonames/countrySubdivision/countryCode");
// SUBDIVISION_COUNTRY_NAME = xpath.compile("//geonames/countrySubdivision/countryName");
// SUBDIVISION_ADMIN_CODE = xpath.compile("//geonames/countrySubdivision/adminCode1");
// SUBDIVISION_ADMIN_NAME = xpath.compile("//geonames/countrySubdivision/adminName1");
}
catch (Exception e)
{
throw new ExceptionInInitializerError(e);
}
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public String getName()
{
return "GeoNames";
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public String getId()
{
return URI_BASE;
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public synchronized GeoCoderEntity getRoot()
{
if (rootGeoEntity == null)
{
rootGeoEntity = new EarthGeoCoderEntity(this);
}
return rootGeoEntity;
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public synchronized GeoCoderEntity findGeoEntityById (@Nonnull String id)
throws NotFoundException
{
logger.finer("findGeoEntityById(%s)", id);
if (id.startsWith(URI_BASE))
{
id = id.substring(URI_BASE.length()).replace("/", "");
}
if (id.equals(getRoot().getId())) // getRoot() is lazily created
{
return getRoot();
}
GeoCoderEntity gcEntity = findCachedGeoEntityById(id, null);
if ((gcEntity == null) || !gcEntity.isParentSet())
{
gcEntity = null;
final Document document = GeoNames.retrieveDocument("hierarchy", "geonameId", id, "style", "FULL");
try
{
final NodeList nodes = (NodeList)GEONAME_SET.evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++)
{
gcEntity = createGeoCoderEntity(nodes.item(i), gcEntity);
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
return NotFoundException.throwWhenNull(gcEntity, "");
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public Finder findChildren (final @Nonnull String id)
{
logger.finer("findChildren(%s)", id);
return createSparseGeoCoderEntities("findChildren()", "children", 9999999, "geonameId", id, "style", "FULL");
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public Finder findNeighbours (final @Nonnull String id)
{
logger.finer("findNeighbours(%s)", id);
return createSparseGeoCoderEntities("findNeighbours()", "neighbours", 10, "geonameId", id, "style", "FULL");
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public Finder findSiblings (final @Nonnull String id)
{
logger.finer("findSiblings(%s)", id);
return createSparseGeoCoderEntities("findSiblings()", "siblings", 10, "geonameId", id, "style", "FULL");
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public Finder findNearbyPostalCodes (final @Nonnull Coordinate coordinates,
final @Nonnegative float radius)
{
throw new UnsupportedOperationException("Not supported yet."); // TODO
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public Finder findNearestEntity (final @Nonnull Coordinate coordinate)
{
logger.finer("findNearestEntity(%s)", coordinate);
return createSparseGeoCoderEntities("findNearestEntity()", "findNearbyPlaceName", 1,
"lat", coordinate.getLatitude(),
"lng", coordinate.getLongitude(),
"style", "FULL");
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public Finder findEntities (final @Nonnull Coordinate coordinate,
final @Nonnegative float radius)
{
logger.finer("findNearByEntities(%s, %f)", coordinate, radius);
return createSparseGeoCoderEntities("findNearByEntities()", "findNearbyPlaceName", 10,
"lat", coordinate.getLatitude(),
"lng", coordinate.getLongitude(),
"radius", radius,
"style", "FULL");
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public String findCountryCode (final @Nonnull Coordinate coordinate)
{
logger.finer("findCountryCode(%s)", coordinate);
return GeoNames.retrieveString("countrycode", "lat", coordinate.getLatitude(), "lng", coordinate.getLongitude());
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public Type typeFromString (final @Nonnull String typeAsString)
{
final Type type = TYPE_MAP.get(typeAsString);
return (type != null) ? type : Type.UNKNOWN;
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
protected GeoCoderEntity createGeoEntity (final @CheckForNull GeoCoderEntity parent,
final @Nonnull String id,
final @Nonnull String name,
final @Nonnull Coordinate coordinates,
final @Nonnull String code,
final @Nonnull String typeAsString,
final @Nonnull Serializable ... capabilities)
{
final QName qName = new QName(URI_BASE + id +"/");
final List temp = new ArrayList(Arrays.asList(capabilities));
temp.add(qName);
temp.add(new EmblemFactory(id));
return super.createGeoEntity(parent, id, name, coordinates, code, typeAsString,
temp.toArray(new Serializable[temp.size()]));
}
/*******************************************************************************************************************
*
* Creates a collection of {@link GeoCoderEntity} instances out of a {@link Document}. This method assumes that the
* entities are sparse, that is not bound by a hierarchy relation, so they get an initally {@code null} parent, that
* will be set later, on demand.
*
* @param finderName the finder name
* @param document the XML document describing the entities
* @return the entities
*
******************************************************************************************************************/
@Nonnull
private Finder createSparseGeoCoderEntities (final @Nonnull String finderName,
final @Nonnull String command,
final @Nonnegative int defaultMaxResults,
final @Nonnull Object ... args)
{
return new GeoNamesFinderSupport(finderName, defaultMaxResults)
{
@Override @Nonnull
protected List computeResults()
{
final List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy