Ejemplo n.º 1
0
  @Override
  public void visit(Way w) {

    if (!w.isUsable() || !w.isClosed()) return;

    String natural = w.get("natural");
    if (natural == null) return;
    else if ("coastline".equals(natural) && Geometry.isClockwise(w)) {
      reportError(w, tr("Reversed coastline: land not on left side"), WRONGLY_ORDERED_COAST);
    } else if ("land".equals(natural) && Geometry.isClockwise(w)) {
      reportError(w, tr("Reversed land: land not on left side"), WRONGLY_ORDERED_LAND);
    } else return;
  }
Ejemplo n.º 2
0
 /**
  * Search by closed status.
  *
  * @throws ParseError if an error has been encountered while compiling
  */
 @Test
 public void testClosed() throws ParseError {
   SearchContext sc = new SearchContext("closed");
   // Closed
   sc.w1.addNode(sc.n1);
   for (Way w : new Way[] {sc.w1}) {
     assertTrue(w.toString(), w.isClosed());
     sc.match(w, true);
   }
   // Unclosed
   for (OsmPrimitive p : new OsmPrimitive[] {sc.n1, sc.n2, sc.w2, sc.r1, sc.r2}) {
     sc.match(p, false);
   }
 }
Ejemplo n.º 3
0
  /**
   * Determines whether a way is oriented clockwise.
   *
   * <p>Internals: Assuming a closed non-looping way, compute twice the area of the polygon using
   * the formula {@code 2 * area = sum (X[n] * Y[n+1] - X[n+1] * Y[n])}. If the area is negative the
   * way is ordered in a clockwise direction.
   *
   * <p>See http://paulbourke.net/geometry/polyarea/
   *
   * @param w the way to be checked.
   * @return true if and only if way is oriented clockwise.
   * @throws IllegalArgumentException if way is not closed (see {@link Way#isClosed}).
   */
  public static boolean isClockwise(Way w) {
    if (!w.isClosed()) {
      throw new IllegalArgumentException("Way must be closed to check orientation.");
    }

    double area2 = 0.;
    int nodesCount = w.getNodesCount();

    for (int node = 1; node <= /*sic! consider last-first as well*/ nodesCount; node++) {
      LatLon coorPrev = w.getNode(node - 1).getCoor();
      LatLon coorCurr = w.getNode(node % nodesCount).getCoor();
      area2 += coorPrev.lon() * coorCurr.lat();
      area2 -= coorCurr.lon() * coorPrev.lat();
    }
    return area2 < 0;
  }
Ejemplo n.º 4
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();
   }
 }
Ejemplo n.º 5
0
  /**
   * Joins the outer ways and deletes all short ways that can't be part of a multipolygon anyway.
   *
   * @param ways The list of outer ways that belong to that multigon.
   * @return The newly created outer way
   */
  private Way joinWays(List<WayInPolygon> ways) throws UserCancelException {

    // leave original orientation, if all paths are reverse.
    boolean allReverse = true;
    for (WayInPolygon way : ways) {
      allReverse &= !way.insideToTheRight;
    }

    if (allReverse) {
      for (WayInPolygon way : ways) {
        way.insideToTheRight = !way.insideToTheRight;
      }
    }

    Way joinedWay = joinOrientedWays(ways);

    // should not happen
    if (joinedWay == null || !joinedWay.isClosed())
      throw new RuntimeException("Join areas internal error.");

    return joinedWay;
  }
Ejemplo n.º 6
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();
    }
  }