| 4 | | import static org.openstreetmap.josm.data.validation.tests.CrossingWays.HIGHWAY; |
| 5 | | import static org.openstreetmap.josm.tools.I18n.tr; |
| | 4 | import org.openstreetmap.josm.command.ChangePropertyCommand; |
| | 5 | import org.openstreetmap.josm.data.osm.BBox; |
| | 6 | import org.openstreetmap.josm.data.osm.DataSet; |
| | 7 | import org.openstreetmap.josm.data.osm.Node; |
| | 8 | import org.openstreetmap.josm.data.osm.OsmPrimitive; |
| | 9 | import org.openstreetmap.josm.data.osm.OsmUtils; |
| | 10 | import org.openstreetmap.josm.data.osm.Way; |
| | 11 | import org.openstreetmap.josm.data.validation.Severity; |
| | 12 | import org.openstreetmap.josm.data.validation.Test; |
| | 13 | import org.openstreetmap.josm.data.validation.TestError; |
| | 14 | import org.openstreetmap.josm.spi.preferences.Config; |
| | 15 | import org.openstreetmap.josm.spi.preferences.IPreferences; |
| | 16 | import org.openstreetmap.josm.tools.GBC; |
| | 17 | import org.openstreetmap.josm.tools.Logging; |
| | 18 | import org.openstreetmap.josm.tools.Utils; |
| 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; |
| | 37 | import static org.openstreetmap.josm.data.validation.tests.CrossingWays.HIGHWAY; |
| | 38 | import static org.openstreetmap.josm.tools.I18n.tr; |
| | 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 | |
| | 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 | }; |
| | 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 | |
| | 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 | } |