Ticket #17188: 17188_v3.patch

File 17188_v3.patch, 9.1 KB (added by reichg, 4 years ago)

includes user input and checking if feature is in download area.

  • src/org/openstreetmap/josm/data/validation/tests/Highways.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.validation.tests;
    33
    4 import static org.openstreetmap.josm.data.validation.tests.CrossingWays.HIGHWAY;
    5 import static org.openstreetmap.josm.tools.I18n.tr;
     4import org.openstreetmap.josm.command.ChangePropertyCommand;
     5import org.openstreetmap.josm.data.osm.BBox;
     6import org.openstreetmap.josm.data.osm.DataSet;
     7import org.openstreetmap.josm.data.osm.Node;
     8import org.openstreetmap.josm.data.osm.OsmPrimitive;
     9import org.openstreetmap.josm.data.osm.OsmUtils;
     10import org.openstreetmap.josm.data.osm.Way;
     11import org.openstreetmap.josm.data.validation.Severity;
     12import org.openstreetmap.josm.data.validation.Test;
     13import org.openstreetmap.josm.data.validation.TestError;
     14import org.openstreetmap.josm.spi.preferences.Config;
     15import org.openstreetmap.josm.spi.preferences.IPreferences;
     16import org.openstreetmap.josm.tools.GBC;
     17import org.openstreetmap.josm.tools.Logging;
     18import org.openstreetmap.josm.tools.Utils;
    619
     20import javax.swing.*;
     21import javax.swing.event.DocumentEvent;
     22import javax.swing.event.DocumentListener;
     23import java.awt.*;
     24import java.awt.event.ActionEvent;
     25import java.awt.event.ActionListener;
     26import java.text.DecimalFormat;
    727import java.util.ArrayList;
    828import java.util.Arrays;
    929import java.util.HashMap;
     
    1434import java.util.Set;
    1535import java.util.stream.Collectors;
    1636
    17 import org.openstreetmap.josm.command.ChangePropertyCommand;
    18 import org.openstreetmap.josm.data.osm.Node;
    19 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    20 import org.openstreetmap.josm.data.osm.OsmUtils;
    21 import org.openstreetmap.josm.data.osm.Way;
    22 import org.openstreetmap.josm.data.validation.Severity;
    23 import org.openstreetmap.josm.data.validation.Test;
    24 import org.openstreetmap.josm.data.validation.TestError;
    25 import org.openstreetmap.josm.tools.Logging;
    26 import org.openstreetmap.josm.tools.Utils;
     37import static org.openstreetmap.josm.data.validation.tests.CrossingWays.HIGHWAY;
     38import static org.openstreetmap.josm.tools.I18n.tr;
    2739
    2840/**
    2941 * Test that performs semantic checks on highways.
     
    3850    protected static final int SOURCE_MAXSPEED_CONTEXT_MISMATCH_VS_MAXSPEED = 2705;
    3951    protected static final int SOURCE_MAXSPEED_CONTEXT_MISMATCH_VS_HIGHWAY = 2706;
    4052    protected static final int SOURCE_WRONG_LINK = 2707;
     53    protected static final int SOURCE_BUS_STOP_NEEDED = 2708;
    4154
    4255    protected static final String SOURCE_MAXSPEED = "source:maxspeed";
     56    protected static final String BUS = "bus";
     57    protected static final String PUBLIC_TRANSPORT = "public_transport";
    4358
     59    private final JFormattedTextField formattedTextField;
     60    private final JLabel warningLabel;
     61    private final JButton defaultButton;
     62
     63    private final JLabel labelForBboxExpansion = new JLabel("Enter bbox expansion constant for searching nearby Nodes (LatLon degrees)");
     64    private static final String BBOX_EXPANSION_PREF_KEY = "bboxExpansionConstant";
     65    private static final double EXPANSE_DISTANCE_DEFAULT = 0.00015;
     66    private final IPreferences preferences = Config.getPref();
     67
     68    private final ActionListener actionListener;
     69    private final DocumentListener documentListener;
     70
    4471    /**
    4572     * Classified highways in order of importance
    4673     */
     
    73100     * Constructs a new {@code Highways} test.
    74101     */
    75102    public Highways() {
     103
    76104        super(tr("Highways"), tr("Performs semantic checks on highways."));
     105        formattedTextField = new JFormattedTextField(new DecimalFormat("#.########"));
     106        defaultButton = new JButton("Set Default (~15 meters)");
     107        warningLabel = new JLabel("Entry must be a valid number.");
     108        warningLabel.setVisible(false);
     109        warningLabel.setForeground(Color.RED);
     110        documentListener = new DocumentListener() {
     111            @Override
     112            public void insertUpdate(DocumentEvent e) {
     113                warn();
     114            }
     115
     116            @Override
     117            public void removeUpdate(DocumentEvent e) {
     118                warn();
     119            }
     120
     121            @Override
     122            public void changedUpdate(DocumentEvent e) {
     123                //unneeded for this listener's purpose.
     124            }
     125        };
     126        actionListener = e -> {
     127            formattedTextField.setValue(EXPANSE_DISTANCE_DEFAULT);
     128        };
    77129    }
    78130
    79131    @Override
     
    90142                // as maxspeed is not set on highways here but on signs, speed cameras, etc.
    91143                testSourceMaxspeed(n, false);
    92144            }
     145            if ((IN_DOWNLOADED_AREA.test(n) || n.isNew()) && n.hasTag(BUS, "yes") && n.hasTag(PUBLIC_TRANSPORT, "stop_position")) {
     146                // Test for 17188: complain about bus stop position without nearby highway=bus_stop
     147                testMissingBusStopNode(n);
     148            }
    93149        }
    94150    }
    95151
     
    243299        }
    244300    }
    245301
     302    /**
     303     * Tests for bus stop ("public_transport"="stop_position"/"bus"="yes") Nodes a long a way that are missing a related nearby Node with "highway=bus_stop"
     304     *
     305     * @param n Node being visited
     306     */
     307    public void testMissingBusStopNode(Node n) {
     308        int countOfNodesWithProperTags = 0;
     309        double expanseDistance = EXPANSE_DISTANCE_DEFAULT;
     310
     311        //Approximately 15 meters depending on Lat/Lon
     312        String expanseDistanceFromPrefs = preferences.get(BBOX_EXPANSION_PREF_KEY);
     313        if (expanseDistanceFromPrefs != null && !expanseDistanceFromPrefs.isEmpty()) {
     314            expanseDistance = Double.parseDouble(expanseDistanceFromPrefs);
     315        }
     316        List<Node> nearbyNodesWithinTwentyFiveMeters = getNearbyNodesWithinShortDistance(n, expanseDistance);
     317        for (Node nearbyNodeWithinTwentyFiveMeters : nearbyNodesWithinTwentyFiveMeters) {
     318            if (nearbyNodeWithinTwentyFiveMeters.hasTag(HIGHWAY, "bus_stop") && nearbyNodeWithinTwentyFiveMeters.hasTag(BUS, "yes")) {
     319                countOfNodesWithProperTags += 1;
     320            }
     321        }
     322        if (countOfNodesWithProperTags == 0) {
     323            errors.add(TestError.builder(this, Severity.WARNING, SOURCE_BUS_STOP_NEEDED)
     324                    .message(tr("Node needs a nearby related Node with tags: {0} and {1}.",
     325                            "highway=bus_stop", "bus=yes"))
     326                    .primitives(n)
     327                    .build());
     328        }
     329    }
     330
     331    /**
     332     * Gathers list of Nodes within specified approximate distance (takes double but unit is LatLon degrees) of Node n.
     333     *
     334     * @param n               Node being visited
     335     * @param expanseDistance Distance to expand Node bounds. Units are in LatLon degrees.
     336     * @return List of Nodes
     337     */
     338    public List<Node> getNearbyNodesWithinShortDistance(Node n, double expanseDistance) {
     339        DataSet nodeDataSet = n.getDataSet();
     340
     341        BBox nodeBBox = n.getBBox();
     342        nodeBBox.addLatLon(nodeBBox.getCenter(), expanseDistance);
     343
     344        return nodeDataSet.searchNodes(nodeBBox);
     345    }
     346
    246347    private void handleCarWay(Node n, Way w) {
    247348        carsWays++;
    248349        if (!w.isFirstLastNode(n) || carsWays > 1) {
     
    295396            }
    296397        }
    297398    }
     399
     400    @Override
     401    public void addGui(JPanel testPanel) {
     402        super.addGui(testPanel);
     403        String bboxExpansionPref = preferences.get(BBOX_EXPANSION_PREF_KEY);
     404        if (bboxExpansionPref != null && !bboxExpansionPref.isEmpty()) {
     405            try {
     406                Double.parseDouble(bboxExpansionPref);
     407                formattedTextField.setValue(Double.parseDouble(bboxExpansionPref));
     408            }
     409            catch (Exception exception) {
     410                formattedTextField.setValue(EXPANSE_DISTANCE_DEFAULT);
     411            }
     412        }
     413        else {
     414            formattedTextField.setValue(EXPANSE_DISTANCE_DEFAULT);
     415        }
     416        formattedTextField.setColumns(5);
     417        formattedTextField.getDocument().addDocumentListener(documentListener);
     418        defaultButton.addActionListener(actionListener);
     419        testPanel.add(labelForBboxExpansion, GBC.eol().insets(20,0,0,0));
     420        testPanel.add(formattedTextField, GBC.eol().insets(20, 0, 0, 0));
     421        testPanel.add(warningLabel, GBC.eol().insets(20,0,0,0));
     422        testPanel.add(defaultButton, GBC.eol().insets(20,0,0,0));
     423    }
     424
     425    public void warn() {
     426        String expansionDegrees = formattedTextField.getText();
     427        if (expansionDegrees.equals(".") || expansionDegrees.equals("")) {
     428            expansionDegrees = "0";
     429        }
     430        try {
     431            Double.parseDouble(expansionDegrees);
     432            warningLabel.setVisible(false);
     433        }
     434        catch (Exception exception) {
     435            warningLabel.setVisible(true);
     436        }
     437    }
     438
     439    @Override
     440    public boolean ok() {
     441        //Uses most recent VALID entry for bbox expanse on OK
     442        preferences.put(BBOX_EXPANSION_PREF_KEY, formattedTextField.getValue().toString());
     443        return false;
     444    }
    298445}