kopia lustrzana https://github.com/JOSM/MapWithAI
Add test for stub ends
Signed-off-by: Taylor Smock <taylor.smock@kaart.com>pull/1/head
rodzic
9ed527b36a
commit
23608a429f
|
@ -18,6 +18,7 @@ import javax.swing.JMenuItem;
|
||||||
|
|
||||||
import org.openstreetmap.josm.actions.JosmAction;
|
import org.openstreetmap.josm.actions.JosmAction;
|
||||||
import org.openstreetmap.josm.data.validation.OsmValidator;
|
import org.openstreetmap.josm.data.validation.OsmValidator;
|
||||||
|
import org.openstreetmap.josm.data.validation.Test;
|
||||||
import org.openstreetmap.josm.gui.MainApplication;
|
import org.openstreetmap.josm.gui.MainApplication;
|
||||||
import org.openstreetmap.josm.gui.MainMenu;
|
import org.openstreetmap.josm.gui.MainMenu;
|
||||||
import org.openstreetmap.josm.gui.MapFrame;
|
import org.openstreetmap.josm.gui.MapFrame;
|
||||||
|
@ -37,6 +38,7 @@ import org.openstreetmap.josm.plugins.mapwithai.backend.MapWithAIUploadHook;
|
||||||
import org.openstreetmap.josm.plugins.mapwithai.backend.MergeDuplicateWaysAction;
|
import org.openstreetmap.josm.plugins.mapwithai.backend.MergeDuplicateWaysAction;
|
||||||
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.ConnectingNodeInformationTest;
|
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.ConnectingNodeInformationTest;
|
||||||
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.RoutingIslandsTest;
|
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.RoutingIslandsTest;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.data.validation.tests.StubEndsTest;
|
||||||
import org.openstreetmap.josm.plugins.mapwithai.frontend.MapWithAIDownloadReader;
|
import org.openstreetmap.josm.plugins.mapwithai.frontend.MapWithAIDownloadReader;
|
||||||
import org.openstreetmap.josm.spi.preferences.Config;
|
import org.openstreetmap.josm.spi.preferences.Config;
|
||||||
import org.openstreetmap.josm.tools.Destroyable;
|
import org.openstreetmap.josm.tools.Destroyable;
|
||||||
|
@ -62,6 +64,9 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
||||||
MENU_ENTRIES.put(MergeDuplicateWaysAction.class, true);
|
MENU_ENTRIES.put(MergeDuplicateWaysAction.class, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static List<Class<? extends Test>> VALIDATORS = Arrays.asList(RoutingIslandsTest.class,
|
||||||
|
ConnectingNodeInformationTest.class, StubEndsTest.class);
|
||||||
|
|
||||||
public MapWithAIPlugin(PluginInformation info) {
|
public MapWithAIPlugin(PluginInformation info) {
|
||||||
super(info);
|
super(info);
|
||||||
|
|
||||||
|
@ -81,12 +86,11 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!OsmValidator.getAllAvailableTestClasses().contains(RoutingIslandsTest.class)) {
|
VALIDATORS.forEach(clazz -> {
|
||||||
OsmValidator.addTest(RoutingIslandsTest.class);
|
if (!OsmValidator.getAllAvailableTestClasses().contains(clazz)) {
|
||||||
}
|
OsmValidator.addTest(clazz);
|
||||||
if (!OsmValidator.getAllAvailableTestClasses().contains(ConnectingNodeInformationTest.class)) {
|
}
|
||||||
OsmValidator.addTest(ConnectingNodeInformationTest.class);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (!Config.getPref().getKeySet().contains(PAINTSTYLE_PREEXISTS)) {
|
if (!Config.getPref().getKeySet().contains(PAINTSTYLE_PREEXISTS)) {
|
||||||
Config.getPref().putBoolean(PAINTSTYLE_PREEXISTS, MapWithAIDataUtils.checkIfMapWithAIPaintStyleExists());
|
Config.getPref().putBoolean(PAINTSTYLE_PREEXISTS, MapWithAIDataUtils.checkIfMapWithAIPaintStyleExists());
|
||||||
|
@ -164,8 +168,7 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
||||||
|
|
||||||
destroyables.forEach(Destroyable::destroy);
|
destroyables.forEach(Destroyable::destroy);
|
||||||
DownloadDialog.removeDownloadSource(mapWithAIDownloadReader);
|
DownloadDialog.removeDownloadSource(mapWithAIDownloadReader);
|
||||||
OsmValidator.removeTest(RoutingIslandsTest.class);
|
VALIDATORS.forEach(OsmValidator::removeTest);
|
||||||
OsmValidator.removeTest(ConnectingNodeInformationTest.class);
|
|
||||||
DownloadListener.destroyAll();
|
DownloadListener.destroyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
// License: GPL. For details, see LICENSE file.
|
||||||
|
package org.openstreetmap.josm.plugins.mapwithai.data.validation.tests;
|
||||||
|
|
||||||
|
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.openstreetmap.josm.command.ChangeCommand;
|
||||||
|
import org.openstreetmap.josm.command.DeleteCommand;
|
||||||
|
import org.openstreetmap.josm.command.SequenceCommand;
|
||||||
|
import org.openstreetmap.josm.data.osm.Node;
|
||||||
|
import org.openstreetmap.josm.data.osm.Way;
|
||||||
|
import org.openstreetmap.josm.data.validation.Severity;
|
||||||
|
import org.openstreetmap.josm.data.validation.Test;
|
||||||
|
import org.openstreetmap.josm.data.validation.TestError;
|
||||||
|
import org.openstreetmap.josm.data.validation.TestError.Builder;
|
||||||
|
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
|
||||||
|
import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin;
|
||||||
|
import org.openstreetmap.josm.spi.preferences.Config;
|
||||||
|
|
||||||
|
public class StubEndsTest extends Test {
|
||||||
|
private static final String HIGHWAY = "highway";
|
||||||
|
private static final List<String> BAD_HIGHWAYS = Arrays.asList("services", "rest_area");
|
||||||
|
private double max_length = Config.getPref().getDouble(MapWithAIPlugin.NAME + ".stubendlength", 10);
|
||||||
|
|
||||||
|
public StubEndsTest() {
|
||||||
|
super(tr("Stub Ends"), tr("Look for short ends on ways"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startTest(ProgressMonitor monitor) {
|
||||||
|
super.startTest(monitor);
|
||||||
|
max_length = Config.getPref().getDouble(MapWithAIPlugin.NAME + ".stubendlength", 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Way way) {
|
||||||
|
if (way.hasTag(HIGHWAY) && !BAD_HIGHWAYS.contains(way.get(HIGHWAY)) && !way.isClosed()) {
|
||||||
|
checkEnds(way);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkEnds(Way way) {
|
||||||
|
List<Node> nodesToFirstConnection = new ArrayList<>();
|
||||||
|
double distanceToFirstConnection = distanceToFirstConnection(way, nodesToFirstConnection);
|
||||||
|
if (distanceToFirstConnection < max_length && !nodesToFirstConnection.isEmpty()) {
|
||||||
|
errors.add(createError(way, nodesToFirstConnection, distanceToFirstConnection));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Node> nodesToLastConnection = new ArrayList<>();
|
||||||
|
double distanceToLastConnection = distanceToLastConnection(way, nodesToLastConnection);
|
||||||
|
if (distanceToLastConnection < max_length && !nodesToLastConnection.isEmpty()) {
|
||||||
|
errors.add(createError(way, nodesToLastConnection, distanceToLastConnection));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TestError createError(Way way, List<Node> nodes, double distance) {
|
||||||
|
Builder error = TestError.builder(this, Severity.ERROR, 333300239).message(tr("Stub end ({0}m)", distance))
|
||||||
|
.primitives(way).highlight(nodes);
|
||||||
|
if (way.isNew()) {
|
||||||
|
Way tWay = new Way(way);
|
||||||
|
List<Node> tNodes = tWay.getNodes();
|
||||||
|
boolean reversed = false;
|
||||||
|
if (tWay.lastNode().equals(nodes.get(0))) {
|
||||||
|
reversed = true;
|
||||||
|
Collections.reverse(tNodes);
|
||||||
|
}
|
||||||
|
for (Node node : nodes) {
|
||||||
|
if (tNodes.get(0).equals(node)) {
|
||||||
|
tNodes.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reversed) {
|
||||||
|
Collections.reverse(tNodes);
|
||||||
|
}
|
||||||
|
List<Node> nodesToDelete = nodes.stream().filter(node -> !node.hasKeys())
|
||||||
|
.filter(node -> node.getReferrers().size() == 1).collect(Collectors.toList());
|
||||||
|
tWay.setNodes(tNodes);
|
||||||
|
nodesToDelete.removeAll(tNodes);
|
||||||
|
error.fix(() -> new SequenceCommand(tr("Remove stub ends"), new ChangeCommand(way, tWay),
|
||||||
|
DeleteCommand.delete(nodesToDelete)));
|
||||||
|
}
|
||||||
|
return error.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double distanceToFirstConnection(Way way, List<Node> nodesToFirstConnection) {
|
||||||
|
return distanceToConnection(way, nodesToFirstConnection, way.getNodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double distanceToLastConnection(Way way, List<Node> nodesToLastConnection) {
|
||||||
|
List<Node> nodes = way.getNodes();
|
||||||
|
Collections.reverse(nodes);
|
||||||
|
return distanceToConnection(way, nodesToLastConnection, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double distanceToConnection(Way way, List<Node> nodesToConnection, List<Node> nodeOrder) {
|
||||||
|
double distance = 0;
|
||||||
|
Node previous = nodeOrder.get(0);
|
||||||
|
for (Node node : nodeOrder) {
|
||||||
|
List<Way> connectingWays = previous.getReferrers().parallelStream().filter(Way.class::isInstance)
|
||||||
|
.map(Way.class::cast).filter(tWay -> !tWay.equals(way))
|
||||||
|
.filter(tWay -> tWay.hasTag(HIGHWAY) && !BAD_HIGHWAYS.contains(tWay.get(HIGHWAY)))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (!node.equals(previous) && connectingWays.isEmpty()) {
|
||||||
|
nodesToConnection.add(previous);
|
||||||
|
distance += node.getCoor().greatCircleDistance(previous.getCoor());
|
||||||
|
previous = node;
|
||||||
|
}
|
||||||
|
if (!connectingWays.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package org.openstreetmap.josm.plugins.mapwithai.data.validation.tests;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.openstreetmap.josm.TestUtils;
|
||||||
|
import org.openstreetmap.josm.data.coor.LatLon;
|
||||||
|
import org.openstreetmap.josm.data.osm.DataSet;
|
||||||
|
import org.openstreetmap.josm.data.osm.Node;
|
||||||
|
import org.openstreetmap.josm.data.osm.Way;
|
||||||
|
import org.openstreetmap.josm.testutils.JOSMTestRules;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
|
public class StubEndsTestTest {
|
||||||
|
@Rule
|
||||||
|
@SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
|
||||||
|
public JOSMTestRules test = new JOSMTestRules().projection();
|
||||||
|
private Way nonStaticWay;
|
||||||
|
private Way staticWay;
|
||||||
|
private StubEndsTest tester;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
staticWay = TestUtils.newWay("highway=residential", new Node(new LatLon(0, 0)),
|
||||||
|
new Node(new LatLon(0.01, 0.01)));
|
||||||
|
DataSet ds = new DataSet();
|
||||||
|
staticWay.getNodes().forEach(ds::addPrimitive);
|
||||||
|
ds.addPrimitive(staticWay);
|
||||||
|
|
||||||
|
nonStaticWay = TestUtils.newWay("highway=residential", new Node(new LatLon(0.010001, 0.010001)),
|
||||||
|
staticWay.lastNode(), new Node(new LatLon(1, 2)));
|
||||||
|
nonStaticWay.getNodes().stream().filter(node -> node.getDataSet() == null).forEach(ds::addPrimitive);
|
||||||
|
ds.addPrimitive(nonStaticWay);
|
||||||
|
|
||||||
|
tester = new StubEndsTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartEnd() {
|
||||||
|
tester.visit(staticWay);
|
||||||
|
assertTrue(tester.getErrors().isEmpty());
|
||||||
|
|
||||||
|
tester.visit(nonStaticWay);
|
||||||
|
assertFalse(tester.getErrors().isEmpty());
|
||||||
|
Node toDelete = nonStaticWay.getNode(0);
|
||||||
|
tester.getErrors().get(0).getFix().executeCommand();
|
||||||
|
assertTrue(toDelete.isDeleted());
|
||||||
|
assertEquals(2, nonStaticWay.getNodesCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEndEnd() {
|
||||||
|
List<Node> nodes = nonStaticWay.getNodes();
|
||||||
|
Collections.reverse(nodes);
|
||||||
|
nonStaticWay.setNodes(nodes);
|
||||||
|
|
||||||
|
tester.visit(staticWay);
|
||||||
|
assertTrue(tester.getErrors().isEmpty());
|
||||||
|
|
||||||
|
tester.visit(nonStaticWay);
|
||||||
|
assertFalse(tester.getErrors().isEmpty());
|
||||||
|
Node toDelete = nonStaticWay.lastNode();
|
||||||
|
tester.getErrors().get(0).getFix().executeCommand();
|
||||||
|
assertTrue(toDelete.isDeleted());
|
||||||
|
assertEquals(2, nonStaticWay.getNodesCount());
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue