All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.venky.geo.GeoCoder Maven / Gradle / Ivy

The newest version!
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.venky.geo;

import com.venky.core.string.StringUtil;
import com.venky.core.util.ObjectUtil;
import com.venky.xml.XMLDocument;
import com.venky.xml.XMLElement;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.w3c.dom.DOMException;

/**
 *
 * @author venky
 */
public class GeoCoder {

    private static final Map availableSps = new HashMap();

    static {
        //registerGeoSP("yahoo",new Yahoo());
        registerGeoSP("google", new Google());
        registerGeoSP("openstreetmap", new Nominatim());
        registerGeoSP("here", new Here());
    }

    private static void registerGeoSP(String sp, GeoSP geoSP) {
        availableSps.put(sp, geoSP);
    }

    private GeoSP preferredServiceProvider = null;

    public GeoCoder(String preferedSP) {
        preferredServiceProvider = availableSps.get(preferedSP);
    }

    public GeoCoder() {
        this(null);
    }

    public void fillGeoInfo(String address, GeoLocation location, Map params) {
        GeoLocation result = getLocation(address, params);
        if (result != null) {
            location.setLat(result.getLat());
            location.setLng(result.getLng());
        }
    }

    Collection sps = null;
    public boolean isEnabled(Map params){
        if (preferredServiceProvider != null){
            return preferredServiceProvider.isEnabled(params);
        }else {
            for (String spName: availableSps.keySet()){
                GeoSP sp = availableSps.get(spName);
                if (sp != null && sp.isEnabled(params)){
                    return true;
                }
            }
            return false;
        }
    }
    public GeoLocation getLocation(String address, Map params) {
        if (preferredServiceProvider == null) {
            sps = Arrays.asList(availableSps.get("here"), availableSps.get("openstreetmap"), availableSps.get("google"));
        } else {
            sps = Arrays.asList(preferredServiceProvider);
        }
        for (GeoSP sp : sps) {
            GeoLocation loc = sp.getLocation(address, params);
            if (loc != null) {
                Logger.getLogger(GeoCoder.class.getName()).info("Lat,Lon found using " + sp.getClass().getSimpleName());
                return loc;
            }
        }
        return null;
    }

    public Double getDrivingDistanceKms(GeoLocation l1, GeoLocation l2, Map params){
        return getDrivingDistanceKms(l1.getLat(),l1.getLng(),l2.getLat(),l2.getLng(),params);
    }
    public Double getDrivingDistanceKms(BigDecimal lat1, BigDecimal lng1, BigDecimal lat2, BigDecimal lng2, Map params){
        if (preferredServiceProvider == null){
            sps = Arrays.asList(availableSps.get("here"), availableSps.get("openstreetmap"), availableSps.get("google"));
        }else {
            sps = Arrays.asList(preferredServiceProvider);
        }
        for (GeoSP sp :sps){
            Double distance = sp.getDrivingDistance(lat1,lng1,lat2,lng2,params);
            if (distance != null){
                Logger.getLogger(GeoCoder.class.getName()).info("Distance found using " + sp.getClass().getSimpleName());
                return distance;
            }
        }
        return null;
    }
    public Double distanceKms(GeoLocation l1, GeoLocation l2){
        return new GeoCoordinate(l1).distanceTo(new GeoCoordinate(l2));
    }

    private static interface GeoSP {

        public GeoLocation getLocation(String address, Map params);

        public GeoAddress getAddress(GeoLocation geoLocation, Map params);

        public boolean isEnabled(Map params);

        default Double getDrivingDistance(BigDecimal lat1, BigDecimal lng1, BigDecimal lat2, BigDecimal lng2, Map params){
            return distance(lat1,lng1,lat2,lng2);
        }
        public static Double distance(BigDecimal lat1, BigDecimal lng1, BigDecimal lat2, BigDecimal lng2){
            return new GeoCoordinate(lat1, lng1).distanceTo(new GeoCoordinate(lat2, lng2));
        }


    }

    public static class GeoAddress {

        String city;
        String state;
        String country;

        public String getCity() {
            return city;
        }

        public String getState() {
            return state;
        }

        public String getCountry() {
            return country;
        }
    }

    private static class Google implements GeoSP {

        private static final String WSURL = "https://maps.googleapis.com/maps/api/geocode/xml?sensor=false&key=%s&address=%s";
        private static final String REVERSE_GEOCODE_URL = 
                "https://maps.googleapis.com/maps/api/geocode/xml?latlng=%f,%f&key=%s";

        @Override
        public boolean isEnabled(Map params) {
            return !ObjectUtil.isVoid(params.get("google.api_key"));
        }
        public GeoLocation getLocation(String address, Map params) {
            try {
                String apiKey = params.get("google.api_key");
                if (!ObjectUtil.isVoid(apiKey)) {
                    String url = String.format(WSURL, apiKey, URLEncoder.encode(address, "UTF-8"));
                    URL u = new URL(url);
                    URLConnection connection = u.openConnection();
                    connection.setConnectTimeout(5000);
                    connection.setReadTimeout(5000);

                    XMLDocument doc = XMLDocument.getDocumentFor(connection.getInputStream());
                    XMLElement status = doc.getDocumentRoot().getChildElement("status");
                    if ("OK".equals(status.getNodeValue())) {
                        Logger.getLogger(getClass().getName()).info("URL:" + url);
                        XMLElement location = doc.getDocumentRoot().getChildElement("result").getChildElement("geometry").getChildElement("location");
                        float lat = -1;
                        float lng = -1;
                        for (Iterator nodeIterator = location.getChildElements(); nodeIterator.hasNext();) {
                            XMLElement node = nodeIterator.next();
                            if (node.getNodeName().equals("lat")) {
                                lat = Float.valueOf(node.getChildren().next().getNodeValue());
                            } else if (node.getNodeName().equals("lng")) {
                                lng = Float.valueOf(node.getChildren().next().getNodeValue());
                            }
                        }
                        return new GeoCoordinate(new BigDecimal(lat), new BigDecimal(lng));
                    }
                }
            } catch (IOException e) {
                Logger.getLogger(getClass().getName()).warning(e.getMessage());
            }
            return null;
        }

        @Override
        public GeoAddress getAddress(GeoLocation geoLocation, Map params) {
            try {
                String apiKey = params.get("google.api_key");
                if (!ObjectUtil.isVoid(apiKey)) {
                    String url = String.format(REVERSE_GEOCODE_URL, geoLocation.getLat().floatValue(), geoLocation.getLng().floatValue(),apiKey);
                    URL u = new URL(url);
                    URLConnection connection = u.openConnection();
                    connection.setConnectTimeout(5000);
                    connection.setReadTimeout(5000);

                    XMLDocument doc = XMLDocument.getDocumentFor(connection.getInputStream());
                    XMLElement status = doc.getDocumentRoot().getChildElement("status");
                    if ("OK".equals(status.getNodeValue())) {
                        Logger.getLogger(getClass().getName()).info("URL:" + url);
                        Iterator li = doc.getDocumentRoot().getChildElement("result").getChildElements("address_component");
                        GeoAddress address = new GeoAddress();
                        while (li.hasNext()){
                            XMLElement addressComponent = li.next();
                            String longName = addressComponent.getChildElement("long_name").getNodeValue();
                            Iterator typeIterator = addressComponent.getChildElements("type");
                            Set types = new HashSet();
                            while (typeIterator.hasNext()){
                                XMLElement typeElement  = typeIterator.next();
                                String type = typeElement.getNodeValue();
                                types.add(type);
                            }
                            if (types.contains("country")){
                                address.country = longName;
                            }else if (types.contains("administrative_area_level_1")){
                                address.state = longName;
                            }else if (types.contains("locality")){
                                address.city = longName;
                            }
                        }
                        return address;
                    }
                }
            } catch (IOException e) {
                Logger.getLogger(getClass().getName()).warning(e.getMessage());
            }
            return null;
        }


    }

    private static class Nominatim implements GeoSP {

        private static final String WSURL = "https://nominatim.openstreetmap.org/search?format=xml&polygon=0&q=%s";
        private static final String REVERSE_GEO_CODE_URL
                = "https://nominatim.openstreetmap.org/reverse?format=xml&lat=%f&lon=%f&zoom=10&addressdetails=1";

        public GeoLocation getLocation(String address, Map params) {
            try {
                String url = String.format(WSURL, URLEncoder.encode(address, "UTF-8"));
                URL u = new URL(url);
                HttpURLConnection conn = (HttpURLConnection) u.openConnection();
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);

                conn.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
                conn.addRequestProperty("User-Agent", "Mozilla");

                boolean redirect = false;

                // normally, 3xx is redirect
                int status = conn.getResponseCode();
                if (status != HttpURLConnection.HTTP_OK) {
                    if (status == HttpURLConnection.HTTP_MOVED_TEMP
                            || status == HttpURLConnection.HTTP_MOVED_PERM
                            || status == HttpURLConnection.HTTP_SEE_OTHER) {
                        redirect = true;
                    }
                }

                if (redirect) {
                    String newUrl = conn.getHeaderField("Location");

                    // get the cookie if need, for login
                    String cookies = conn.getHeaderField("Set-Cookie");

                    // open the new connnection again
                    conn = (HttpURLConnection) new URL(newUrl).openConnection();
                    conn.setConnectTimeout(5000);
                    conn.setReadTimeout(5000);

                    conn.setRequestProperty("Cookie", cookies);
                    conn.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
                    conn.addRequestProperty("User-Agent", "Mozilla");
                }
                XMLDocument doc = XMLDocument.getDocumentFor(conn.getInputStream());
                XMLElement place = doc.getDocumentRoot().getChildElement("place");

                if (place != null) {
                    Logger.getLogger(getClass().getName()).info("URL:" + url);
                    return new GeoCoordinate(new BigDecimal(place.getAttribute("lat")), new BigDecimal(place.getAttribute("lon")));
                }
            } catch (Exception e) {
                Logger.getLogger(getClass().getName()).warning(e.getMessage());
            } finally {
                try {
                    Thread.sleep(1000); //To ensure nominatim response code 429 doesnot happen.
                } catch (InterruptedException ex) {
                    //
                }
            }
            return null;
        }

        @Override
        public GeoAddress getAddress(GeoLocation location, Map params) {
            GeoAddress address = null;
            String url = String.format(REVERSE_GEO_CODE_URL, location.getLat().floatValue(), location.getLng().floatValue());
            try {
                URL u = new URL(url);
                HttpURLConnection conn = (HttpURLConnection) u.openConnection();
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);

                conn.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
                conn.addRequestProperty("User-Agent", "Mozilla");
                XMLDocument doc = XMLDocument.getDocumentFor(conn.getInputStream());
                XMLElement addressparts = doc.getDocumentRoot().getChildElement("addressparts");

                if (addressparts != null) {
                    address = new GeoAddress();
                    address.city = addressparts.getChildElement("city").getNodeValue();
                    address.state = addressparts.getChildElement("state").getNodeValue();
                    address.country = addressparts.getChildElement("country").getNodeValue();
                }
                return address;
            } catch (IOException | DOMException ex) {
                return null;
            }

        }

        @Override
        public boolean isEnabled(Map params) {
            return true;
        }
    }

    private static class Here implements GeoSP {
        @Override
        public boolean isEnabled(Map params) {
            return getGeoCodingUrl("",params) != null;
        }

        private String getGeoCodingUrl(String address, Map params){
            try {
                String appKey = params.get("here.app_key");
                String appId = params.get("here.app_id");
                String appCode = params.get("here.app_code");
                if (!ObjectUtil.isVoid(appKey)){
                    return String.format("https://geocoder.ls.hereapi.com/6.2/geocode.json?apiKey=%s&searchtext=%s",
                            URLEncoder.encode(appKey,"UTF-8"),
                            URLEncoder.encode(address,"UTF-8"));
                }else if (!ObjectUtil.isVoid(appCode)) {
                    return String.format("https://geocoder.api.here.com/6.2/geocode.json?app_id=%s&app_code=%s&searchtext=%s",
                            URLEncoder.encode(appId,"UTF-8"),
                            URLEncoder.encode(appCode,"UTF-8"),
                            URLEncoder.encode(address,"UTF-8"));
                }else {
                    return null;
                }
            }catch (Exception ex){
                throw new RuntimeException(ex);
            }
        }

        public GeoLocation getLocation(String address, Map params) {
            try {
                String url = getGeoCodingUrl(address, params);
                
                if (!ObjectUtil.isVoid(url)) {
                    URL u = new URL(url);
                    URLConnection connection = u.openConnection();
                    connection.setConnectTimeout(5000);
                    connection.setReadTimeout(5000);
                    JSONObject doc = (JSONObject) JSONValue.parse(new InputStreamReader(connection.getInputStream()));
                    JSONObject place = (JSONObject) doc.get("Response");
                    JSONArray views = (JSONArray) place.get("View");
                    JSONObject position = null;

                    if (views != null && !views.isEmpty()) {
                        JSONObject view = (JSONObject) views.get(0);
                        JSONObject location = (JSONObject) ((JSONObject) ((JSONArray) view.get("Result")).get(0)).get("Location");
                        position = (JSONObject) location.get("DisplayPosition");
                    }

                    if (position != null) {
                        Logger.getLogger(getClass().getName()).info("URL:" + url);
                        return new GeoCoordinate(new BigDecimal((Double) position.get("Latitude")), new BigDecimal((Double) position.get("Longitude")));
                    }
                }
            } catch (Exception e) {
                Logger.getLogger(getClass().getName()).warning(e.getMessage());
            }
            return null;
        }

        private String getReverseGeoCodeUrl(GeoLocation geoLocation, Map params) throws UnsupportedEncodingException{
            String appKey = params.get("here.app_key");                
            String appId = params.get("here.app_id");
            String appCode = params.get("here.app_code");
            if (!ObjectUtil.isVoid(appKey)){
                return String.format("https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json?prox=%f,%f"
                                                + "&mode=retrieveLandmarks&maxresults=1&gen=9&apiKey=%s",
                        geoLocation.getLat().floatValue(), geoLocation.getLng().floatValue(),URLEncoder.encode(appKey,"UTF-8"));
            }else if (!ObjectUtil.isVoid(appCode)) {
                return String.format("https://reverse.geocoder.api.here.com/6.2/reversegeocode.json?prox=%f,%f"
                        + "&mode=retrieveLandmarks&maxresults=1&gen=9&app_id=%s&app_code=%s",
                        geoLocation.getLat().floatValue(), geoLocation.getLng().floatValue(),
                        URLEncoder.encode(appId,"UTF-8"),
                        URLEncoder.encode(appCode,"UTF-8"));
            }else {
                    return null;
            }
            
        }
        
        
        @Override
        public GeoAddress getAddress(GeoLocation geoLocation, Map params) {
            GeoAddress address = null;
            try {
                String url = getReverseGeoCodeUrl(geoLocation, params);
                if (!ObjectUtil.isVoid(url)) {
                    URL u = new URL(url);
                    HttpURLConnection conn = (HttpURLConnection) u.openConnection();
                    conn.setConnectTimeout(5000);
                    conn.setReadTimeout(5000);

                    conn.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
                    conn.addRequestProperty("User-Agent", "Mozilla");
                    JSONObject doc = (JSONObject) JSONValue.parse(new InputStreamReader(conn.getInputStream()));
                    JSONObject place = (JSONObject) doc.get("Response");
                    JSONArray views = (JSONArray) place.get("View");
                    JSONObject addressparts = null;
                    if (views != null && !views.isEmpty()) {
                        JSONObject view = (JSONObject) views.get(0);
                        JSONObject location = (JSONObject) ((JSONObject) ((JSONArray) view.get("Result")).get(0)).get("Location");
                        addressparts = (JSONObject) location.get("Address");
                    }

                    
                    if (addressparts != null) {
                        address = new GeoAddress();
                        address.city = StringUtil.valueOf(addressparts.get("City"));
                        address.state = StringUtil.valueOf(addressparts.get("State"));
                        address.country = StringUtil.valueOf(addressparts.get("Country"));
                    }
                    return address;
                }
            } catch (IOException | DOMException ex) {
                
            }
            return null;
        }

        @Override
        public Double getDrivingDistance(BigDecimal lat1, BigDecimal lng1, BigDecimal lat2, BigDecimal lng2, Map params)  {
            String appKey = params.get("here.app_key");
            /*
            curl 'https://router.hereapi.com/v8/routes?transportMode=car&origin=12.902987,77.599674&destination=13.198533,77.707956&return=summary&apiKey=4Su9WUwZJ7_dvMn1uDJDs0IoSgDltWlI6Efm4CPnfBM'

                {"routes":[{"id":"8ac052c1-b446-403e-b867-65744a5dc8e5","sections":[{"id":"6692dcb0-2b75-4ad7-b9d0-983cf0e1176d","type":"vehicle","departure":{"place":{"type":"place","location":{"lat":12.9029757,"lng":77.6000587},"originalLocation":{"lat":12.902987,"lng":77.599674}}},"arrival":{"place":{"type":"place","location":{"lat":13.19847,"lng":77.7079559},"originalLocation":{"lat":13.198533,"lng":77.7079559}}},"summary":{"duration":5353,"length":43380,"baseDuration":4055},"transport":{"mode":"car"}}]}]}
             */

            if (!ObjectUtil.isVoid(appKey)) {
                try {
                    String url = String.format("https://router.hereapi.com/v8/routes?transportMode=%s&apiKey=%s&origin=%f,%f&destination=%f,%f&return=summary",
                            URLEncoder.encode(params.getOrDefault("transportMode", "car")),
                            URLEncoder.encode(appKey, "UTF-8"),
                            lat1.floatValue(), lng1.floatValue(),
                            lat2.floatValue(), lng2.floatValue());

                    URL u = new URL(url);
                    HttpURLConnection conn = (HttpURLConnection) u.openConnection();
                    conn.setConnectTimeout(5000);
                    conn.setReadTimeout(5000);

                    conn.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
                    conn.addRequestProperty("User-Agent", "Mozilla");
                    JSONObject out = (JSONObject) JSONValue.parse(new InputStreamReader(conn.getInputStream()));
                    JSONArray routes = (JSONArray)out.get("routes");
                    JSONObject route = (JSONObject)routes.get(0);
                    JSONArray sections = (JSONArray)route.get("sections");
                    JSONObject section = (JSONObject)sections.get(0);
                    JSONObject summary = (JSONObject)section.get("summary");
                    Object length = summary.get("length");
                    if (length instanceof  Number){
                        return ((Number)length).doubleValue()/1000.0;
                    }
                } catch (Exception ex) {
                    //
                }
            }
            return GeoSP.distance(lat1, lng1, lat2, lng2);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy