Ticket #11975: 0001-Extract-NameFinder-for-Nominatim-queries.patch
File 0001-Extract-NameFinder-for-Nominatim-queries.patch, 14.7 KB (added by , 8 years ago) |
---|
-
src/org/openstreetmap/josm/gui/download/PlaceSelection.java
From dcec90bfa02c5984c6e80bd8bf4343fc3836c4af Mon Sep 17 00:00:00 2001 From: Simon Legner <Simon.Legner@gmail.com> Date: Thu, 4 Aug 2016 21:10:48 +0200 Subject: [PATCH 1/2] Extract NameFinder for Nominatim queries --- .../josm/gui/download/PlaceSelection.java | 125 +--------------- src/org/openstreetmap/josm/io/NameFinder.java | 165 +++++++++++++++++++++ 2 files changed, 169 insertions(+), 121 deletions(-) create mode 100644 src/org/openstreetmap/josm/io/NameFinder.java diff --git a/src/org/openstreetmap/josm/gui/download/PlaceSelection.java b/src/org/openstreetmap/josm/gui/download/PlaceSelection.java index db93adc..b8f61c8 100644
a b 50 50 import org.openstreetmap.josm.gui.util.GuiHelper; 51 51 import org.openstreetmap.josm.gui.widgets.HistoryComboBox; 52 52 import org.openstreetmap.josm.gui.widgets.JosmComboBox; 53 import org.openstreetmap.josm.io.NameFinder; 54 import org.openstreetmap.josm.io.NameFinder.SearchResult; 53 55 import org.openstreetmap.josm.io.OsmTransferException; 54 56 import org.openstreetmap.josm.tools.GBC; 55 57 import org.openstreetmap.josm.tools.HttpClient; 56 58 import org.openstreetmap.josm.tools.ImageProvider; 57 import org.openstreetmap.josm.tools.OsmUrlToBounds;58 59 import org.openstreetmap.josm.tools.Utils; 59 import org.xml.sax.Attributes;60 import org.xml.sax.InputSource;61 60 import org.xml.sax.SAXException; 62 61 import org.xml.sax.SAXParseException; 63 import org.xml.sax.helpers.DefaultHandler;64 62 65 63 /** 66 64 * Place selector. … … 75 73 private JTable tblSearchResults; 76 74 private DownloadDialog parent; 77 75 private static final Server[] SERVERS = new Server[] { 78 new Server("Nominatim", "https://nominatim.openstreetmap.org/search?format=xml&q=", tr("Class Type"), tr("Bounds"))76 new Server("Nominatim", NameFinder.NOMINATIM_URL, tr("Class Type"), tr("Bounds")) 79 77 }; 80 78 private final JosmComboBox<Server> server = new JosmComboBox<>(SERVERS); 81 79 … … public void setDownloadArea(Bounds area) { 174 172 tblSearchResults.clearSelection(); 175 173 } 176 174 177 /**178 * Data storage for search results.179 */180 private static class SearchResult {181 public String name;182 public String info;183 public String nearestPlace;184 public String description;185 public double lat;186 public double lon;187 public int zoom;188 public Bounds bounds;189 190 public Bounds getDownloadArea() {191 return bounds != null ? bounds : OsmUrlToBounds.positionToBounds(lat, lon, zoom);192 }193 }194 195 /**196 * A very primitive parser for the name finder's output.197 * Structure of xml described here: http://wiki.openstreetmap.org/index.php/Name_finder198 *199 */200 private static class NameFinderResultParser extends DefaultHandler {201 private SearchResult currentResult;202 private StringBuilder description;203 private int depth;204 private final List<SearchResult> data = new LinkedList<>();205 206 /**207 * Detect starting elements.208 *209 */210 @Override211 public void startElement(String namespaceURI, String localName, String qName, Attributes atts)212 throws SAXException {213 depth++;214 try {215 if ("searchresults".equals(qName)) {216 // do nothing217 } else if ("named".equals(qName) && (depth == 2)) {218 currentResult = new PlaceSelection.SearchResult();219 currentResult.name = atts.getValue("name");220 currentResult.info = atts.getValue("info");221 if (currentResult.info != null) {222 currentResult.info = tr(currentResult.info);223 }224 currentResult.lat = Double.parseDouble(atts.getValue("lat"));225 currentResult.lon = Double.parseDouble(atts.getValue("lon"));226 currentResult.zoom = Integer.parseInt(atts.getValue("zoom"));227 data.add(currentResult);228 } else if ("description".equals(qName) && (depth == 3)) {229 description = new StringBuilder();230 } else if ("named".equals(qName) && (depth == 4)) {231 // this is a "named" place in the nearest places list.232 String info = atts.getValue("info");233 if ("city".equals(info) || "town".equals(info) || "village".equals(info)) {234 currentResult.nearestPlace = atts.getValue("name");235 }236 } else if ("place".equals(qName) && atts.getValue("lat") != null) {237 currentResult = new PlaceSelection.SearchResult();238 currentResult.name = atts.getValue("display_name");239 currentResult.description = currentResult.name;240 currentResult.info = atts.getValue("class");241 if (currentResult.info != null) {242 currentResult.info = tr(currentResult.info);243 }244 currentResult.nearestPlace = tr(atts.getValue("type"));245 currentResult.lat = Double.parseDouble(atts.getValue("lat"));246 currentResult.lon = Double.parseDouble(atts.getValue("lon"));247 String[] bbox = atts.getValue("boundingbox").split(",");248 currentResult.bounds = new Bounds(249 Double.parseDouble(bbox[0]), Double.parseDouble(bbox[2]),250 Double.parseDouble(bbox[1]), Double.parseDouble(bbox[3]));251 data.add(currentResult);252 }253 } catch (NumberFormatException x) {254 Main.error(x); // SAXException does not chain correctly255 throw new SAXException(x.getMessage(), x);256 } catch (NullPointerException x) {257 Main.error(x); // SAXException does not chain correctly258 throw new SAXException(tr("Null pointer exception, possibly some missing tags."), x);259 }260 }261 262 /**263 * Detect ending elements.264 */265 @Override266 public void endElement(String namespaceURI, String localName, String qName) throws SAXException {267 if ("description".equals(qName) && description != null) {268 currentResult.description = description.toString();269 description = null;270 }271 depth--;272 }273 274 /**275 * Read characters for description.276 */277 @Override278 public void characters(char[] data, int start, int length) throws SAXException {279 if (description != null) {280 description.append(data, start, length);281 }282 }283 284 public List<SearchResult> getResult() {285 return data;286 }287 }288 289 175 class SearchAction extends AbstractAction implements DocumentListener { 290 176 291 177 SearchAction() { … … protected void realRun() throws SAXException, IOException, OsmTransferException 375 261 connection.connect(); 376 262 } 377 263 try (Reader reader = connection.getResponse().getContentReader()) { 378 InputSource inputSource = new InputSource(reader); 379 NameFinderResultParser parser = new NameFinderResultParser(); 380 Utils.parseSafeSAX(inputSource, parser); 381 this.data = parser.getResult(); 264 data = NameFinder.parseSearchResults(reader); 382 265 } 383 266 } catch (SAXParseException e) { 384 267 if (!canceled) { -
new file src/org/openstreetmap/josm/io/NameFinder.java
diff --git a/src/org/openstreetmap/josm/io/NameFinder.java b/src/org/openstreetmap/josm/io/NameFinder.java new file mode 100644 index 0000000..c7f9256
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.io; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.io.IOException; 7 import java.io.Reader; 8 import java.net.URL; 9 import java.util.LinkedList; 10 import java.util.List; 11 12 import javax.xml.parsers.ParserConfigurationException; 13 14 import org.openstreetmap.josm.Main; 15 import org.openstreetmap.josm.data.Bounds; 16 import org.openstreetmap.josm.tools.HttpClient; 17 import org.openstreetmap.josm.tools.OsmUrlToBounds; 18 import org.openstreetmap.josm.tools.UncheckedParseException; 19 import org.openstreetmap.josm.tools.Utils; 20 import org.xml.sax.Attributes; 21 import org.xml.sax.InputSource; 22 import org.xml.sax.SAXException; 23 import org.xml.sax.helpers.DefaultHandler; 24 25 /** 26 * Search for names and related items. 27 */ 28 public class NameFinder { 29 30 public static final String NOMINATIM_URL = "https://nominatim.openstreetmap.org/search?format=xml&q="; 31 32 private NameFinder() { 33 } 34 35 public static List<SearchResult> queryNominatim(final String searchExpression) throws IOException { 36 return query(new URL(NOMINATIM_URL + Utils.encodeUrl(searchExpression))); 37 } 38 39 public static List<SearchResult> query(final URL url) throws IOException { 40 final HttpClient connection = HttpClient.create(url); 41 connection.connect(); 42 try (Reader reader = connection.getResponse().getContentReader()) { 43 return parseSearchResults(reader); 44 } catch (ParserConfigurationException | SAXException ex) { 45 throw new UncheckedParseException(ex); 46 } 47 } 48 49 public static List<SearchResult> parseSearchResults(Reader reader) throws IOException, ParserConfigurationException, SAXException { 50 InputSource inputSource = new InputSource(reader); 51 NameFinderResultParser parser = new NameFinderResultParser(); 52 Utils.parseSafeSAX(inputSource, parser); 53 return parser.getResult(); 54 } 55 56 /** 57 * Data storage for search results. 58 */ 59 public static class SearchResult { 60 public String name; 61 public String info; 62 public String nearestPlace; 63 public String description; 64 public double lat; 65 public double lon; 66 public int zoom; 67 public Bounds bounds; 68 69 public Bounds getDownloadArea() { 70 return bounds != null ? bounds : OsmUrlToBounds.positionToBounds(lat, lon, zoom); 71 } 72 } 73 74 /** 75 * A very primitive parser for the name finder's output. 76 * Structure of xml described here: http://wiki.openstreetmap.org/index.php/Name_finder 77 */ 78 private static class NameFinderResultParser extends DefaultHandler { 79 private SearchResult currentResult; 80 private StringBuilder description; 81 private int depth; 82 private final List<SearchResult> data = new LinkedList<>(); 83 84 /** 85 * Detect starting elements. 86 */ 87 @Override 88 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) 89 throws SAXException { 90 depth++; 91 try { 92 if ("searchresults".equals(qName)) { 93 // do nothing 94 } else if ("named".equals(qName) && (depth == 2)) { 95 currentResult = new SearchResult(); 96 currentResult.name = atts.getValue("name"); 97 currentResult.info = atts.getValue("info"); 98 if (currentResult.info != null) { 99 currentResult.info = tr(currentResult.info); 100 } 101 currentResult.lat = Double.parseDouble(atts.getValue("lat")); 102 currentResult.lon = Double.parseDouble(atts.getValue("lon")); 103 currentResult.zoom = Integer.parseInt(atts.getValue("zoom")); 104 data.add(currentResult); 105 } else if ("description".equals(qName) && (depth == 3)) { 106 description = new StringBuilder(); 107 } else if ("named".equals(qName) && (depth == 4)) { 108 // this is a "named" place in the nearest places list. 109 String info = atts.getValue("info"); 110 if ("city".equals(info) || "town".equals(info) || "village".equals(info)) { 111 currentResult.nearestPlace = atts.getValue("name"); 112 } 113 } else if ("place".equals(qName) && atts.getValue("lat") != null) { 114 currentResult = new SearchResult(); 115 currentResult.name = atts.getValue("display_name"); 116 currentResult.description = currentResult.name; 117 currentResult.info = atts.getValue("class"); 118 if (currentResult.info != null) { 119 currentResult.info = tr(currentResult.info); 120 } 121 currentResult.nearestPlace = tr(atts.getValue("type")); 122 currentResult.lat = Double.parseDouble(atts.getValue("lat")); 123 currentResult.lon = Double.parseDouble(atts.getValue("lon")); 124 String[] bbox = atts.getValue("boundingbox").split(","); 125 currentResult.bounds = new Bounds( 126 Double.parseDouble(bbox[0]), Double.parseDouble(bbox[2]), 127 Double.parseDouble(bbox[1]), Double.parseDouble(bbox[3])); 128 data.add(currentResult); 129 } 130 } catch (NumberFormatException x) { 131 Main.error(x); // SAXException does not chain correctly 132 throw new SAXException(x.getMessage(), x); 133 } catch (NullPointerException x) { 134 Main.error(x); // SAXException does not chain correctly 135 throw new SAXException(tr("Null pointer exception, possibly some missing tags."), x); 136 } 137 } 138 139 /** 140 * Detect ending elements. 141 */ 142 @Override 143 public void endElement(String namespaceURI, String localName, String qName) throws SAXException { 144 if ("description".equals(qName) && description != null) { 145 currentResult.description = description.toString(); 146 description = null; 147 } 148 depth--; 149 } 150 151 /** 152 * Read characters for description. 153 */ 154 @Override 155 public void characters(char[] data, int start, int length) throws SAXException { 156 if (description != null) { 157 description.append(data, start, length); 158 } 159 } 160 161 public List<SearchResult> getResult() { 162 return data; 163 } 164 } 165 }