Changeset 25783 in osm
- Timestamp:
- 2011-04-04T02:12:14+02:00 (14 years ago)
- Location:
- applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes
- Files:
-
- 18 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/CollectionUtils.java
r25606 r25783 1 1 package org.openstreetmap.josm.plugins.turnlanes; 2 2 3 import java.util.ArrayList; 4 import java.util.Collections; 5 import java.util.HashSet; 3 6 import java.util.Iterator; 4 7 import java.util.List; 5 8 import java.util.ListIterator; 9 import java.util.Set; 6 10 7 11 public class CollectionUtils { … … 31 35 }; 32 36 } 37 38 public static <E> Set<E> toSet(Iterable<? extends E> iterable) { 39 final Set<E> set = new HashSet<E>(); 40 41 for (E e : iterable) { 42 set.add(e); 43 } 44 45 return Collections.unmodifiableSet(set); 46 } 47 48 public static <E> List<E> toList(Iterable<? extends E> iterable) { 49 final List<E> list = new ArrayList<E>(); 50 51 for (E e : iterable) { 52 list.add(e); 53 } 54 55 return Collections.unmodifiableList(list); 56 } 33 57 } -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/GuiContainer.java
r25606 r25783 1 1 package org.openstreetmap.josm.plugins.turnlanes.gui; 2 3 import static java.lang.Math.sqrt; 4 import static org.openstreetmap.josm.plugins.turnlanes.gui.GuiUtil.locs; 2 5 3 6 import java.awt.BasicStroke; … … 5 8 import java.awt.Stroke; 6 9 import java.awt.geom.Point2D; 10 import java.awt.geom.Rectangle2D; 11 import java.util.ArrayList; 12 import java.util.Collections; 7 13 import java.util.HashMap; 14 import java.util.List; 8 15 import java.util.Map; 9 16 17 import org.openstreetmap.josm.Main; 18 import org.openstreetmap.josm.data.coor.EastNorth; 19 import org.openstreetmap.josm.data.coor.LatLon; 10 20 import org.openstreetmap.josm.plugins.turnlanes.model.Junction; 11 21 import org.openstreetmap.josm.plugins.turnlanes.model.Lane; 22 import org.openstreetmap.josm.plugins.turnlanes.model.ModelContainer; 12 23 import org.openstreetmap.josm.plugins.turnlanes.model.Road; 13 24 … … 16 27 static final Color GREEN = new Color(66, 234, 108); 17 28 29 private final ModelContainer mc; 30 18 31 private final Point2D translation; 19 32 /** … … 21 34 */ 22 35 private final double mpp; 23 /**24 * Meters per source unit.25 */26 private final double mpsu;27 36 private final double scale; 28 37 private final double laneWidth; … … 33 42 private final Stroke connectionStroke; 34 43 35 public GuiContainer(Point2D origin, double mpsu) { 44 public GuiContainer(ModelContainer mc) { 45 final Point2D origin = avgOrigin(locs(mc.getPrimaryJunctions())); 46 47 final LatLon originCoor = Main.proj.eastNorth2latlon(new EastNorth(origin.getX(), origin.getY())); 48 final LatLon relCoor = Main.proj.eastNorth2latlon(new EastNorth(origin.getX() + 1, origin.getY() + 1)); 49 50 // meters per source unit 51 final double mpsu = relCoor.greatCircleDistance(originCoor) / sqrt(2); 52 53 this.mc = mc; 36 54 this.translation = new Point2D.Double(-origin.getX(), -origin.getY()); 37 55 this.mpp = 0.2; 38 this.mpsu = mpsu;39 56 this.scale = mpsu / mpp; 40 57 this.laneWidth = 2 / mpp; 41 58 42 59 this.connectionStroke = new BasicStroke((float) (laneWidth / 4), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); 60 61 for (Junction j : mc.getPrimaryJunctions()) { 62 getGui(j); 63 } 64 } 65 66 private static Point2D avgOrigin(List<Point2D> locs) { 67 double x = 0; 68 double y = 0; 69 70 for (Point2D l : locs) { 71 x += l.getX(); 72 y += l.getY(); 73 } 74 75 return new Point2D.Double(x / locs.size(), y / locs.size()); 43 76 } 44 77 … … 105 138 } 106 139 107 public GuiContainer empty() { 108 return new GuiContainer(new Point2D.Double(-translation.getX(), -translation.getY()), mpsu); 140 public ModelContainer getModel() { 141 return mc; 142 } 143 144 public Rectangle2D getBounds() { 145 final List<Junction> primaries = new ArrayList<Junction>(mc.getPrimaryJunctions()); 146 final List<Double> top = new ArrayList<Double>(); 147 final List<Double> left = new ArrayList<Double>(); 148 final List<Double> right = new ArrayList<Double>(); 149 final List<Double> bottom = new ArrayList<Double>(); 150 151 for (Junction j : primaries) { 152 final JunctionGui g = getGui(j); 153 final Rectangle2D b = g.getBounds(); 154 155 top.add(b.getMinY()); 156 left.add(b.getMinX()); 157 right.add(b.getMaxX()); 158 bottom.add(b.getMaxY()); 159 } 160 161 final double t = Collections.min(top); 162 final double l = Collections.min(left); 163 final double r = Collections.max(right); 164 final double b = Collections.max(bottom); 165 166 return new Rectangle2D.Double(l, t, r - l, b - t); 167 } 168 169 public GuiContainer recalculate() { 170 return new GuiContainer(mc.recalculate()); 171 } 172 173 public Iterable<RoadGui> getRoads() { 174 return roads.values(); 175 } 176 177 public Iterable<JunctionGui> getJunctions() { 178 return junctions.values(); 109 179 } 110 180 } -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/GuiUtil.java
r25606 r25783 14 14 import org.openstreetmap.josm.data.coor.EastNorth; 15 15 import org.openstreetmap.josm.data.osm.Node; 16 import org.openstreetmap.josm.plugins.turnlanes.model.Junction; 16 17 17 18 class GuiUtil { … … 131 132 } 132 133 133 public static List<Point2D> locs(Iterable< Node> nodes) {134 public static List<Point2D> locs(Iterable<Junction> junctions) { 134 135 final List<Point2D> locs = new ArrayList<Point2D>(); 135 136 136 for ( Node n : nodes) {137 locs.add(loc( n));137 for (Junction j : junctions) { 138 locs.add(loc(j.getNode())); 138 139 } 139 140 -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/InteractiveElement.java
r25606 r25783 11 11 Type LANE_ADDER = new Type() {}; 12 12 Type EXTENDER = new Type() {}; 13 Type VIA_CONNECTOR = new Type() {}; 13 14 } 14 15 -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/JunctionGui.java
r25606 r25783 3 3 import static java.lang.Math.PI; 4 4 import static java.lang.Math.abs; 5 import static java.lang.Math.hypot; 5 6 import static java.lang.Math.max; 6 7 import static java.lang.Math.tan; … … 16 17 import java.awt.Color; 17 18 import java.awt.Graphics2D; 19 import java.awt.geom.FlatteningPathIterator; 18 20 import java.awt.geom.Line2D; 19 21 import java.awt.geom.Path2D; 22 import java.awt.geom.PathIterator; 20 23 import java.awt.geom.Point2D; 21 24 import java.awt.geom.Rectangle2D; … … 37 40 private final Turn turn; 38 41 39 private final Line2D line = new Line2D.Double();40 41 42 private Point2D dragBegin; 42 43 private Point2D dragOffset = new Point2D.Double();43 private double dragOffsetX = 0; 44 private double dragOffsetY = 0; 44 45 45 46 public TurnConnection(Turn turn) { … … 50 51 void paint(Graphics2D g2d, State state) { 51 52 if (isVisible(state)) { 52 final LaneGui laneGui = getContainer().getGui(turn.getFrom()); 53 final RoadGui roadGui = getContainer().getGui(turn.getTo().getRoad()); 53 g2d.setStroke(getContainer().getConnectionStroke()); 54 g2d.setColor(isRemoveDragOffset() ? GuiContainer.RED : GuiContainer.GREEN); 55 g2d.translate(dragOffsetX, dragOffsetY); 56 g2d.draw(getPath()); 57 g2d.translate(-dragOffsetX, -dragOffsetY); 58 } 59 } 60 61 private Path2D getPath() { 62 final Path2D path = new Path2D.Double(); 63 64 final LaneGui laneGui = getContainer().getGui(turn.getFrom()); 65 final RoadGui roadGui = getContainer().getGui(turn.getTo().getRoad()); 66 67 path.moveTo(laneGui.outgoing.getCenter().getX(), laneGui.outgoing.getCenter().getY()); 68 69 Junction j = laneGui.getModel().getOutgoingJunction(); 70 for (Road v : turn.getVia()) { 71 final PathIterator it; 72 if (v.getFromEnd().getJunction().equals(j)) { 73 it = getContainer().getGui(v).getLaneMiddle(true).getIterator(); 74 j = v.getToEnd().getJunction(); 75 } else { 76 it = getContainer().getGui(v).getLaneMiddle(false).getIterator(); 77 j = v.getFromEnd().getJunction(); 78 } 54 79 55 g2d.setStroke(getContainer().getConnectionStroke()); 56 57 g2d.setColor(isRemoveDragOffset() ? GuiContainer.RED : GuiContainer.GREEN); 58 59 line.setLine(laneGui.outgoing.getCenter(), roadGui.getConnector(turn.getTo()).getCenter()); 60 line.setLine(line.getX1() + dragOffset.getX(), line.getY1() + dragOffset.getY(), 61 line.getX2() + dragOffset.getX(), line.getY2() + dragOffset.getY()); 62 g2d.draw(line); 63 } 80 path.append(it, true); 81 } 82 83 path.lineTo(roadGui.getConnector(turn.getTo()).getCenter().getX(), roadGui.getConnector(turn.getTo()).getCenter() 84 .getY()); 85 86 return path; 64 87 } 65 88 66 89 private boolean isVisible(State state) { 67 if (state instanceof State.OutgoingActive) { 90 if (state instanceof State.AllTurns) { 91 return true; 92 } else if (state instanceof State.OutgoingActive) { 68 93 return turn.getFrom().equals(((State.OutgoingActive) state).getLane().getModel()); 69 94 } else if (state instanceof State.IncomingActive) { … … 76 101 @Override 77 102 boolean contains(Point2D p, State state) { 78 final Point2D closest = closest(line, p); 79 return p.distance(closest) <= strokeWidth() / 2; 103 if (!isVisible(state)) { 104 return false; 105 } 106 107 final PathIterator it = new FlatteningPathIterator(getPath().getPathIterator(null), 0.05 / getContainer() 108 .getMpp()); 109 final double[] coords = new double[6]; 110 double lastX = 0; 111 double lastY = 0; 112 while (!it.isDone()) { 113 if (it.currentSegment(coords) == PathIterator.SEG_LINETO) { 114 final Point2D closest = closest(new Line2D.Double(lastX, lastY, coords[0], coords[1]), p); 115 116 if (p.distance(closest) <= strokeWidth() / 2) { 117 return true; 118 } 119 } 120 121 lastX = coords[0]; 122 lastY = coords[1]; 123 it.next(); 124 } 125 126 return false; 80 127 } 81 128 … … 98 145 boolean beginDrag(double x, double y) { 99 146 dragBegin = new Point2D.Double(x, y); 100 dragOffset = new Point2D.Double(); 147 dragOffsetX = 0; 148 dragOffsetY = 0; 101 149 return true; 102 150 } … … 104 152 @Override 105 153 State drag(double x, double y, InteractiveElement target, State old) { 106 dragOffset = new Point2D.Double(x - dragBegin.getX(), y - dragBegin.getY()); 154 dragOffsetX = x - dragBegin.getX(); 155 dragOffsetY = y - dragBegin.getY(); 107 156 return old; 108 157 } … … 113 162 114 163 if (isRemoveDragOffset()) { 115 getModel().removeTurn(turn); 116 } 117 118 dragOffset = new Point2D.Double(); 164 turn.remove(); 165 } 166 167 dragBegin = null; 168 dragOffsetX = 0; 169 dragOffsetY = 0; 119 170 return new State.Dirty(old); 120 171 } … … 123 174 final double r = getContainer().getGui(turn.getFrom().getRoad()).connectorRadius; 124 175 final double max = r - strokeWidth() / 2; 125 return dragOffset.distance(0, 0) > max;176 return hypot(dragOffsetX, dragOffsetY) > max; 126 177 } 127 178 } … … 386 437 final List<InteractiveElement> result = new ArrayList<InteractiveElement>(); 387 438 388 for (Turn t : getModel().getTurns()) { 389 result.add(new TurnConnection(t)); 439 if (getModel().isPrimary()) { 440 for (Road.End r : new HashSet<Road.End>(getModel().getRoadEnds())) { 441 for (Turn t : r.getTurns()) { 442 result.add(new TurnConnection(t)); 443 } 444 } 390 445 } 391 446 -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/JunctionPane.java
r25606 r25783 6 6 import java.awt.Graphics2D; 7 7 import java.awt.RenderingHints; 8 import java.awt.event.ActionEvent; 9 import java.awt.event.InputEvent; 10 import java.awt.event.KeyEvent; 8 11 import java.awt.event.MouseAdapter; 9 12 import java.awt.event.MouseEvent; … … 19 22 import java.util.TreeMap; 20 23 24 import javax.swing.AbstractAction; 21 25 import javax.swing.JComponent; 22 23 import org.openstreetmap.josm.data.osm.Node; 24 import org.openstreetmap.josm.plugins.turnlanes.model.ModelContainer; 26 import javax.swing.KeyStroke; 25 27 26 28 class JunctionPane extends JComponent { … … 31 33 32 34 public void mousePressed(MouseEvent e) { 35 setFocusable(true); 33 36 button = e.getButton(); 34 37 … … 76 79 if (ie.contains(mouse, state)) { 77 80 setState(ie.click(state)); 78 repaint();79 81 break; 80 82 } … … 103 105 setState(dragging.drag(mouse.getX(), mouse.getY(), dropTarget(mouse), state)); 104 106 } 105 106 repaint();107 107 } else if (button == MouseEvent.BUTTON3) { 108 108 translate(e.getX() - originX, e.getY() - originY); … … 133 133 private final MouseInputProcessor mip = new MouseInputProcessor(); 134 134 135 private JunctionGui junction;135 private GuiContainer container; 136 136 137 137 private int width = 0; … … 148 148 private InteractiveElement dragging; 149 149 150 public JunctionPane(JunctionGui junction) { 151 setJunction(junction); 152 } 153 154 public void setJunction(JunctionGui junction) { 150 public JunctionPane(GuiContainer container) { 151 setJunction(container); 152 153 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), "refresh"); 154 getActionMap().put("refresh", new AbstractAction() { 155 private static final long serialVersionUID = 1L; 156 157 @Override 158 public void actionPerformed(ActionEvent e) { 159 setState(new State.Invalid(state)); 160 } 161 }); 162 163 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "zoomIn"); 164 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "zoomIn"); 165 getActionMap().put("zoomIn", new AbstractAction() { 166 private static final long serialVersionUID = 1L; 167 168 @Override 169 public void actionPerformed(ActionEvent e) { 170 scale(Math.pow(0.8, -1)); 171 } 172 }); 173 174 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "zoomOut"); 175 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "zoomOut"); 176 getActionMap().put("zoomOut", new AbstractAction() { 177 private static final long serialVersionUID = 1L; 178 179 @Override 180 public void actionPerformed(ActionEvent e) { 181 scale(Math.pow(0.8, 1)); 182 } 183 }); 184 185 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "center"); 186 getActionMap().put("center", new AbstractAction() { 187 private static final long serialVersionUID = 1L; 188 189 @Override 190 public void actionPerformed(ActionEvent e) { 191 center(); 192 } 193 }); 194 195 getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK), "toggleAllTurns"); 196 getActionMap().put("toggleAllTurns", new AbstractAction() { 197 private static final long serialVersionUID = 1L; 198 199 @Override 200 public void actionPerformed(ActionEvent e) { 201 toggleAllTurns(); 202 } 203 }); 204 } 205 206 public void setJunction(GuiContainer container) { 155 207 removeMouseListener(mip); 156 208 removeMouseMotionListener(mip); … … 158 210 interactives.clear(); 159 211 dragging = null; 160 this.junction = junction; 161 162 dirty = true; 163 repaint(); 164 165 if (junction == null) { 212 this.container = container; 213 214 if (container == null) { 166 215 this.state = null; 167 216 } else { 168 this.state = new State.Default(junction); 169 170 final Rectangle2D bounds = junction.getBounds(); 171 scale = Math.min(getHeight() / 2 / bounds.getHeight(), getWidth() / 2 / bounds.getWidth()); 172 173 translationX = -bounds.getCenterX(); 174 translationY = -bounds.getCenterY(); 175 176 translate(getWidth() / 2d, getHeight() / 2d); 217 setState(new State.Dirty(new State.Default())); 218 219 center(); 177 220 178 221 addMouseListener(mip); … … 182 225 } 183 226 227 private void center() { 228 final Rectangle2D bounds = container.getBounds(); 229 scale = Math.min(getHeight() / 2 / bounds.getHeight(), getWidth() / 2 / bounds.getWidth()); 230 231 translationX = -bounds.getCenterX(); 232 translationY = -bounds.getCenterY(); 233 234 translate(getWidth() / 2d, getHeight() / 2d); 235 } 236 237 private void toggleAllTurns() { 238 if (state instanceof State.AllTurns) { 239 setState(((State.AllTurns) state).unwrap()); 240 } else { 241 setState(new State.AllTurns(state)); 242 } 243 } 244 184 245 private void setState(State state) { 185 if (state instanceof State.Invalid) { 186 final Node n = junction.getModel().getNode(); 187 final ModelContainer m = ModelContainer.create(n); 188 189 final GuiContainer c = junction.getContainer().empty(); 190 junction = c.getGui(m.getJunction(n)); 191 246 if (state instanceof State.AllTurns) { 192 247 dirty = true; 193 this.state = new State.Default(junction); 248 this.state = state; 249 } else if (state instanceof State.Invalid) { 250 container = container.recalculate(); 251 dirty = true; 252 this.state = new State.Default(); 194 253 } else if (state instanceof State.Dirty) { 195 254 dirty = true; … … 198 257 this.state = state; 199 258 } 259 260 repaint(); 200 261 } 201 262 … … 211 272 dirty = true; 212 273 repaint(); 274 } 275 276 void scale(double scale) { 277 scale(getWidth() / 2, getHeight() / 2, scale); 213 278 } 214 279 … … 233 298 } 234 299 235 if ( junction== null) {300 if (container == null) { 236 301 super.paintComponent(g); 237 302 return; … … 295 360 296 361 g2d.setColor(Color.GRAY); 297 for (RoadGui r : junction.getRoads()) {362 for (RoadGui r : container.getRoads()) { 298 363 addAllInteractives(r.paint(g2d)); 299 364 } 300 365 301 addAllInteractives(junction.paint(g2d)); 302 303 dot(g2d, new Point2D.Double(junction.x, junction.y), junction.getContainer().getLaneWidth() / 5); 366 for (JunctionGui j : container.getJunctions()) { 367 addAllInteractives(j.paint(g2d)); 368 dot(g2d, new Point2D.Double(j.x, j.y), container.getLaneWidth() / 5); 369 } 304 370 } 305 371 -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/LaneGui.java
r25606 r25783 11 11 import java.awt.Shape; 12 12 import java.awt.geom.Ellipse2D; 13 import java.awt.geom.Line2D;14 13 import java.awt.geom.Path2D; 14 import java.awt.geom.PathIterator; 15 15 import java.awt.geom.Point2D; 16 16 import java.awt.geom.Rectangle2D; 17 17 import java.text.DecimalFormat; 18 18 import java.text.NumberFormat; 19 import java.util.ArrayList; 19 20 import java.util.List; 20 21 21 22 import org.openstreetmap.josm.plugins.turnlanes.gui.RoadGui.IncomingConnector; 22 import org.openstreetmap.josm.plugins.turnlanes.model.Junction;23 23 import org.openstreetmap.josm.plugins.turnlanes.model.Lane; 24 import org.openstreetmap.josm.plugins.turnlanes.model.Road; 24 25 25 26 final class LaneGui { … … 79 80 80 81 final double offset = getRoad().getOffset(x, y); 81 final double newLength = getModel(). isReverse() ? offset : getRoad().getLength() - offset;82 final double newLength = getModel().getOutgoingRoadEnd().isFromEnd() ? offset : getRoad().getLength() - offset; 82 83 if (newLength > 0) { 83 84 getModel().setLength(newLength * getRoad().getContainer().getMpp()); … … 125 126 126 127 if (dragLocation != null) { 128 final State.Connecting s = (State.Connecting) state; 129 final Path2D path = new Path2D.Double(); 130 path.moveTo(center.getX(), center.getY()); 131 132 final List<RoadGui.ViaConnector> vias = s.getViaConnectors(); 133 for (int i = 0; i < vias.size() - 1; i += 2) { 134 final RoadGui.ViaConnector v = vias.get(i); 135 final PathIterator it = v.getRoad().getLaneMiddle(v.getRoadEnd().isFromEnd()).getIterator(); 136 path.append(it, true); 137 } 138 if ((vias.size() & 1) != 0) { 139 final RoadGui.ViaConnector last = vias.get(vias.size() - 1); 140 path.lineTo(last.getCenter().getX(), last.getCenter().getY()); 141 } 142 143 if (dropTarget == null) { 144 g2d.setColor(GuiContainer.RED); 145 path.lineTo(dragLocation.getX(), dragLocation.getY()); 146 } else { 147 g2d.setColor(GuiContainer.GREEN); 148 path.lineTo(dropTarget.getCenter().getX(), dropTarget.getCenter().getY()); 149 } 150 127 151 g2d.setStroke(getContainer().getConnectionStroke()); 128 g2d.setColor(dropTarget == null ? GuiContainer.RED : GuiContainer.GREEN); 129 g2d.draw(new Line2D.Double(getCenter(), dropTarget == null ? dragLocation : dropTarget.getCenter())); 152 g2d.draw(path); 130 153 } 131 154 } … … 150 173 151 174 private boolean isVisible(State state) { 152 return getModel().getOutgoingJunction().equals(state.getJunction().getModel()); 175 if (state instanceof State.Connecting) { 176 return ((State.Connecting) state).getLane().equals(getModel()); 177 } 178 179 return !getRoad().getModel().isPrimary() && getModel().getOutgoingJunction().isPrimary(); 153 180 } 154 181 … … 165 192 @Override 166 193 public State activate(State old) { 167 return new State.OutgoingActive( old.getJunction(),LaneGui.this);194 return new State.OutgoingActive(LaneGui.this); 168 195 } 169 196 … … 174 201 175 202 @Override 176 State drag(double x, double y, InteractiveElement target, State old) {203 State.Connecting drag(double x, double y, InteractiveElement target, State old) { 177 204 dragLocation = new Point2D.Double(x, y); 178 dropTarget = target != null && target.getType() == Type.INCOMING_CONNECTOR ? (IncomingConnector) target : null; 179 return old; 205 dropTarget = null; 206 207 if (!(old instanceof State.Connecting)) { 208 return new State.Connecting(getModel()); 209 } 210 211 final State.Connecting s = (State.Connecting) old; 212 if (target != null && target.getType() == Type.INCOMING_CONNECTOR) { 213 dropTarget = (IncomingConnector) target; 214 215 return (s.getViaConnectors().size() & 1) == 0 ? s : s.pop(); 216 } else if (target != null && target.getType() == Type.VIA_CONNECTOR) { 217 return s.next((RoadGui.ViaConnector) target); 218 } 219 220 return s; 180 221 } 181 222 182 223 @Override 183 224 State drop(double x, double y, InteractiveElement target, State old) { 184 drag(x, y, target, old);225 final State.Connecting s = drag(x, y, target, old); 185 226 dragLocation = null; 186 187 227 if (dropTarget == null) { 188 return old; 189 } 190 191 final Junction j = (getModel().isReverse() ? getRoad().getA() : getRoad().getB()).getModel(); 192 193 j.addTurn(getModel(), dropTarget.getRoadEnd()); 194 228 return activate(old); 229 } 230 231 final List<Road> via = new ArrayList<Road>(); 232 assert (s.getViaConnectors().size() & 1) == 0; 233 for (int i = 0; i < s.getViaConnectors().size(); i += 2) { 234 final RoadGui.ViaConnector a = s.getViaConnectors().get(i); 235 final RoadGui.ViaConnector b = s.getViaConnectors().get(i + 1); 236 assert a.getRoadEnd().getOppositeEnd().equals(b); 237 via.add(a.getRoadEnd().getRoad()); 238 } 239 240 getModel().addTurn(via, dropTarget.getRoadEnd()); 195 241 dropTarget = null; 196 return new State.Dirty( old);242 return new State.Dirty(activate(old)); 197 243 } 198 244 … … 256 302 final double WW = 3 / getContainer().getMpp(); 257 303 258 final List<LaneGui> lanes = getRoad().getLanes(); 259 final int i = lanes.indexOf(this); 260 final LaneGui left = getModel().isReverse() ? (i < lanes.size() - 1 ? lanes.get(i + 1) : null) : (i > 0 ? lanes 261 .get(i - 1) : null); 304 final LaneGui left = left(); 262 305 final Lane leftModel = left == null ? null : left.getModel(); 263 final double leftLength = leftModel == null || leftModel.isReverse() != getModel().isReverse() ? Double.NEGATIVE_INFINITY 306 final double leftLength = leftModel == null 307 || !leftModel.getOutgoingRoadEnd().equals(getModel().getOutgoingRoadEnd()) ? Double.NEGATIVE_INFINITY 264 308 : leftModel.getKind() == Lane.Kind.EXTRA_LEFT ? left.getLength() : L; 265 309 … … 276 320 if (L > leftLength) { 277 321 innerLine.append(inner.subpath(max(0, leftLength + WW), L).getIterator(), leftLength >= 0 278 || getModel(). isReverse());322 || getModel().getOutgoingRoadEnd().isFromEnd()); 279 323 final Point2D op = outer.getPoint(L + WW); 280 324 innerLine.lineTo(op.getX(), op.getY()); … … 291 335 if (leftLength < L) { 292 336 innerLine.append(inner.subpath(max(0, leftLength + WW), L).getIterator(), leftLength >= 0 293 || getModel(). isReverse());337 || getModel().getOutgoingRoadEnd().isFromEnd()); 294 338 } 295 339 } 296 340 297 341 return outer; 342 } 343 344 private LaneGui left() { 345 final List<LaneGui> lanes = getRoad().getLanes(getModel().getOutgoingRoadEnd()); 346 final int i = lanes.indexOf(this); 347 return i > 0 ? lanes.get(i - 1) : null; 298 348 } 299 349 -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/RoadGui.java
r25606 r25783 29 29 import java.awt.geom.Rectangle2D; 30 30 import java.util.ArrayList; 31 import java.util.Arrays; 31 32 import java.util.Collections; 32 33 import java.util.List; … … 34 35 import org.openstreetmap.josm.data.osm.Node; 35 36 import org.openstreetmap.josm.data.osm.OsmPrimitive; 36 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;37 37 import org.openstreetmap.josm.data.osm.Way; 38 import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils;39 38 import org.openstreetmap.josm.plugins.turnlanes.model.Lane; 40 39 import org.openstreetmap.josm.plugins.turnlanes.model.Road; … … 42 41 43 42 class RoadGui { 43 final class ViaConnector extends InteractiveElement { 44 private final Road.End end; 45 46 private final Line2D line; 47 private final float strokeWidth; 48 49 public ViaConnector(Road.End end) { 50 this.end = end; 51 this.line = new Line2D.Double(getLeftCorner(end), getRightCorner(end)); 52 this.strokeWidth = (float) (3 * getContainer().getLaneWidth() / 4); 53 } 54 55 @Override 56 void paint(Graphics2D g2d, State state) { 57 if (isVisible(state)) { 58 g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER)); 59 g2d.setColor(Color.ORANGE); 60 g2d.draw(line); 61 } 62 } 63 64 @Override 65 boolean contains(Point2D p, State state) { 66 if (!isVisible(state)) { 67 return false; 68 } 69 70 final Point2D closest = closest(line, p); 71 return p.distance(closest) <= strokeWidth / 2; 72 } 73 74 private boolean isVisible(State state) { 75 if (!(state instanceof State.Connecting)) { 76 return false; 77 } 78 79 final State.Connecting s = (State.Connecting) state; 80 81 if (s.getJunction().equals(end.getJunction()) || equals(s.getBacktrackViaConnector())) { 82 return true; 83 } else if (!s.getViaConnectors().isEmpty() 84 && s.getViaConnectors().get(s.getViaConnectors().size() - 1).getRoadModel().equals(getRoadModel())) { 85 return true; 86 } 87 88 return false; 89 } 90 91 private Road getRoadModel() { 92 return getModel(); 93 } 94 95 public RoadGui getRoad() { 96 return RoadGui.this; 97 } 98 99 @Override 100 Type getType() { 101 return Type.VIA_CONNECTOR; 102 } 103 104 @Override 105 int getZIndex() { 106 return 1; 107 } 108 109 public Road.End getRoadEnd() { 110 return end; 111 } 112 113 public Point2D getCenter() { 114 return relativePoint(line.getP1(), line.getP1().distance(line.getP2()) / 2, angle(line.getP1(), line.getP2())); 115 } 116 } 117 44 118 private final class Extender extends InteractiveElement { 119 private final Road.End end; 45 120 private final Way way; 46 121 47 122 private final Line2D line; 48 123 49 public Extender(Way way, double angle) { 124 public Extender(Road.End end, Way way, double angle) { 125 this.end = end; 50 126 this.way = way; 51 127 this.line = new Line2D.Double(a.getPoint(), relativePoint(a.getPoint(), getContainer().getLaneWidth() * 4, angle)); … … 70 146 @Override 71 147 State click(State old) { 72 getModel().extend(way);148 end.extend(way); 73 149 return new State.Invalid(old); 74 150 } … … 83 159 return 0; 84 160 } 85 86 161 } 87 162 88 163 private final class LaneAdder extends InteractiveElement { 89 164 private final Road.End end; 90 private final boolean left;165 private final Lane.Kind kind; 91 166 92 167 private final Point2D center; 93 168 private final Ellipse2D background; 94 169 95 public LaneAdder(Road.End end, boolean left) {170 public LaneAdder(Road.End end, Lane.Kind kind) { 96 171 this.end = end; 97 this. left = left;172 this.kind = kind; 98 173 99 174 final double a = getAngle(end) + PI; … … 104 179 final double cx; 105 180 final double cy; 106 if ( left) {181 if (kind == Lane.Kind.EXTRA_LEFT) { 107 182 final JunctionGui j = getContainer().getGui(end.getJunction()); 108 183 final Point2D i = intersection(line(j.getPoint(), a), new Line2D.Double(lc, rc)); … … 139 214 140 215 private boolean isVisible(State state) { 141 return end.getJunction(). equals(state.getJunction().getModel());216 return end.getJunction().isPrimary(); 142 217 } 143 218 … … 159 234 @Override 160 235 public State click(State old) { 161 end.add ExtraLane(left);236 end.addLane(kind); 162 237 return new State.Invalid(old); 163 238 } … … 165 240 166 241 final class IncomingConnector extends InteractiveElement { 167 private final boolean forward; 242 private final Road.End end; 243 private final List<LaneGui> lanes; 244 168 245 private final Point2D center = new Point2D.Double(); 169 246 private final Ellipse2D circle = new Ellipse2D.Double(); 170 247 171 private final List<LaneGui> lanes = new ArrayList<LaneGui>(); 172 173 private IncomingConnector(boolean forward) { 174 this.forward = forward; 248 private IncomingConnector(Road.End end) { 249 this.end = end; 250 251 final List<LaneGui> lanes = new ArrayList<LaneGui>(end.getLanes().size()); 252 for (Lane l : end.getOppositeEnd().getLanes()) { 253 lanes.add(new LaneGui(RoadGui.this, l)); 254 } 255 this.lanes = Collections.unmodifiableList(lanes); 175 256 } 176 257 … … 217 298 218 299 private boolean isVisible(State state) { 219 if (!getRoadEnd().getJunction().equals(state.getJunction().getModel())) { 300 if (getModel().isPrimary() || !getRoadEnd().getJunction().isPrimary() 301 || getRoadEnd().getOppositeEnd().getLanes().isEmpty()) { 220 302 return false; 221 303 } 222 304 223 // must be at least one lane in that direction 224 final boolean reverse = getRoadEnd().isToEnd(); 225 for (Lane l : getModel().getLanes()) { 226 if (l.isReverse() == reverse) { 227 return true; 228 } 229 } 230 231 return false; 305 if (state instanceof State.Connecting) { 306 return ((State.Connecting) state).getJunction().equals(getRoadEnd().getJunction()); 307 } 308 309 return true; 232 310 } 233 311 … … 256 334 @Override 257 335 public State activate(State old) { 258 return new State.IncomingActive( old.getJunction(),getRoadEnd());336 return new State.IncomingActive(getRoadEnd()); 259 337 } 260 338 … … 270 348 } 271 349 272 Road.End getRoadEnd() { 273 return forward ? getModel().getFromEnd() : getModel().getToEnd(); 350 public Road.End getRoadEnd() { 351 return end; 352 } 353 354 public List<LaneGui> getLanes() { 355 return lanes; 274 356 } 275 357 … … 400 482 private final double length; 401 483 402 private final IncomingConnector incomingA = new IncomingConnector(true);403 private final IncomingConnector incomingB = new IncomingConnector(false);484 private final IncomingConnector incomingA; 485 private final IncomingConnector incomingB; 404 486 405 487 private final Road road; 406 private final List<LaneGui> lanes = new ArrayList<LaneGui>();407 488 private final List<Segment> segments = new ArrayList<Segment>(); 408 489 409 490 final double connectorRadius; 410 491 411 public RoadGui(GuiContainer container, Road r ) {492 public RoadGui(GuiContainer container, Road road) { 412 493 this.container = container; 413 494 414 this.a = container.getGui(r.getFromEnd().getJunction()); 415 this.b = container.getGui(r.getToEnd().getJunction()); 416 417 this.road = r; 495 this.road = road; 496 497 this.a = container.getGui(road.getFromEnd().getJunction()); 498 this.b = container.getGui(road.getToEnd().getJunction()); 499 500 this.incomingA = new IncomingConnector(road.getFromEnd()); 501 this.incomingB = new IncomingConnector(road.getToEnd()); 418 502 419 503 final List<Point2D> bends = new ArrayList<Point2D>(); 420 final List<Node> nodes = r .getRoute().getNodes();504 final List<Node> nodes = road.getRoute().getNodes(); 421 505 for (int i = nodes.size() - 2; i > 0; --i) { 422 506 bends.add(container.translateAndScale(loc(nodes.get(i)))); … … 425 509 // they add themselves to this.segments 426 510 new Segment(b, bends, a); 427 428 511 double l = 0; 429 512 for (Segment s : segments) { 430 513 l += s.length; 431 514 } 432 433 515 this.length = l; 434 516 435 for (Lane lane : r.getLanes()) { 436 final LaneGui gui = new LaneGui(this, lane); 437 lanes.add(gui); 438 (lane.isReverse() ? incomingB : incomingA).add(gui); 439 } 440 441 this.innerMargin = getLaneCount(true) > 0 && getLaneCount(false) > 0 ? 1 * container.getLaneWidth() / 15 : 0; 517 this.innerMargin = !incomingA.getLanes().isEmpty() && !incomingB.getLanes().isEmpty() ? 1 * container 518 .getLaneWidth() / 15 : 0; 442 519 this.outerMargin = container.getLaneWidth() / 6; 443 520 this.connectorRadius = 3 * container.getLaneWidth() / 8; … … 457 534 } 458 535 459 private int laneMiddle() {460 int i = 0;461 while (lanes.size() > i && lanes.get(i).getModel().isReverse()) {462 ++i;463 }464 return i;465 }466 467 536 public Line2D getLeftCurb(Road.End end) { 468 537 return GuiUtil.line(getCorner(end, true), getAngle(end) + PI); … … 499 568 } 500 569 501 final int lcForward = getLaneCount(false);502 final int lcBackward = getLaneCount(true);570 final int lcForward = incomingA.getLanes().size(); 571 final int lcBackward = incomingB.getLanes().size(); 503 572 504 573 final double LW = getContainer().getLaneWidth(); … … 512 581 } 513 582 514 private int getLaneCount(boolean reverse) {515 int result = 0;516 517 for (LaneGui l : lanes) {518 if (l.getModel().isReverse() == reverse) {519 ++result;520 }521 }522 523 return result;524 }525 526 583 List<InteractiveElement> paint(Graphics2D g2d) { 527 584 final List<InteractiveElement> result = new ArrayList<InteractiveElement>(); 528 585 529 586 result.addAll(paintLanes(g2d)); 530 result.addAll(laneAdders()); 531 result.addAll(extenders()); 587 588 if (getModel().isPrimary()) { 589 result.add(new ViaConnector(getModel().getFromEnd())); 590 result.add(new ViaConnector(getModel().getToEnd())); 591 } else { 592 result.addAll(laneAdders()); 593 result.addAll(extenders(getModel().getFromEnd())); 594 result.addAll(extenders(getModel().getToEnd())); 595 } 532 596 533 597 g2d.setColor(Color.RED); … … 542 606 final List<LaneAdder> result = new ArrayList<LaneAdder>(4); 543 607 544 boolean to = false; 545 boolean from = false; 546 for (Lane l : getModel().getLanes()) { 547 to = to || (!l.isReverse() && !l.isExtra()); 548 from = from || (l.isReverse() && !l.isExtra()); 549 } 550 551 if (to) { 552 result.add(new LaneAdder(getModel().getToEnd(), true)); 553 result.add(new LaneAdder(getModel().getToEnd(), false)); 554 } 555 556 if (from) { 557 result.add(new LaneAdder(getModel().getFromEnd(), true)); 558 result.add(new LaneAdder(getModel().getFromEnd(), false)); 608 if (!incomingA.getLanes().isEmpty()) { 609 result.add(new LaneAdder(getModel().getToEnd(), Lane.Kind.EXTRA_LEFT)); 610 result.add(new LaneAdder(getModel().getToEnd(), Lane.Kind.EXTRA_RIGHT)); 611 } 612 613 if (!incomingB.getLanes().isEmpty()) { 614 result.add(new LaneAdder(getModel().getFromEnd(), Lane.Kind.EXTRA_LEFT)); 615 result.add(new LaneAdder(getModel().getFromEnd(), Lane.Kind.EXTRA_RIGHT)); 559 616 } 560 617 … … 562 619 } 563 620 564 private List<Extender> extenders( ) {565 if (! getModel().isExtendable()) {621 private List<Extender> extenders(Road.End end) { 622 if (!end.isExtendable()) { 566 623 return Collections.emptyList(); 567 624 } … … 569 626 final List<Extender> result = new ArrayList<Extender>(); 570 627 571 final Node n = a.getModel().getNode(); 572 for (OsmPrimitive p : n.getReferrers()) { 573 if (p.getType() != OsmPrimitiveType.WAY) { 574 continue; 575 } 576 577 Way w = (Way) p; 578 if (w.isFirstLastNode(n) && w.getNodesCount() > 1 && Utils.isRoad(w)) { 579 if (getModel().getRoute().getFirstSegment().getWay().equals(w)) { 580 continue; 581 } 582 628 final Node n = end.getJunction().getNode(); 629 for (Way w : OsmPrimitive.getFilteredList(n.getReferrers(), Way.class)) { 630 if (w.getNodesCount() > 1 && !end.getWay().equals(w) && w.isFirstLastNode(n) && Utils.isRoad(w)) { 583 631 final Node nextNode = w.firstNode().equals(n) ? w.getNode(1) : w.getNode(w.getNodesCount() - 2); 584 632 final Point2D nextNodeLoc = getContainer().translateAndScale(loc(nextNode)); 585 result.add(new Extender( w, angle(a.getPoint(), nextNodeLoc)));633 result.add(new Extender(end, w, angle(a.getPoint(), nextNodeLoc))); 586 634 } 587 635 } … … 603 651 g2d.setStroke(regularStroke); 604 652 605 final boolean forward = getLaneCount(false) > 0;606 final boolean backward = getLaneCount(true) > 0;653 final boolean forward = !incomingA.getLanes().isEmpty(); 654 final boolean backward = !incomingB.getLanes().isEmpty(); 607 655 608 656 final Path2D middleArea; … … 630 678 g2d.draw(middleLines); 631 679 680 final List<InteractiveElement> result = new ArrayList<InteractiveElement>(); 681 632 682 moveIncoming(getModel().getFromEnd()); 633 683 moveIncoming(getModel().getToEnd()); 634 635 final int laneMiddle = laneMiddle();636 for (int i = laneMiddle; i < lanes.size(); ++i) {637 moveOutgoing(lanes.get(i), i - laneMiddle);638 }639 for (int i = laneMiddle - 1; i >= 0; --i) {640 moveOutgoing(lanes.get(i), laneMiddle - i - 1);641 }642 643 final List<InteractiveElement> result = new ArrayList<InteractiveElement>();644 645 684 result.add(incomingA); 646 685 result.add(incomingB); 647 for (LaneGui l : lanes) { 648 result.add(l.outgoing); 649 650 if (l.getModel().isExtra()) { 651 result.add(l.lengthSlider); 686 687 for (IncomingConnector c : Arrays.asList(incomingA, incomingB)) { 688 int offset = 0; 689 for (LaneGui l : c.getLanes()) { 690 moveOutgoing(l, offset++); 691 692 result.add(l.outgoing); 693 if (l.getModel().isExtra()) { 694 result.add(l.lengthSlider); 695 } 652 696 } 653 697 } … … 666 710 linePaths.add(innerPath); 667 711 668 for (LaneGui l : lanes(forward)) {712 for (LaneGui l : forward ? incomingA.getLanes() : incomingB.getLanes()) { 669 713 l.setClip(clip); 670 714 innerPath = l.recalculate(innerPath, middleLines); … … 719 763 final double d = rc.getP1().distance(j.getPoint()) + lc.getP1().distance(j.getPoint()); 720 764 721 final Point2D r1 = relativePoint(rc.getP1(), 1, angle(lc.getP1(), rc.getP1())); 722 final Point2D r2 = relativePoint(r1, d, angle(rc) + PI); 723 final Point2D l1 = relativePoint(lc.getP1(), 1, angle(rc.getP1(), lc.getP1())); 724 final Point2D l2 = relativePoint(l1, d, angle(lc) + PI); 765 final double cm = 0.01 / getContainer().getMpp(); // 1 centimeter 766 final double rca = angle(rc) + PI; 767 final double lca = angle(lc) + PI; 768 final Point2D r1 = relativePoint(relativePoint(rc.getP1(), 1, angle(lc.getP1(), rc.getP1())), cm, rca); 769 final Point2D r2 = relativePoint(r1, d, rca); 770 final Point2D l1 = relativePoint(relativePoint(lc.getP1(), 1, angle(rc.getP1(), lc.getP1())), cm, lca); 771 final Point2D l2 = relativePoint(l1, d, lca); 725 772 726 773 negativeClip.moveTo(r1.getX(), r1.getY()); … … 733 780 } 734 781 735 private Iterable<LaneGui> lanes(boolean forward) { 736 final int laneMiddle = laneMiddle(); 737 738 return forward ? lanes.subList(laneMiddle, lanes.size()) : CollectionUtils.reverse(lanes.subList(0, laneMiddle)); 782 public Path getLaneMiddle(boolean forward) { 783 final Path mid = middlePath(!forward); 784 final double w = getWidth(forward ? getModel().getFromEnd() : getModel().getToEnd(), true); 785 final double o = (w - outerMargin) / 2; 786 787 return o > 0 ? mid.offset(-o, -1, -1, -o) : mid; 739 788 } 740 789 … … 778 827 779 828 lane.outgoing.move(loc.getX(), loc.getY()); 829 } 830 831 public JunctionGui getJunction(Road.End end) { 832 if (!getModel().equals(end.getRoad())) { 833 throw new IllegalArgumentException(); 834 } 835 836 return end.isFromEnd() ? getA() : getB(); 780 837 } 781 838 … … 801 858 } 802 859 803 public List<LaneGui> getLanes() {804 return Collections.unmodifiableList(lanes);805 }806 807 860 public double getOffset(double x, double y) { 808 861 return segments.get(0).getOffset(x, y); … … 812 865 return container; 813 866 } 867 868 public List<LaneGui> getLanes() { 869 final List<LaneGui> result = new ArrayList<LaneGui>(); 870 871 result.addAll(incomingB.getLanes()); 872 result.addAll(incomingA.getLanes()); 873 874 return Collections.unmodifiableList(result); 875 } 876 877 public List<LaneGui> getLanes(Road.End end) { 878 return getConnector(end.getOppositeEnd()).getLanes(); 879 } 814 880 } -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/State.java
r25606 r25783 1 1 package org.openstreetmap.josm.plugins.turnlanes.gui; 2 2 3 import java.util.ArrayList; 4 import java.util.Arrays; 5 import java.util.Collections; 6 import java.util.List; 7 8 import org.openstreetmap.josm.plugins.turnlanes.gui.RoadGui.ViaConnector; 9 import org.openstreetmap.josm.plugins.turnlanes.model.Junction; 10 import org.openstreetmap.josm.plugins.turnlanes.model.Lane; 3 11 import org.openstreetmap.josm.plugins.turnlanes.model.Road; 4 12 5 13 interface State { 14 public class AllTurns implements State { 15 private final State wrapped; 16 17 public AllTurns(State wrapped) { 18 this.wrapped = wrapped; 19 } 20 21 public State unwrap() { 22 return wrapped; 23 } 24 } 25 26 public class Connecting implements State { 27 private final Lane lane; 28 private final List<RoadGui.ViaConnector> vias; 29 30 public Connecting(Lane lane) { 31 this(lane, Collections.<RoadGui.ViaConnector> emptyList()); 32 } 33 34 public Connecting(Lane lane, List<ViaConnector> vias) { 35 this.lane = lane; 36 this.vias = vias; 37 } 38 39 public Connecting next(RoadGui.ViaConnector via) { 40 if (vias.isEmpty()) { 41 return new Connecting(lane, Collections.unmodifiableList(Arrays.asList(via))); 42 } 43 44 final List<RoadGui.ViaConnector> tmp = new ArrayList<RoadGui.ViaConnector>(vias.size() + 1); 45 final boolean even = (vias.size() & 1) == 0; 46 final RoadGui.ViaConnector last = vias.get(vias.size() - 1); 47 48 if (last.equals(via) || !even && last.getRoadEnd().getJunction().equals(via.getRoadEnd().getJunction())) { 49 return pop().next(via); 50 } 51 52 if (vias.size() >= 2) { 53 if (lane.getOutgoingJunction().equals(via.getRoadEnd().getJunction())) { 54 return new Connecting(lane); 55 } else if (via.equals(getBacktrackViaConnector())) { 56 return new Connecting(lane, vias.subList(0, vias.size() - 1)); 57 } 58 } 59 60 for (RoadGui.ViaConnector v : vias) { 61 tmp.add(v); 62 63 if (!(even && v.equals(last)) && v.getRoadEnd().getJunction().equals(via.getRoadEnd().getJunction())) { 64 return new Connecting(lane, Collections.unmodifiableList(tmp)); 65 } 66 } 67 68 tmp.add(via); 69 return new Connecting(lane, Collections.unmodifiableList(tmp)); 70 } 71 72 public Junction getJunction() { 73 return vias.isEmpty() ? lane.getOutgoingJunction() : vias.get(vias.size() - 1).getRoadEnd().getJunction(); 74 } 75 76 public RoadGui.ViaConnector getBacktrackViaConnector() { 77 return vias.size() < 2 ? null : vias.get(vias.size() - 2); 78 } 79 80 public List<RoadGui.ViaConnector> getViaConnectors() { 81 return vias; 82 } 83 84 public Lane getLane() { 85 return lane; 86 } 87 88 public Connecting pop() { 89 return new Connecting(lane, vias.subList(0, vias.size() - 1)); 90 } 91 } 92 6 93 public class Invalid implements State { 7 94 private final State wrapped; … … 9 96 public Invalid(State wrapped) { 10 97 this.wrapped = wrapped; 11 }12 13 public JunctionGui getJunction() {14 return wrapped.getJunction();15 98 } 16 99 … … 27 110 } 28 111 29 public JunctionGui getJunction() {30 return wrapped.getJunction();31 }32 33 112 public State unwrap() { 34 113 return wrapped; … … 37 116 38 117 class Default implements State { 39 private final JunctionGui junction; 40 41 public Default(JunctionGui junction) { 42 this.junction = junction; 43 } 44 45 public JunctionGui getJunction() { 46 return junction; 47 } 118 public Default() {} 48 119 } 49 120 50 121 class IncomingActive implements State { 51 private final JunctionGui junction;52 122 private final Road.End roadEnd; 53 123 54 public IncomingActive(JunctionGui junction, Road.End roadEnd) { 55 this.junction = junction; 124 public IncomingActive(Road.End roadEnd) { 56 125 this.roadEnd = roadEnd; 57 126 } … … 60 129 return roadEnd; 61 130 } 62 63 @Override64 public JunctionGui getJunction() {65 return junction;66 }67 131 } 68 132 69 133 class OutgoingActive implements State { 70 private final JunctionGui junction;71 134 private final LaneGui lane; 72 135 73 public OutgoingActive(JunctionGui junction, LaneGui lane) { 74 this.junction = junction; 136 public OutgoingActive(LaneGui lane) { 75 137 this.lane = lane; 76 138 } … … 79 141 return lane; 80 142 } 81 82 @Override83 public JunctionGui getJunction() {84 return junction;85 }86 143 } 87 88 JunctionGui getJunction();89 144 } -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/gui/TurnLanesDialog.java
r25606 r25783 1 1 package org.openstreetmap.josm.plugins.turnlanes.gui; 2 2 3 import static org.openstreetmap.josm.plugins.turnlanes.gui.GuiUtil.loc;4 3 import static org.openstreetmap.josm.tools.I18n.tr; 5 4 … … 9 8 import java.awt.event.ActionEvent; 10 9 import java.util.Collection; 10 import java.util.Collections; 11 import java.util.List; 11 12 12 13 import javax.swing.Action; 14 import javax.swing.ButtonGroup; 13 15 import javax.swing.JLabel; 14 16 import javax.swing.JPanel; 17 import javax.swing.JToggleButton; 15 18 16 import org.openstreetmap.josm.Main;17 19 import org.openstreetmap.josm.actions.JosmAction; 18 20 import org.openstreetmap.josm.data.SelectionChangedListener; 19 import org.openstreetmap.josm.data.coor.EastNorth;20 21 import org.openstreetmap.josm.data.osm.DataSet; 21 22 import org.openstreetmap.josm.data.osm.Node; 22 23 import org.openstreetmap.josm.data.osm.OsmPrimitive; 23 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 24 import org.openstreetmap.josm.gui.SideButton; 24 import org.openstreetmap.josm.data.osm.Way; 25 25 import org.openstreetmap.josm.gui.dialogs.ToggleDialog; 26 import org.openstreetmap.josm.plugins.turnlanes.model.Junction;27 26 import org.openstreetmap.josm.plugins.turnlanes.model.ModelContainer; 28 27 … … 71 70 @Override 72 71 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 73 if (newSelection.size() != 1) { 72 final Collection<OsmPrimitive> s = Collections.unmodifiableCollection(newSelection); 73 final List<Node> nodes = OsmPrimitive.getFilteredList(s, Node.class); 74 final List<Way> ways = OsmPrimitive.getFilteredList(s, Way.class); 75 76 if (nodes.isEmpty()) { 77 setJunction(null); 74 78 return; 75 79 } 76 80 77 final OsmPrimitive p = newSelection.iterator().next(); 78 79 if (p.getType() == OsmPrimitiveType.NODE) { 80 final Node n = (Node) p; 81 82 final ModelContainer mc; 83 final Junction j; 84 85 try { 86 mc = ModelContainer.create(n); 87 j = mc.getJunction(n); 88 } catch (RuntimeException e) { 89 displayError(e); 90 91 return; 92 } 93 94 final EastNorth en = Main.proj.latlon2eastNorth(n.getCoor()); 95 final EastNorth rel = new EastNorth(en.getX() + 1, en.getY()); 96 97 // meters per source unit 98 final double mpsu = Main.proj.eastNorth2latlon(rel).greatCircleDistance(n.getCoor()); 99 final GuiContainer model = new GuiContainer(loc(n), mpsu); 100 101 setJunction(model.getGui(j)); 81 try { 82 setJunction(ModelContainer.create(nodes, ways)); 83 } catch (RuntimeException e) { 84 displayError(e); 85 return; 102 86 } 103 87 } 104 105 88 }); 106 89 107 90 final JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 4, 4)); 108 buttonPanel.add(new SideButton(editAction)); 109 buttonPanel.add(new SideButton(validateAction)); 91 final ButtonGroup group = new ButtonGroup(); 92 final JToggleButton editButton = new JToggleButton(editAction); 93 final JToggleButton validateButton = new JToggleButton(validateAction); 94 group.add(editButton); 95 group.add(validateButton); 96 buttonPanel.add(editButton); 97 buttonPanel.add(validateButton); 110 98 111 99 body.setLayout(new CardLayout(4, 4)); … … 117 105 body.add(new ValidationPanel(), CARD_VALIDATE); 118 106 body.add(error, CARD_ERROR); 119 editAction.actionPerformed(null); 107 108 editButton.doClick(); 120 109 } 121 110 122 111 void displayError(RuntimeException e) { 123 112 if (editing) { 124 //e.printStackTrace();113 e.printStackTrace(); 125 114 126 115 error.setText("<html>An error occured while constructing the model." … … 133 122 } 134 123 135 void setJunction( JunctionGui j) {136 if ( j!= null && editing) {137 junctionPane.setJunction( j);124 void setJunction(ModelContainer mc) { 125 if (mc != null && editing) { 126 junctionPane.setJunction(new GuiContainer(mc)); 138 127 final CardLayout cl = (CardLayout) body.getLayout(); 139 128 cl.show(body, CARD_EDIT); -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Constants.java
r25606 r25783 5 5 public interface Constants { 6 6 String SEPARATOR = ";"; 7 String SPLIT_REGEX = " [,:;]";7 String SPLIT_REGEX = "\\p{Zs}*[,:;]\\p{Zs}*"; 8 8 Pattern SPLIT_PATTERN = Pattern.compile(SPLIT_REGEX); 9 9 -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Junction.java
r25606 r25783 2 2 3 3 import java.util.ArrayList; 4 import java.util.HashSet; 4 5 import java.util.List; 5 6 import java.util.Set; 6 7 7 8 import org.openstreetmap.josm.data.osm.Node; 8 import org.openstreetmap.josm.data.osm.OsmPrimitive;9 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;10 import org.openstreetmap.josm.data.osm.Relation;11 import org.openstreetmap.josm.data.osm.RelationMember;12 9 import org.openstreetmap.josm.data.osm.Way; 13 10 14 11 public class Junction { 15 private static final List<Way> filterHighways(List<OsmPrimitive> of) {16 final List<Way> result = new ArrayList<Way>();17 18 for (OsmPrimitive p : of) {19 if (p.getType() == OsmPrimitiveType.WAY && Utils.isRoad((Way) p)) {20 result.add((Way) p);21 }22 }23 24 return result;25 }26 27 private static List<Way> filterBeginsOrEndsAt(List<Way> ways, Node node) {28 final List<Way> result = new ArrayList<Way>();29 30 for (Way w : ways) {31 if (w.isFirstLastNode(node)) {32 result.add(w);33 }34 }35 36 return result;37 }38 39 private static List<Road> loadRoads(ModelContainer container, Junction j) {40 final List<Way> ways = filterBeginsOrEndsAt(filterHighways(j.getNode().getReferrers()), j.getNode());41 42 return Road.map(container, ways, j);43 }44 45 12 private final ModelContainer container; 46 13 47 14 private final Node node; 48 private final List<Road> roads = new ArrayList<Road>();15 private final Set<Way> roads = new HashSet<Way>(); 49 16 50 17 Junction(ModelContainer container, Node n) { … … 55 22 56 23 if (isPrimary()) { 57 loadRoads(container, this);58 24 // if turn data is invalid, this will force an exception now, not later during painting 59 getTurns();25 // getTurns(); TODO force this again 60 26 } 61 27 } 62 28 63 boolean isPrimary() {64 return container.getPrimary().equals(this);29 public boolean isPrimary() { 30 return getContainer().isPrimary(this); 65 31 } 66 32 … … 70 36 71 37 public List<Road> getRoads() { 72 return roads; 38 final List<Road> result = new ArrayList<Road>(roads.size()); 39 40 for (Way w : roads) { 41 result.add(container.getRoad(w)); 42 } 43 44 return result; 73 45 } 74 46 75 void addRoad(Road r) { 76 roads.add(r); 47 public List<Road.End> getRoadEnds() { 48 final List<Road.End> result = new ArrayList<Road.End>(roads.size()); 49 50 for (Way w : roads) { 51 result.add(getRoadEnd(w)); 52 } 53 54 return result; 77 55 } 78 56 79 public void addTurn(Lane from, Road.End to) { 80 assert equals(from.getOutgoingJunction()); 81 assert equals(to.getJunction()); 57 void addRoad(Way w) { 58 roads.add(w); 59 } 60 61 Road.End getRoadEnd(Way w) { 62 final Road r = getContainer().getRoad(w); 82 63 83 final Way fromWay = from.isReverse() ? from.getRoad().getRoute().getFirstSegment().getWay() : from.getRoad() 84 .getRoute().getLastSegment().getWay(); 85 final Way toWay = to.isFromEnd() ? to.getRoad().getRoute().getFirstSegment().getWay() : to.getRoad().getRoute() 86 .getLastSegment().getWay(); 87 88 Relation existing = null; 89 for (Turn t : getTurns()) { 90 if ((from.isReverse() ? from.getRoad().getRoute().getFirstSegment() : from.getRoad().getRoute().getLastSegment()) 91 .getWay().equals(t.getFromWay()) && t.getTo().equals(to)) { 92 if (t.getFrom().isExtra() == from.isExtra() && t.getFrom().getIndex() == from.getIndex()) { 93 // was already added 94 return; 95 } 96 97 existing = t.getRelation(); 64 if (r.getRoute().getSegments().size() == 1) { 65 final boolean starts = r.getRoute().getStart().equals(node); 66 final boolean ends = r.getRoute().getEnd().equals(node); 67 68 if (starts && ends) { 69 throw new IllegalArgumentException("Ambiguous: The way starts and ends at the junction node."); 70 } else if (starts) { 71 return r.getFromEnd(); 72 } else if (ends) { 73 return r.getToEnd(); 98 74 } 75 } else if (r.getRoute().getFirstSegment().getWay().equals(w)) { 76 return r.getFromEnd(); 77 } else if (r.getRoute().getLastSegment().getWay().equals(w)) { 78 return r.getToEnd(); 99 79 } 100 80 101 final Relation r = existing == null ? new Relation() : existing; 102 103 final String key = from.isExtra() ? Constants.TURN_KEY_EXTRA_LANES : Constants.TURN_KEY_LANES; 104 final List<Integer> lanes = Turn.split(r.get(key)); 105 lanes.add(from.getIndex()); 106 r.put(key, Turn.join(lanes)); 107 108 if (existing == null) { 109 r.put("type", Constants.TYPE_TURNS); 110 111 r.addMember(new RelationMember(Constants.TURN_ROLE_VIA, node)); 112 r.addMember(new RelationMember(Constants.TURN_ROLE_FROM, fromWay)); 113 r.addMember(new RelationMember(Constants.TURN_ROLE_TO, toWay)); 114 115 node.getDataSet().addPrimitive(r); 116 } 81 throw new IllegalArgumentException("While there exists a road for the given way, the way neither " 82 + "starts nor ends at the junction node."); 117 83 } 118 84 119 public Set<Turn> getTurns() { 120 return Turn.load(this); 121 } 122 123 Road.End getRoadEnd(Way way) { 124 final List<Road.End> candidates = new ArrayList<Road.End>(); 125 126 for (Road r : getRoads()) { 127 if (r.getFromEnd().getJunction().equals(this)) { 128 if (r.getRoute().getSegments().get(0).getWay().equals(way)) { 129 candidates.add(r.getFromEnd()); 130 } 131 } 132 133 if (r.getToEnd().getJunction().equals(this)) { 134 if (r.getRoute().getSegments().get(r.getRoute().getSegments().size() - 1).getWay().equals(way)) { 135 candidates.add(r.getToEnd()); 136 } 137 } 138 } 139 140 if (candidates.isEmpty()) { 141 throw new IllegalArgumentException("No such road end."); 142 } else if (candidates.size() > 1) { 143 throw new IllegalArgumentException("There are " + candidates.size() 144 + " road ends at this junction for the given way."); 145 } 146 147 return candidates.get(0); 148 } 149 150 public void removeTurn(Turn turn) { 151 turn.remove(); 85 public ModelContainer getContainer() { 86 return container; 152 87 } 153 88 } -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Lane.java
r25606 r25783 2 2 3 3 import java.util.ArrayList; 4 import java.util.Collections;5 4 import java.util.List; 5 import java.util.Set; 6 6 7 7 import org.openstreetmap.josm.data.osm.Node; 8 8 import org.openstreetmap.josm.data.osm.Relation; 9 import org.openstreetmap.josm.data.osm.RelationMember; 9 10 import org.openstreetmap.josm.data.osm.Way; 10 11 import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils; … … 15 16 EXTRA_RIGHT, 16 17 REGULAR; 17 } 18 19 /** 20 * 21 * @param road 22 * @param r 23 * lengths relation 24 * @param w 25 * @return 26 */ 27 public static List<Lane> load(Road road, Relation left, Relation right, Way w) { 28 double extraLengthLeft = left == null ? 0 : Route.load(left).getLengthFrom(w); 29 double extraLengthRight = right == null ? 0 : Route.load(right).getLengthFrom(w); 30 31 final List<Double> leftLengths = loadLengths(left, Constants.LENGTHS_KEY_LENGTHS_LEFT, extraLengthLeft); 32 final List<Double> rightLengths = loadLengths(right, Constants.LENGTHS_KEY_LENGTHS_RIGHT, extraLengthRight); 33 34 final List<Lane> result = new ArrayList<Lane>(load(road)); 35 final int mid = getReverseCount(result); 36 result.addAll(mid, map(leftLengths, road, true, extraLengthLeft)); 37 result.addAll(map(rightLengths, road, false, extraLengthRight)); 38 39 return result; 40 } 41 42 private static List<Lane> map(List<Double> lengths, Road road, boolean left, double extraLength) { 43 final List<Lane> result = new ArrayList<Lane>(lengths.size()); 44 45 int index = left ? -lengths.size() : 1; 46 for (Double l : (left ? CollectionUtils.reverse(lengths) : lengths)) { 47 // TODO road may connect twice with junction => reverse might not always be false 48 result.add(new Lane(road, index++, false, left, extraLength, l - extraLength)); 49 } 50 51 return result; 52 } 53 54 private static int getReverseCount(List<Lane> ls) { 55 int result = 0; 56 57 for (Lane l : ls) { 58 if (l.isReverse()) { 59 ++result; 60 } 18 19 public boolean isExtra() { 20 return this == EXTRA_LEFT || this == EXTRA_RIGHT; 21 } 22 } 23 24 static List<Lane> load(Road.End roadEnd) { 25 final List<Lane> result = new ArrayList<Lane>(); 26 int i; 27 28 i = 0; 29 for (double l : CollectionUtils.reverse(roadEnd.getLengths(Kind.EXTRA_LEFT))) { 30 result.add(new Lane(roadEnd, --i, Kind.EXTRA_LEFT, l)); 31 } 32 33 final int regulars = getRegularCount(roadEnd.getWay(), roadEnd.getJunction().getNode()); 34 for (i = 1; i <= regulars; ++i) { 35 result.add(new Lane(roadEnd, i)); 36 } 37 38 i = 0; 39 for (double l : roadEnd.getLengths(Kind.EXTRA_RIGHT)) { 40 result.add(new Lane(roadEnd, ++i, Kind.EXTRA_RIGHT, l)); 61 41 } 62 42 … … 112 92 } 113 93 114 public static List<Lane> load(Road r) { 115 final Route.Segment first = r.getRoute().getFirstSegment(); 116 final Route.Segment last = r.getRoute().getLastSegment(); 117 118 final int back = getRegularCount(first.getWay(), first.getStart()); 119 final int forth = getRegularCount(last.getWay(), last.getEnd()); 120 121 final List<Lane> result = new ArrayList<Lane>(back + forth); 122 123 for (int i = back; i >= 1; --i) { 124 result.add(new Lane(r, i, true)); 125 } 126 for (int i = 1; i <= forth; ++i) { 127 result.add(new Lane(r, i, false)); 128 } 129 130 return Collections.unmodifiableList(result); 131 } 132 133 private final Road road; 94 private final Road.End roadEnd; 134 95 private final int index; 135 96 private final Kind kind; 136 private final boolean reverse;137 138 /**139 * If this extra lane extends past the given road, this value equals the length of the way(s)140 * which come after the end of the road.141 */142 private final double extraLength;143 97 144 98 private double length = -1; 145 99 146 public Lane(Road road, int index, boolean reverse) {147 this.road = road;100 public Lane(Road.End roadEnd, int index) { 101 this.roadEnd = roadEnd; 148 102 this.index = index; 149 103 this.kind = Kind.REGULAR; 150 this.reverse = reverse;151 this.extraLength = -1;152 }153 154 public Lane(Road road, int index, boolean reverse, boolean left, double extraLength, double length) {155 this.road = road;104 } 105 106 public Lane(Road.End roadEnd, int index, Kind kind, double length) { 107 assert kind == Kind.EXTRA_LEFT || kind == Kind.EXTRA_RIGHT; 108 109 this.roadEnd = roadEnd; 156 110 this.index = index; 157 this.kind = left ? Kind.EXTRA_LEFT : Kind.EXTRA_RIGHT; 158 this.reverse = reverse; 159 this.extraLength = extraLength; 111 this.kind = kind; 160 112 this.length = length; 161 113 … … 163 115 throw new IllegalArgumentException("Length must be positive"); 164 116 } 165 166 if (extraLength < 0) {167 throw new IllegalArgumentException("Extra length must not be negative");168 }169 117 } 170 118 171 119 public Road getRoad() { 172 return road; 173 } 174 175 public double getExtraLength() { 176 return extraLength; 120 return roadEnd.getRoad(); 177 121 } 178 122 … … 182 126 183 127 public double getLength() { 184 return isExtra() ? length : road.getLength(); 185 } 186 187 double getTotalLength() { 188 if (!isExtra()) { 189 throw new UnsupportedOperationException(); 190 } 191 192 return length + extraLength; 128 return isExtra() ? length : getRoad().getLength(); 193 129 } 194 130 … … 201 137 202 138 // TODO if needed, increase length of other lanes 203 road.updateLengths();139 getOutgoingRoadEnd().updateLengths(); 204 140 205 141 this.length = length; … … 214 150 } 215 151 216 public boolean isReverse() {217 return reverse;218 }219 220 152 public Junction getOutgoingJunction() { 221 return isReverse() ? getRoad().getFromEnd().getJunction() : getRoad().getToEnd().getJunction();153 return getOutgoingRoadEnd().getJunction(); 222 154 } 223 155 224 156 public Junction getIncomingJunction() { 225 return isReverse() ? getRoad().getToEnd().getJunction() : getRoad().getFromEnd().getJunction();157 return getIncomingRoadEnd().getJunction(); 226 158 } 227 159 228 160 public Road.End getOutgoingRoadEnd() { 229 return isReverse() ? getRoad().getFromEnd() : getRoad().getToEnd();161 return roadEnd; 230 162 } 231 163 232 164 public Road.End getIncomingRoadEnd() { 233 return isReverse() ? getRoad().getToEnd() : getRoad().getFromEnd(); 165 return roadEnd.getOppositeEnd(); 166 } 167 168 public ModelContainer getContainer() { 169 return getRoad().getContainer(); 170 } 171 172 public void addTurn(List<Road> via, Road.End to) { 173 assert equals(to.getJunction()); 174 175 Relation existing = null; 176 for (Turn t : to.getTurns()) { 177 if (t.getFrom().getOutgoingRoadEnd().equals(getOutgoingRoadEnd()) && t.getVia().equals(via)) { 178 if (t.getFrom().equals(this)) { 179 // was already added 180 return; 181 } 182 183 existing = t.getRelation(); 184 } 185 } 186 187 final Relation r; 188 if (existing == null) { 189 r = new Relation(); 190 r.put("type", Constants.TYPE_TURNS); 191 192 r.addMember(new RelationMember(Constants.TURN_ROLE_FROM, getOutgoingRoadEnd().getWay())); 193 if (via.isEmpty()) { 194 r.addMember(new RelationMember(Constants.TURN_ROLE_VIA, getOutgoingJunction().getNode())); 195 } else { 196 for (Way w : Utils.flattenVia(getOutgoingJunction().getNode(), via, to.getJunction().getNode())) { 197 r.addMember(new RelationMember(Constants.TURN_ROLE_VIA, w)); 198 } 199 } 200 r.addMember(new RelationMember(Constants.TURN_ROLE_TO, to.getWay())); 201 202 getOutgoingJunction().getNode().getDataSet().addPrimitive(r); 203 } else { 204 r = existing; 205 } 206 207 final String key = isExtra() ? Constants.TURN_KEY_EXTRA_LANES : Constants.TURN_KEY_LANES; 208 final List<Integer> lanes = Turn.indices(r, key); 209 lanes.add(getIndex()); 210 r.put(key, Turn.join(lanes)); 211 } 212 213 public Set<Turn> getTurns() { 214 return Turn.load(getContainer(), Constants.TURN_ROLE_FROM, getOutgoingRoadEnd().getWay()); 234 215 } 235 216 } -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/ModelContainer.java
r25606 r25783 1 1 package org.openstreetmap.josm.plugins.turnlanes.model; 2 2 3 import java.util.ArrayList; 4 import java.util.Collections; 3 5 import java.util.HashMap; 6 import java.util.HashSet; 7 import java.util.List; 4 8 import java.util.Map; 9 import java.util.Set; 5 10 6 11 import org.openstreetmap.josm.data.osm.Node; 12 import org.openstreetmap.josm.data.osm.OsmPrimitive; 13 import org.openstreetmap.josm.data.osm.Relation; 14 import org.openstreetmap.josm.data.osm.RelationMember; 7 15 import org.openstreetmap.josm.data.osm.Way; 16 import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils; 17 import org.openstreetmap.josm.tools.Pair; 8 18 9 19 public class ModelContainer { 10 public static ModelContainer create(Node n) { 11 final ModelContainer container = new ModelContainer(n); 12 container.getOrCreateJunction(n); 13 return container; 20 public static ModelContainer create(Iterable<Node> primaryNodes, Iterable<Way> primaryWays) { 21 final Set<Node> closedNodes = new HashSet<Node>(CollectionUtils.toList(primaryNodes)); 22 final Set<Way> closedWays = new HashSet<Way>(CollectionUtils.toList(primaryWays)); 23 24 close(closedNodes, closedWays); 25 26 return new ModelContainer(closedNodes, closedWays); 27 } 28 29 private static void close(Set<Node> closedNodes, Set<Way> closedWays) { 30 boolean closed = false; 31 32 while (!closed) { 33 closed = true; 34 35 for (Node n : closedNodes) { 36 for (Way w : Utils.filterRoads(n.getReferrers())) { 37 if (w.isFirstLastNode(n)) { 38 closed &= close(closedNodes, closedWays, w, Constants.TURN_ROLE_FROM); 39 closed &= close(closedNodes, closedWays, w, Constants.TURN_ROLE_TO); 40 } 41 } 42 43 for (Way w : closedWays) { 44 closed &= close(closedNodes, closedWays, w, Constants.TURN_ROLE_VIA); 45 } 46 } 47 } 48 } 49 50 private static boolean close(Set<Node> closedNodes, Set<Way> closedWays, Way w, String role) { 51 boolean closed = true; 52 53 for (Relation r : OsmPrimitive.getFilteredList(w.getReferrers(), Relation.class)) { 54 if (!r.get("type").equals(Constants.TYPE_TURNS)) { 55 continue; 56 } 57 58 for (RelationMember m : r.getMembers()) { 59 if (m.getRole().equals(role) && m.getMember().equals(w)) { 60 closed &= close(closedNodes, closedWays, r); 61 } 62 } 63 } 64 65 return closed; 66 } 67 68 private static boolean close(Set<Node> closedNodes, Set<Way> closedWays, Relation r) { 69 boolean closed = true; 70 71 final List<Way> via = new ArrayList<Way>(); 72 for (RelationMember m : Utils.getMembers(r, Constants.TURN_ROLE_VIA)) { 73 if (m.isWay()) { 74 closed &= !closedWays.add(m.getWay()); 75 via.add(m.getWay()); 76 } else if (m.isNode()) { 77 closed &= !closedNodes.add(m.getNode()); 78 } 79 } 80 81 if (!via.isEmpty()) { 82 final Way from = Utils.getMemberWay(r, Constants.TURN_ROLE_FROM); 83 final Way to = Utils.getMemberWay(r, Constants.TURN_ROLE_TO); 84 85 closed &= !closedNodes.add(Utils.lineUp(from, via.get(0))); 86 closed &= !closedNodes.add(Utils.lineUp(via.get(via.size() - 1), to)); 87 } 88 89 return closed; 14 90 } 15 91 … … 17 93 private final Map<Way, Road> roads = new HashMap<Way, Road>(); 18 94 19 private final Node primary; 20 21 private ModelContainer(Node primary) { 22 this.primary = primary; 95 private final Set<Node> primaryNodes; 96 private final Set<Way> primaryWays; 97 98 private ModelContainer(Set<Node> primaryNodes, Set<Way> primaryWays) { 99 this.primaryNodes = Collections.unmodifiableSet(new HashSet<Node>(primaryNodes)); 100 this.primaryWays = Collections.unmodifiableSet(new HashSet<Way>(primaryWays)); 101 102 final Set<Pair<Way, Junction>> ws = new HashSet<Pair<Way, Junction>>(); 103 for (Node n : primaryNodes) { 104 final Junction j = getOrCreateJunction(n); 105 106 for (Way w : Utils.filterRoads(n.getReferrers())) { 107 if (w.isFirstLastNode(n)) { 108 ws.add(new Pair<Way, Junction>(w, j)); 109 } 110 } 111 } 112 113 final List<Route> rs = Utils.orderWays(primaryWays, primaryNodes); 114 for (Route r : rs) { 115 addRoad(new Road(this, r)); 116 } 117 118 for (Pair<Way, Junction> w : ws) { 119 if (!primaryWays.contains(w.a)) { 120 addRoad(new Road(this, w.a, w.b)); 121 } 122 } 23 123 } 24 124 … … 43 143 } 44 144 45 Road getRoad(Way w, Junction j) { 46 final Road existing = roads.get(w); 47 48 if (existing != null && j.equals(existing.getToEnd().getJunction())) { 49 return existing; 50 } 51 52 final Road newRoad = new Road(this, w, j); 145 Road getRoad(Way w) { 146 final Road r = roads.get(w); 147 148 if (r == null) { 149 throw new IllegalArgumentException("There is no road containing the given way."); 150 } 151 152 return r; 153 } 154 155 private void addRoad(Road newRoad, Road mergedA, Road mergedB) { 156 assert (mergedA == null) == (mergedB == null); 53 157 54 158 for (Route.Segment s : newRoad.getRoute().getSegments()) { … … 56 160 57 161 if (oldRoad != null) { 58 return mergeRoads(oldRoad, newRoad); 59 } 60 } 61 62 return newRoad; 162 if (mergedA == null) { 163 final Road mergedRoad = mergeRoads(oldRoad, newRoad); 164 addRoad(mergedRoad, oldRoad, newRoad); 165 } else if (!oldRoad.equals(mergedA) && !oldRoad.equals(mergedB)) { 166 throw new RuntimeException("A road can't be connected to more than two junctions."); 167 } 168 } 169 } 170 } 171 172 private void addRoad(Road newRoad) { 173 addRoad(newRoad, null, null); 63 174 } 64 175 65 176 private Road mergeRoads(Road a, Road b) { 66 throw null; // TODO implement 177 final String ERR_ILLEGAL_ARGS = "The given roads can not be merged into one."; 178 179 final List<Way> ws = new ArrayList<Way>(CollectionUtils.toList(CollectionUtils.reverse(a.getRoute().getWays()))); 180 final List<Way> bws = b.getRoute().getWays(); 181 182 int i = -1; 183 for (Way w : ws) { 184 if (w.equals(bws.get(i + 1))) { 185 ++i; 186 } else if (i >= 0) { 187 throw new IllegalArgumentException(ERR_ILLEGAL_ARGS); 188 } 189 } 190 191 if (i < 0) { 192 throw new IllegalArgumentException(ERR_ILLEGAL_ARGS); 193 } 194 ws.addAll(bws.subList(i + 1, bws.size())); 195 196 final Route mergedRoute = Route.create(ws, a.getRoute().getLastSegment().getEnd()); 197 return new Road(this, mergedRoute); 67 198 } 68 199 … … 73 204 } 74 205 75 Junction getPrimary() { 76 return junctions.get(primary); 206 public Set<Junction> getPrimaryJunctions() { 207 final Set<Junction> pjs = new HashSet<Junction>(); 208 209 for (Node n : primaryNodes) { 210 pjs.add(getOrCreateJunction(n)); 211 } 212 213 return pjs; 214 } 215 216 public Set<Road> getPrimaryRoads() { 217 final Set<Road> prs = new HashSet<Road>(); 218 219 for (Way w : primaryWays) { 220 prs.add(roads.get(w)); 221 } 222 223 return prs; 224 } 225 226 public ModelContainer recalculate() { 227 return new ModelContainer(primaryNodes, primaryWays); 228 } 229 230 public boolean isPrimary(Junction j) { 231 return primaryNodes.contains(j.getNode()); 232 } 233 234 public boolean isPrimary(Road r) { 235 return primaryWays.contains(r.getRoute().getFirstSegment().getWay()); 77 236 } 78 237 } -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Road.java
r25606 r25783 7 7 import java.util.Arrays; 8 8 import java.util.List; 9 import java.util.Set; 9 10 10 11 import org.openstreetmap.josm.data.osm.Node; … … 14 15 import org.openstreetmap.josm.data.osm.RelationMember; 15 16 import org.openstreetmap.josm.data.osm.Way; 17 import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils; 16 18 import org.openstreetmap.josm.plugins.turnlanes.model.Route.Segment; 17 19 import org.openstreetmap.josm.tools.Pair; … … 19 21 public class Road { 20 22 public class End { 23 private final boolean from; 21 24 private final Junction junction; 22 25 23 private End(Junction junction) { 26 private final Relation lengthsLeft; 27 private final Relation lengthsRight; 28 29 private final double extraLengthLeft; 30 private final double extraLengthRight; 31 32 private final List<Lane> lanes; 33 34 private End(boolean from, Junction junction, Relation lengthsLeft, Relation lengthsRight) { 35 this.from = from; 24 36 this.junction = junction; 37 this.lengthsLeft = lengthsLeft; 38 this.lengthsRight = lengthsRight; 39 this.extraLengthLeft = lengthsLeft == null ? 0 : Route.load(lengthsLeft).getLengthFrom(getWay()); 40 this.extraLengthRight = lengthsRight == null ? 0 : Route.load(lengthsRight).getLengthFrom(getWay()); 41 this.lanes = Lane.load(this); 42 43 junction.addRoad(getWay()); 44 } 45 46 private End(boolean from, Junction junction) { 47 this.from = from; 48 this.junction = junction; 49 this.lengthsLeft = null; 50 this.lengthsRight = null; 51 this.extraLengthLeft = 0; 52 this.extraLengthRight = 0; 53 this.lanes = Lane.load(this); 54 55 junction.addRoad(getWay()); 25 56 } 26 57 … … 29 60 } 30 61 62 public Way getWay() { 63 return isFromEnd() ? getRoute().getFirstSegment().getWay() : getRoute().getLastSegment().getWay(); 64 } 65 31 66 public Junction getJunction() { 32 67 return junction; 33 68 } 34 69 35 private boolean isFrom() {36 if (this == fromEnd) {37 return true;38 } else if (this == toEnd) {39 return false;40 }41 42 throw new IllegalStateException();43 }44 45 70 public boolean isFromEnd() { 46 return isFrom();71 return from; 47 72 } 48 73 49 74 public boolean isToEnd() { 50 return !isFrom(); 51 } 52 53 public void addExtraLane(boolean left) { 54 if (!isToEnd()) { 55 throw new UnsupportedOperationException(); 75 return !isFromEnd(); 76 } 77 78 public End getOppositeEnd() { 79 return isFromEnd() ? toEnd : fromEnd; 80 } 81 82 /** 83 * @return the turns <em>onto</em> this road at this end 84 */ 85 public Set<Turn> getTurns() { 86 return Turn.load(getContainer(), Constants.TURN_ROLE_TO, getWay()); 87 } 88 89 public void addLane(Lane.Kind kind) { 90 if (kind == Lane.Kind.REGULAR) { 91 throw new IllegalArgumentException("Only extra lanes can be added."); 56 92 } 57 93 58 94 double length = Double.POSITIVE_INFINITY; 59 for (Lane l : getLanes()) { 60 if (l.getKind() == (left ? Lane.Kind.EXTRA_LEFT : Lane.Kind.EXTRA_RIGHT)) { 61 { 62 length = Math.min(length, l.getLength()); 63 } 95 for (Lane l : lanes) { 96 if (l.getKind() == kind) { 97 length = Math.max(0, Math.min(length, l.getLength() - 1)); 64 98 } 65 99 } … … 69 103 } 70 104 71 addLength(left, length); 72 } 73 } 74 75 public static List<Road> map(ModelContainer container, List<Way> ws, Junction j) { 76 final List<Road> result = new ArrayList<Road>(); 77 78 for (Way w : ws) { 79 result.add(container.getRoad(w, j)); 80 } 81 82 return result; 105 addLane(kind, length); 106 } 107 108 private void addLane(Lane.Kind kind, double length) { 109 assert kind == Lane.Kind.EXTRA_LEFT || kind == Lane.Kind.EXTRA_RIGHT; 110 111 final boolean left = kind == Lane.Kind.EXTRA_LEFT; 112 final Relation rel = left ? lengthsLeft : lengthsRight; 113 final Relation other = left ? lengthsRight : lengthsLeft; 114 final Node n = getJunction().getNode(); 115 116 final String lengthStr = toLengthString(length); 117 final Relation target; 118 if (rel == null) { 119 if (other == null || !Utils.getMemberNode(other, "end").equals(n)) { 120 target = createLengthsRelation(); 121 } else { 122 target = other; 123 } 124 } else { 125 target = rel; 126 } 127 128 final String key = left ? Constants.LENGTHS_KEY_LENGTHS_LEFT : Constants.LENGTHS_KEY_LENGTHS_RIGHT; 129 final String old = target.get(key); 130 if (old == null) { 131 target.put(key, lengthStr); 132 } else { 133 target.put(key, old + Constants.SEPARATOR + lengthStr); 134 } 135 } 136 137 private Relation createLengthsRelation() { 138 final Node n = getJunction().getNode(); 139 140 final Relation r = new Relation(); 141 r.put("type", Constants.TYPE_LENGTHS); 142 143 r.addMember(new RelationMember(Constants.LENGTHS_ROLE_END, n)); 144 for (Route.Segment s : isFromEnd() ? route.getSegments() : CollectionUtils.reverse(route.getSegments())) { 145 r.addMember(new RelationMember(Constants.LENGTHS_ROLE_WAYS, s.getWay())); 146 } 147 148 n.getDataSet().addPrimitive(r); 149 150 return r; 151 } 152 153 void updateLengths() { 154 for (final boolean left : Arrays.asList(true, false)) { 155 final Lane.Kind kind = left ? Lane.Kind.EXTRA_LEFT : Lane.Kind.EXTRA_RIGHT; 156 final Relation r = left ? lengthsLeft : lengthsRight; 157 final double extra = left ? extraLengthLeft : extraLengthRight; 158 159 if (r == null) { 160 continue; 161 } 162 163 final StringBuilder lengths = new StringBuilder(32); 164 for (Lane l : left ? CollectionUtils.reverse(lanes) : lanes) { 165 if (l.getKind() == kind) { 166 lengths.append(toLengthString(extra + l.getLength())).append(Constants.SEPARATOR); 167 } 168 } 169 170 lengths.setLength(lengths.length() - Constants.SEPARATOR.length()); 171 r.put(left ? Constants.LENGTHS_KEY_LENGTHS_LEFT : Constants.LENGTHS_KEY_LENGTHS_RIGHT, lengths.toString()); 172 } 173 } 174 175 public List<Lane> getLanes() { 176 return lanes; 177 } 178 179 public Lane getLane(Lane.Kind kind, int index) { 180 for (Lane l : lanes) { 181 if (l.getKind() == kind && l.getIndex() == index) { 182 return l; 183 } 184 } 185 186 throw new IllegalArgumentException("No such lane."); 187 } 188 189 public Lane getExtraLane(int index) { 190 return index < 0 ? getLane(Lane.Kind.EXTRA_LEFT, index) : getLane(Lane.Kind.EXTRA_RIGHT, index); 191 } 192 193 public boolean isExtendable() { 194 final End o = getOppositeEnd(); 195 return (lengthsLeft == null && lengthsRight == null) && (o.lengthsLeft != null || o.lengthsRight != null); 196 } 197 198 public void extend(Way way) { 199 if (!isExtendable()) { 200 throw new IllegalStateException(); 201 } 202 203 final End o = getOppositeEnd(); 204 if (o.lengthsLeft != null) { 205 o.lengthsLeft.addMember(new RelationMember(Constants.LENGTHS_ROLE_WAYS, way)); 206 } 207 if (o.lengthsRight != null) { 208 o.lengthsRight.addMember(new RelationMember(Constants.LENGTHS_ROLE_WAYS, way)); 209 } 210 } 211 212 public List<Double> getLengths(Lane.Kind kind) { 213 switch (kind) { 214 case EXTRA_LEFT: 215 return Lane.loadLengths(lengthsLeft, Constants.LENGTHS_KEY_LENGTHS_LEFT, extraLengthLeft); 216 case EXTRA_RIGHT: 217 return Lane.loadLengths(lengthsRight, Constants.LENGTHS_KEY_LENGTHS_RIGHT, extraLengthRight); 218 default: 219 throw new IllegalArgumentException(String.valueOf(kind)); 220 } 221 } 83 222 } 84 223 … … 142 281 143 282 private final ModelContainer container; 144 145 private final Relation lengthsLeft;146 private final Relation lengthsRight;147 283 private final Route route; 148 private final List<Lane> lanes;149 150 284 private final End fromEnd; 151 285 private final End toEnd; 152 286 153 287 Road(ModelContainer container, Way w, Junction j) { 154 this.container = container;155 this.toEnd = new End(j);156 157 288 final Node n = j.getNode(); 158 159 289 if (!w.isFirstLastNode(n)) { 160 290 throw new IllegalArgumentException("Way must start or end in given node."); 161 291 } 162 163 final Pair<Relation, Relation> lr = getLengthRelations(w, n); 164 165 this.lengthsLeft = lr.a; 166 this.lengthsRight = lr.b; 167 this.route = lengthsLeft == null && lengthsRight == null ? Route.create(Arrays.asList(w), n) : Route.load( 168 lengthsLeft, lengthsRight, w); 169 this.lanes = lengthsLeft == null && lengthsRight == null ? Lane.load(this) : Lane.load(this, lengthsLeft, 170 lengthsRight, w); 171 172 final List<Node> nodes = route.getNodes(); 173 this.fromEnd = new End(container.getOrCreateJunction(nodes.get(0))); 174 175 fromEnd.getJunction().addRoad(this); 176 toEnd.getJunction().addRoad(this); 292 final Pair<Relation, Relation> lengthsRelations = getLengthRelations(w, n); 293 294 this.container = container; 295 this.route = lengthsRelations.a == null && lengthsRelations.b == null ? Route.create(Arrays.asList(w), n) : Route 296 .load(lengthsRelations.a, lengthsRelations.b, w); 297 this.fromEnd = new End(true, container.getOrCreateJunction(route.getFirstSegment().getStart())); 298 this.toEnd = new End(false, j, lengthsRelations.a, lengthsRelations.b); 299 } 300 301 Road(ModelContainer container, Route route) { 302 this.container = container; 303 this.route = route; 304 this.fromEnd = new End(true, container.getJunction(route.getStart())); 305 this.toEnd = new End(false, container.getJunction(route.getEnd())); 177 306 } 178 307 … … 189 318 } 190 319 191 public List<Lane> getLanes() {192 return lanes;193 }194 195 320 public double getLength() { 196 321 return route.getLength(); 197 }198 199 void updateLengths() {200 if (lengthsLeft != null) { // TODO only forward lanes at this point201 String lengths = "";202 for (int i = lanes.size() - 1; i >= 0; --i) {203 final Lane l = lanes.get(i);204 if (l.getKind() == Lane.Kind.EXTRA_LEFT) {205 lengths += toLengthString(l.getTotalLength()) + Constants.SEPARATOR;206 }207 }208 lengthsLeft.put(Constants.LENGTHS_KEY_LENGTHS_LEFT,209 lengths.substring(0, lengths.length() - Constants.SEPARATOR.length()));210 }211 if (lengthsRight != null) {212 String lengths = "";213 for (Lane l : lanes) {214 if (l.getKind() == Lane.Kind.EXTRA_RIGHT) {215 lengths += toLengthString(l.getTotalLength()) + Constants.SEPARATOR;216 }217 }218 lengthsRight.put(Constants.LENGTHS_KEY_LENGTHS_RIGHT,219 lengths.substring(0, lengths.length() - Constants.SEPARATOR.length()));220 }221 322 } 222 323 … … 229 330 } 230 331 231 // TODO create a special Lanes class which deals with this, misplaced here232 public Lane getLane(boolean reverse, int index) {233 for (Lane l : lanes) {234 if (!l.isExtra() && l.isReverse() == reverse && l.getIndex() == index) {235 return l;236 }237 }238 239 throw new IllegalArgumentException("No such lane.");240 }241 242 public Lane getExtraLane(boolean reverse, int index) {243 for (Lane l : lanes) {244 if (l.isExtra() && l.isReverse() == reverse && l.getIndex() == index) {245 return l;246 }247 }248 249 throw new IllegalArgumentException("No such lane.");250 }251 252 332 public ModelContainer getContainer() { 253 333 return container; 254 334 } 255 335 256 private void addLength(boolean left, double length) { 257 final Relation l = lengthsLeft; 258 final Relation r = lengthsRight; 259 final Node n = toEnd.getJunction().getNode(); 260 261 final String lengthStr = toLengthString(length); 262 final Relation target; 263 if (left) { 264 if (l == null) { 265 if (r == null || !Utils.getMemberNode(r, "end").equals(n)) { 266 target = createLengthsRelation(); 267 } else { 268 target = r; 269 } 270 } else { 271 target = l; 272 } 273 } else { 274 if (r == null) { 275 if (l == null || !Utils.getMemberNode(l, "end").equals(n)) { 276 target = createLengthsRelation(); 277 } else { 278 target = l; 279 } 280 } else { 281 target = r; 282 } 283 } 284 285 final String key = left ? Constants.LENGTHS_KEY_LENGTHS_LEFT : Constants.LENGTHS_KEY_LENGTHS_RIGHT; 286 final String old = target.get(key); 287 if (old == null) { 288 target.put(key, lengthStr); 289 } else { 290 target.put(key, old + Constants.SEPARATOR + lengthStr); 291 } 292 } 293 294 private Relation createLengthsRelation() { 295 final Node n = toEnd.getJunction().getNode(); 296 297 final Relation r = new Relation(); 298 r.put("type", Constants.TYPE_LENGTHS); 299 300 r.addMember(new RelationMember(Constants.LENGTHS_ROLE_END, n)); 301 for (Route.Segment s : route.getSegments()) { 302 r.addMember(1, new RelationMember(Constants.LENGTHS_ROLE_WAYS, s.getWay())); 303 } 304 305 n.getDataSet().addPrimitive(r); 306 307 return r; 308 } 309 310 public boolean isExtendable() { 311 return lengthsLeft != null || lengthsRight != null; 312 } 313 314 public void extend(Way way) { 315 if (lengthsLeft != null) { 316 lengthsLeft.addMember(new RelationMember(Constants.LENGTHS_ROLE_WAYS, way)); 317 } 318 if (lengthsRight != null) { 319 lengthsRight.addMember(new RelationMember(Constants.LENGTHS_ROLE_WAYS, way)); 320 } 336 public boolean isPrimary() { 337 return getContainer().isPrimary(this); 321 338 } 322 339 } -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Route.java
r25606 r25783 199 199 } 200 200 201 public Node getStart() { 202 return getFirstSegment().getStart(); 203 } 204 205 public Node getEnd() { 206 return getLastSegment().getEnd(); 207 } 208 201 209 public Segment getFirstSegment() { 202 210 return getSegments().get(0); … … 210 218 return new Route(segments.subList(fromIndex, toIndex)); 211 219 } 220 221 public List<Way> getWays() { 222 final List<Way> ws = new ArrayList<Way>(); 223 224 for (Segment s : segments) { 225 ws.add(s.getWay()); 226 } 227 228 return Collections.unmodifiableList(ws); 229 } 212 230 } -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Turn.java
r25606 r25783 2 2 3 3 import java.util.ArrayList; 4 import java.util.Collections; 4 5 import java.util.HashSet; 6 import java.util.Iterator; 7 import java.util.LinkedList; 5 8 import java.util.List; 6 9 import java.util.Set; … … 8 11 import org.openstreetmap.josm.data.osm.Node; 9 12 import org.openstreetmap.josm.data.osm.OsmPrimitive; 10 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;11 13 import org.openstreetmap.josm.data.osm.Relation; 12 14 import org.openstreetmap.josm.data.osm.RelationMember; 13 15 import org.openstreetmap.josm.data.osm.Way; 16 import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils; 14 17 15 public class Turn {16 public static Set<Turn> load(Junction junction) {18 public final class Turn { 19 static Set<Turn> load(ModelContainer c, String role, OsmPrimitive primitive) { 17 20 final Set<Turn> result = new HashSet<Turn>(); 18 19 for (OsmPrimitive p : junction.getNode().getReferrers()) { 20 if (p.getType() != OsmPrimitiveType.RELATION) { 21 continue; 22 } 23 24 Relation r = (Relation) p; 25 21 22 for (Relation r : OsmPrimitive.getFilteredList(primitive.getReferrers(), Relation.class)) { 26 23 if (!r.get("type").equals(Constants.TYPE_TURNS)) { 27 24 continue; 28 25 } 29 30 result.addAll(Turn.load(junction, r)); 26 27 for (RelationMember m : r.getMembers()) { 28 if (m.getRole().equals(role) && m.getMember().equals(primitive)) { 29 result.addAll(load(c, r)); 30 } 31 } 31 32 } 32 33 33 34 return result; 34 35 } 35 36 private static Set<Turn> load(Junction junction, Relation turns) { 37 if (!Constants.TYPE_TURNS.equals(turns.get("type"))) { 38 throw new IllegalArgumentException("Relation must be a of type \"" 39 + Constants.TYPE_TURNS + "\"."); 40 } 41 42 Way fromWay = null; 43 Way toWay = null; 44 Node via = null; 45 final List<Integer> lanes = split(turns.get(Constants.TURN_KEY_LANES)); 46 final List<Integer> extraLanes = split(turns 47 .get(Constants.TURN_KEY_EXTRA_LANES)); 48 49 for (RelationMember m : turns.getMembers()) { 50 final String r = m.getRole(); 51 if (Constants.TURN_ROLE_VIA.equals(r)) { 52 if (via != null) { 53 throw new IllegalArgumentException("More than one \"" 54 + Constants.TURN_ROLE_VIA + "\" members."); 36 37 static Set<Turn> load(ModelContainer c, Relation r) { 38 for (RelationMember m : r.getMembers()) { 39 if (m.getRole().equals(Constants.TURN_ROLE_VIA)) { 40 if (m.isNode()) { 41 return loadWithViaNode(c, r); 42 } else if (m.isWay()) { 43 return loadWithViaWays(c, r); 55 44 } 56 57 via = m.getNode();58 } else if (Constants.TURN_ROLE_FROM.equals(r)) {59 if (fromWay != null) {60 throw new IllegalArgumentException("More than one \""61 + Constants.TURN_ROLE_FROM + "\" members.");62 }63 64 fromWay = m.getWay();65 } else if (Constants.TURN_ROLE_TO.equals(r)) {66 if (toWay != null) {67 throw new IllegalArgumentException("More than one \""68 + Constants.TURN_ROLE_TO + "\" members.");69 }70 71 toWay = m.getWay();72 45 } 73 46 } 74 75 if (!via.equals(junction.getNode())) { 76 throw new IllegalArgumentException( 77 "Turn is for a different junction."); 78 } 79 80 final Road.End to = junction.getRoadEnd(toWay); 81 82 final Set<Turn> result = new HashSet<Turn>(); 83 for (Road r : junction.getRoads()) { 84 final boolean reverse; 85 if (r.getRoute().getFirstSegment().getWay().equals(fromWay) 86 && r.getRoute().getFirstSegment().getStart().equals(via)) { 87 reverse = true; 88 } else if (r.getRoute().getLastSegment().getWay().equals(fromWay) 89 && r.getRoute().getLastSegment().getEnd().equals(via)) { 90 reverse = false; 91 } else { 92 continue; 47 48 throw new IllegalArgumentException("No via node or way(s)."); 49 } 50 51 private static Set<Turn> loadWithViaWays(ModelContainer c, Relation r) { 52 final Way from = Utils.getMemberWay(r, Constants.TURN_ROLE_FROM); 53 final Way to = Utils.getMemberWay(r, Constants.TURN_ROLE_TO); 54 55 final List<Way> tmp = Utils.getMemberWays(r, Constants.TURN_ROLE_VIA); 56 final LinkedList<Road> via = new LinkedList<Road>(); 57 58 final Road.End fromRoadEnd = c.getJunction(Utils.lineUp(from, tmp.get(0))).getRoadEnd(from); 59 60 Node n = fromRoadEnd.getJunction().getNode(); 61 final Iterator<Way> it = tmp.iterator(); 62 while (it.hasNext()) { 63 final Way w = it.next(); 64 final Road v = c.getRoad(w); 65 via.add(v); 66 n = Utils.getOppositeEnd(w, n); 67 68 if (!v.isPrimary()) { 69 throw new IllegalStateException("The road is not part of the junction."); 93 70 } 94 95 for (int l : lanes) { 96 result.add(new Turn(turns, r.getLane(reverse, l), junction, to, 97 fromWay, toWay)); 98 } 99 for (int l : extraLanes) { 100 result.add(new Turn(turns, r.getExtraLane(reverse, l), 101 junction, to, fromWay, toWay)); 71 72 final Iterator<Route.Segment> it2 = (v.getRoute().getFirstSegment().getWay().equals(w) ? v.getRoute() 73 .getSegments() : CollectionUtils.reverse(v.getRoute().getSegments())).iterator(); 74 it2.next(); // first is done 75 76 while (it2.hasNext()) { 77 final Way w2 = it2.next().getWay(); 78 n = Utils.getOppositeEnd(w2, n); 79 80 if (!it.hasNext() || !w2.equals(it.next())) { 81 throw new IllegalStateException("The via ways of the relation do not form a road."); 82 } 102 83 } 103 84 } 104 85 final Road.End toRoadEnd = c.getJunction(n).getRoadEnd(to); 86 n = Utils.getOppositeEnd(to, n); 87 88 final Set<Turn> result = new HashSet<Turn>(); 89 for (int i : indices(r, Constants.TURN_KEY_LANES)) { 90 result.add(new Turn(r, fromRoadEnd.getLane(Lane.Kind.REGULAR, i), via, toRoadEnd)); 91 } 92 for (int i : indices(r, Constants.TURN_KEY_EXTRA_LANES)) { 93 result.add(new Turn(r, fromRoadEnd.getExtraLane(i), via, toRoadEnd)); 94 } 105 95 return result; 106 96 } 107 108 static List<Integer> split(String lanes) { 109 if (lanes == null) { 97 98 static List<Integer> indices(Relation r, String key) { 99 final String joined = r.get(key); 100 101 if (joined == null) { 110 102 return new ArrayList<Integer>(1); 111 103 } 112 104 113 105 final List<Integer> result = new ArrayList<Integer>(); 114 for (String lane : Constants.SPLIT_PATTERN.split( lanes)) {106 for (String lane : Constants.SPLIT_PATTERN.split(joined)) { 115 107 result.add(Integer.parseInt(lane)); 116 108 } 117 109 118 110 return result; 119 111 } 120 112 113 private static Set<Turn> loadWithViaNode(ModelContainer c, Relation r) { 114 final Way from = Utils.getMemberWay(r, Constants.TURN_ROLE_FROM); 115 final Node via = Utils.getMemberNode(r, Constants.TURN_ROLE_VIA); 116 final Way to = Utils.getMemberWay(r, Constants.TURN_ROLE_TO); 117 118 final Junction j = c.getJunction(via); 119 120 final Road.End fromRoadEnd = j.getRoadEnd(from); 121 final Road.End toRoadEnd = j.getRoadEnd(to); 122 123 final Set<Turn> result = new HashSet<Turn>(); 124 for (int i : indices(r, Constants.TURN_KEY_LANES)) { 125 result.add(new Turn(r, fromRoadEnd.getLane(Lane.Kind.REGULAR, i), Collections.<Road> emptyList(), toRoadEnd)); 126 } 127 for (int i : indices(r, Constants.TURN_KEY_EXTRA_LANES)) { 128 result.add(new Turn(r, fromRoadEnd.getExtraLane(i), Collections.<Road> emptyList(), toRoadEnd)); 129 } 130 return result; 131 } 132 121 133 static String join(List<Integer> list) { 122 134 if (list.isEmpty()) { 123 135 return null; 124 136 } 125 126 final StringBuilder builder = new StringBuilder(list.size() 127 * (2 + Constants.SEPARATOR.length())); 128 137 138 final StringBuilder builder = new StringBuilder(list.size() * (2 + Constants.SEPARATOR.length())); 139 129 140 for (int e : list) { 130 141 builder.append(e).append(Constants.SEPARATOR); 131 142 } 132 143 133 144 builder.setLength(builder.length() - Constants.SEPARATOR.length()); 134 145 return builder.toString(); 135 146 } 136 147 137 148 private final Relation relation; 138 149 139 150 private final Lane from; 140 private final Junctionvia;151 private final List<Road> via; 141 152 private final Road.End to; 142 143 private final Way fromWay; 144 private final Way toWay; 145 146 public Turn(Relation relation, Lane from, Junction via, Road.End to, 147 Way fromWay, Way toWay) { 153 154 public Turn(Relation relation, Lane from, List<Road> via, Road.End to) { 148 155 this.relation = relation; 149 156 this.from = from; 150 157 this.via = via; 151 158 this.to = to; 152 this.fromWay = fromWay;153 this.toWay = toWay;154 159 } 155 160 156 161 public Lane getFrom() { 157 162 return from; 158 163 } 159 160 public JunctiongetVia() {164 165 public List<Road> getVia() { 161 166 return via; 162 167 } 163 168 164 169 public Road.End getTo() { 165 170 return to; 166 171 } 167 172 168 173 Relation getRelation() { 169 174 return relation; 170 175 } 171 172 Way getFromWay() { 173 return fromWay; 174 } 175 176 Way getToWay() { 177 return toWay; 178 } 179 180 void remove() { 181 final List<Integer> lanes = split(relation 182 .get(Constants.TURN_KEY_LANES)); 183 final List<Integer> extraLanes = split(relation 184 .get(Constants.TURN_KEY_EXTRA_LANES)); 185 186 if (lanes.size() + extraLanes.size() == 1 187 && (from.isExtra() ^ !lanes.isEmpty())) { 176 177 public void remove() { 178 final List<Integer> lanes = indices(relation, Constants.TURN_KEY_LANES); 179 final List<Integer> extraLanes = indices(relation, Constants.TURN_KEY_EXTRA_LANES); 180 181 if (lanes.size() + extraLanes.size() == 1 && (from.isExtra() ^ !lanes.isEmpty())) { 188 182 relation.getDataSet().removePrimitive(relation.getPrimitiveId()); 189 183 } else if (from.isExtra()) { … … 192 186 lanes.remove(Integer.valueOf(from.getIndex())); 193 187 } 194 195 relation.put(Constants.TURN_KEY_LANES, join(lanes));196 relation.put(Constants.TURN_KEY_EXTRA_LANES, join(extraLanes));188 189 relation.put(Constants.TURN_KEY_LANES, lanes.isEmpty() ? null : join(lanes)); 190 relation.put(Constants.TURN_KEY_EXTRA_LANES, extraLanes.isEmpty() ? null : join(extraLanes)); 197 191 } 198 192 } -
applications/editors/josm/plugins/turnlanes/src/org/openstreetmap/josm/plugins/turnlanes/model/Utils.java
r25606 r25783 3 3 import java.util.ArrayList; 4 4 import java.util.Arrays; 5 import java.util.Collection; 5 6 import java.util.Collections; 6 7 import java.util.HashSet; 8 import java.util.Iterator; 9 import java.util.LinkedList; 7 10 import java.util.List; 8 11 import java.util.Set; 9 12 10 13 import org.openstreetmap.josm.data.osm.Node; 14 import org.openstreetmap.josm.data.osm.OsmPrimitive; 11 15 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 12 16 import org.openstreetmap.josm.data.osm.Relation; 13 17 import org.openstreetmap.josm.data.osm.RelationMember; 14 18 import org.openstreetmap.josm.data.osm.Way; 19 import org.openstreetmap.josm.plugins.turnlanes.CollectionUtils; 15 20 16 21 public class Utils { … … 24 29 } 25 30 31 public static final List<Way> filterRoads(List<OsmPrimitive> of) { 32 final List<Way> result = new ArrayList<Way>(); 33 34 for (OsmPrimitive p : of) { 35 if (p.getType() == OsmPrimitiveType.WAY && Utils.isRoad((Way) p)) { 36 result.add((Way) p); 37 } 38 } 39 40 return result; 41 } 42 26 43 public static Node getMemberNode(Relation r, String role) { 27 44 return getMember(r, role, OsmPrimitiveType.NODE).getNode(); 28 45 } 29 46 47 public static Way getMemberWay(Relation r, String role) { 48 return getMember(r, role, OsmPrimitiveType.WAY).getWay(); 49 } 50 30 51 public static RelationMember getMember(Relation r, String role, OsmPrimitiveType type) { 31 52 final List<RelationMember> candidates = getMembers(r, role, type); 32 33 if (candidates.size() == 0) { 53 if (candidates.isEmpty()) { 34 54 throw new IllegalStateException("No member with given role and type."); 35 55 } else if (candidates.size() > 1) { 36 56 throw new IllegalStateException(candidates.size() + " members with given role and type."); 37 57 } 38 39 58 return candidates.get(0); 40 59 } 41 60 42 61 public static List<RelationMember> getMembers(Relation r, String role, OsmPrimitiveType type) { 62 final List<RelationMember> result = getMembers(r, role); 63 for (RelationMember m : getMembers(r, role)) { 64 if (m.getType() != type) { 65 throw new IllegalArgumentException("Member has the specified role, but wrong type."); 66 } 67 } 68 return result; 69 } 70 71 public static List<RelationMember> getMembers(Relation r, String role) { 43 72 final List<RelationMember> result = new ArrayList<RelationMember>(); 44 45 73 for (RelationMember m : r.getMembers()) { 46 if (m.getRole().equals(role) && m.getType() == type) {74 if (m.getRole().equals(role)) { 47 75 result.add(m); 48 76 } 49 77 } 50 51 return result; 78 return result; 79 } 80 81 public static List<Node> getMemberNodes(Relation r, String role) { 82 return mapMembers(getMembers(r, role, OsmPrimitiveType.NODE), Node.class); 52 83 } 53 84 54 85 public static List<Way> getMemberWays(Relation r, String role) { 55 final List<Way> result = new ArrayList<Way>(); 56 57 for (RelationMember m : getMembers(r, role, OsmPrimitiveType.WAY)) { 58 result.add(m.getWay()); 59 } 60 61 return result; 62 } 63 64 public static List<Node> getMemberNodes(Relation r, String role) { 65 final List<Node> result = new ArrayList<Node>(); 66 67 for (RelationMember m : getMembers(r, role, OsmPrimitiveType.NODE)) { 68 result.add(m.getNode()); 69 } 70 71 return result; 86 return mapMembers(getMembers(r, role, OsmPrimitiveType.WAY), Way.class); 87 } 88 89 private static <T> List<T> mapMembers(List<RelationMember> ms, Class<T> t) { 90 final List<T> result = new ArrayList<T>(ms.size()); 91 for (RelationMember m : ms) { 92 result.add(t.cast(m.getMember())); 93 } 94 return result; 95 } 96 97 /** 98 * 99 * @param a 100 * @param b 101 * @return the node at which {@code a} and {@code b} are connected 102 */ 103 public static Node lineUp(Way a, Way b) { 104 final Set<Node> s = new HashSet<Node>(Arrays.asList(a.firstNode(), a.lastNode(), b.firstNode(), b.lastNode())); 105 if (a.firstNode() == a.lastNode() || b.firstNode().equals(b.lastNode()) || s.size() == 2) { 106 throw new IllegalArgumentException("Cycles are not allowed."); 107 } else if (s.size() == 4) { 108 throw new IllegalArgumentException("Ways are not connected (at their first and last nodes)."); 109 } 110 111 if (a.firstNode() == b.firstNode() || a.lastNode() == b.firstNode()) { 112 return b.firstNode(); 113 } else if (a.firstNode() == b.lastNode() || a.lastNode() == b.lastNode()) { 114 return b.lastNode(); 115 } else { 116 throw new AssertionError(); 117 } 72 118 } 73 119 … … 86 132 } 87 133 } 134 135 /** 136 * Orders the {@code ways} such that the combined ways out of each returned list form a path (in 137 * order) from one node out of {@code nodes} to another out of {@code nodes}. 138 * 139 * <ul> 140 * <li>Each way is used exactly once.</li> 141 * <li>Paths contain no {@code nodes} excepting the first and last nodes.</li> 142 * <li>Paths contain no loops w.r.t. the ways' first and last nodes</li> 143 * </ul> 144 * 145 * @param ways 146 * ways to be ordered 147 * @param nodes 148 * start/end nodes 149 * @return 150 * @throws IllegalArgumentException 151 * if the ways can't be ordered 152 */ 153 public static List<Route> orderWays(Iterable<Way> ways, Iterable<Node> nodes) { 154 final List<Way> ws = new LinkedList<Way>(CollectionUtils.toList(ways)); 155 final Set<Node> ns = new HashSet<Node>(CollectionUtils.toList(nodes)); 156 157 final List<Route> result = new ArrayList<Route>(); 158 159 while (!ws.isEmpty()) { 160 result.add(findPath(ws, ns)); 161 } 162 163 return result; 164 } 165 166 private static Route findPath(List<Way> ws, Set<Node> ns) { 167 final Way w = findPathSegment(ws, ns); 168 final boolean first = ns.contains(w.firstNode()); 169 final boolean last = ns.contains(w.lastNode()); 170 171 if (first && last) { 172 return Route.create(Arrays.asList(w), w.firstNode()); 173 } else if (!first && !last) { 174 throw new AssertionError(); 175 } 176 177 final List<Way> result = new ArrayList<Way>(); 178 result.add(w); 179 Node n = first ? w.lastNode() : w.firstNode(); 180 while (true) { 181 final Way next = findPathSegment(ws, Arrays.asList(n)); 182 result.add(next); 183 n = getOppositeEnd(next, n); 184 185 if (ns.contains(n)) { 186 return Route.create(result, first ? w.firstNode() : w.lastNode()); 187 } 188 } 189 } 190 191 private static Way findPathSegment(List<Way> ws, Collection<Node> ns) { 192 final Iterator<Way> it = ws.iterator(); 193 194 while (it.hasNext()) { 195 final Way w = it.next(); 196 197 if (ns.contains(w.firstNode()) || ns.contains(w.lastNode())) { 198 it.remove(); 199 return w; 200 } 201 } 202 203 throw new IllegalArgumentException("Ways can't be ordered."); 204 } 205 206 public static Iterable<Way> flattenVia(Node start, List<Road> via, Node end) { 207 final List<Way> result = new ArrayList<Way>(); 208 209 Node n = start; 210 for (Road r : via) { 211 final Iterable<Route.Segment> segments = r.getRoute().getFirstSegment().getWay().isFirstLastNode(n) ? r 212 .getRoute().getSegments() : CollectionUtils.reverse(r.getRoute().getSegments()); 213 214 for (Route.Segment s : segments) { 215 result.add(s.getWay()); 216 n = Utils.getOppositeEnd(s.getWay(), n); 217 } 218 } 219 if (!end.equals(n)) { 220 throw new IllegalArgumentException("The given via ways don't end at the given node."); 221 } 222 223 return result; 224 } 88 225 }
Note:
See TracChangeset
for help on using the changeset viewer.