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

Last change on this file since 12929 was 12620, checked in by Don-vip, 7 years ago

see #15182 - deprecate all Main logging methods and introduce suitable replacements in Logging for most of them

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