Package org.geomajas.plugin.geocoder.service

Source Code of org.geomajas.plugin.geocoder.service.YahooPlaceFinderGeocoderService

/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2011 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/

package org.geomajas.plugin.geocoder.service;

import com.vividsolutions.jts.geom.Coordinate;
import org.geomajas.geometry.Bbox;
import org.geomajas.annotation.Api;
import org.geomajas.geometry.Crs;
import org.geomajas.plugin.geocoder.api.GeocoderService;
import org.geomajas.plugin.geocoder.api.GetLocationResult;
import org.geomajas.service.DtoConverterService;
import org.geomajas.service.GeoService;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
* Geocoder service using the Yahoo! PlaceFinder.
* See <a href="http://developer.yahoo.com/geo/placefinder/">http://developer.yahoo.com/geo/placefinder/</a>
*
* @author Joachim Van der Auwera
*/
public class YahooPlaceFinderGeocoderService implements GeocoderService {

  private final Logger log = LoggerFactory.getLogger(YahooPlaceFinderGeocoderService.class);

  private static final double DELTA = 1e-20; // max coordinate variation for equal locations
  private static final String USER_AGENT = "Geomajas Yahoo! PlaceFinder geocoder service";
  private static final int READ_TIMEOUT = 120000;
  private static final int CONNECT_TIMEOUT = 10000;
  private static final String URL_BASE = "http://where.yahooapis.com/geocode?";

  @Autowired
  private GeoService geoService;

  @Autowired
  private DtoConverterService dtoConverterService;

  @Autowired
  private SplitCommaReverseService splitCommaReverseService;

  private Crs crs;

  private String name = "YahooPlaceFinder";

  private String appId;
  private String appIdProperty;
  private boolean skipAppIdCheck;

  @PostConstruct
  protected void initCrs() throws Exception {
    crs = geoService.getCrs2("EPSG:4326"); // WGS-84 latlong

    if (null != appIdProperty) {
      appId = System.getProperty(appIdProperty);
    }
    if (null == appId && !skipAppIdCheck) {
      throw new RuntimeException("Cannot instantiate YahooPlaceFinderGeocoderService, appId not known, " +
          "you need to set appId or appIdProperty.");
    }
  }

  /** {@inheritDoc} */
  public String getName() {
    return name;
  }

  /**
   * Set the name for this geocoder service.
   *
   * @param name name
   */
  @Api
  public void setName(String name) {
    this.name = name;
  }

  /**
   * Set the appId which is required for accessing the service.
   *
   * @param appId Yahoo! appId
   */
  @Api
  public void setAppId(String appId) {
    this.appId = appId;
  }

  /**
   * Set a property which contains the appId. This allows setting the appId from the command line instead of the
   * configuration files.
   *
   * @param appIdProperty property which contains the appId
   */
  @Api
  public void setAppIdProperty(String appIdProperty) {
    this.appIdProperty = appIdProperty;
  }

  /**
   * When setting this to true, you can avoid having an exception throws when the appId is not known.
   * It will just find nothing (it won't even try).
   * This can be useful in combination with the setAappIdProperty() to make it run then the property is not set.
   *
   * @param skipAppIdCheck true to disable throwing the error when no appId is specified
   */
  @Api
  public void setSkipAppIdCheck(boolean skipAppIdCheck) {
    this.skipAppIdCheck = skipAppIdCheck;
  }

  /** {@inheritDoc} */
  public CoordinateReferenceSystem getCrs() {
    return crs;
  }

   /** {@inheritDoc} */
  public GetLocationResult[] getLocation(List<String> location, int maxAlternatives, Locale locale) {
    if (null == appId) {
      return null;
    }
    try {
      String query = splitCommaReverseService.combine(location);
      List<GetLocationResult> locations = search(query, maxAlternatives, locale);
      return locations.toArray(new GetLocationResult[locations.size()]);
    } catch (Exception ex) {
      log.error("Search failed", ex);
      return null;
    }
  }

  /**
   * Call the Yahoo! PlaceFinder service for a result.
   *
   * @param q search string
   * @param maxRows max number of rows in result, or 0 for all
   * @param locale locale for strings
   * @return list of found results
   * @throws Exception oops
   * @see <a href="http://developer.yahoo.com/geo/placefinder/guide/">Yahoo! PlaceFinder Guide</a>
   */
  public List<GetLocationResult> search(String q, int maxRows, Locale locale) throws Exception {
    List<GetLocationResult> searchResult = new ArrayList<GetLocationResult>();

    String url = "q=" + URLEncoder.encode(q, "UTF8");
    if (maxRows > 0) {
      url = url + "&count=" + maxRows;
    }
    url = url + "&flags=GX";
    if (null != locale) {
      url = url + "&locale=" + locale;
    }
    url = url + "&appid=" + appId;

    InputStream inputStream = connect(url);
    if (null != inputStream) {
      SAXBuilder parser = new SAXBuilder();
      Document doc = parser.build(inputStream);

      Element root = doc.getRootElement();

      // check code for exception
      String message = root.getChildText("Error");
      Integer errorCode = Integer.parseInt(message);
      if (errorCode != 0) {
        throw new Exception(root.getChildText("ErrorMessage"));
      }

      for (Object obj : root.getChildren("Result")) {
        Element toponymElement = (Element) obj;
        GetLocationResult location = getLocationFromElement(toponymElement);
        searchResult.add(location);
      }
    }
    return searchResult;
  }

  /**
   * Open the connection to the server.
   *
   * @param url the url to connect to
   * @return returns the input stream for the connection
   * @throws java.io.IOException cannot get result
   */
  private InputStream connect(String url) throws IOException {
    URLConnection conn = new URL(URL_BASE + url).openConnection();
    conn.setConnectTimeout(CONNECT_TIMEOUT);
    conn.setReadTimeout(READ_TIMEOUT);
    conn.setRequestProperty("User-Agent", USER_AGENT);
    return conn.getInputStream();
  }

  private GetLocationResult getLocationFromElement(Element locationElement) {
    GetLocationResult location = new GetLocationResult();

    List<String> canonical = new ArrayList<String>();
    canonicalAdd(canonical, locationElement, "line4");
    canonicalAdd(canonical, locationElement, "line3");
    canonicalAdd(canonical, locationElement, "line2");
    canonicalAdd(canonical, locationElement, "line1");
    if (0 == canonical.size()) {
      canonicalAdd(canonical, locationElement, "level5");
      canonicalAdd(canonical, locationElement, "level4");
      canonicalAdd(canonical, locationElement, "level3");
      canonicalAdd(canonical, locationElement, "level2");
      canonicalAdd(canonical, locationElement, "level1");
      canonicalAdd(canonical, locationElement, "level0");
    }
    location.setCanonicalStrings(canonical);

    Element area = locationElement.getChild("boundingbox");
    if (null != area) {
      double north = Double.parseDouble(area.getChildText("north"));
      double south = Double.parseDouble(area.getChildText("south"));
      double east = Double.parseDouble(area.getChildText("east"));
      double west = Double.parseDouble(area.getChildText("west"));
      if (Math.abs(north - south) > DELTA || Math.abs(east - west) > DELTA) {
        // return point when bbox is a point
        Bbox bbox = new Bbox(west, south, east - west, north - south);
        location.setEnvelope(dtoConverterService.toInternal(bbox));
      }
    }

    location.setCoordinate(new Coordinate(Double.parseDouble(locationElement.getChildText("longitude")),
        Double.parseDouble(locationElement.getChildText("latitude"))));

    return location;
  }

  private void canonicalAdd(List<String> canonical, Element locationElement, String tag) {
    String value = locationElement.getChildText(tag);
    if (null != value && value.length() > 0) {
      canonical.add(value);
    }
  }

}
TOP

Related Classes of org.geomajas.plugin.geocoder.service.YahooPlaceFinderGeocoderService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.