Changeset 3636 in josm for trunk/src


Ignore:
Timestamp:
2010-10-24T10:25:12+02:00 (14 years ago)
Author:
bastiK
Message:

see #5577 (patch by extropy) - Move some methods and make them public static

Location:
trunk/src/org/openstreetmap/josm
Files:
2 added
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/JoinAreasAction.java

    r3595 r3636  
    22package org.openstreetmap.josm.actions;
    33
    4 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.combineTigerTags;
    5 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.completeTagCollectionForEditing;
    6 import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing;
    74import static org.openstreetmap.josm.tools.I18n.marktr;
    85import static org.openstreetmap.josm.tools.I18n.tr;
     
    129import java.awt.event.KeyEvent;
    1310import java.awt.geom.Area;
    14 import java.awt.geom.Line2D;
    1511import java.util.ArrayList;
    1612import java.util.Collection;
    1713import java.util.Collections;
    18 import java.util.Comparator;
    1914import java.util.HashMap;
    2015import java.util.HashSet;
     
    3934import org.openstreetmap.josm.data.UndoRedoHandler;
    4035import org.openstreetmap.josm.data.coor.EastNorth;
    41 import org.openstreetmap.josm.data.coor.LatLon;
    4236import org.openstreetmap.josm.data.osm.DataSet;
    4337import org.openstreetmap.josm.data.osm.Node;
     38import org.openstreetmap.josm.data.osm.NodePositionComparator;
    4439import org.openstreetmap.josm.data.osm.OsmPrimitive;
    4540import org.openstreetmap.josm.data.osm.Relation;
     
    4843import org.openstreetmap.josm.data.osm.Way;
    4944import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog;
     45import org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil;
     46import org.openstreetmap.josm.tools.Geometry;
    5047import org.openstreetmap.josm.tools.Pair;
    5148import org.openstreetmap.josm.tools.Shortcut;
    5249
     50/**
     51 * Join Areas (i.e. closed ways and multipolygons)
     52 */
    5353public class JoinAreasAction extends JosmAction {
    5454    // This will be used to commit commands and unite them into one large command sequence at the end
     
    110110    //saves a way and the "inside" side
    111111    // insideToTheLeft: if true left side is "in", false -right side is "in". Left and right are determined along the orientation of way.
    112     private static class WayInPolygon {
     112    public static class WayInPolygon {
    113113        public final Way way;
    114114        public boolean insideToTheRight;
     
    137137     *
    138138     */
    139     private static class AssembledPolygon {
     139    public static class AssembledPolygon {
    140140        public List<WayInPolygon> ways;
    141141
     
    164164    }
    165165
    166 
    167166    public static class AssembledMultipolygon {
    168167        public AssembledPolygon outerWay;
     
    209208
    210209        public WayInPolygon startNewWay() {
    211             if (availableWays.size() == 0) {
     210            if (availableWays.isEmpty()) {
    212211                lastWay = null;
    213212            } else {
     
    247246                        //this is the path we came from - ignore it.
    248247                    }
    249                     else if (bestWay == null || (isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode) == rightmost)) {
     248                    else if (bestWay == null || (Geometry.isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode) == rightmost)) {
    250249                        //the new way is better
    251250                        bestWay = way;
     
    262261                        //this is the path we came from - ignore it.
    263262                    }
    264                     else if (bestWay == null || (isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode) == rightmost)) {
     263                    else if (bestWay == null || (Geometry.isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode) == rightmost)) {
    265264                        //the new way is better
    266265                        bestWay = way;
     
    291290
    292291    /**
    293      * Provides some node order , based on coordinates, nodes with equal coordinates are equal.
    294      * @author viesturs
    295      *
    296      */
    297     private class NodePositionComparator implements Comparator<Node> {
    298 
    299         @Override
    300         public int compare(Node n1, Node n2) {
    301 
    302             double dLat = n1.getCoor().lat() - n2.getCoor().lat();
    303             double dLon = n1.getCoor().lon() - n2.getCoor().lon();
    304 
    305             if (dLat > 0)
    306                 return 1;
    307             else if (dLat < 0)
    308                 return -1;
    309             else if (dLon == 0) //dlat is 0 here
    310                 return 0;
    311             else
    312                 return dLon > 0 ? 1 : -1;
    313         }
    314     }
    315 
    316 
    317     /**
    318292     * Helper storage class for finding findOuterWays
    319293     * @author viesturs
     
    328302        }
    329303    }
    330 
    331304
    332305    // Adds the menu entry, Shortcuts, etc.
     
    415388    }
    416389
    417 
    418390    /**
    419391     * Tests if the areas have some intersections to join.
     
    430402
    431403        //find intersection points
    432         ArrayList<Node> nodes = addIntersections(allStartingWays, true);
     404        ArrayList<Node> nodes = Geometry.addIntersections(allStartingWays, true, cmds);
    433405        return nodes.size() > 0;
    434406    }
     
    466438
    467439        //find intersection points
    468         ArrayList<Node> nodes = addIntersections(allStartingWays, false);
     440        ArrayList<Node> nodes = Geometry.addIntersections(allStartingWays, false, cmds);
    469441
    470442        //no intersections, return.
    471         if (nodes.size() == 0) return result;
     443        if (nodes.isEmpty())
     444            return result;
    472445        commitCommands(marktr("Added node on all intersections"));
    473446
     
    529502        }
    530503
    531 
    532504        makeCommitsOneAction(marktr("Joined overlapping areas"));
    533505
     
    563535        TagCollection wayTags = TagCollection.unionOfAllPrimitives(ways);
    564536        TagCollection completeWayTags = new TagCollection(wayTags);
    565         combineTigerTags(completeWayTags);
    566         normalizeTagCollectionBeforeEditing(completeWayTags, ways);
     537        TagConflictResolutionUtil.combineTigerTags(completeWayTags);
     538        TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(completeWayTags, ways);
    567539        TagCollection tagsToEdit = new TagCollection(completeWayTags);
    568         completeTagCollectionForEditing(tagsToEdit);
     540        TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit);
    569541
    570542        CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();
     
    596568    }
    597569
    598 
    599570    /**
    600571     * This method removes duplicate points (if any) from the input way.
     
    659630        return totalNodesRemoved > 0;
    660631    }
    661 
    662 
    663 
    664     /**
    665      * Will find all intersection and add nodes there for list of given ways. Handles self-intersections too.
    666      * And make commands to add the intersection points to ways.
    667      * @param List<Way> - a list of ways to test
    668      * @return ArrayList<Node> List of new nodes
    669      * Prerequisite: no two nodes have the same coordinates.
    670      */
    671     private ArrayList<Node> addIntersections(List<Way> ways, boolean test) {
    672         //TODO: this is a bit slow - O( (number of nodes)^2 + numberOfIntersections * numberOfNodes )
    673 
    674         //stupid java, cannot instantiate array of generic classes..
    675         @SuppressWarnings("unchecked")
    676         ArrayList<Node>[] newNodes = new ArrayList[ways.size()];
    677         boolean[] changedWays = new boolean[ways.size()];
    678 
    679         Set<Node> intersectionNodes = new LinkedHashSet<Node>();
    680 
    681         for (int pos = 0; pos < ways.size(); pos ++) {
    682             newNodes[pos] = new ArrayList<Node>(ways.get(pos).getNodes());
    683             changedWays[pos] = false;
    684         }
    685 
    686         //iterate over all segment pairs and introduce the intersections
    687 
    688         Comparator<Node> coordsComparator = new NodePositionComparator();
    689 
    690         int seg1Way = 0;
    691         int seg1Pos = -1;
    692 
    693         while (true) {
    694             //advance to next segment
    695             seg1Pos++;
    696             if (seg1Pos > newNodes[seg1Way].size() - 2) {
    697                 seg1Way++;
    698                 seg1Pos = 0;
    699 
    700                 if (seg1Way == ways.size()) { //finished
    701                     break;
    702                 }
    703             }
    704 
    705 
    706             //iterate over secondary segment
    707 
    708             int seg2Way = seg1Way;
    709             int seg2Pos = seg1Pos + 1;//skip the adjacent segment
    710 
    711             while (true) {
    712 
    713                 //advance to next segment
    714                 seg2Pos++;
    715                 if (seg2Pos > newNodes[seg2Way].size() - 2) {
    716                     seg2Way++;
    717                     seg2Pos = 0;
    718 
    719                     if (seg2Way == ways.size()) { //finished
    720                         break;
    721                     }
    722                 }
    723 
    724                 //need to get them again every time, because other segments may be changed
    725                 Node seg1Node1 = newNodes[seg1Way].get(seg1Pos);
    726                 Node seg1Node2 = newNodes[seg1Way].get(seg1Pos + 1);
    727                 Node seg2Node1 = newNodes[seg2Way].get(seg2Pos);
    728                 Node seg2Node2 = newNodes[seg2Way].get(seg2Pos + 1);
    729 
    730                 int commonCount = 0;
    731                 //test if we have common nodes to add.
    732                 if (seg1Node1 == seg2Node1 || seg1Node1 == seg2Node2) {
    733                     commonCount ++;
    734 
    735                     if (seg1Way == seg2Way &&
    736                             seg1Pos == 0 &&
    737                             seg2Pos == newNodes[seg2Way].size() -2) {
    738                         //do not add - this is first and last segment of the same way.
    739                     } else {
    740                         intersectionNodes.add(seg1Node1);
    741                     }
    742                 }
    743 
    744                 if (seg1Node2 == seg2Node1 || seg1Node2 == seg2Node2) {
    745                     commonCount ++;
    746 
    747                     intersectionNodes.add(seg1Node2);
    748                 }
    749 
    750                 //no common nodes - find intersection
    751                 if (commonCount == 0) {
    752                     LatLon intersection = getLineLineIntersection(
    753                             seg1Node1.getEastNorth().east(), seg1Node1.getEastNorth().north(),
    754                             seg1Node2.getEastNorth().east(), seg1Node2.getEastNorth().north(),
    755                             seg2Node1.getEastNorth().east(), seg2Node1.getEastNorth().north(),
    756                             seg2Node2.getEastNorth().east(), seg2Node2.getEastNorth().north());
    757 
    758                     if (intersection != null) {
    759                         if (test) {
    760                             intersectionNodes.add(seg2Node1);
    761                             return new ArrayList<Node>(intersectionNodes);
    762                         }
    763 
    764                         Node newNode = new Node(intersection);
    765                         Node intNode = newNode;
    766                         boolean insertInSeg1 = false;
    767                         boolean insertInSeg2 = false;
    768 
    769                         //find if the intersection point is at end point of one of the segments, if so use that point
    770 
    771                         //segment 1
    772                         if (coordsComparator.compare(newNode, seg1Node1) == 0) {
    773                             intNode = seg1Node1;
    774                         } else if (coordsComparator.compare(newNode, seg1Node2) == 0) {
    775                             intNode = seg1Node2;
    776                         } else {
    777                             insertInSeg1 = true;
    778                         }
    779 
    780                         //segment 2
    781                         if (coordsComparator.compare(newNode, seg2Node1) == 0) {
    782                             intNode = seg2Node1;
    783                         } else if (coordsComparator.compare(newNode, seg2Node2) == 0) {
    784                             intNode = seg2Node2;
    785                         } else {
    786                             insertInSeg2 = true;
    787                         }
    788 
    789                         if (insertInSeg1) {
    790                             newNodes[seg1Way].add(seg1Pos +1, intNode);
    791                             changedWays[seg1Way] = true;
    792 
    793                             //fix seg2 position, as indexes have changed, seg2Pos is always bigger than seg1Pos on the same segment.
    794                             if (seg2Way == seg1Way) {
    795                                 seg2Pos ++;
    796                             }
    797                         }
    798 
    799                         if (insertInSeg2) {
    800                             newNodes[seg2Way].add(seg2Pos +1, intNode);
    801                             changedWays[seg2Way] = true;
    802 
    803                             //Do not need to compare again to already split segment
    804                             seg2Pos ++;
    805                         }
    806 
    807                         intersectionNodes.add(intNode);
    808 
    809                         if (intNode == newNode) {
    810                             cmds.add(new AddCommand(intNode));
    811                         }
    812                     }
    813                 }
    814                 else if (test && intersectionNodes.size() > 0)
    815                     return new ArrayList<Node>(intersectionNodes);
    816             }
    817         }
    818 
    819         for (int pos = 0; pos < ways.size(); pos ++) {
    820             if (changedWays[pos] == false) {
    821                 continue;
    822             }
    823 
    824             Way way = ways.get(pos);
    825             Way newWay = new Way(way);
    826             newWay.setNodes(newNodes[pos]);
    827 
    828             cmds.add(new ChangeCommand(way, newWay));
    829         }
    830 
    831         return new ArrayList<Node>(intersectionNodes);
    832     }
    833 
    834     /**
    835      * Finds the intersection of two lines
    836      * @return LatLon null if no intersection was found, the LatLon coordinates of the intersection otherwise
    837      */
    838     static private LatLon getLineLineIntersection(
    839             double x1, double y1, double x2, double y2,
    840             double x3, double y3, double x4, double y4) {
    841 
    842         if (!Line2D.linesIntersect(x1, y1, x2, y2, x3, y3, x4, y4)) return null;
    843 
    844         // Convert line from (point, point) form to ax+by=c
    845         double a1 = y2 - y1;
    846         double b1 = x1 - x2;
    847         double c1 = x2*y1 - x1*y2;
    848 
    849         double a2 = y4 - y3;
    850         double b2 = x3 - x4;
    851         double c2 = x4*y3 - x3*y4;
    852 
    853         // Solve the equations
    854         double det = a1*b2 - a2*b1;
    855         if (det == 0) return null; // Lines are parallel
    856 
    857         return Main.proj.eastNorth2latlon(new EastNorth(
    858                 (b1*c2 - b2*c1)/det,
    859                 (a2*c1 -a1*c2)/det
    860         ));
    861     }
    862 
    863 
    864632
    865633    /**
     
    884652    }
    885653
    886 
    887654    /**
    888655     * This method analyzes the way and assigns each part what direction polygon "inside" is.
     
    950717                    Node nextNode = way.getNode(1);
    951718
    952                     if (topWay == null || !isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode)) {
     719                    if (topWay == null || !Geometry.isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode)) {
    953720                        //the new way is better
    954721                        topWay = way;
     
    962729                    Node nextNode = way.getNode(way.getNodesCount() - 2);
    963730
    964                     if (topWay == null || !isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode)) {
     731                    if (topWay == null || !Geometry.isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode)) {
    965732                        //the new way is better
    966733                        topWay = way;
     
    976743
    977744            //there will be no parallel segments in the middle of way, so all fine.
    978             wayClockwise = angleIsClockwise(prev, topNode, next);
     745            wayClockwise = Geometry.angleIsClockwise(prev, topNode, next);
    979746        }
    980747
     
    999766                break;
    1000767            }
    1001 
    1002768
    1003769            //find intersecting segments
     
    1037803                    Node wayBNode = wayB.getNode(1);
    1038804
    1039                     boolean wayAToTheRight = isToTheRightSideOfLine(prevNode, headNode, nextNode, wayANode);
    1040                     boolean wayBToTheRight = isToTheRightSideOfLine(prevNode, headNode, nextNode, wayBNode);
     805                    boolean wayAToTheRight = Geometry.isToTheRightSideOfLine(prevNode, headNode, nextNode, wayANode);
     806                    boolean wayBToTheRight = Geometry.isToTheRightSideOfLine(prevNode, headNode, nextNode, wayBNode);
    1041807
    1042808                    if (wayAToTheRight != wayBToTheRight) {
     
    1084850    }
    1085851
    1086 
    1087     /**
    1088      * Simple chunking version. Does not care about circular ways and result being proper, we will glue it all back together later on.
     852    /**
     853     * Simple chunking version. Does not care about circular ways and result being
     854     * proper, we will glue it all back together later on.
    1089855     * @param way the way to chunk
    1090856     * @param splitNodes the places where to cut.
     
    1185951        return result;
    1186952    }
    1187 
    1188 
    1189953
    1190954    /**
     
    12851049    }
    12861050
    1287 
    12881051    /**
    12891052     * This method checks if polygons have several touching parts and splits them in several polygons.
     
    13231086        return newPolygons;
    13241087    }
    1325 
    1326 
    1327     /**
    1328      * Tests if given point is to the right side of path consisting of 3 points.
    1329      * @param lineP1 first point in path
    1330      * @param lineP2 second point in path
    1331      * @param lineP3 third point in path
    1332      * @param testPoint
    1333      * @return true if to the right side, false otherwise
    1334      */
    1335     public static boolean isToTheRightSideOfLine(Node lineP1, Node lineP2, Node lineP3, Node testPoint) {
    1336         boolean pathBendToRight = angleIsClockwise(lineP1, lineP2, lineP3);
    1337         boolean rightOfSeg1 = angleIsClockwise(lineP1, lineP2, testPoint);
    1338         boolean rightOfSeg2 = angleIsClockwise(lineP2, lineP3, testPoint);
    1339 
    1340         if (pathBendToRight)
    1341             return rightOfSeg1 && rightOfSeg2;
    1342         else
    1343             return !(!rightOfSeg1 && !rightOfSeg2);
    1344     }
    1345 
    1346     /**
    1347      * This method tests if secondNode is clockwise to first node.
    1348      * @param commonNode starting point for both vectors
    1349      * @param firstNode first vector end node
    1350      * @param secondNode second vector end node
    1351      * @return true if first vector is clockwise before second vector.
    1352      */
    1353 
    1354     public static boolean angleIsClockwise(Node commonNode, Node firstNode, Node secondNode) {
    1355         double dy1 = (firstNode.getEastNorth().getY() - commonNode.getEastNorth().getY());
    1356         double dy2 = (secondNode.getEastNorth().getY() - commonNode.getEastNorth().getY());
    1357         double dx1 = (firstNode.getEastNorth().getX() - commonNode.getEastNorth().getX());
    1358         double dx2 = (secondNode.getEastNorth().getX() - commonNode.getEastNorth().getX());
    1359 
    1360         return dy1 * dx2 - dx1 * dy2 > 0;
    1361     }
    1362 
    13631088
    13641089    /**
     
    13751100            if (!outsideNodes.contains(insideNode))
    13761101                //simply test the one node
    1377                 return nodeInsidePolygon(insideNode, outside.getNodes());
     1102                return Geometry.nodeInsidePolygon(insideNode, outside.getNodes());
    13781103        }
    13791104
     
    13811106        return false;
    13821107    }
    1383 
    1384     /**
    1385      * Tests if point is inside a polygon. The polygon can be self-intersecting. In such case the contains function works in xor-like manner.
    1386      * @param polygonNodes list of nodes from polygon path.
    1387      * @param point the point to test
    1388      * @return true if the point is inside polygon.
    1389      * FIXME: this should probably be moved to tools..
    1390      */
    1391     public static boolean nodeInsidePolygon(Node point, List<Node> polygonNodes) {
    1392         if (polygonNodes.size() < 3)
    1393             return false;
    1394 
    1395         boolean inside = false;
    1396         Node p1, p2;
    1397 
    1398         //iterate each side of the polygon, start with the last segment
    1399         Node oldPoint = polygonNodes.get(polygonNodes.size() - 1);
    1400 
    1401         for (Node newPoint : polygonNodes) {
    1402             //skip duplicate points
    1403             if (newPoint.equals(oldPoint)) {
    1404                 continue;
    1405             }
    1406 
    1407             //order points so p1.lat <= p2.lat;
    1408             if (newPoint.getEastNorth().getY() > oldPoint.getEastNorth().getY()) {
    1409                 p1 = oldPoint;
    1410                 p2 = newPoint;
    1411             } else {
    1412                 p1 = newPoint;
    1413                 p2 = oldPoint;
    1414             }
    1415 
    1416             //test if the line is crossed and if so invert the inside flag.
    1417             if ((newPoint.getEastNorth().getY() < point.getEastNorth().getY()) == (point.getEastNorth().getY() <= oldPoint.getEastNorth().getY())
    1418                     && (point.getEastNorth().getX() - p1.getEastNorth().getX()) * (p2.getEastNorth().getY() - p1.getEastNorth().getY())
    1419                     < (p2.getEastNorth().getX() - p1.getEastNorth().getX()) * (point.getEastNorth().getY() - p1.getEastNorth().getY()))
    1420             {
    1421                 inside = !inside;
    1422             }
    1423 
    1424             oldPoint = newPoint;
    1425         }
    1426 
    1427         return inside;
    1428     }
    1429 
    14301108
    14311109    /**
     
    14431121        return result;
    14441122    }
    1445 
    14461123
    14471124    /**
     
    14731150    }
    14741151
    1475 
    14761152    /**
    14771153     * Joins a list of ways (using CombineWayAction and ReverseWayAction as specified in WayInPath)
     
    15061182        return result.a;
    15071183    }
    1508 
    15091184
    15101185    /**
     
    16041279    }
    16051280
    1606 
    16071281    /**
    16081282     * This method filters the list of relations that form the multipolygons.
     
    16271301        return result;
    16281302    }
    1629 
    16301303
    16311304    /**
     
    16501323    }
    16511324
    1652 
    16531325    /**
    16541326     * Removes a given OsmPrimitive from all relations
     
    16851357        return result;
    16861358    }
    1687 
    16881359
    16891360    /**
Note: See TracChangeset for help on using the changeset viewer.