se.kodapan.geography.geocoding.Nominatim.java Source code

Java tutorial

Introduction

Here is the source code for se.kodapan.geography.geocoding.Nominatim.java

Source

package se.kodapan.geography.geocoding;

import org.apache.commons.io.IOUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.kodapan.geography.domain.*;
import se.kodapan.geography.geocoding.geocoding.Geocoding;
import se.kodapan.geography.geocoding.geocoding.Result;
import se.kodapan.geography.geocoding.geocoding.ResultImpl;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author kalle
 * @since 2011-11-13 03:39
 */
public class Nominatim extends Geocoder {

    private static final Logger log = LoggerFactory.getLogger(Nominatim.class);
    private String emailAddress;

    @Override
    public String getDefaultLicense() {
        return "Data Copyright OpenStreetMap Contributors, Some Rights Reserved. CC-BY-SA 2.0.";
    }

    @Override
    public String getName() {
        return "nominatim.openstreetmap.org";
    }

    @Override
    public String getVersion() {
        return "1";
    }

    @Override
    public int getMaximumResultsReturned() {
        return 10; // todo  setting
    }

    @Override
    public Geocoding reverse(CoordinateQuery query) throws Exception {

        DecimalFormat df = new DecimalFormat("0.##############");

        StringBuilder urlFactory = new StringBuilder(512);
        urlFactory.append("http://nominatim.openstreetmap.org/reverse?format=json&addressdetails=1");
        urlFactory.append("&lat=").append(df.format(query.getCoordinate().getLatitude()));
        urlFactory.append("&lon=").append(df.format(query.getCoordinate().getLongitude()));
        if (getEmailAddress() != null) {
            urlFactory.append("&email=").append(URLEncoder.encode(emailAddress, "UTF8"));
        }
        if (query.getPreferredResponseLanguage() != null) {
            urlFactory.append("&acceptLanguage=").append(query.getPreferredResponseLanguage());
        }

        URLConnection connection = new URL(urlFactory.toString()).openConnection();

        InputStream inputStream = connection.getInputStream();
        try {
            return parseReverseServerResponse(inputStream);
        } finally {
            inputStream.close();
        }

    }

    @Override
    public Geocoding geocode(TextQuery query) throws Exception {

        StringBuilder urlFactory = new StringBuilder(512);
        urlFactory.append("http://nominatim.openstreetmap.org/search?format=json&addressdetails=1&polygon=1");
        if (query.getBounds() != null) {
            urlFactory.append("&viewboxlbrt=");
            Envelope envelope = new EnvelopeImpl();
            envelope.addBounds(query.getBounds());
            urlFactory.append(envelope.getSouthwest().getLatitude()).append(",");
            urlFactory.append(envelope.getSouthwest().getLongitude()).append(",");
            urlFactory.append(envelope.getNortheast().getLatitude()).append(",");
            urlFactory.append(envelope.getNortheast().getLongitude());
        }
        urlFactory.append("&q=").append(URLEncoder.encode(query.getText(), "UTF8"));

        if (getEmailAddress() != null) {
            urlFactory.append("&email=").append(URLEncoder.encode(emailAddress, "UTF8"));
        }
        if (query.getPreferredResponseLanguage() != null) {
            urlFactory.append("&acceptLanguage=").append(query.getPreferredResponseLanguage());
        }

        URLConnection connection = new URL(urlFactory.toString()).openConnection();

        Geocoding geocoding = new Geocoding();

        Reader reader = new InputStreamReader(connection.getInputStream(), "UTF8");
        StringWriter jsonFactory = new StringWriter(49152);
        IOUtils.copy(reader, jsonFactory);
        reader.close();
        String json = jsonFactory.toString();
        reader = new StringReader(json);

        geocoding.setServerResponse(jsonFactory.toString());

        JSONParser parser = new JSONParser();

        JSONArray results = (JSONArray) parser.parse(reader);
        reader.close();

        if (results.size() == 0) {
            geocoding.setSuccess(false);
        } else {
            geocoding.setSuccess(results.size() == 1);
            for (Object result : results) {
                geocoding.getResults().add(resultFactory((JSONObject) result));
            }

        }

        return geocoding;
    }

    private Result resultFactory(JSONObject jsonResult) {
        Result result = new ResultImpl();
        result.setSource(sourceFactory());

        String licence = ((String) jsonResult.remove("licence"));
        result.getSource().setLicense(licence);
        // todo place_id can be a string error message, at least if reverse geocoding out of area
        Number placeIdentity = Long.valueOf(((String) jsonResult.remove("place_id")));
        String placeClass = ((String) jsonResult.remove("class"));
        String placeType = ((String) jsonResult.remove("type"));
        String osmIdentity = ((String) jsonResult.remove("osm_id"));

        String osmType = ((String) jsonResult.remove("osm_type"));

        String iconURL = ((String) jsonResult.remove("icon"));

        String displayName = ((String) jsonResult.remove("display_name"));
        result.getAddressComponents().setFormattedAddress(displayName);

        Number latitude = Double.valueOf((String) jsonResult.remove("lat"));
        Number longitude = Double.valueOf((String) jsonResult.remove("lon"));
        result.setLocation(new CoordinateImpl(latitude, longitude));

        JSONObject address = (JSONObject) jsonResult.remove("address");
        if (address != null) {
            for (Map.Entry<String, String> ae : ((Set<Map.Entry<String, String>>) address.entrySet())) {
                result.getAddressComponents().add(new AddressComponent(ae.getValue(), ae.getKey()));
            }
        }

        JSONArray boundingBox = (JSONArray) jsonResult.remove("boundingbox");
        if (boundingBox != null) {
            result.setBounds(new EnvelopeImpl(
                    new CoordinateImpl(Double.valueOf(boundingBox.get(0).toString()),
                            Double.valueOf(boundingBox.get(2).toString())),
                    new CoordinateImpl(Double.valueOf(boundingBox.get(1).toString()),
                            Double.valueOf(boundingBox.get(3).toString()))));
        }

        JSONArray polygonPoints = (JSONArray) jsonResult.remove("polygonpoints");
        if (polygonPoints != null) {
            if (result.getBounds() != null) {
                // according to the osm irc this means that the bounding box enclose the polygon
                log.info("Both boundingbox and polygon in response, using the latter");
            }
            List<Coordinate> polygonCoordinates = new ArrayList<Coordinate>(polygonPoints.size());
            for (int ppsi = 0; ppsi < polygonPoints.size(); ppsi++) {
                JSONArray polygonPoint = (JSONArray) polygonPoints.get(ppsi);
                polygonCoordinates.add(new CoordinateImpl(Double.valueOf(polygonPoint.get(1).toString()),
                        Double.valueOf(polygonPoint.get(0).toString())));
            }
            if (!polygonCoordinates.isEmpty()) {
                EnvelopeImpl envelope = new EnvelopeImpl();
                envelope.addBounds(polygonCoordinates);
                result.setBounds(envelope);
            }

        }

        if (!jsonResult.isEmpty()) {
            log.error("Unknown data left in result: " + jsonResult.toJSONString());
        }

        return result;
    }

    @Override
    public Geocoding parseReverseServerResponse(InputStream inputStream) throws Exception {
        JSONParser parser = new JSONParser();
        Geocoding geocoding = new Geocoding();

        Reader reader = new InputStreamReader(inputStream, "UTF8");
        StringWriter jsonFactory = new StringWriter(49152);
        IOUtils.copy(reader, jsonFactory);
        reader.close();
        String json = jsonFactory.toString();
        reader = new StringReader(json);

        geocoding.setServerResponse(jsonFactory.toString());

        geocoding.getResults().add(resultFactory((JSONObject) parser.parse(reader)));

        if (!geocoding.getResults().isEmpty()) {
            geocoding.setSuccess(true);
        }

        return geocoding;
    }

    @Override
    public Geocoding parseGeocodeServerResponse(InputStream inputStream) throws Exception {

        JSONParser parser = new JSONParser();
        Geocoding geocoding = new Geocoding();

        Reader reader = new InputStreamReader(inputStream, "UTF8");
        StringWriter jsonFactory = new StringWriter(49152);
        IOUtils.copy(reader, jsonFactory);
        reader.close();
        String json = jsonFactory.toString();
        reader = new StringReader(json);

        geocoding.setServerResponse(jsonFactory.toString());

        JSONArray array = (JSONArray) parser.parse(reader);
        for (int i = 0; i < array.size(); i++) {
            geocoding.getResults().add(resultFactory((JSONObject) array.get(i)));
        }

        if (!geocoding.getResults().isEmpty()) {
            geocoding.setSuccess(true);
        }

        return geocoding;

    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }
}