source: josm/trunk/src/org/openstreetmap/josm/io/NameFinder.java @ 11746

Last change on this file since 11746 was 11746, checked in by Don-vip, 7 months ago

PMD - Strict Exceptions

File size: 9.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.IOException;
7import java.io.Reader;
8import java.net.URL;
9import java.util.Collections;
10import java.util.LinkedList;
11import java.util.List;
12
13import javax.xml.parsers.ParserConfigurationException;
14
15import org.openstreetmap.josm.Main;
16import org.openstreetmap.josm.data.Bounds;
17import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
18import org.openstreetmap.josm.data.osm.PrimitiveId;
19import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
20import org.openstreetmap.josm.tools.HttpClient;
21import org.openstreetmap.josm.tools.OsmUrlToBounds;
22import org.openstreetmap.josm.tools.UncheckedParseException;
23import org.openstreetmap.josm.tools.Utils;
24import org.xml.sax.Attributes;
25import org.xml.sax.InputSource;
26import org.xml.sax.SAXException;
27import org.xml.sax.helpers.DefaultHandler;
28
29/**
30 * Search for names and related items.
31 * @since 11002
32 */
33public final class NameFinder {
34
35    /**
36     * Nominatim URL.
37     */
38    public static final String NOMINATIM_URL = "https://nominatim.openstreetmap.org/search?format=xml&q=";
39
40    private NameFinder() {
41    }
42
43    /**
44     * Performs a Nominatim search.
45     * @param searchExpression Nominatim search expression
46     * @return search results
47     * @throws IOException if any IO error occurs.
48     */
49    public static List<SearchResult> queryNominatim(final String searchExpression) throws IOException {
50        return query(new URL(NOMINATIM_URL + Utils.encodeUrl(searchExpression)));
51    }
52
53    /**
54     * Performs a custom search.
55     * @param url search URL to any Nominatim instance
56     * @return search results
57     * @throws IOException if any IO error occurs.
58     */
59    public static List<SearchResult> query(final URL url) throws IOException {
60        final HttpClient connection = HttpClient.create(url);
61        connection.connect();
62        try (Reader reader = connection.getResponse().getContentReader()) {
63            return parseSearchResults(reader);
64        } catch (ParserConfigurationException | SAXException ex) {
65            throw new UncheckedParseException(ex);
66        }
67    }
68
69    /**
70     * Parse search results as returned by Nominatim.
71     * @param reader reader
72     * @return search results
73     * @throws ParserConfigurationException if a parser cannot be created which satisfies the requested configuration.
74     * @throws SAXException for SAX errors.
75     * @throws IOException if any IO error occurs.
76     */
77    public static List<SearchResult> parseSearchResults(Reader reader) throws IOException, ParserConfigurationException, SAXException {
78        InputSource inputSource = new InputSource(reader);
79        NameFinderResultParser parser = new NameFinderResultParser();
80        Utils.parseSafeSAX(inputSource, parser);
81        return parser.getResult();
82    }
83
84    /**
85     * Data storage for search results.
86     */
87    public static class SearchResult {
88        private String name;
89        private String info;
90        private String nearestPlace;
91        private String description;
92        private double lat;
93        private double lon;
94        private int zoom;
95        private Bounds bounds;
96        private PrimitiveId osmId;
97
98        /**
99         * Returns the name.
100         * @return the name
101         */
102        public final String getName() {
103            return name;
104        }
105
106        /**
107         * Returns the info.
108         * @return the info
109         */
110        public final String getInfo() {
111            return info;
112        }
113
114        /**
115         * Returns the nearest place.
116         * @return the nearest place
117         */
118        public final String getNearestPlace() {
119            return nearestPlace;
120        }
121
122        /**
123         * Returns the description.
124         * @return the description
125         */
126        public final String getDescription() {
127            return description;
128        }
129
130        /**
131         * Returns the latitude.
132         * @return the latitude
133         */
134        public final double getLat() {
135            return lat;
136        }
137
138        /**
139         * Returns the longitude.
140         * @return the longitude
141         */
142        public final double getLon() {
143            return lon;
144        }
145
146        /**
147         * Returns the zoom.
148         * @return the zoom
149         */
150        public final int getZoom() {
151            return zoom;
152        }
153
154        /**
155         * Returns the bounds.
156         * @return the bounds
157         */
158        public final Bounds getBounds() {
159            return bounds;
160        }
161
162        /**
163         * Returns the OSM id.
164         * @return the OSM id
165         */
166        public final PrimitiveId getOsmId() {
167            return osmId;
168        }
169
170        /**
171         * Returns the download area.
172         * @return the download area
173         */
174        public Bounds getDownloadArea() {
175            return bounds != null ? bounds : OsmUrlToBounds.positionToBounds(lat, lon, zoom);
176        }
177    }
178
179    /**
180     * A very primitive parser for the name finder's output.
181     * Structure of xml described here:  http://wiki.openstreetmap.org/index.php/Name_finder
182     */
183    private static class NameFinderResultParser extends DefaultHandler {
184        private SearchResult currentResult;
185        private StringBuilder description;
186        private int depth;
187        private final List<SearchResult> data = new LinkedList<>();
188
189        /**
190         * Detect starting elements.
191         */
192        @Override
193        public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
194                throws SAXException {
195            depth++;
196            try {
197                if ("searchresults".equals(qName)) {
198                    // do nothing
199                } else if (depth == 2 && "named".equals(qName)) {
200                    currentResult = new SearchResult();
201                    currentResult.name = atts.getValue("name");
202                    currentResult.info = atts.getValue("info");
203                    if (currentResult.info != null) {
204                        currentResult.info = tr(currentResult.info);
205                    }
206                    currentResult.lat = Double.parseDouble(atts.getValue("lat"));
207                    currentResult.lon = Double.parseDouble(atts.getValue("lon"));
208                    currentResult.zoom = Integer.parseInt(atts.getValue("zoom"));
209                    data.add(currentResult);
210                } else if (depth == 3 && "description".equals(qName)) {
211                    description = new StringBuilder();
212                } else if (depth == 4 && "named".equals(qName)) {
213                    // this is a "named" place in the nearest places list.
214                    String info = atts.getValue("info");
215                    if ("city".equals(info) || "town".equals(info) || "village".equals(info)) {
216                        currentResult.nearestPlace = atts.getValue("name");
217                    }
218                } else if ("place".equals(qName) && atts.getValue("lat") != null) {
219                    currentResult = new SearchResult();
220                    currentResult.name = atts.getValue("display_name");
221                    currentResult.description = currentResult.name;
222                    currentResult.info = atts.getValue("class");
223                    if (currentResult.info != null) {
224                        currentResult.info = tr(currentResult.info);
225                    }
226                    currentResult.nearestPlace = tr(atts.getValue("type"));
227                    currentResult.lat = Double.parseDouble(atts.getValue("lat"));
228                    currentResult.lon = Double.parseDouble(atts.getValue("lon"));
229                    String[] bbox = atts.getValue("boundingbox").split(",");
230                    currentResult.bounds = new Bounds(
231                            Double.parseDouble(bbox[0]), Double.parseDouble(bbox[2]),
232                            Double.parseDouble(bbox[1]), Double.parseDouble(bbox[3]));
233                    final String osmId = atts.getValue("osm_id");
234                    final String osmType = atts.getValue("osm_type");
235                    if (osmId != null && osmType != null) {
236                        currentResult.osmId = new SimplePrimitiveId(Long.parseLong(osmId), OsmPrimitiveType.from(osmType));
237                    }
238                    data.add(currentResult);
239                }
240            } catch (NumberFormatException ex) {
241                Main.error(ex); // SAXException does not chain correctly
242                throw new SAXException(ex.getMessage(), ex);
243            } catch (NullPointerException ex) { // NOPMD
244                Main.error(ex); // SAXException does not chain correctly
245                throw new SAXException(tr("Null pointer exception, possibly some missing tags."), ex);
246            }
247        }
248
249        /**
250         * Detect ending elements.
251         */
252        @Override
253        public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
254            if (description != null && "description".equals(qName)) {
255                currentResult.description = description.toString();
256                description = null;
257            }
258            depth--;
259        }
260
261        /**
262         * Read characters for description.
263         */
264        @Override
265        public void characters(char[] data, int start, int length) throws SAXException {
266            if (description != null) {
267                description.append(data, start, length);
268            }
269        }
270
271        public List<SearchResult> getResult() {
272            return Collections.unmodifiableList(data);
273        }
274    }
275}
Note: See TracBrowser for help on using the repository browser.