Ticket #10205: 10205-beta.patch

File 10205-beta.patch, 12.9 KB (added by GerdP, 5 years ago)

work in progress

  • src/org/openstreetmap/josm/actions/AlignInCircleAction.java

     
    2929import org.openstreetmap.josm.data.osm.Way;
    3030import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
    3131import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay;
     32import org.openstreetmap.josm.data.validation.tests.CrossingWays;
    3233import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay;
    3334import org.openstreetmap.josm.gui.Notification;
    3435import org.openstreetmap.josm.tools.Geometry;
     
    4243 * @author Petr Dlouhý
    4344 * @author Teemu Koskinen
    4445 * @author Alain Delplanque
     46 * @author Gerd Petermann
    4547 *
    4648 * @since 146
    4749 */
     
    136138     * In all cases, selected nodes are fix, nodes with more than one referrers are fix
    137139     * (first referrer is the selected way)
    138140     * <p>
    139      * Case 3: Only nodes are selected
    140      * --&gt; Align these nodes, all are fix
    141141     * @param ds data set in which the command operates
    142142     * @return the resulting command to execute to perform action
    143143     * @throws InvalidSelection if selection cannot be used
     
    146146     */
    147147    public static Command buildCommand(DataSet ds) throws InvalidSelection {
    148148        Collection<OsmPrimitive> sel = ds.getSelected();
    149         List<Node> nodes = new LinkedList<>();
     149        List<Node> selectedNodes = new LinkedList<>();
    150150        // fixNodes: All nodes for which the angle relative to center should not be modified
    151         Set<Node> fixNodes = new HashSet<>();
    152151        List<Way> ways = new LinkedList<>();
    153         EastNorth center = null;
    154         double radius = 0;
    155152
    156153        for (OsmPrimitive osm : sel) {
    157154            if (osm instanceof Node) {
    158                 nodes.add((Node) osm);
     155                selectedNodes.add((Node) osm);
    159156            } else if (osm instanceof Way) {
    160157                ways.add((Way) osm);
    161158            }
    162159        }
     160        if (ways.isEmpty()) {
     161            throw new InvalidSelection(tr("Please select at least one way."));
     162        }
    163163
     164        Set<Node> fixNodes = new HashSet<>();
     165        EastNorth center = null;
     166        double radius = 0;
     167        final List<Node> nodes;
    164168        if (ways.size() == 1 && !ways.get(0).isClosed()) {
    165169            // Case 1
    166170            Way w = ways.get(0);
    167             if (SelfIntersectingWay.isSelfIntersecting(w)) {
    168                 throw new InvalidSelection(tr("Self-intersecting way"));
    169             }
    170 
    171171            fixNodes.add(w.firstNode());
    172172            fixNodes.add(w.lastNode());
    173             fixNodes.addAll(nodes);
    174             fixNodes.addAll(collectNodesWithExternReferrers(ways));
     173            fixNodes.addAll(selectedNodes);
    175174            // Temporary closed way used to reorder nodes
    176175            Way closedWay = new Way(w);
    177176            closedWay.addNode(w.firstNode());
    178177            nodes = collectNodesAnticlockwise(Collections.singletonList(closedWay));
    179178            closedWay.setNodes(null); // see #19885
    180         } else if (!ways.isEmpty() && checkWaysArePolygon(ways)) {
     179        } else if (checkWaysArePolygon(ways)) {
    181180            // Case 2
     181            // nodes on selected ways
    182182            List<Node> inside = new ArrayList<>();
    183183            List<Node> outside = new ArrayList<>();
    184184
    185             for (Node n: nodes) {
    186                 boolean isInside = ways.stream().anyMatch(w -> w.getNodes().contains(n));
    187                 if (isInside)
     185            for (Node n: selectedNodes) {
     186                if (ways.stream().anyMatch(w -> w.containsNode(n)))
    188187                    inside.add(n);
    189188                else
    190189                    outside.add(n);
     
    204203            }
    205204
    206205            fixNodes.addAll(inside);
    207             fixNodes.addAll(collectNodesWithExternReferrers(ways));
    208206            nodes = collectNodesAnticlockwise(ways);
    209             if (nodes.size() < 4) {
    210                 throw new InvalidSelection(tr("Not enough nodes in selected ways."));
    211             }
    212         } else if (ways.isEmpty() && nodes.size() > 3) {
    213             // Case 3
    214             fixNodes.addAll(nodes);
    215             // No need to reorder nodes since all are fix
    216207        } else {
    217             if (ways.isEmpty() && nodes.size() <= 3)
    218                 throw new InvalidSelection(tr("Please select at least four nodes."));
    219208            throw new InvalidSelection();
    220209        }
     210        fixNodes.addAll(collectNodesWithExternReferrers(ways));
    221211
     212        if (nodes.size() < 4) {
     213            throw new InvalidSelection(tr("Not enough nodes in selected ways."));
     214        }
    222215        // Check if one or more nodes are outside of download area
    223216        if (nodes.stream().anyMatch(Node::isOutsideDownloadArea))
    224217            throw new InvalidSelection(tr("One or more nodes involved in this action is outside of the downloaded area."));
    225218
     219
     220
    226221        if (center == null) {
    227             // Compute the center of nodes
    228             center = Geometry.getCenter(nodes);
     222            if (validateGeometry(nodes)) {
     223                // Compute the center of nodes
     224                center = Geometry.getCenter(nodes);
     225            }
    229226            if (center == null) {
    230                 throw new InvalidSelection(tr("Cannot determine center of selected nodes."));
     227                throw new InvalidSelection(tr("Cannot determine center of circle for this geometry."));
    231228            }
    232229        }
     230        Logging.debug("center: {0}", new Node(center).getCoor().toString());
    233231
    234232        // Now calculate the average distance to each node from the
    235233        // center. This method is ok as long as distances are short
     
    285283        return new SequenceCommand(tr("Align Nodes in Circle"), cmds);
    286284    }
    287285
     286    private static boolean validateGeometry(List<Node> nodes) {
     287        Way test = new Way();
     288        try {
     289            test.setNodes(nodes);
     290            if (!test.isClosed())
     291                test.addNode(test.firstNode());
     292            if (CrossingWays.isSelfCrossing(test))
     293                return false;
     294            return !SelfIntersectingWay.isSelfIntersecting(test);
     295        } finally {
     296            test.setNodes(null); // see #19855
     297        }
     298    }
     299
    288300    /**
    289301     * Collect all nodes with more than one referrer.
    290302     * @param ways Ways from witch nodes are selected
     
    343355        }
    344356        return true;
    345357    }
    346 
    347358}
  • src/org/openstreetmap/josm/data/validation/tests/CrossingWays.java

     
    2626import org.openstreetmap.josm.data.validation.TestError;
    2727import org.openstreetmap.josm.data.validation.util.ValUtil;
    2828import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     29import org.openstreetmap.josm.tools.CheckParameterUtil;
    2930import org.openstreetmap.josm.tools.Logging;
    3031
    3132/**
     
    468469        }
    469470    }
    470471
     472    /**
     473     * Check if the given way is self crossing
     474     * @param way the way to check
     475     * @return {@code true} if one or more segments of the way are crossing
     476     * @see SelfIntersectingWay
     477     * @since xxx
     478     */
     479    public static boolean isSelfCrossing(Way way) {
     480        CheckParameterUtil.ensureParameterNotNull(way, "way");
     481        SelfCrossing test = new SelfCrossing();
     482        test.visit(way);
     483        return !test.getErrors().isEmpty();
     484    }
    471485}
  • test/data/regress/20041/circle-action.osm

     
     1<?xml version='1.0' encoding='UTF-8'?>
     2<osm version='0.6' generator='JOSM'>
     3  <node id='3353142168' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90029704945' lon='8.44321406009' />
     4  <node id='3353142170' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90031807489' lon='8.44326193915' />
     5  <node id='3353142171' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90030468523' lon='8.44306677855' />
     6  <node id='3353142174' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90033729149' lon='8.44301884234' />
     7  <node id='3353142175' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90034976283' lon='8.44329321571' />
     8  <node id='3353142183' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90040118461' lon='8.44329685665' />
     9  <node id='3353142184' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.9004078959' lon='8.44300955386' />
     10  <node id='3353142189' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90046511808' lon='8.44319779138' />
     11  <node id='3353142190' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90044790476' lon='8.44305434646' />
     12  <node id='3353142192' action='modify' timestamp='2015-02-15T23:33:27Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323' lat='52.90046284581' lon='8.44309505092' />
     13  <way id='-104527' action='modify' visible='true'>
     14    <nd ref='3353142171' />
     15    <nd ref='3353142168' />
     16    <nd ref='3353142170' />
     17    <nd ref='3353142175' />
     18    <nd ref='3353142183' />
     19    <nd ref='3353142189' />
     20    <nd ref='3353142192' />
     21    <nd ref='3353142190' />
     22    <nd ref='3353142184' />
     23    <nd ref='3353142174' />
     24    <nd ref='3353142171' />
     25    <tag k='barrier' v='fence' />
     26  </way>
     27  <way id='328483730' timestamp='2015-02-15T23:33:35Z' uid='715371' user='715371' visible='true' version='1' changeset='28875323'>
     28    <nd ref='3353142174' />
     29    <nd ref='3353142184' />
     30    <nd ref='3353142190' />
     31    <nd ref='3353142192' />
     32    <nd ref='3353142189' />
     33    <nd ref='3353142183' />
     34    <nd ref='3353142175' />
     35    <nd ref='3353142170' />
     36    <nd ref='3353142168' />
     37    <nd ref='3353142171' />
     38    <nd ref='3353142174' />
     39    <tag k='natural' v='water' />
     40  </way>
     41</osm>
  • test/unit/org/openstreetmap/josm/actions/AlignInCircleActionTest.java

     
    9292    }
    9393
    9494    /**
    95      * Test case: way with several nodes selected
    96      * @throws Exception if an error occurs
    97      */
    98     @Test
    99     void testNodesSelected() throws Exception {
    100         DataSet ds = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleBefore.osm")), null);
    101         DataSet ds2 = OsmReader.parseDataSet(Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "alignCircleAfter2.osm")), null);
    102 
    103         Way circularWay = null;
    104         for (Way w : ds.getWays()) {
    105             if ("roundabout".equals(w.get("junction"))) {
    106                 circularWay = w;
    107                 break;
    108             }
    109         }
    110         assertNotNull(circularWay);
    111         if (circularWay != null) {
    112             ds.setSelected(circularWay.getNodes());
    113             Command c = AlignInCircleAction.buildCommand(ds);
    114             assertNotNull(c);
    115             c.executeCommand();
    116             Way expected = (Way) ds2.getPrimitiveById(circularWay);
    117             assertNotNull(expected);
    118             assertEquals(expected, circularWay);
    119             assertEquals(expected.getNodesCount(), circularWay.getNodesCount());
    120             for (Node n1 : circularWay.getNodes()) {
    121                 Node n2 = (Node) ds2.getPrimitiveById(n1);
    122                 assertEquals(n1.lat(), n2.lat(), 1e-5);
    123                 assertEquals(n1.lon(), n2.lon(), 1e-5);
    124             }
    125         }
    126     }
    127 
    128     /**
    12995     * Test case: original roundabout was split, two ways selected, they build a closed ring
    13096     * @throws Exception if an error occurs
    13197     */
     
    149115        }
    150116    }
    151117
     118    /**
     119     * Test case: original roundabout was split, two ways selected, they build a closed ring
     120     * @throws Exception if an error occurs
     121     */
     122    @Test
     123    void testPShapedWay() throws Exception {
     124        DataSet ds = new DataSet();
     125
     126    }
    152127}