Esempio n. 1
0
  /**
   * This method removes duplicate points (if any) from the input way.
   *
   * @param ways the ways to process
   * @return {@code true} if any changes where made
   */
  private boolean removeDuplicateNodes(List<Way> ways) {
    // TODO: maybe join nodes with JoinNodesAction, rather than reconnect the ways.

    Map<Node, Node> nodeMap = new TreeMap<Node, Node>(new NodePositionComparator());
    int totalNodesRemoved = 0;

    for (Way way : ways) {
      if (way.getNodes().size() < 2) {
        continue;
      }

      int nodesRemoved = 0;
      List<Node> newNodes = new ArrayList<Node>();
      Node prevNode = null;

      for (Node node : way.getNodes()) {
        if (!nodeMap.containsKey(node)) {
          // new node
          nodeMap.put(node, node);

          // avoid duplicate nodes
          if (prevNode != node) {
            newNodes.add(node);
          } else {
            nodesRemoved++;
          }
        } else {
          // node with same coordinates already exists, substitute with existing node
          Node representator = nodeMap.get(node);

          if (representator != node) {
            nodesRemoved++;
          }

          // avoid duplicate node
          if (prevNode != representator) {
            newNodes.add(representator);
          }
        }
        prevNode = node;
      }

      if (nodesRemoved > 0) {

        if (newNodes.size()
            == 1) { // all nodes in the same coordinate - add one more node, to have closed way.
          newNodes.add(newNodes.get(0));
        }

        Way newWay = new Way(way);
        newWay.setNodes(newNodes);
        cmds.add(new ChangeCommand(way, newWay));
        totalNodesRemoved += nodesRemoved;
      }
    }

    return totalNodesRemoved > 0;
  }
Esempio n. 2
0
  /**
   * Return a list of all objects in the selection, respecting the different modifier.
   *
   * @param alt Whether the alt key was pressed, which means select all objects that are touched,
   *     instead those which are completely covered.
   * @return The collection of selected objects.
   */
  public Collection<OsmPrimitive> getSelectedObjects(boolean alt) {

    Collection<OsmPrimitive> selection = new LinkedList<>();

    // whether user only clicked, not dragged.
    boolean clicked = false;
    Rectangle bounding = lasso.getBounds();
    if (bounding.height <= 2 && bounding.width <= 2) {
      clicked = true;
    }

    if (clicked) {
      Point center = new Point(lasso.xpoints[0], lasso.ypoints[0]);
      OsmPrimitive osm = nc.getNearestNodeOrWay(center, OsmPrimitive.isSelectablePredicate, false);
      if (osm != null) {
        selection.add(osm);
      }
    } else {
      // nodes
      for (Node n : nc.getCurrentDataSet().getNodes()) {
        if (n.isSelectable() && lasso.contains(nc.getPoint2D(n))) {
          selection.add(n);
        }
      }

      // ways
      for (Way w : nc.getCurrentDataSet().getWays()) {
        if (!w.isSelectable() || w.getNodesCount() == 0) {
          continue;
        }
        if (alt) {
          for (Node n : w.getNodes()) {
            if (!n.isIncomplete() && lasso.contains(nc.getPoint2D(n))) {
              selection.add(w);
              break;
            }
          }
        } else {
          boolean allIn = true;
          for (Node n : w.getNodes()) {
            if (!n.isIncomplete() && !lasso.contains(nc.getPoint(n))) {
              allIn = false;
              break;
            }
          }
          if (allIn) {
            selection.add(w);
          }
        }
      }
    }
    return selection;
  }
Esempio n. 3
0
 @Override
 public void visit(Way w) {
   if (e.child == null && left.matches(new Environment(w))) {
     if (e.osm instanceof Way
             && Geometry.PolygonIntersection.FIRST_INSIDE_SECOND.equals(
                 Geometry.polygonIntersection(w.getNodes(), ((Way) e.osm).getNodes()))
         || e.osm instanceof Relation
             && ((Relation) e.osm).isMultipolygon()
             && Geometry.isPolygonInsideMultiPolygon(w.getNodes(), (Relation) e.osm, null)) {
       e.child = w;
     }
   }
 }
Esempio n. 4
0
    /**
     * Extract and store relation information based on the relation member
     *
     * @param src The relation member to store information about
     */
    public RelMember(RelationMember src) {
      role = src.getRole();
      type = src.getType();
      rel_id = 0;
      coor = new ArrayList<LatLon>();

      if (src.isNode()) {
        Node r = src.getNode();
        tags = r.getKeys();
        coor = new ArrayList<LatLon>(1);
        coor.add(r.getCoor());
      }
      if (src.isWay()) {
        Way r = src.getWay();
        tags = r.getKeys();
        List<Node> wNodes = r.getNodes();
        coor = new ArrayList<LatLon>(wNodes.size());
        for (Node wNode : wNodes) {
          coor.add(wNode.getCoor());
        }
      }
      if (src.isRelation()) {
        Relation r = src.getRelation();
        tags = r.getKeys();
        rel_id = r.getId();
        coor = new ArrayList<LatLon>();
      }
    }
  @Test
  public void testBackrefrenceForWay_Full() throws OsmTransferException {
    Way w = lookupWay(ds, 1);
    assertNotNull(w);
    // way with name "way-1" is referred to by two relations
    //

    OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(w);
    reader.setReadFull(true);
    DataSet referers = reader.parseOsm(NullProgressMonitor.INSTANCE);
    assertEquals(6, referers.getWays().size()); // 6 ways referred by two relations
    for (Way w1 : referers.getWays()) {
      assertEquals(false, w1.isIncomplete());
    }
    assertEquals(2, referers.getRelations().size()); // two relations referring to
    Set<Long> expectedNodeIds = new HashSet<Long>();
    for (Way way : referers.getWays()) {
      Way orig = (Way) ds.getPrimitiveById(way);
      for (Node n : orig.getNodes()) {
        expectedNodeIds.add(n.getId());
      }
    }
    assertEquals(expectedNodeIds.size(), referers.getNodes().size());
    for (Node n : referers.getNodes()) {
      assertEquals(true, expectedNodeIds.contains(n.getId()));
    }

    Relation r = lookupRelation(referers, 0);
    assertNotNull(r);
    assertEquals(false, r.isIncomplete());
    r = lookupRelation(referers, 1);
    assertEquals(false, r.isIncomplete());
  }
 protected Set<Long> getNodeIdsInWay(Way way) {
   HashSet<Long> ret = new HashSet<Long>();
   if (way == null) return ret;
   for (Node n : way.getNodes()) {
     ret.add(n.getId());
   }
   return ret;
 }
 void addWayNodes(Way w) {
   add(tr("{0} Nodes: ", w.getNodesCount()));
   for (Node n : w.getNodes()) {
     s.append(INDENT).append(INDENT);
     addNameAndId(n);
     s.append(NL);
   }
 }
Esempio n. 8
0
  /**
   * Splits the nodes of {@code wayToSplit} into a list of node sequences which are separated at the
   * nodes in {@code splitPoints}.
   *
   * <p>This method displays warning messages if {@code wayToSplit} and/or {@code splitPoints}
   * aren't consistent.
   *
   * <p>Returns null, if building the split chunks fails.
   *
   * @param wayToSplit the way to split. Must not be null.
   * @param splitPoints the nodes where the way is split. Must not be null.
   * @return the list of chunks
   */
  public static List<List<Node>> buildSplitChunks(Way wayToSplit, List<Node> splitPoints) {
    CheckParameterUtil.ensureParameterNotNull(wayToSplit, "wayToSplit");
    CheckParameterUtil.ensureParameterNotNull(splitPoints, "splitPoints");

    Set<Node> nodeSet = new HashSet<>(splitPoints);
    List<List<Node>> wayChunks = new LinkedList<>();
    List<Node> currentWayChunk = new ArrayList<>();
    wayChunks.add(currentWayChunk);

    Iterator<Node> it = wayToSplit.getNodes().iterator();
    while (it.hasNext()) {
      Node currentNode = it.next();
      boolean atEndOfWay = currentWayChunk.isEmpty() || !it.hasNext();
      currentWayChunk.add(currentNode);
      if (nodeSet.contains(currentNode) && !atEndOfWay) {
        currentWayChunk = new ArrayList<>();
        currentWayChunk.add(currentNode);
        wayChunks.add(currentWayChunk);
      }
    }

    // Handle circular ways specially.
    // If you split at a circular way at two nodes, you just want to split
    // it at these points, not also at the former endpoint.
    // So if the last node is the same first node, join the last and the
    // first way chunk.
    List<Node> lastWayChunk = wayChunks.get(wayChunks.size() - 1);
    if (wayChunks.size() >= 2
        && wayChunks.get(0).get(0) == lastWayChunk.get(lastWayChunk.size() - 1)
        && !nodeSet.contains(wayChunks.get(0).get(0))) {
      if (wayChunks.size() == 2) {
        new Notification(tr("You must select two or more nodes to split a circular way."))
            .setIcon(JOptionPane.WARNING_MESSAGE)
            .show();
        return null;
      }
      lastWayChunk.remove(lastWayChunk.size() - 1);
      lastWayChunk.addAll(wayChunks.get(0));
      wayChunks.remove(wayChunks.size() - 1);
      wayChunks.set(0, lastWayChunk);
    }

    if (wayChunks.size() < 2) {
      if (wayChunks.get(0).get(0) == wayChunks.get(0).get(wayChunks.get(0).size() - 1)) {
        new Notification(tr("You must select two or more nodes to split a circular way."))
            .setIcon(JOptionPane.WARNING_MESSAGE)
            .show();
      } else {
        new Notification(
                tr(
                    "The way cannot be split at the selected nodes. (Hint: Select nodes in the middle of the way.)"))
            .setIcon(JOptionPane.WARNING_MESSAGE)
            .show();
      }
      return null;
    }
    return wayChunks;
  }
Esempio n. 9
0
 @Override
 public void visit(Way w) {
   if (e.child == null && left.matches(new Environment(w))) {
     if (e.osm instanceof Way
         && Geometry.PolygonIntersection.CROSSING.equals(
             Geometry.polygonIntersection(w.getNodes(), ((Way) e.osm).getNodes()))) {
       e.child = w;
     }
   }
 }
Esempio n. 10
0
 public void visit(Way w) {
   if (w.isNewOrUndeleted() || w.isModified() || w.isDeleted()) {
     // upload new ways as well as modified and deleted ones
     hull.add(w);
     for (Node n : w.getNodes()) {
       // we upload modified nodes even if they aren't in the current
       // selection.
       n.visit(this);
     }
   }
 }
Esempio n. 11
0
 @Override
 public void cloneFrom(OsmPrimitive osm) {
   boolean locked = writeLock();
   try {
     super.cloneFrom(osm);
     Way otherWay = (Way) osm;
     setNodes(otherWay.getNodes());
   } finally {
     writeUnlock(locked);
   }
 }
Esempio n. 12
0
 /**
  * Collect all nodes with more than one referrer.
  *
  * @param ways Ways from witch nodes are selected
  * @return List of nodes with more than one referrer
  */
 private static List<Node> collectNodesWithExternReferers(List<Way> ways) {
   List<Node> withReferrers = new ArrayList<>();
   for (Way w : ways) {
     for (Node n : w.getNodes()) {
       if (n.getReferrers().size() > 1) {
         withReferrers.add(n);
       }
     }
   }
   return withReferrers;
 }
Esempio n. 13
0
    Segment(Node start, Way way, Node end) {
      this.start = start;
      this.way = way;
      this.end = end;

      final List<Node> ns = way.getNodes();
      if (way.lastNode().equals(start)) {
        Collections.reverse(ns);
      }

      this.nodes = Collections.unmodifiableList(ns);
    }
Esempio n. 14
0
 /**
  * Assuming all ways can be joined into polygon, create an ordered list of node.
  *
  * @param ways List of ways to be joined
  * @return Nodes anticlockwise ordered
  */
 private static List<Node> collectNodesAnticlockwise(List<Way> ways) {
   List<Node> nodes = new ArrayList<>();
   Node firstNode = ways.get(0).firstNode();
   Node lastNode = null;
   Way lastWay = null;
   while (firstNode != lastNode) {
     if (lastNode == null) lastNode = firstNode;
     for (Way way : ways) {
       if (way == lastWay) continue;
       if (way.firstNode() == lastNode) {
         List<Node> wayNodes = way.getNodes();
         for (int i = 0; i < wayNodes.size() - 1; i++) {
           nodes.add(wayNodes.get(i));
         }
         lastNode = way.lastNode();
         lastWay = way;
         break;
       }
       if (way.lastNode() == lastNode) {
         List<Node> wayNodes = way.getNodes();
         for (int i = wayNodes.size() - 1; i > 0; i--) {
           nodes.add(wayNodes.get(i));
         }
         lastNode = way.firstNode();
         lastWay = way;
         break;
       }
     }
   }
   // Check if nodes are in anticlockwise order
   int nc = nodes.size();
   double area = 0;
   for (int i = 0; i < nc; i++) {
     EastNorth p1 = nodes.get(i).getEastNorth();
     EastNorth p2 = nodes.get((i + 1) % nc).getEastNorth();
     area += p1.east() * p2.north() - p2.east() * p1.north();
   }
   if (area < 0) Collections.reverse(nodes);
   return nodes;
 }
Esempio n. 15
0
 @Override
 public void visit(Way w) {
   if (!w.isUsable()) return;
   List<Node> wNodes = w.getNodes();
   List<LatLon> wLat = new ArrayList<LatLon>(wNodes.size());
   for (int i = 0; i < wNodes.size(); i++) {
     wLat.add(wNodes.get(i).getCoor());
   }
   Map<String, String> wkeys = w.getKeys();
   removeUninterestingKeys(wkeys);
   WayPair wKey = new WayPair(wLat, wkeys);
   ways.put(wKey, w);
   WayPairNoTags wKeyN = new WayPairNoTags(wLat);
   waysNoTags.put(wKeyN, w);
 }
Esempio n. 16
0
  /**
   * Returns area of a closed way in square meters. (approximate(?), but should be OK for small
   * areas)
   *
   * <p>Relies on the current projection: Works correctly, when one unit in projected coordinates
   * corresponds to one meter. This is true for most projections, but not for WGS84 and Mercator
   * (EPSG:3857).
   *
   * @param way Way to measure, should be closed (first node is the same as last node)
   * @return area of the closed way.
   */
  public static double closedWayArea(Way way) {

    // http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
    double area = 0;
    Node lastN = null;
    for (Node n : way.getNodes()) {
      if (lastN != null) {
        n.getEastNorth().getX();

        area += (calcX(n) * calcY(lastN)) - (calcY(n) * calcX(lastN));
      }
      lastN = n;
    }
    return Math.abs(area / 2);
  }
Esempio n. 17
0
  /**
   * My dataset includes a deleted node. Their dataset includes a way with three nodes, the first
   * one being my node.
   *
   * <p>=> the merged way should include all three nodes. Deleted node should have deleted=false and
   * special conflict with isDeleted should exist
   */
  @Test
  public void wayComplex_mergingADeletedNode() {

    // -- my dataset

    Node mn1 = new Node(new LatLon(0, 0));
    mn1.setOsmId(1, 1);
    mn1.setDeleted(true);
    my.addPrimitive(mn1);

    Node tn1 = new Node(new LatLon(0, 0));
    tn1.setOsmId(1, 1);
    their.addPrimitive(tn1);

    Node tn2 = new Node(new LatLon(1, 1));
    tn2.setOsmId(2, 1);
    their.addPrimitive(tn2);

    Node tn3 = new Node(new LatLon(2, 2));
    tn3.setOsmId(3, 1);
    their.addPrimitive(tn3);

    // -- their data set
    Way theirWay = new Way();
    theirWay.setOsmId(4, 1);
    theirWay.addNode(tn1);
    theirWay.addNode(tn2);
    theirWay.addNode(tn3);
    theirWay.setUser(User.createOsmUser(1111, "their"));
    theirWay.setTimestamp(new Date());
    their.addPrimitive(theirWay);

    DataSetMerger visitor = new DataSetMerger(my, their);
    visitor.merge();

    assertEquals(1, visitor.getConflicts().size());
    assertTrue(visitor.getConflicts().get(0).isMyDeleted());

    Way myWay = (Way) my.getPrimitiveById(4, OsmPrimitiveType.WAY);
    assertEquals(3, myWay.getNodesCount());

    Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
    assertTrue(myWay.getNodes().contains(n));

    assertFalse(myWay.isModified());
  }
Esempio n. 18
0
  public void visit(Way w) {
    if (w.isIncomplete() || w.getNodesCount() < 2) return;
    c.setStrokeStyle(w.isSelected() ? "#ff0000" : "#000000");
    c.beginPath();

    Iterator<Node> it = w.getNodes().iterator();
    if (it.hasNext()) {
      Point lastP = nc.getPoint(it.next());
      c.moveTo(lastP.x, lastP.y);

      for (int orderNumber = 1; it.hasNext(); orderNumber++) {
        Point p = nc.getPoint(it.next());
        c.lineTo(p.x, p.y);
        lastP = p;
      }
      c.stroke();
    }
  }
Esempio n. 19
0
 /**
  * Determines if ways can be joined into a polygon.
  *
  * @param ways The ways collection to check
  * @return true if all ways can be joined into a polygon
  */
 protected static boolean checkWaysArePolygon(Collection<Way> ways) {
   // For each way, nodes strictly between first and last should't be reference by an other way
   for (Way way : ways) {
     for (Node node : way.getNodes()) {
       if (way.isFirstLastNode(node)) continue;
       for (Way wayOther : ways) {
         if (way == wayOther) continue;
         if (node.getReferrers().contains(wayOther)) return false;
       }
     }
   }
   // Test if ways can be joined
   Way currentWay = null;
   Node startNode = null, endNode = null;
   int used = 0;
   while (true) {
     Way nextWay = null;
     for (Way w : ways) {
       if (w.isClosed()) return ways.size() == 1;
       if (w == currentWay) continue;
       if (currentWay == null) {
         nextWay = w;
         startNode = w.firstNode();
         endNode = w.lastNode();
         break;
       }
       if (w.firstNode() == endNode) {
         nextWay = w;
         endNode = w.lastNode();
         break;
       }
       if (w.lastNode() == endNode) {
         nextWay = w;
         endNode = w.firstNode();
         break;
       }
     }
     if (nextWay == null) return false;
     used += 1;
     currentWay = nextWay;
     if (endNode == startNode) return used == ways.size();
   }
 }
Esempio n. 20
0
  /**
   * Simple chunking version. Does not care about circular ways and result being proper, we will
   * glue it all back together later on.
   *
   * @param way the way to chunk
   * @param splitNodes the places where to cut.
   * @return list of node paths to produce.
   */
  private List<List<Node>> buildNodeChunks(Way way, Collection<Node> splitNodes) {
    List<List<Node>> result = new ArrayList<List<Node>>();
    List<Node> curList = new ArrayList<Node>();

    for (Node node : way.getNodes()) {
      curList.add(node);
      if (curList.size() > 1 && splitNodes.contains(node)) {
        result.add(curList);
        curList = new ArrayList<Node>();
        curList.add(node);
      }
    }

    if (curList.size() > 1) {
      result.add(curList);
    }

    return result;
  }
Esempio n. 21
0
    /**
     * Estimate the direction of the segments, given the first segment points in the direction
     * <code>pInitialDirection</code>. Then sum up all horizontal / vertical segments to have a good
     * guess for the heading of the entire way.
     *
     * @param pInitialDirection initial direction
     * @throws InvalidUserInputException if selected ways have an angle different from 90 or 180
     *     degrees
     */
    public void calcDirections(Direction pInitialDirection) throws InvalidUserInputException {
      final EastNorth[] en =
          new EastNorth[nNode]; // alias: way.getNodes().get(i).getEastNorth() ---> en[i]
      for (int i = 0; i < nNode; i++) {
        en[i] =
            new EastNorth(
                way.getNodes().get(i).getEastNorth().east(),
                way.getNodes().get(i).getEastNorth().north());
      }
      segDirections = new Direction[nSeg];
      Direction direction = pInitialDirection;
      segDirections[0] = direction;
      for (int i = 0; i < nSeg - 1; i++) {
        double h1 = EN.polar(en[i], en[i + 1]);
        double h2 = EN.polar(en[i + 1], en[i + 2]);
        try {
          direction = direction.changeBy(angleToDirectionChange(h2 - h1, TOLERANCE1));
        } catch (RejectedAngleException ex) {
          throw new InvalidUserInputException(
              tr("Please select ways with angles of approximately 90 or 180 degrees."), ex);
        }
        segDirections[i + 1] = direction;
      }

      // sum up segments
      EastNorth h = new EastNorth(0., 0.);
      EastNorth v = new EastNorth(0., 0.);
      for (int i = 0; i < nSeg; ++i) {
        EastNorth segment = EN.diff(en[i + 1], en[i]);
        if (segDirections[i] == Direction.RIGHT) {
          h = EN.sum(h, segment);
        } else if (segDirections[i] == Direction.UP) {
          v = EN.sum(v, segment);
        } else if (segDirections[i] == Direction.LEFT) {
          h = EN.diff(h, segment);
        } else if (segDirections[i] == Direction.DOWN) {
          v = EN.diff(v, segment);
        } else throw new IllegalStateException();
      }
      // rotate the vertical vector by 90 degrees (clockwise) and add it to the horizontal vector
      segSum = EN.sum(h, new EastNorth(v.north(), -v.east()));
      this.heading = EN.polar(new EastNorth(0., 0.), segSum);
    }
Esempio n. 22
0
 /**
  * Replies the collection of nodes referred to by primitives in <code>primitivesToDelete</code>
  * which can be deleted too. A node can be deleted if
  *
  * <ul>
  *   <li>it is untagged (see {@link Node#isTagged()}
  *   <li>it is not referred to by other non-deleted primitives outside of <code>primitivesToDelete
  *       </code>
  * </ul>
  *
  * @param primitivesToDelete the primitives to delete
  * @return the collection of nodes referred to by primitives in <code>primitivesToDelete</code>
  *     which can be deleted too
  */
 protected static Collection<Node> computeNodesToDelete(
     Collection<OsmPrimitive> primitivesToDelete) {
   Collection<Node> nodesToDelete = new HashSet<>();
   for (Way way : OsmPrimitive.getFilteredList(primitivesToDelete, Way.class)) {
     for (Node n : way.getNodes()) {
       if (n.isTagged()) {
         continue;
       }
       Collection<OsmPrimitive> referringPrimitives = n.getReferrers();
       referringPrimitives.removeAll(primitivesToDelete);
       int count = 0;
       for (OsmPrimitive p : referringPrimitives) {
         if (!p.isDeleted()) {
           count++;
         }
       }
       if (count == 0) {
         nodesToDelete.add(n);
       }
     }
   }
   return nodesToDelete;
 }
Esempio n. 23
0
  /**
   * Perform AlignInCircle action.
   *
   * <p>A fixed node is a node for which it is forbidden to change the angle relative to center of
   * the circle. All other nodes are uniformly distributed.
   *
   * <p>Case 1: One unclosed way. --&gt; allow action, and align selected way nodes If nodes
   * contained by this way are selected, there are fix. If nodes outside from the way are selected
   * there are ignored.
   *
   * <p>Case 2: One or more ways are selected and can be joined into a polygon --&gt; allow action,
   * and align selected ways nodes If 1 node outside of way is selected, it became center If 1 node
   * outside and 1 node inside are selected there define center and radius If no outside node and 2
   * inside nodes are selected those 2 nodes define diameter In all other cases outside nodes are
   * ignored In all cases, selected nodes are fix, nodes with more than one referrers are fix (first
   * referrer is the selected way)
   *
   * <p>Case 3: Only nodes are selected --&gt; Align these nodes, all are fix
   */
  @Override
  public void actionPerformed(ActionEvent e) {
    if (!isEnabled()) return;

    Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected();
    List<Node> nodes = new LinkedList<>();
    // fixNodes: All nodes for which the angle relative to center should not be modified
    Set<Node> fixNodes = new HashSet<>();
    List<Way> ways = new LinkedList<>();
    EastNorth center = null;
    double radius = 0;

    for (OsmPrimitive osm : sel) {
      if (osm instanceof Node) {
        nodes.add((Node) osm);
      } else if (osm instanceof Way) {
        ways.add((Way) osm);
      }
    }

    if (ways.size() == 1 && !ways.get(0).isClosed()) {
      // Case 1
      Way w = ways.get(0);
      fixNodes.add(w.firstNode());
      fixNodes.add(w.lastNode());
      fixNodes.addAll(nodes);
      fixNodes.addAll(collectNodesWithExternReferers(ways));
      // Temporary closed way used to reorder nodes
      Way closedWay = new Way(w);
      closedWay.addNode(w.firstNode());
      List<Way> usedWays = new ArrayList<>(1);
      usedWays.add(closedWay);
      nodes = collectNodesAnticlockwise(usedWays);
    } else if (!ways.isEmpty() && checkWaysArePolygon(ways)) {
      // Case 2
      List<Node> inside = new ArrayList<>();
      List<Node> outside = new ArrayList<>();

      for (Node n : nodes) {
        boolean isInside = false;
        for (Way w : ways) {
          if (w.getNodes().contains(n)) {
            isInside = true;
            break;
          }
        }
        if (isInside) inside.add(n);
        else outside.add(n);
      }

      if (outside.size() == 1 && inside.isEmpty()) {
        center = outside.get(0).getEastNorth();
      } else if (outside.size() == 1 && inside.size() == 1) {
        center = outside.get(0).getEastNorth();
        radius = distance(center, inside.get(0).getEastNorth());
      } else if (inside.size() == 2 && outside.isEmpty()) {
        // 2 nodes inside, define diameter
        EastNorth en0 = inside.get(0).getEastNorth();
        EastNorth en1 = inside.get(1).getEastNorth();
        center = new EastNorth((en0.east() + en1.east()) / 2, (en0.north() + en1.north()) / 2);
        radius = distance(en0, en1) / 2;
      }

      fixNodes.addAll(inside);
      fixNodes.addAll(collectNodesWithExternReferers(ways));
      nodes = collectNodesAnticlockwise(ways);
      if (nodes.size() < 4) {
        new Notification(tr("Not enough nodes in selected ways."))
            .setIcon(JOptionPane.INFORMATION_MESSAGE)
            .setDuration(Notification.TIME_SHORT)
            .show();
        return;
      }
    } else if (ways.isEmpty() && nodes.size() > 3) {
      // Case 3
      fixNodes.addAll(nodes);
      // No need to reorder nodes since all are fix
    } else {
      // Invalid action
      new Notification(tr("Please select at least four nodes."))
          .setIcon(JOptionPane.INFORMATION_MESSAGE)
          .setDuration(Notification.TIME_SHORT)
          .show();
      return;
    }

    if (center == null) {
      // Compute the center of nodes
      center = Geometry.getCenter(nodes);
      if (center == null) {
        new Notification(tr("Cannot determine center of selected nodes."))
            .setIcon(JOptionPane.INFORMATION_MESSAGE)
            .setDuration(Notification.TIME_SHORT)
            .show();
        return;
      }
    }

    // Now calculate the average distance to each node from the
    // center. This method is ok as long as distances are short
    // relative to the distance from the N or S poles.
    if (radius == 0) {
      for (Node n : nodes) {
        radius += distance(center, n.getEastNorth());
      }
      radius = radius / nodes.size();
    }

    if (!actionAllowed(nodes)) return;

    Collection<Command> cmds = new LinkedList<>();

    // Move each node to that distance from the center.
    // Nodes that are not "fix" will be adjust making regular arcs.
    int nodeCount = nodes.size();
    // Search first fixed node
    int startPosition = 0;
    for (startPosition = 0; startPosition < nodeCount; startPosition++) {
      if (fixNodes.contains(nodes.get(startPosition % nodeCount))) break;
    }
    int i = startPosition; // Start position for current arc
    int j; // End position for current arc
    while (i < startPosition + nodeCount) {
      for (j = i + 1; j < startPosition + nodeCount; j++) {
        if (fixNodes.contains(nodes.get(j % nodeCount))) break;
      }
      Node first = nodes.get(i % nodeCount);
      PolarCoor pcFirst = new PolarCoor(first.getEastNorth(), center, 0);
      pcFirst.radius = radius;
      cmds.add(pcFirst.createMoveCommand(first));
      if (j > i + 1) {
        double delta;
        if (j == i + nodeCount) {
          delta = 2 * Math.PI / nodeCount;
        } else {
          PolarCoor pcLast = new PolarCoor(nodes.get(j % nodeCount).getEastNorth(), center, 0);
          delta = pcLast.angle - pcFirst.angle;
          if (delta < 0) // Assume each PolarCoor.angle is in range ]-pi; pi]
          delta += 2 * Math.PI;
          delta /= j - i;
        }
        for (int k = i + 1; k < j; k++) {
          PolarCoor p = new PolarCoor(radius, pcFirst.angle + (k - i) * delta, center, 0);
          cmds.add(p.createMoveCommand(nodes.get(k % nodeCount)));
        }
      }
      i = j; // Update start point for next iteration
    }

    Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Circle"), cmds));
    Main.map.repaint();
  }
Esempio n. 24
0
 WayData(Way pWay) {
   way = pWay;
   nNode = way.getNodes().size();
   nSeg = nNode - 1;
 }
 @Override
 public Iterable<Node> getNodes(Way way) {
   return new FilteredOsmPrimitiveIterable<>(way.getNodes());
 }
Esempio n. 26
0
  /**
   * Gets called whenever the shortcut is pressed or the menu entry is selected Checks whether the
   * selected objects are suitable to join and joins them if so
   */
  @Override
  public void actionPerformed(ActionEvent e) {
    LinkedList<Way> ways = new LinkedList<Way>(Main.main.getCurrentDataSet().getSelectedWays());

    if (ways.isEmpty()) {
      new Notification(tr("Please select at least one closed way that should be joined."))
          .setIcon(JOptionPane.INFORMATION_MESSAGE)
          .show();
      return;
    }

    List<Node> allNodes = new ArrayList<Node>();
    for (Way way : ways) {
      if (!way.isClosed()) {
        new Notification(
                tr("One of the selected ways is not closed and therefore cannot be joined."))
            .setIcon(JOptionPane.INFORMATION_MESSAGE)
            .show();
        return;
      }

      allNodes.addAll(way.getNodes());
    }

    // TODO: Only display this warning when nodes outside dataSourceArea are deleted
    boolean ok =
        Command.checkAndConfirmOutlyingOperation(
            "joinarea",
            tr("Join area confirmation"),
            trn(
                    "The selected way has nodes outside of the downloaded data region.",
                    "The selected ways have nodes outside of the downloaded data region.",
                    ways.size())
                + "<br/>"
                + tr("This can lead to nodes being deleted accidentally.")
                + "<br/>"
                + tr("Are you really sure to continue?")
                + tr("Please abort if you are not sure"),
            tr("The selected area is incomplete. Continue?"),
            allNodes,
            null);
    if (!ok) return;

    // analyze multipolygon relations and collect all areas
    List<Multipolygon> areas = collectMultipolygons(ways);

    if (areas == null)
      // too complex multipolygon relations found
      return;

    if (!testJoin(areas)) {
      new Notification(tr("No intersection found. Nothing was changed."))
          .setIcon(JOptionPane.INFORMATION_MESSAGE)
          .show();
      return;
    }

    if (!resolveTagConflicts(areas)) return;
    // user canceled, do nothing.

    try {
      JoinAreasResult result = joinAreas(areas);

      if (result.hasChanges) {

        List<Way> allWays = new ArrayList<Way>();
        for (Multipolygon pol : result.polygons) {
          allWays.add(pol.outerWay);
          allWays.addAll(pol.innerWays);
        }
        DataSet ds = Main.main.getCurrentDataSet();
        ds.setSelected(allWays);
        Main.map.mapView.repaint();
      } else {
        new Notification(tr("No intersection found. Nothing was changed."))
            .setIcon(JOptionPane.INFORMATION_MESSAGE)
            .show();
      }
    } catch (UserCancelException exception) {
      // revert changes
      // FIXME: this is dirty hack
      makeCommitsOneAction(tr("Reverting changes"));
      Main.main.undoRedo.undo();
      Main.main.undoRedo.redoCommands.clear();
    }
  }
Esempio n. 27
0
  /**
   * Create OSM graph for routing
   *
   * @return
   */
  public void createGraph() {

    logger.debug("Creating Graph...");
    graph = new DirectedWeightedMultigraph<>(OsmEdge.class);
    rgDelegator = new RoutingGraphDelegator(graph);
    rgDelegator.setRouteType(this.routeType);
    // iterate all ways and segments for all nodes:
    for (Way way : data.getWays()) {

      // skip way if not suitable for routing.
      if (way == null || way.isDeleted() || !this.isvalidWay(way) || way.getNodes().size() < 1)
        continue;

      // INIT
      Node from = null;
      Node to = null;
      List<Node> nodes = way.getNodes();
      int nodes_count = nodes.size();

      /*
       * Assume node is A B C D E. The procedure should be
       *
       *  case 1 - bidirectional ways:
       *  1) Add vertex A B C D E
       *  2) Link A<->B, B<->C, C<->D, D<->E as Edges
       *
       *  case 2 - oneway reverse:
       *  1) Add vertex A B C D E
       *  2) Link B->A,C->B,D->C,E->D as Edges. result: A<-B<-C<-D<-E
       *
       *  case 3 - oneway normal:
       *  1) Add vertex A B C D E
       *  2) Link A->B, B->C, C->D, D->E as Edges. result: A->B->C->D->E
       *
       *
       */

      String oneway_val = way.get("oneway"); /*   get (oneway=?) tag for this way.   */
      String junction_val = way.get("junction"); /*   get (junction=?) tag for this way.   */

      from = nodes.get(0); /*   1st node A  */
      graph.addVertex(from); /*   add vertex A */

      for (int i = 1; i < nodes_count; i++) {
        /*   loop from B until E */

        to = nodes.get(i); /*   2nd node B   */

        if (to != null && !to.isDeleted()) {
          graph.addVertex(to); /*   add vertex B */

          // this is where we link the vertices
          if (!routingProfile.isOnewayUsed()) {
            // "Ignore oneways" is selected
            addEdgeBidirectional(way, from, to);

          } else if (oneway_val == null && junction_val == "roundabout") {
            // Case (roundabout): oneway=implicit yes
            addEdgeNormalOneway(way, from, to);

          } else if (oneway_val == null
              || oneway_val == "false"
              || oneway_val == "no"
              || oneway_val == "0") {
            // Case (bi-way): oneway=false OR oneway=unset OR oneway=0 OR oneway=no
            addEdgeBidirectional(way, from, to);

          } else if (oneway_val == "-1") {
            // Case (oneway reverse): oneway=-1
            addEdgeReverseOneway(way, from, to);

          } else if (oneway_val == "1" || oneway_val == "yes" || oneway_val == "true") {
            // Case (oneway normal): oneway=yes OR 1 OR true
            addEdgeNormalOneway(way, from, to);
          }

          from = to; /*   we did A<->B, next loop we will do B<->C, so from=B,to=C for next loop. */
        }
      } // end of looping thru nodes
    } // end of looping thru ways

    logger.debug("End Create Graph");
    logger.debug("Vertex: " + graph.vertexSet().size());
    logger.debug("Edges: " + graph.edgeSet().size());
  }
Esempio n. 28
0
    @Override
    public boolean matches(Environment e) {

      if (!right.matches(e)) return false;

      if (ChildOrParentSelectorType.ELEMENT_OF.equals(type)) {

        if (e.osm instanceof Node || e.osm.getDataSet() == null) {
          // nodes cannot contain elements
          return false;
        }

        ContainsFinder containsFinder;
        try {
          // if right selector also matches relations and if matched primitive is a way which is
          // part of a multipolygon,
          // use the multipolygon for further analysis
          if (!(e.osm instanceof Way)
              || (right instanceof OptimizedGeneralSelector
                  && !((OptimizedGeneralSelector) right).matchesBase(OsmPrimitiveType.RELATION))) {
            throw new NoSuchElementException();
          }
          final Collection<Relation> multipolygons =
              Utils.filteredCollection(
                  Utils.filter(e.osm.getReferrers(), Predicates.hasTag("type", "multipolygon")),
                  Relation.class);
          final Relation multipolygon = multipolygons.iterator().next();
          if (multipolygon == null) throw new NoSuchElementException();
          containsFinder =
              new ContainsFinder(new Environment(multipolygon)) {
                @Override
                public boolean isPrimitiveUsable(OsmPrimitive p) {
                  return super.isPrimitiveUsable(p)
                      && !multipolygon.getMemberPrimitives().contains(p);
                }
              };
        } catch (NoSuchElementException ignore) {
          containsFinder = new ContainsFinder(e);
        }
        e.parent = e.osm;

        if (left instanceof OptimizedGeneralSelector) {
          if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.NODE)) {
            containsFinder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
          }
          if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.WAY)) {
            containsFinder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
          }
        } else {
          // use slow test
          containsFinder.visit(e.osm.getDataSet().allPrimitives());
        }

        return e.child != null;

      } else if (ChildOrParentSelectorType.CROSSING.equals(type) && e.osm instanceof Way) {
        e.parent = e.osm;
        final CrossingFinder crossingFinder = new CrossingFinder(e);
        if (right instanceof OptimizedGeneralSelector
            && ((OptimizedGeneralSelector) right).matchesBase(OsmPrimitiveType.WAY)) {
          crossingFinder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
        }
        return e.child != null;
      } else if (ChildOrParentSelectorType.SIBLING.equals(type)) {
        if (e.osm instanceof Node) {
          for (Way w : Utils.filteredCollection(e.osm.getReferrers(true), Way.class)) {
            final int i = w.getNodes().indexOf(e.osm);
            if (i - 1 >= 0) {
              final Node n = w.getNode(i - 1);
              final Environment e2 = e.withPrimitive(n).withParent(w).withChild(e.osm);
              if (left.matches(e2) && link.matches(e2.withLinkContext())) {
                e.child = n;
                e.index = i;
                e.count = w.getNodesCount();
                e.parent = w;
                return true;
              }
            }
          }
        }
      } else if (ChildOrParentSelectorType.CHILD.equals(type)
          && link.conds != null
          && !link.conds.isEmpty()
          && link.conds.get(0) instanceof Condition.OpenEndPseudoClassCondition) {
        if (e.osm instanceof Node) {
          e.osm.visitReferrers(new MultipolygonOpenEndFinder(e));
          return e.parent != null;
        }
      } else if (ChildOrParentSelectorType.CHILD.equals(type)) {
        MatchingReferrerFinder collector = new MatchingReferrerFinder(e);
        e.osm.visitReferrers(collector);
        if (e.parent != null) return true;
      } else if (ChildOrParentSelectorType.PARENT.equals(type)) {
        if (e.osm instanceof Way) {
          List<Node> wayNodes = ((Way) e.osm).getNodes();
          for (int i = 0; i < wayNodes.size(); i++) {
            Node n = wayNodes.get(i);
            if (left.matches(e.withPrimitive(n))) {
              if (link.matches(e.withChildAndIndexAndLinkContext(n, i, wayNodes.size()))) {
                e.child = n;
                e.index = i;
                e.count = wayNodes.size();
                return true;
              }
            }
          }
        } else if (e.osm instanceof Relation) {
          List<RelationMember> members = ((Relation) e.osm).getMembers();
          for (int i = 0; i < members.size(); i++) {
            OsmPrimitive member = members.get(i).getMember();
            if (left.matches(e.withPrimitive(member))) {
              if (link.matches(e.withChildAndIndexAndLinkContext(member, i, members.size()))) {
                e.child = member;
                e.index = i;
                e.count = members.size();
                return true;
              }
            }
          }
        }
      }
      return false;
    }
Esempio n. 29
0
 public void visit(Way w) {
   visit(w.getNodes());
 }
Esempio n. 30
0
  @Override
  public void actionPerformed(ActionEvent e) {
    if (!isEnabled()) return;

    Collection<OsmPrimitive> sel = getCurrentDataSet().getAllSelected();
    layer = Main.map.mapView.getEditLayer();

    toPurge = new HashSet<OsmPrimitive>(sel);
    toPurgeAdditionally = new ArrayList<OsmPrimitive>();
    toPurgeChecked = new HashSet<OsmPrimitive>();

    // Add referrer, unless the object to purge is not new
    // and the parent is a relation
    HashSet<OsmPrimitive> toPurgeRecursive = new HashSet<OsmPrimitive>();
    while (!toPurge.isEmpty()) {

      for (OsmPrimitive osm : toPurge) {
        for (OsmPrimitive parent : osm.getReferrers()) {
          if (toPurge.contains(parent)
              || toPurgeChecked.contains(parent)
              || toPurgeRecursive.contains(parent)) {
            continue;
          }
          if (parent instanceof Way || (parent instanceof Relation && osm.isNew())) {
            toPurgeAdditionally.add(parent);
            toPurgeRecursive.add(parent);
          }
        }
        toPurgeChecked.add(osm);
      }
      toPurge = toPurgeRecursive;
      toPurgeRecursive = new HashSet<OsmPrimitive>();
    }

    makeIncomplete = new HashSet<OsmPrimitive>();

    // Find the objects that will be incomplete after purging.
    // At this point, all parents of new to-be-purged primitives are
    // also to-be-purged and
    // all parents of not-new to-be-purged primitives are either
    // to-be-purged or of type relation.
    TOP:
    for (OsmPrimitive child : toPurgeChecked) {
      if (child.isNew()) {
        continue;
      }
      for (OsmPrimitive parent : child.getReferrers()) {
        if (parent instanceof Relation && !toPurgeChecked.contains(parent)) {
          makeIncomplete.add(child);
          continue TOP;
        }
      }
    }

    // Add untagged way nodes. Do not add nodes that have other
    // referrers not yet to-be-purged.
    if (Main.pref.getBoolean("purge.add_untagged_waynodes", true)) {
      Set<OsmPrimitive> wayNodes = new HashSet<OsmPrimitive>();
      for (OsmPrimitive osm : toPurgeChecked) {
        if (osm instanceof Way) {
          Way w = (Way) osm;
          NODE:
          for (Node n : w.getNodes()) {
            if (n.isTagged() || toPurgeChecked.contains(n)) {
              continue;
            }
            for (OsmPrimitive ref : n.getReferrers()) {
              if (ref != w && !toPurgeChecked.contains(ref)) {
                continue NODE;
              }
            }
            wayNodes.add(n);
          }
        }
      }
      toPurgeChecked.addAll(wayNodes);
      toPurgeAdditionally.addAll(wayNodes);
    }

    if (Main.pref.getBoolean("purge.add_relations_with_only_incomplete_members", true)) {
      Set<Relation> relSet = new HashSet<Relation>();
      for (OsmPrimitive osm : toPurgeChecked) {
        for (OsmPrimitive parent : osm.getReferrers()) {
          if (parent instanceof Relation
              && !(toPurgeChecked.contains(parent))
              && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relSet)) {
            relSet.add((Relation) parent);
          }
        }
      }

      /** Add higher level relations (list gets extended while looping over it) */
      List<Relation> relLst = new ArrayList<Relation>(relSet);
      for (int i = 0; i < relLst.size(); ++i) {
        for (OsmPrimitive parent : relLst.get(i).getReferrers()) {
          if (!(toPurgeChecked.contains(parent))
              && hasOnlyIncompleteMembers((Relation) parent, toPurgeChecked, relLst)) {
            relLst.add((Relation) parent);
          }
        }
      }
      relSet = new HashSet<Relation>(relLst);
      toPurgeChecked.addAll(relSet);
      toPurgeAdditionally.addAll(relSet);
    }

    boolean modified = false;
    for (OsmPrimitive osm : toPurgeChecked) {
      if (osm.isModified()) {
        modified = true;
        break;
      }
    }

    ExtendedDialog confirmDlg =
        new ExtendedDialog(
            Main.parent, tr("Confirm Purging"), new String[] {tr("Purge"), tr("Cancel")});
    confirmDlg.setContent(buildPanel(modified), false);
    confirmDlg.setButtonIcons(new String[] {"ok", "cancel"});

    int answer = confirmDlg.showDialog().getValue();
    if (answer != 1) return;

    Main.pref.put("purge.clear_undo_redo", cbClearUndoRedo.isSelected());

    Main.main.undoRedo.add(
        new PurgeCommand(Main.map.mapView.getEditLayer(), toPurgeChecked, makeIncomplete));

    if (cbClearUndoRedo.isSelected()) {
      Main.main.undoRedo.clean();
      getCurrentDataSet().clearSelectionHistory();
    }
  }