com.bdaum.zoom.gps.naming.geonaming.internal.GeoNamesService.java Source code

Java tutorial

Introduction

Here is the source code for com.bdaum.zoom.gps.naming.geonaming.internal.GeoNamesService.java

Source

/*
 * This file is part of the ZoRa project: http://www.photozora.org.
 *
 * ZoRa 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 2 of the License, or
 * (at your option) any later version.
 *
 * ZoRa 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 ZoRa; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * (c) 2018 Berthold Daum  
 */
package com.bdaum.zoom.gps.naming.geonaming.internal;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.httpclient.HttpException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IPreferencesService;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.xml.sax.SAXException;

import com.bdaum.zoom.gps.geonames.Place;
import com.bdaum.zoom.gps.geonames.WebServiceException;
import com.bdaum.zoom.gps.internal.AbstractGeocodingService;
import com.bdaum.zoom.gps.internal.preferences.GpsPreferencePage;
import com.bdaum.zoom.net.ui.internal.NetActivator;
import com.bdaum.zoom.ui.gps.WaypointArea;
import com.bdaum.zoom.ui.internal.widgets.CheckboxButton;
import com.bdaum.zoom.ui.internal.widgets.WidgetFactory;
import com.bdaum.zoom.ui.widgets.CGroup;

@SuppressWarnings("restriction")
public class GeoNamesService extends AbstractGeocodingService {

    private static final String USERNAME = "photozora"; //$NON-NLS-1$
    private static final String[] CONTINENTCODES = new String[] { "AF", "AS", "EU", "NA", "OC", "SA", "AN" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
    private static final String CONTINENT_CODE = "&continentCode="; //$NON-NLS-1$
    private static final String FEATURE_CLASS_V = "&featureClass=V"; //$NON-NLS-1$
    private static final String FEATURE_CLASS_U = "&featureClass=U"; //$NON-NLS-1$
    private static final String FEATURE_CLASS_T = "&featureClass=T"; //$NON-NLS-1$
    private static final String FEATURE_CLASS_S = "&featureClass=S"; //$NON-NLS-1$
    private static final String FEATURE_CLASS_R = "&featureClass=R"; //$NON-NLS-1$
    private static final String FEATURE_CLASS_P = "&featureClass=P"; //$NON-NLS-1$
    private static final String FEATURE_CLASS_L = "&featureClass=L"; //$NON-NLS-1$
    private static final String FEATURE_CLASS_H = "&featureClass=H"; //$NON-NLS-1$
    private static final String FEATURE_CLASS_A = "&featureClass=A"; //$NON-NLS-1$
    private static boolean asked;

    private CheckboxButton nameRequiredButton;
    private Combo modeCombo;
    private CheckboxButton aButton;
    private CheckboxButton hButton;
    private CheckboxButton lButton;
    private CheckboxButton pButton;
    private CheckboxButton rButton;
    private CheckboxButton sButton;
    private CheckboxButton tButton;
    private CheckboxButton uButton;
    private CheckboxButton vButton;
    private Combo contCombo;

    public Place fetchPlaceInfo(double lat, double lon) throws SocketTimeoutException, IOException,
            WebServiceException, SAXException, ParserConfigurationException, UnknownHostException, HttpException {
        return fetchPlaceInfoFromGeoNames(Locale.getDefault().getLanguage(),
                new String[] { usformat.format(lat), usformat.format(lon), Locale.getDefault().getLanguage() });
    }

    private static Place fetchPlaceInfoFromGeoNames(String lang, String[] parms) throws IOException, SAXException,
            ParserConfigurationException, WebServiceException, UnknownHostException, HttpException {
        Place place = new Place();
        try (InputStream in = openGeonamesService(
                NLS.bind("http://api.geonames.org/findNearbyPlaceName?lat={0}&lng={1}&lang={2}", //$NON-NLS-1$
                        parms))) {
            new PlaceParser(in).parse(place);
        }
        if (place.getId() != null) {
            try (InputStream in = openGeonamesService(
                    NLS.bind("http://api.geonames.org/hierarchy?geonameId={0}&lang={1}", //$NON-NLS-1$
                            place.getId(), lang))) {
                new HierarchyParser(in).parse(place);
            }
        }
        if ("US".equals(place.getCountryCode())) //$NON-NLS-1$
            try (InputStream in = openGeonamesService(
                    NLS.bind("http://api.geonames.org/findNearestAddress?lat={0}&lng={1}&lang={2}", //$NON-NLS-1$
                            parms))) {
                new AddressParser(in).parse(place);
                return (place.getName() != null) ? place : null;
            }
        try (InputStream in = openGeonamesService(
                NLS.bind("http://api.geonames.org/findNearbyPostalCodes?lat={0}&lng={1}&lang={2}", //$NON-NLS-1$
                        parms))) {
            new PostcodeParser(in).parse(place);
            return (place.getName() != null) ? place : null;
        }
    }

    private static InputStream openGeonamesService(String query)
            throws IOException, UnknownHostException, HttpException {
        IPreferencesService preferencesService = Platform.getPreferencesService();
        String parms = preferencesService.getString(GeonamingActivator.PLUGIN_ID, PreferenceConstants.GEONAMESPARMS,
                "", null); //$NON-NLS-1$
        if ((parms == null || parms.isEmpty()) && !asked) {
            IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
            if (activeWorkbenchWindow != null) {
                PreferencesUtil.createPreferenceDialogOn(activeWorkbenchWindow.getShell(), GpsPreferencePage.ID,
                        null, GpsPreferencePage.ACCOUNTS).open();
                parms = preferencesService.getString(GeonamingActivator.PLUGIN_ID,
                        PreferenceConstants.GEONAMESPARMS, "", null); //$NON-NLS-1$
                asked = true;
            }
        }
        if (parms != null && !parms.trim().isEmpty()) {
            parms = parms.trim();
            if (parms.startsWith("&")) //$NON-NLS-1$
                query += parms;
            else if (parms.indexOf('=') >= 0 || parms.indexOf('&') >= 0)
                query += '&' + parms;
            else
                query += "&username=" + parms; //$NON-NLS-1$
        } else
            query += "&username=" + USERNAME; //$NON-NLS-1$
        return NetActivator.getDefault().openStream(query);
    }

    public WaypointArea[] findLocation(String address)
            throws IOException, WebServiceException, SAXException, ParserConfigurationException, HttpException {
        List<WaypointArea> pnts = new ArrayList<WaypointArea>();
        Locale locale = Locale.getDefault();
        String template = "http://api.geonames.org/search?" + getSearchParms() //$NON-NLS-1$
                + "&countryBias={1}&lang={2}&maxRows={3}"; //$NON-NLS-1$
        String query = NLS.bind(template, new Object[] { URLEncoder.encode(address, "UTF-8"), locale.getCountry(), //$NON-NLS-1$
                locale.getLanguage(), GeoCodeParser.MAXRESULTS });
        try (InputStream in = openGeonamesService(query)) {
            new GeoCodeParser(in).parse(pnts);
            return pnts.toArray(new WaypointArea[pnts.size()]);
        }
    }

    @Override
    public Control createParameterGroup(CGroup parmGroup) {
        Composite composite = new Composite(parmGroup, SWT.NONE);
        composite.setLayout(new GridLayout(2, false));
        new Label(composite, SWT.NONE).setText(Messages.getString("GeoNamesService.search_mode")); //$NON-NLS-1$
        modeCombo = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY);
        modeCombo.setItems(Messages.getString("GeoNamesService.whole_text"), //$NON-NLS-1$
                Messages.getString("GeoNamesService.contains"), Messages.getString("GeoNamesService.starts_with"), //$NON-NLS-1$ //$NON-NLS-2$
                Messages.getString("GeoNamesService.equals")); //$NON-NLS-1$
        Listener listener = new Listener() {
            @Override
            public void handleEvent(Event e) {
                updateSettings();
            }
        };
        modeCombo.addListener(SWT.Selection, listener);
        new Label(composite, SWT.NONE).setText(Messages.getString("GeoNamesService.continent")); //$NON-NLS-1$
        contCombo = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY);
        contCombo.setItems(Messages.getString("GeoNamesService.all"), Messages.getString("GeoNamesService.af"), //$NON-NLS-1$ //$NON-NLS-2$
                Messages.getString("GeoNamesService.as"), Messages.getString("GeoNamesService.eu"), //$NON-NLS-1$ //$NON-NLS-2$
                Messages.getString("GeoNamesService.na"), Messages.getString("GeoNamesService.oc"), //$NON-NLS-1$ //$NON-NLS-2$
                Messages.getString("GeoNamesService.sa"), //$NON-NLS-1$
                Messages.getString("GeoNamesService.an")); //$NON-NLS-1$
        contCombo.addListener(SWT.Selection, listener);
        nameRequiredButton = WidgetFactory.createCheckButton(composite,
                Messages.getString("GeoNamesService.at_least_one"), //$NON-NLS-1$
                new GridData(SWT.BEGINNING, SWT.CENTER, true, true, 2, 1));
        new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL)
                .setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
        Composite classGroup = new Composite(composite, SWT.NONE);
        classGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
        classGroup.setLayout(new GridLayout(2, true));
        aButton = WidgetFactory.createCheckButton(classGroup, Messages.getString("GeoNamesService.country"), //$NON-NLS-1$
                null);
        aButton.addListener(listener);
        hButton = WidgetFactory.createCheckButton(classGroup, Messages.getString("GeoNamesService.stream"), //$NON-NLS-1$
                null);
        hButton.addListener(listener);
        lButton = WidgetFactory.createCheckButton(classGroup, Messages.getString("GeoNamesService.parks"), //$NON-NLS-1$
                null);
        lButton.addListener(listener);
        pButton = WidgetFactory.createCheckButton(classGroup, Messages.getString("GeoNamesService.city"), //$NON-NLS-1$
                null);
        pButton.addListener(listener);
        rButton = WidgetFactory.createCheckButton(classGroup, Messages.getString("GeoNamesService.road"), //$NON-NLS-1$
                null);
        rButton.addListener(listener);
        sButton = WidgetFactory.createCheckButton(classGroup, Messages.getString("GeoNamesService.spot"), //$NON-NLS-1$
                null);
        sButton.addListener(listener);
        tButton = WidgetFactory.createCheckButton(classGroup, Messages.getString("GeoNamesService.mountain"), //$NON-NLS-1$
                null);
        tButton.addListener(listener);
        uButton = WidgetFactory.createCheckButton(classGroup, Messages.getString("GeoNamesService.undersea"), //$NON-NLS-1$
                null);
        uButton.addListener(listener);
        vButton = WidgetFactory.createCheckButton(classGroup, Messages.getString("GeoNamesService.forest"), //$NON-NLS-1$
                null);
        vButton.addListener(listener);
        nameRequiredButton.addListener(listener);
        searchParms = getSearchParms();
        if (searchParms.startsWith("q")) //$NON-NLS-1$
            modeCombo.select(0);
        else if (searchParms.startsWith("name_startsWith")) //$NON-NLS-1$
            modeCombo.select(2);
        else if (searchParms.startsWith("name_equals")) { //$NON-NLS-1$
            modeCombo.select(3);
            nameRequiredButton.setEnabled(false);
        } else if (searchParms.startsWith("name")) //$NON-NLS-1$
            modeCombo.select(1);
        nameRequiredButton.setSelection(searchParms.indexOf("isNameRequired=true") >= 0); //$NON-NLS-1$
        aButton.setSelection(searchParms.indexOf(FEATURE_CLASS_A) >= 0);
        hButton.setSelection(searchParms.indexOf(FEATURE_CLASS_H) >= 0);
        lButton.setSelection(searchParms.indexOf(FEATURE_CLASS_L) >= 0);
        pButton.setSelection(searchParms.indexOf(FEATURE_CLASS_P) >= 0);
        rButton.setSelection(searchParms.indexOf(FEATURE_CLASS_R) >= 0);
        sButton.setSelection(searchParms.indexOf(FEATURE_CLASS_S) >= 0);
        tButton.setSelection(searchParms.indexOf(FEATURE_CLASS_T) >= 0);
        uButton.setSelection(searchParms.indexOf(FEATURE_CLASS_U) >= 0);
        vButton.setSelection(searchParms.indexOf(FEATURE_CLASS_V) >= 0);
        int p = searchParms.indexOf(CONTINENT_CODE);
        if (p >= 0) {
            p += CONTINENT_CODE.length();
            int q = searchParms.indexOf("&", p); //$NON-NLS-1$
            String code = q < 0 ? searchParms.substring(p) : searchParms.substring(p, q);
            for (int i = 0; i < CONTINENTCODES.length; i++)
                if (code.equals(CONTINENTCODES[i]))
                    contCombo.select(i + 1);
        } else
            contCombo.select(0);
        return composite;
    }

    protected void updateSettings() {
        StringBuilder sb = new StringBuilder(64);
        switch (modeCombo.getSelectionIndex()) {
        case 0:
            sb.append("q"); //$NON-NLS-1$
            nameRequiredButton.setEnabled(true);
            break;
        case 2:
            sb.append("name_startsWith"); //$NON-NLS-1$
            nameRequiredButton.setEnabled(true);
            break;
        case 3:
            sb.append("name_equals"); //$NON-NLS-1$
            nameRequiredButton.setEnabled(false);
            break;
        default:
            sb.append("name"); //$NON-NLS-1$
            nameRequiredButton.setEnabled(true);
            break;
        }
        sb.append("={0}"); //$NON-NLS-1$
        if (nameRequiredButton.getSelection() && nameRequiredButton.isEnabled())
            sb.append("&isNameRequired=true"); //$NON-NLS-1$
        if (aButton.getSelection())
            sb.append(FEATURE_CLASS_A);
        if (hButton.getSelection())
            sb.append(FEATURE_CLASS_H);
        if (lButton.getSelection())
            sb.append(FEATURE_CLASS_L);
        if (pButton.getSelection())
            sb.append(FEATURE_CLASS_P);
        if (rButton.getSelection())
            sb.append(FEATURE_CLASS_R);
        if (sButton.getSelection())
            sb.append(FEATURE_CLASS_S);
        if (tButton.getSelection())
            sb.append(FEATURE_CLASS_T);
        if (uButton.getSelection())
            sb.append(FEATURE_CLASS_U);
        if (vButton.getSelection())
            sb.append(FEATURE_CLASS_V);
        int si = contCombo.getSelectionIndex();
        if (si > 0)
            sb.append(CONTINENT_CODE).append(CONTINENTCODES[si - 1]);
        searchParms = sb.toString();
    }

    @Override
    public void saveSearchParameters() {
        if (searchParms != null)
            GeonamingActivator.getDefault().getPreferenceStore().setValue(PreferenceConstants.SEARCHPARAMETERS,
                    searchParms);
    }

    protected String getSearchParms() {
        return GeonamingActivator.getDefault().getPreferenceStore().getString(PreferenceConstants.SEARCHPARAMETERS);
    }

    @Override
    public double getElevation(double lat, double lon) throws UnknownHostException, HttpException, IOException {
        NumberFormat usformat = NumberFormat.getInstance(Locale.US);
        usformat.setMaximumFractionDigits(5);
        usformat.setGroupingUsed(false);
        InputStream in = openGeonamesService(NLS.bind("http://api.geonames.org/srtm3?lat={0}&lng={1}", //$NON-NLS-1$
                usformat.format(lat), usformat.format(lon)));
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
            String readLine = reader.readLine();
            if (readLine == null)
                return Double.NaN;
            try {
                double v = Double.parseDouble(readLine);
                return (v < -32000) ? Double.NaN : v;
            } catch (NumberFormatException e) {
                return Double.NaN;
            }
        }
    }

}