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

Last change on this file since 12517 was 11746, checked in by Don-vip, 7 years 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.