View Javadoc
1   package org.opentrafficsim.editor.extensions.map;
2   
3   import java.rmi.RemoteException;
4   
5   import org.djunits.value.vdouble.scalar.Direction;
6   import org.djutils.draw.bounds.Bounds2d;
7   import org.djutils.draw.line.Polygon2d;
8   import org.djutils.draw.point.OrientedPoint2d;
9   import org.djutils.draw.point.Point2d;
10  import org.djutils.event.Event;
11  import org.djutils.event.EventListener;
12  import org.djutils.event.reference.ReferenceType;
13  import org.opentrafficsim.base.geometry.OtsLocatable;
14  import org.opentrafficsim.base.geometry.OtsShape;
15  import org.opentrafficsim.draw.network.NodeAnimation.NodeData;
16  import org.opentrafficsim.editor.OtsEditor;
17  import org.opentrafficsim.editor.XsdTreeNode;
18  import org.opentrafficsim.editor.extensions.Adapters;
19  
20  /**
21   * NodeData for the editor Map.
22   * <p>
23   * Copyright (c) 2023-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
24   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
25   * </p>
26   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
27   */
28  public class MapNodeData extends MapData implements NodeData, EventListener
29  {
30  
31      /** */
32      private static final long serialVersionUID = 20231003L;
33  
34      /** Bounds. */
35      private static final Bounds2d BOUNDS = new Bounds2d(2.0, 2.0);
36  
37      /** String attribute. */
38      private String id = "";
39  
40      /** Coordinate attribute. */
41      private Point2d coordinate = null;
42  
43      /** Direction attribute. */
44      private Direction direction = null;
45  
46      /** Location. */
47      private OrientedPoint2d location = new OrientedPoint2d(0.0, 0.0);
48  
49      /** Contour. */
50      private final Polygon2d contour;
51  
52      /** Shape (cached). */
53      private OtsShape shape;
54  
55      /**
56       * Constructor.
57       * @param map map.
58       * @param nodeNode node Ots.Network.Node.
59       * @param editor editor.
60       */
61      public MapNodeData(final EditorMap map, final XsdTreeNode nodeNode, final OtsEditor editor)
62      {
63          super(map, nodeNode, editor);
64          getNode().addListener(this, XsdTreeNode.ATTRIBUTE_CHANGED, ReferenceType.WEAK);
65          // for when node is duplicated, set data immediately
66          try
67          {
68              if (getNode().isActive())
69              {
70                  notify(new Event(XsdTreeNode.ATTRIBUTE_CHANGED, new Object[] {getNode(), "Id", null}));
71                  notify(new Event(XsdTreeNode.ATTRIBUTE_CHANGED, new Object[] {getNode(), "Coordinate", null}));
72                  notify(new Event(XsdTreeNode.ATTRIBUTE_CHANGED, new Object[] {getNode(), "Direction", null}));
73              }
74          }
75          catch (RemoteException e)
76          {
77              throw new RuntimeException(e);
78          }
79          this.contour = OtsLocatable.boundsAsContour(this);
80      }
81  
82      @Override
83      public Bounds2d getBounds()
84      {
85          return BOUNDS;
86      }
87  
88      @Override
89      public Polygon2d getContour()
90      {
91          return this.contour;
92      }
93  
94      @Override
95      public OtsShape getShape()
96      {
97          if (this.shape == null)
98          {
99              this.shape = NodeData.super.getShape();
100         }
101         return this.shape;
102     }
103 
104     @Override
105     public String getId()
106     {
107         return this.id;
108     }
109 
110     @Override
111     public void destroy()
112     {
113         super.destroy();
114         getNode().removeListener(this, XsdTreeNode.ATTRIBUTE_CHANGED);
115     }
116 
117     @Override
118     public OrientedPoint2d getLocation()
119     {
120         return this.location;
121     }
122 
123     @Override
124     public void notify(final Event event) throws RemoteException
125     {
126         String attribute = (String) ((Object[]) event.getContent())[1];
127         String value = getNode().getAttributeValue(attribute);
128         if ("Id".equals(attribute))
129         {
130             this.id = value == null ? "" : value;
131             return;
132         }
133         else if ("Coordinate".equals(attribute))
134         {
135             setValue((v) -> this.coordinate = v, Adapters.get(Point2d.class), getNode(), "Coordinate");
136         }
137         else if ("Direction".equals(attribute))
138         {
139             setValue((v) -> this.direction = v, Adapters.get(Direction.class), getNode(), "Direction");
140         }
141         else
142         {
143             return;
144         }
145         setLocation();
146     }
147 
148     @Override
149     public void evalChanged()
150     {
151         this.id = getNode().getId() == null ? "" : getNode().getId();
152         setValue((v) -> this.coordinate = v, Adapters.get(Point2d.class), getNode(), "Coordinate");
153         setValue((v) -> this.direction = v, Adapters.get(Direction.class), getNode(), "Direction");
154         setLocation();
155     }
156 
157     /**
158      * Set the location from the coordinate and direction. Notify when invalid or valid.
159      */
160     private void setLocation()
161     {
162         if (this.coordinate == null)
163         {
164             setInvalid();
165             return;
166         }
167         this.location = new OrientedPoint2d(this.coordinate, this.direction == null ? 0.0 : this.direction.si);
168         setValid();
169     }
170 
171     @Override
172     public String toString()
173     {
174         return "Node " + this.id;
175     }
176 
177 }