ar.com.zauber.commons.gis.street.impl.GMapsStreetDao.java Source code

Java tutorial

Introduction

Here is the source code for ar.com.zauber.commons.gis.street.impl.GMapsStreetDao.java

Source

/**
 * Copyright (c) 2005-2011 Zauber S.A. <http://www.zaubersoftware.com/>
 *
 * 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.
 */
package ar.com.zauber.commons.gis.street.impl;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.UnhandledException;
import org.apache.commons.lang.Validate;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import ar.com.zauber.commons.dao.Paging;
import ar.com.zauber.commons.gis.Result;
import ar.com.zauber.commons.gis.street.StreetsDAO;
import ar.com.zauber.commons.gis.street.model.results.GeocodeResult;
import ar.com.zauber.commons.gis.street.model.results.IntersectionResult;
import ar.com.zauber.commons.gis.street.model.results.StreetResult;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.PrecisionModel;

/**
 * Finds streets using gmaps API. StreetDAO decorator for the rest.
 *
 * @author C. Andrs Moratti
 * @since Dec 2, 2008
 */
public class GMapsStreetDao implements StreetsDAO {

    private final String apiKey;
    private final StreetsDAO streetsDAO;
    private int precision;

    /**
     * Creates the GMapsStreetDao.
     *
     * @param apiKey api key
     * @param streetsDAO streets dao
     */
    public GMapsStreetDao(final String apiKey, final StreetsDAO streetsDAO) {
        this(apiKey, streetsDAO, 0);
    }

    /**
     * Creates the GMapsStreetDao.
     *
     * @param apiKey api key
     * @param streetsDAO streets dao
     */
    public GMapsStreetDao(final String apiKey, final StreetsDAO streetsDAO, final String countryCodeBias) {
        this(apiKey, streetsDAO, 0);
    }

    /**
     * Creates the GMapsStreetDao.
     *
     * @param apiKey api key
     * @param streetsDAO streets dao
     * @param precision la precision minima q se desea.
     * @see http://code.google.com/apis/maps/documentation/reference.html#GGeoAddressAccuracy
     */
    public GMapsStreetDao(final String apiKey, final StreetsDAO streetsDAO, final int precision) {
        Validate.isTrue(!StringUtils.isEmpty(apiKey));
        Validate.notNull(streetsDAO);
        this.apiKey = apiKey;
        this.streetsDAO = streetsDAO;
        this.precision = precision;
    }

    /** @see StreetsDAO#getStreets(String) */
    public final List<Result> getStreets(final String query) {
        Validate.isTrue(!StringUtils.isEmpty(query));
        final List<Result> results = new LinkedList<Result>();
        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);

        try {
            final String uri = "http://maps.google.com/maps/geo?" + "q=" + URLEncoder.encode(query, "UTF-8")
                    + "&output=xml" + "&sensor=true_or_false" + "&oe=utf8" + "&key=" + apiKey;

            final DocumentBuilder parser = factory.newDocumentBuilder();
            final Document document = parser.parse(uri);
            GMapsResultGenerator resultGenerator = new GMapsResultGenerator(precision);
            results.addAll(resultGenerator.getResults(document));
        } catch (final IOException e) {
            results.addAll(streetsDAO.getStreets(query));
            throw new UnhandledException(e);
        } catch (final ParserConfigurationException e) {
            throw new UnhandledException(e);
        } catch (final SAXException e) {
            throw new UnhandledException(e);
        } catch (final DOMException e) {
            throw new UnhandledException(e);
        }

        return results;
    }

    /** @see StreetsDAO#suggestAddresses(String) */
    public final List<Result> suggestAddresses(final String text) {
        final List<Result> suggestResults = streetsDAO.suggestAddresses(text);
        final List<Result> results = new ArrayList<Result>();

        // Los resultados que se sugieren son solamente de CABA
        // producto del geocoding de flof
        for (final Result suggestResult : suggestResults) {
            results.add(new StreetResult(suggestResult.getDescription(), suggestResult.getPoint(),
                    "Ciudad Autnoma de Buenos Aires", suggestResult.getCountryCode()));
        }

        return results;
    }

    /** @see StreetsDAO#fullNameStreetExist(String) */
    public final boolean fullNameStreetExist(final String name) {
        return streetsDAO.fullNameStreetExist(name);
    }

    /** @see StreetsDAO#geocode(String, int) */
    public final Collection<GeocodeResult> geocode(final String street, final int altura) {
        return streetsDAO.geocode(street, altura);
    }

    /** @see StreetsDAO#geocode(String, int, int) */
    public final Collection<GeocodeResult> geocode(final String street, final int altura, final int id) {
        return streetsDAO.geocode(street, altura, id);
    }

    /** @see StreetsDAO#getIntersection(String, String) */
    public final Collection<IntersectionResult> getIntersection(final String street1, final String street2) {
        return streetsDAO.getIntersection(street1, street2);
    }

    /** @see StreetsDAO#getIntersectionsFor(String) */
    public final List<String> getIntersectionsFor(final String fullStreetName) {
        return streetsDAO.getIntersectionsFor(fullStreetName);
    }

    /** @see StreetsDAO#getSinonimos(String) */
    public final List<String> getSinonimos(final String fullStreetName) {
        return streetsDAO.getSinonimos(fullStreetName);
    }

    /** @see StreetsDAO#guessStreetName(java.util.List, String) */
    public final List<GuessStreetResult> guessStreetName(final List<String> streets,
            final String unnomalizedStreetName) {
        return streetsDAO.guessStreetName(streets, unnomalizedStreetName);
    }

    /** @see StreetsDAO#suggestStreets(String, Paging) */
    public final List<String> suggestStreets(final String beggining, final Paging paging) {
        return streetsDAO.suggestStreets(beggining, paging);
    }

}

/**
 *Devuelve un List de results a partir de un document
 *
 * @author Mariano Semelman
 * @since Dec 14, 2009
 */
class GMapsResultGenerator {

    private final GeometryFactory geometryFactory;
    private int accu;

    /** Creates the GMapsResultGenerator. */
    public GMapsResultGenerator() {
        this(8);

    }

    /** constructor que permite definir la precision minima que se desea
     * http://code.google.com/apis/maps/documentation/reference.html#GGeoAddressAccuracy */
    public GMapsResultGenerator(final int accu) {
        geometryFactory = new GeometryFactory(new PrecisionModel(), 4326);
        this.accu = accu;
    }

    /**
     *
     * @param document de donde obtener resultados
     * @return la lista de resultados.
     */
    public final Collection<Result> getResults(final Document document) {
        Collection<Result> collection = new LinkedList<Result>();
        getPlacemarks(document, collection);
        return collection;
    }

    /**
     *
     * @param node nodo donde buscar los "placemark" (ver kml reference)
     * @param collection donde almacenar los results
     */
    private void getPlacemarks(final Node node, final Collection<Result> collection) {
        Validate.notNull(node);
        Validate.notNull(collection);
        final NodeList list = node.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node subnode = list.item(i);
            String nombre = subnode.getNodeName().trim();
            if (nombre.equals("Placemark")) {
                Result element = getPlace(subnode);
                if (element != null) {
                    collection.add(element);
                }
            } else if (!nombre.equals("#text")) {
                getPlacemarks(subnode, collection);
            }
        }

    }

    /**
     *
     * @param node en este nodo esta el placemark
     * @return un {@link Result} con los datos.
     */
    private Result getPlace(final Node node) throws NumberFormatException {
        Validate.notNull(node);
        Validate.isTrue(node.getNodeName().equals("Placemark"));
        Result res = null;

        Node details = find("AddressDetails", node);
        Node coordenadas = find("coordinates", node);
        // obtengo coordenadas y si es preciso
        if (coordenadas != null && details != null && details.hasAttributes()) {
            Node nprecision = details.getAttributes().getNamedItem("Accuracy");
            if (nprecision != null) {
                String precision = nprecision.getTextContent();
                boolean preciso = Integer.parseInt(precision) >= accu;
                String coord = coordenadas.getTextContent();
                final String[] lonLat = coord.split(",");
                if (lonLat.length > 2 && preciso) {
                    final Double lon = Double.valueOf(lonLat[0]);
                    final Double lat = Double.valueOf(lonLat[1]);

                    // obtengo nombre entero de la direccion
                    String name = find("address", node).getTextContent();

                    //obtengo pais
                    Node country = find("CountryNameCode", details);
                    String countryCode = (country == null) ? null : country.getTextContent();

                    // obtengo ciudad
                    Node place = find("LocalityName", details);
                    Node ciudad = (place == null) ? find("AdministrativeAreaName", details) : place;
                    String city = (ciudad == null) ? null : ciudad.getTextContent();

                    //armo el resultado
                    if (lon != null && lat != null && name != null && city != null && countryCode != null) {
                        res = new StreetResult(name, geometryFactory.createPoint(new Coordinate(lon, lat)), city,
                                countryCode);
                    }
                }
            }
        }
        return res;
    }

    /**
     * @param key nombre del tag a buscar
     * @param node estructura donde buscar.
     * @return el nodo con el tag con ese key como nombre
     */
    private Node find(final String key, final Node node) {
        Validate.notNull(node);
        Validate.isTrue(StringUtils.isNotBlank(key));
        NodeList list = node.getChildNodes();
        Node res = null;
        for (int index = 0; index < list.getLength(); index++) {
            String name = list.item(index).getNodeName();
            if (name.equals(key) && res == null) {
                res = list.item(index);
            } else if (name.equals(key)) {
                throw new UnhandledException("Este metodo debe ser llamado cuando se sabe que"
                        + "no existen mas de un nodo con este nombre", null);
            } else if (res == null) {
                res = find(key, list.item(index));
            }
        }
        return res;
    }

}