/** * Tracer - plugin for JOSM * Jan Bilak, Petr DlouhĂ˝ * This program is free software and licensed under GPL. */ package org.openstreetmap.josm.plugins.tracer; import static org.openstreetmap.josm.tools.I18n.tr; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.command.ChangeCommand; import org.openstreetmap.josm.command.Command; import org.openstreetmap.josm.command.DeleteCommand; import org.openstreetmap.josm.command.MoveCommand; import org.openstreetmap.josm.command.SequenceCommand; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.data.osm.BBox; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.tools.Pair; public class ConnectWays { final static double MIN_DISTANCE = 0.000005; //Minimal distance, when nodes are merged final static double MIN_DISTANCE_TW = 0.000005; //Minimal distance, when node is connected to other way final static double MIN_DISTANCE_SQ = 0.000005; //Minimal distance, when other node is connected this way final static double MAX_ANGLE = 30; //Minimal angle, when other node is connected this way /** * Try connect way to other buidings. * @param way Way to connect. * @return Commands. */ public static Command connect(Way way) { Map<Way, Way> modifiedWays = new HashMap<>(); LinkedList<Command> cmds = new LinkedList<>(); Way newWay = new Way(way); for (int i = 0; i < way.getNodesCount() - 1; i++) { Node n = way.getNode(i); System.out.println("-------"); System.out.println("Node: " + n); LatLon ll = n.getCoor(); BBox bbox = new BBox( ll.getX() - MIN_DISTANCE, ll.getY() - MIN_DISTANCE, ll.getX() + MIN_DISTANCE, ll.getY() + MIN_DISTANCE); // bude se node slucovat s jinym? double minDistanceSq = MIN_DISTANCE; List<Node> nodes = Main.getLayerManager().getEditDataSet().searchNodes(bbox); Node nearestNode = null; for (Node nn : nodes) { if (!nn.isUsable() || way.containsNode(nn) || newWay.containsNode(nn) || !isInBuilding(nn)) { continue; } double dist = nn.getCoor().distance(ll); if (dist < minDistanceSq) { minDistanceSq = dist; nearestNode = nn; } } System.out.println("Nearest: " + nearestNode); if (nearestNode == null) { tryConnectNodeToAnyWay(n, modifiedWays); } else { cmds.addAll(mergeNodes(n, nearestNode, newWay)); } } for(Map.Entry<Way, Way> e : modifiedWays.entrySet()){ cmds.add(new ChangeCommand(e.getKey(), e.getValue())); } cmds.addFirst(new ChangeCommand(way, trySplitWayByAnyNodes(newWay))); Command cmd = new SequenceCommand(tr("Merge objects nodes"), cmds); return cmd; } /** * Merges two nodes * @param n1 First node * @param n2 Second node * @param way Way containing first node * @return List of Commands. */ private static List<Command> mergeNodes(Node n1, Node n2, Way way){ List<Command> cmds = new LinkedList<>(); cmds.add(new MoveCommand(n2, (n1.getEastNorth().getX() - n2.getEastNorth().getX())/2, (n1.getEastNorth().getY() - n2.getEastNorth().getY())/2 )); int j = way.getNodes().indexOf(n1); way.addNode(j, n2); if (j == 0) { // first + last point way.addNode(way.getNodesCount(), n2); } way.removeNode(n1); cmds.add(new DeleteCommand(n1)); return cmds; } /** * Try connect node "node" to way of other building. * * Zkusi zjistit, zda node neni tak blizko nejake usecky existujici budovy, * ze by mel byt zacnenen do teto usecky. Pokud ano, provede to. * * @param node Node to connect. * @throws IllegalStateException * @throws IndexOutOfBoundsException * @return List of Commands. */ private static void tryConnectNodeToAnyWay(Node node, Map<Way, Way> m) throws IllegalStateException, IndexOutOfBoundsException { LatLon ll = node.getCoor(); BBox bbox = new BBox( ll.getX() - MIN_DISTANCE_TW, ll.getY() - MIN_DISTANCE_TW, ll.getX() + MIN_DISTANCE_TW, ll.getY() + MIN_DISTANCE_TW); // node nebyl slouceny s jinym // hledani pripadne blizke usecky, kam bod pridat List<Way> ways = Main.getLayerManager().getEditDataSet().searchWays(bbox); double minDist = Double.MAX_VALUE; Way nearestWay = null; int nearestNodeIndex = 0; for (Way ww : ways) { if (!ww.isUsable() || ww.containsNode(node) || !isBuilding(ww)) { continue; } if(m.get(ww) != null){ ww = m.get(ww); } for (Pair<Node, Node> np : ww.getNodePairs(false)) { double dist = TracerGeometry.distanceFromSegment(ll, np.a.getCoor(), np.b.getCoor()); if (dist < minDist) { minDist = dist; nearestWay = ww; nearestNodeIndex = ww.getNodes().indexOf(np.a); } } } System.out.println("Nearest way: " + nearestWay + " distance: " + minDist); if (minDist < MIN_DISTANCE_TW) { Way newNWay = new Way(nearestWay); newNWay.addNode(nearestNodeIndex + 1, node); System.out.println("New way:" + newNWay); m.put(nearestWay, newNWay); } } /** * Try split way by any existing buiding nodes. * * Zkusi zjistit zda nejake usecka z way by nemela prochazet nejakym existujicim bodem, * ktery je ji velmi blizko. Pokud ano, tak puvodni usecku rozdeli na dve tak, aby * prochazela takovym bodem. * * @param way Way to split. * @throws IndexOutOfBoundsException * @throws IllegalStateException * @return Modified way */ private static Way trySplitWayByAnyNodes(Way way) throws IndexOutOfBoundsException, IllegalStateException { // projdi kazdou novou usecku a zjisti, zda by nemela vest pres existujici body int i = 0; while (i < way.getNodesCount()) { // usecka n1, n2 LatLon n1 = way.getNodes().get(i).getCoor(); LatLon n2 = way.getNodes().get((i + 1) % way.getNodesCount()).getCoor(); System.out.println(way.getNodes().get(i) + "-----" + way.getNodes().get((i + 1) % way.getNodesCount())); double minDistanceSq = MIN_DISTANCE_SQ; //double maxAngle = MAX_ANGLE; List<Node> nodes = Main.getLayerManager().getEditDataSet().searchNodes(new BBox( Math.min(n1.getX(), n2.getX()) - minDistanceSq, Math.min(n1.getY(), n2.getY()) - minDistanceSq, Math.max(n1.getX(), n2.getX()) + minDistanceSq, Math.max(n1.getY(), n2.getY()) + minDistanceSq )); Node nearestNode = null; for (Node nod : nodes) { if (!nod.isUsable() || way.containsNode(nod) || !isInBuilding(nod)) { continue; } LatLon nn = nod.getCoor(); double dist = TracerGeometry.distanceFromSegment(nn, n1, n2); double angle = TracerGeometry.angleOfLines(n1, nn, nn, n2); System.out.println("Angle: " + angle + " distance: " + dist + " Node: " + nod); if (!n1.equalsEpsilon(nn) && !n2.equalsEpsilon(nn) && dist < minDistanceSq){ // && Math.abs(angle) < maxAngle) { //maxAngle = angle; nearestNode = nod; } } System.out.println("Nearest_: " + nearestNode); System.out.println(""); if (nearestNode == null) { // tato usecka se nerozdeli i++; continue; } else { // rozdeleni usecky way.addNode(i + 1, nearestNode); continue; // i nezvetsuji, treba bude treba rozdelit usecku znovu } } return way; } private static boolean isInBuilding(Node n) { for (OsmPrimitive op : n.getReferrers()) { if (op instanceof Way) { if (isBuilding((Way) op)) { return true; } } } return false; } private static boolean isBuilding(Way w) { return (w.getKeys().get("building") == null ? false : w.getKeys().get("building").equals("yes")); } }