Ticket #11975: 0001-Extract-NameFinder-for-Nominatim-queries.patch

File 0001-Extract-NameFinder-for-Nominatim-queries.patch, 14.7 KB (added by simon04, 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  
    5050import org.openstreetmap.josm.gui.util.GuiHelper;
    5151import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
    5252import org.openstreetmap.josm.gui.widgets.JosmComboBox;
     53import org.openstreetmap.josm.io.NameFinder;
     54import org.openstreetmap.josm.io.NameFinder.SearchResult;
    5355import org.openstreetmap.josm.io.OsmTransferException;
    5456import org.openstreetmap.josm.tools.GBC;
    5557import org.openstreetmap.josm.tools.HttpClient;
    5658import org.openstreetmap.josm.tools.ImageProvider;
    57 import org.openstreetmap.josm.tools.OsmUrlToBounds;
    5859import org.openstreetmap.josm.tools.Utils;
    59 import org.xml.sax.Attributes;
    60 import org.xml.sax.InputSource;
    6160import org.xml.sax.SAXException;
    6261import org.xml.sax.SAXParseException;
    63 import org.xml.sax.helpers.DefaultHandler;
    6462
    6563/**
    6664 * Place selector.
     
    7573    private JTable tblSearchResults;
    7674    private DownloadDialog parent;
    7775    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"))
    7977    };
    8078    private final JosmComboBox<Server> server = new JosmComboBox<>(SERVERS);
    8179
    public void setDownloadArea(Bounds area) {  
    174172        tblSearchResults.clearSelection();
    175173    }
    176174
    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_finder
    198      *
    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         @Override
    211         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 nothing
    217                 } 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 correctly
    255                 throw new SAXException(x.getMessage(), x);
    256             } catch (NullPointerException x) {
    257                 Main.error(x); // SAXException does not chain correctly
    258                 throw new SAXException(tr("Null pointer exception, possibly some missing tags."), x);
    259             }
    260         }
    261 
    262         /**
    263          * Detect ending elements.
    264          */
    265         @Override
    266         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         @Override
    278         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 
    289175    class SearchAction extends AbstractAction implements DocumentListener {
    290176
    291177        SearchAction() {
    protected void realRun() throws SAXException, IOException, OsmTransferException  
    375261                    connection.connect();
    376262                }
    377263                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);
    382265                }
    383266            } catch (SAXParseException e) {
    384267                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.
     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.LinkedList;
     10import java.util.List;
     11
     12import javax.xml.parsers.ParserConfigurationException;
     13
     14import org.openstreetmap.josm.Main;
     15import org.openstreetmap.josm.data.Bounds;
     16import org.openstreetmap.josm.tools.HttpClient;
     17import org.openstreetmap.josm.tools.OsmUrlToBounds;
     18import org.openstreetmap.josm.tools.UncheckedParseException;
     19import org.openstreetmap.josm.tools.Utils;
     20import org.xml.sax.Attributes;
     21import org.xml.sax.InputSource;
     22import org.xml.sax.SAXException;
     23import org.xml.sax.helpers.DefaultHandler;
     24
     25/**
     26 * Search for names and related items.
     27 */
     28public 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}