| | 225 | /** |
| | 226 | * Align way in case of multiple way #6819 |
| | 227 | * @param ways Collection of way to align |
| | 228 | */ |
| | 229 | private void alignMultiWay(Collection<Way> ways) { |
| | 230 | // Collect all nodes and compute line equation |
| | 231 | HashSet<Node> nodes = new HashSet<Node>(); |
| | 232 | HashMap<Way, Line> lines = new HashMap<Way, Line>(); |
| | 233 | for(Way w: ways) { |
| | 234 | if(w.firstNode() == w.lastNode()) { |
| | 235 | showWarning(tr("Can not align a polygon. Abort.")); |
| | 236 | return; |
| | 237 | } |
| | 238 | nodes.addAll(w.getNodes()); |
| | 239 | lines.put(w, new Line(w)); |
| | 240 | } |
| | 241 | Collection<Command> cmds = new ArrayList<Command>(nodes.size()); |
| | 242 | List<Way> referers = new ArrayList<Way>(ways.size()); |
| | 243 | for(Node n: nodes) { |
| | 244 | referers.clear(); |
| | 245 | for(OsmPrimitive o: n.getReferrers()) |
| | 246 | if(ways.contains(o)) |
| | 247 | referers.add((Way) o); |
| | 248 | if(referers.size() == 1) { |
| | 249 | Way way = referers.get(0); |
| | 250 | if(n == way.firstNode() || n == way.lastNode()) continue; |
| | 251 | cmds.add(lines.get(way).projectionCommand(n)); |
| | 252 | } |
| | 253 | else if(referers.size() == 2) { |
| | 254 | Command cmd = lines.get(referers.get(0)).intersectionCommand(n, lines.get(referers.get(1))); |
| | 255 | if(cmd == null) { |
| | 256 | showWarning(tr("Two parallels ways found. Abort.")); |
| | 257 | return; |
| | 258 | } |
| | 259 | cmds.add(cmd); |
| | 260 | } |
| | 261 | else { |
| | 262 | showWarning(tr("Intersection of three or more ways can not be solved. Abort.")); |
| | 263 | return; |
| | 264 | } |
| | 265 | } |
| | 266 | Main.main.undoRedo.add(new SequenceCommand(tr("Align Nodes in Line"), cmds)); |
| | 267 | Main.map.repaint(); |
| | 268 | } |
| | 269 | |
| | 270 | /** |
| | 271 | * Class that describe a line |
| | 272 | */ |
| | 273 | private class Line { |
| | 274 | |
| | 275 | /** |
| | 276 | * Line equation ax + by + c = 0 |
| | 277 | * Such as a^2 + b^2 = 1, ie (-b, a) is a unit vector of line |
| | 278 | */ |
| | 279 | private double a, b, c; // Line equation ax+by+c=0 |
| | 280 | /** |
| | 281 | * (xM, yM) are coordinate of a point of the line |
| | 282 | */ |
| | 283 | private double xM, yM; // Coordinate of a point of the line |
| | 284 | |
| | 285 | /** |
| | 286 | * Init a line equation from a way. |
| | 287 | * @param way |
| | 288 | */ |
| | 289 | public Line(Way way) { |
| | 290 | xM = way.firstNode().getEastNorth().getX(); |
| | 291 | yM = way.firstNode().getEastNorth().getY(); |
| | 292 | double xB = way.lastNode().getEastNorth().getX(); |
| | 293 | double yB = way.lastNode().getEastNorth().getY(); |
| | 294 | a = yB - yM; |
| | 295 | b = xM - xB; |
| | 296 | double norm = Math.sqrt(a*a + b*b); |
| | 297 | a /= norm; |
| | 298 | b /= norm; |
| | 299 | c = -(a*xM + b*yM); |
| | 300 | } |
| | 301 | |
| | 302 | /** |
| | 303 | * Orthogonal projection of a node N along this line. |
| | 304 | * @param n Node to be projected |
| | 305 | * @return The command that do the projection of this node |
| | 306 | */ |
| | 307 | public Command projectionCommand(Node n) { |
| | 308 | double s = (xM - n.getEastNorth().getX()) * a + (yM - n.getEastNorth().getY()) * b; |
| | 309 | return new MoveCommand(n, a*s, b*s); |
| | 310 | } |
| | 311 | |
| | 312 | /** |
| | 313 | * Intersection of two line. |
| | 314 | * @param n Node to move to the intersection |
| | 315 | * @param other Second line for intersection |
| | 316 | * @return The command that move the node or null if line are parallels |
| | 317 | */ |
| | 318 | public Command intersectionCommand(Node n, Line other) { |
| | 319 | double d = this.a * other.b - other.a * this.b; |
| | 320 | if(d == 0) return null; |
| | 321 | double x = (this.b * other.c - other.b * this.c) / d; |
| | 322 | double y = (other.a * this.c - this.a * other.c) / d; |
| | 323 | return new MoveCommand(n, x - n.getEastNorth().getX(), y - n.getEastNorth().getY()); |
| | 324 | } |
| | 325 | } |
| | 326 | |