org.structr.common.geo.BingGeoCodingProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.structr.common.geo.BingGeoCodingProvider.java

Source

/**
 * Copyright (C) 2010-2016 Structr GmbH
 *
 * This file is part of Structr <http://structr.org>.
 *
 * Structr 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.
 *
 * Structr 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 Structr.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.structr.common.geo;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.structr.common.geo.GeoCodingResult.Type;

/**
 *
 *
 */
public class BingGeoCodingProvider extends AbstractGeoCodingProvider {

    private static final Logger logger = Logger.getLogger(BingGeoCodingProvider.class.getName());

    @Override
    public GeoCodingResult geocode(String street, String house, String postalCode, String city, String state,
            String country, String language) throws IOException {

        if (apiKey != null && !apiKey.isEmpty()) {

            StringBuilder urlBuffer = new StringBuilder("http://dev.virtualearth.net/REST/v1/Locations");

            // api key
            urlBuffer.append("?key=").append(apiKey);

            // culture for language-specific formatting
            urlBuffer.append("&c=").append(language);

            // output format: XML
            urlBuffer.append("&o=xml");

            // address line string
            urlBuffer.append("&query=");

            // house
            if (house != null && !house.isEmpty()) {
                urlBuffer.append(encodeURL(house)).append("+");
            }

            // street
            if (street != null && !street.isEmpty()) {
                urlBuffer.append(encodeURL(street)).append("+");
            }

            // city
            if (city != null && !city.isEmpty()) {
                urlBuffer.append(encodeURL(city)).append("+");
            }

            // postalCode
            if (postalCode != null && !postalCode.isEmpty()) {
                urlBuffer.append("&postalCode=").append(encodeURL(postalCode));
            }

            /* disabled because the ISO country code is required here which we don't have
            // countryRegion
            if (country != null && !country.isEmpty()) {
               urlBuffer.append("&countryRegion=").append(encodeURL(country));
            }
            */

            // max results
            urlBuffer.append("&maxResults=1");

            String url = urlBuffer.toString();

            try {
                logger.log(Level.INFO, "Using url {0}", url);

                URL mapsUrl = new URL(urlBuffer.toString());
                HttpURLConnection connection = (HttpURLConnection) mapsUrl.openConnection();

                connection.connect();

                Reader reader = new InputStreamReader(connection.getInputStream());
                SAXReader saxReader = new SAXReader();

                // skip leading 0xFEFF character if present
                if (reader.read() != 65279) {
                    reader.reset();
                }

                Document xmlDoc = saxReader.read(reader);

                connection.disconnect();
                reader.close();

                if (xmlDoc != null) {

                    Map<String, String> data = new LinkedHashMap<>();
                    Element root = xmlDoc.getRootElement();

                    try {
                        data.put("lat", root.element("ResourceSets").element("ResourceSet").element("Resources")
                                .element("Location").element("Point").element("Latitude").getTextTrim());
                    } catch (Throwable t) {
                    }
                    try {
                        data.put("lon", root.element("ResourceSets").element("ResourceSet").element("Resources")
                                .element("Location").element("Point").element("Longitude").getTextTrim());
                    } catch (Throwable t) {
                    }
                    try {
                        data.put("postalCode",
                                root.element("ResourceSets").element("ResourceSet").element("Resources")
                                        .element("Location").element("Address").element("PostalCode")
                                        .getTextTrim());
                    } catch (Throwable t) {
                    }
                    try {
                        data.put("adminDistrict",
                                root.element("ResourceSets").element("ResourceSet").element("Resources")
                                        .element("Location").element("Address").element("AdminDistrict")
                                        .getTextTrim());
                    } catch (Throwable t) {
                    }
                    try {
                        data.put("adminDistrict2",
                                root.element("ResourceSets").element("ResourceSet").element("Resources")
                                        .element("Location").element("Address").element("AdminDistrict2")
                                        .getTextTrim());
                    } catch (Throwable t) {
                    }
                    try {
                        data.put("locality",
                                root.element("ResourceSets").element("ResourceSet").element("Resources")
                                        .element("Location").element("Address").element("Locality").getTextTrim());
                    } catch (Throwable t) {
                    }
                    try {
                        data.put("countryRegion",
                                root.element("ResourceSets").element("ResourceSet").element("Resources")
                                        .element("Location").element("Address").element("CountryRegion")
                                        .getTextTrim());
                    } catch (Throwable t) {
                    }

                    if (data.containsKey("lat") && data.containsKey("lon")) {

                        String address =

                                (StringUtils.isNotBlank(street) ? street : "") + " "
                                        + (StringUtils.isNotBlank(house) ? house : "") + " "
                                        + (StringUtils.isNotBlank(postalCode) ? postalCode
                                                : "" + (StringUtils.isNotBlank(city) ? city : "") + " "
                                                        + (StringUtils.isNotBlank(state) ? state : "") + " "
                                                        + (StringUtils.isNotBlank(country) ? country : "") + " ");

                        return new BingGeoCodingResult(address, data);

                    } else {

                        logger.log(Level.WARNING, "Geocoding result did not contain location information:\n{0}",
                                xmlDoc.asXML());
                    }
                }

            } catch (DocumentException dex) {

                logger.log(Level.WARNING, "Unable to use Bing geocoding provider: {0}", dex.getMessage());

                // maybe not a permanent error => wrap in IOException so the request is retried later
                throw new IOException(dex);
            }

        } else {

            logger.log(Level.WARNING,
                    "Unable to use Bing geocoding provider, missing API key. Please supply API key in structr.conf using the key geocoding.apikey.");
        }

        return null;
    }

    private static class BingGeoCodingResult implements GeoCodingResult {

        private List<AddressComponent> addressComponents = new LinkedList<>();
        private Double latitude = null;
        private Double longitude = null;
        private String address = null;

        public BingGeoCodingResult(String address, Map<String, String> data) {

            this.address = address;
            this.latitude = Double.parseDouble(data.get("lat"));
            this.longitude = Double.parseDouble(data.get("lon"));

            this.addressComponents.add(new BingAddressComponent(data.get("postalCode"), Type.postal_code));
            this.addressComponents
                    .add(new BingAddressComponent(data.get("adminDistrict"), Type.administrative_area_level_1));
            this.addressComponents
                    .add(new BingAddressComponent(data.get("adminDistrict2"), Type.administrative_area_level_3));
            this.addressComponents.add(new BingAddressComponent(data.get("countryRegion"), Type.country));
            this.addressComponents.add(new BingAddressComponent(data.get("locality"), Type.locality));
        }

        @Override
        public String getAddress() {
            return address;
        }

        @Override
        public AddressComponent getAddressComponent(Type... types) {

            for (AddressComponent addressComponent : addressComponents) {

                if (addressComponent.getTypes().containsAll(Arrays.asList(types))) {
                    return addressComponent;
                }
            }

            return null;
        }

        @Override
        public List<AddressComponent> getAddressComponents() {
            return addressComponents;
        }

        @Override
        public double getLatitude() {
            return latitude;
        }

        @Override
        public double getLongitude() {
            return longitude;
        }

        @Override
        public void setAddress(String address) {
            this.address = address;
        }

        @Override
        public void setLatitude(double latitude) {
            this.latitude = latitude;
        }

        @Override
        public void setLongitude(double longitude) {
            this.longitude = longitude;
        }

        @Override
        public Double[] toArray() {
            return new Double[] { latitude, longitude };
        }
    }

    private static class BingAddressComponent implements AddressComponent {

        Set<Type> types = new LinkedHashSet<Type>();
        String longValue = null;
        String shortValue = null;

        public BingAddressComponent(String value, Type... types) {

            this.types.addAll(Arrays.asList(types));
            this.longValue = value;
            this.shortValue = value;
        }

        @Override
        public String getLongValue() {
            return longValue;
        }

        @Override
        public String getShortValue() {
            return shortValue;
        }

        @Override
        public Set<Type> getTypes() {
            return types;
        }

    }
}