View Javadoc
1   package org.opentrafficsim.road.network.factory.shape;
2   
3   import java.awt.Dimension;
4   import java.awt.geom.Rectangle2D;
5   import java.io.File;
6   import java.io.IOException;
7   import java.io.Serializable;
8   import java.net.URL;
9   import java.rmi.RemoteException;
10  import java.util.ArrayList;
11  import java.util.LinkedHashMap;
12  import java.util.List;
13  import java.util.Map;
14  import java.util.UUID;
15  
16  import javax.naming.NamingException;
17  
18  import org.djunits.unit.DurationUnit;
19  import org.djunits.value.vdouble.scalar.Duration;
20  import org.djunits.value.vdouble.scalar.Time;
21  import org.djutils.io.URLResource;
22  import org.djutils.logger.CategoryLogger;
23  import org.geotools.data.FileDataStore;
24  import org.geotools.data.FileDataStoreFinder;
25  import org.geotools.data.shapefile.ShapefileDataStore;
26  import org.geotools.data.shapefile.ShapefileDataStoreFactory;
27  import org.geotools.data.simple.SimpleFeatureCollection;
28  import org.geotools.data.simple.SimpleFeatureSource;
29  import org.geotools.feature.FeatureIterator;
30  import org.locationtech.jts.geom.Coordinate;
31  import org.locationtech.jts.geom.Geometry;
32  import org.locationtech.jts.geom.LineString;
33  import org.locationtech.jts.geom.MultiLineString;
34  import org.opengis.feature.Feature;
35  import org.opengis.feature.Property;
36  import org.opentrafficsim.core.dsol.AbstractOTSModel;
37  import org.opentrafficsim.core.dsol.OTSAnimator;
38  import org.opentrafficsim.core.dsol.OTSReplication;
39  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
40  import org.opentrafficsim.core.geometry.OTSGeometryException;
41  import org.opentrafficsim.core.geometry.OTSLine3D;
42  import org.opentrafficsim.core.geometry.OTSPoint3D;
43  import org.opentrafficsim.core.network.LinkType;
44  import org.opentrafficsim.core.network.NetworkException;
45  import org.opentrafficsim.core.network.OTSLink;
46  import org.opentrafficsim.core.network.OTSNetwork;
47  import org.opentrafficsim.core.network.OTSNode;
48  import org.opentrafficsim.draw.network.LinkAnimation;
49  import org.opentrafficsim.draw.network.NodeAnimation;
50  import org.opentrafficsim.road.network.OTSRoadNetwork;
51  import org.pmw.tinylog.Level;
52  
53  import nl.tudelft.simulation.dsol.SimRuntimeException;
54  import nl.tudelft.simulation.dsol.experiment.ReplicationMode;
55  import nl.tudelft.simulation.dsol.simulators.AnimatorInterface;
56  import nl.tudelft.simulation.dsol.swing.animation.D2.AnimationPanel;
57  import nl.tudelft.simulation.dsol.swing.gui.DSOLApplication;
58  import nl.tudelft.simulation.dsol.swing.gui.DSOLPanel;
59  import nl.tudelft.simulation.language.DSOLException;
60  
61  /**
62   * Test model for parsing ESRI shape files.
63   */
64  public class TestShapeParser extends DSOLApplication
65  {
66      /** */
67      private static final long serialVersionUID = 1L;
68  
69      /**
70       * @param title the title of the window
71       * @param panel the panel to display the animation
72       */
73      public TestShapeParser(final String title, final DSOLPanel panel)
74      {
75          super(title, panel);
76          panel.getConsole().setLogLevel(Level.TRACE);
77      }
78  
79      /**
80       * Main program.
81       * @param args String[]; the command line arguments (not used)
82       * @throws SimRuntimeException should never happen
83       * @throws NamingException on animation creation error
84       * @throws RemoteException on animation panel failure
85       * @throws DSOLException when simulator does not implement AnimatorInterface
86       */
87      public static void main(final String[] args) throws SimRuntimeException, NamingException, RemoteException, DSOLException
88      {
89          CategoryLogger.setAllLogLevel(Level.TRACE);
90          OTSAnimator simulator = new OTSAnimator("TestShapeParser");
91          GisNDWImport model = new GisNDWImport(simulator);
92          DSOLPanel panel = new DSOLPanel(model, simulator);
93          AnimationPanel animationPanel =
94                  new AnimationPanel(new Rectangle2D.Double(125000, 375000, 40000, 35000), new Dimension(800, 600), simulator);
95          animationPanel.toggleClass(LinkAnimation.Text.class);
96          animationPanel.toggleClass(NodeAnimation.Text.class);
97          panel.getTabbedPane().add("animation", animationPanel);
98          panel.getTabbedPane().setSelectedIndex(1);
99          OTSReplication replication =
100                 OTSReplication.create("rep1", Time.ZERO, Duration.ZERO, new Duration(60.0, DurationUnit.MINUTE), model);
101         simulator.initialize(replication, ReplicationMode.TERMINATING);
102         new TestShapeParser("TestShapeParser", panel);
103     }
104 
105     /** {@inheritDoc} */
106     @Override
107     public final String toString()
108     {
109         return "TestShapeParser []";
110     }
111 
112     /**
113      * Model to test the Esri Shape File Format parser.
114      * <p>
115      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. <br>
116      * All rights reserved. BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim
117      * License</a>.
118      * <p>
119      * initial version Jun 27, 2015 <br>
120      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
121      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
122      */
123     static class GisNDWImport extends AbstractOTSModel
124     {
125         /** */
126         private static final long serialVersionUID = 20141121L;
127 
128         /** The network. */
129         private final OTSRoadNetwork network;
130 
131         /**
132          * Create the test model.
133          * @param simulator the simulator
134          */
135         GisNDWImport(final OTSSimulatorInterface simulator)
136         {
137             super(simulator);
138             this.network  = new OTSRoadNetwork("test network", true, simulator);
139         }
140 
141         /** {@inheritDoc} */
142         @Override
143         public final void constructModel() throws SimRuntimeException
144         {
145             try
146             {
147                 // open the NWB basic shape file
148                 Map<String, AbstractNWBRoadElement> roadMapNWB = getRoadMapNWB("/A58", "NWB_A58", "NWB_wegvakken");
149 
150                 // open the shape file with driving lane information
151                 Map<String, AbstractNWBRoadElement> laneMapNWB = getRoadMapNWB("/A58", "rijstroken_A58", "NWB_rijstroken");
152 
153                 // open the shape file with specific lane information such as on and off ramps and weaving areas
154                 Map<String, AbstractNWBRoadElement> specialLaneMapNWB =
155                         getRoadMapNWB("/A58", "mengstroken_A58", "NWB_mengstroken");
156 
157                 combineNWBMaps(roadMapNWB, laneMapNWB, specialLaneMapNWB);
158             }
159             catch (NetworkException nwe)
160             {
161                 nwe.printStackTrace();
162             }
163 
164         }
165 
166         /**
167          * Combine the data (split road elements and add lane-attributes).
168          * @param roadMapNWB Map&lt;String,AbstractNWBRoadElement&gt;;
169          * @param laneMapNWB Map&lt;String,AbstractNWBRoadElement&gt;;
170          * @param specialLaneMapNWB Map&lt;String,AbstractNWBRoadElement&gt;;
171          */
172         private void combineNWBMaps(Map<String, AbstractNWBRoadElement> roadMapNWB,
173                 Map<String, AbstractNWBRoadElement> laneMapNWB, Map<String, AbstractNWBRoadElement> specialLaneMapNWB)
174         {
175             // TODO : Alexander, de combinemaps moet verder worden uitgewerkt!
176             // Here, a segment of the NWB wegvak is being extracted (for instance the first part of the wegvak has an on-ramp)
177             // We should thus split this NWB-wegvak in two parts, the first part with the on-ramp (additional lane) and the
178             // remainder with the original attributes
179             for (AbstractNWBRoadElement laneElement : laneMapNWB.values())
180             {
181                 NWBDrivingLane lane = (NWBDrivingLane) laneElement;
182                 NWBRoadElement road = (NWBRoadElement) roadMapNWB.get(lane.getRoadId());
183                 if (road != null)
184                 {
185                     List<LineString> lineSegmentList = splitRoad(road, lane);
186                 }
187                 else
188                 {
189                     System.err.println("road for lane " + lane.toString() + " does not exist");
190                 }
191             }
192         }
193 
194         /**
195          * Split a road if there is a lane along a PART of this road.
196          * @param road NWBRoadElement;
197          * @param segment NWBDrivingLane;
198          * @return list of linestrings
199          */
200         private List<LineString> splitRoad(NWBRoadElement road, NWBDrivingLane segment)
201         {
202             MultiLineString lines = (MultiLineString) road.getMyGeom();
203             LineString line = (LineString) lines.getGeometryN(0);
204             List<LineString> lineSegmentList = new ArrayList<>();
205 
206             // The getSubstring is copied from The JCS Conflation Suite (JCS): I assume it is not supported anymore, but can
207             // still be found...
208             lineSegmentList.add(SubstringLine.getSubstring(line, segment.getBeginDistance(), segment.getEndDistance()));
209             if (segment.getBeginDistance() > 0)
210             {
211                 lineSegmentList.add(SubstringLine.getSubstring(line, 0, segment.getBeginDistance()));
212             }
213             if (segment.getEndDistance() < road.getEndDistance())
214             {
215                 lineSegmentList.add(SubstringLine.getSubstring(line, 0, segment.getBeginDistance()));
216             }
217             return lineSegmentList;
218         }
219 
220         /**
221          * Import a list of road (link) elements from a shape file.
222          * @param initialDir String;
223          * @param fileName String;
224          * @param shapeIdentifier String;
225          * @return map of naames road elements
226          * @throws NetworkException network exception
227          */
228         private Map<String, AbstractNWBRoadElement> getRoadMapNWB(String initialDir, String fileName, String shapeIdentifier)
229                 throws NetworkException
230         {
231             FileDataStore dataStoreLink = null;
232             try
233             {
234                 dataStoreLink = newDatastore(initialDir, fileName);
235             }
236             catch (IOException e)
237             {
238                 // TODO Auto-generated catch block
239                 e.printStackTrace();
240             }
241             // open and read shape file links
242             FeatureIterator feautureIterator = getFeatureIterator(dataStoreLink);
243             // loop through the features and first retrieve the geometry
244             Map<String, AbstractNWBRoadElement> roadMApNWB = getFeatureAttributes(feautureIterator, shapeIdentifier);
245             return roadMApNWB;
246 
247         }
248 
249         /**
250          * @param initialDir String;
251          * @param fileName String;
252          * @return shapefile datastore
253          * @throws IOException on IO exception
254          */
255         private FileDataStore newDatastore(String initialDir, final String fileName) throws IOException
256         {
257             try
258             {
259                 URL url = URLResource.getResource(initialDir);
260                 File iniDir = new File(url.getFile());
261                 File file = new File(iniDir, fileName + ".shp");
262                 System.out.println(file + "  -- exists: " + file.exists());
263 
264                 ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
265                 ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createDataStore(file.toURI().toURL());
266 
267                 FileDataStore dataStoreLink = FileDataStoreFinder.getDataStore(file);
268                 System.out.println(dataStore);
269                 return dataStore;
270 
271             }
272             catch (IOException exception)
273             {
274                 exception.printStackTrace();
275             }
276             return null;
277 
278         }
279 
280         /**
281          * @param dataStore FileDataStore;
282          * @return iterator
283          */
284         private FeatureIterator getFeatureIterator(FileDataStore dataStore)
285         {
286             try
287             {
288                 String[] typeNameLink = dataStore.getTypeNames();
289                 SimpleFeatureSource sourceLink;
290                 sourceLink = dataStore.getFeatureSource(typeNameLink[0]);
291                 SimpleFeatureCollection featuresLink = sourceLink.getFeatures();
292                 return featuresLink.features();
293 
294             }
295             catch (IOException e)
296             {
297                 // TODO Auto-generated catch block
298                 e.printStackTrace();
299             }
300             return null;
301         }
302 
303         /**
304          * @param feautureIterator FeatureIterator;
305          * @param shapeIdentifier String;
306          * @return feature attributes
307          * @throws NetworkException on network exception
308          */
309         private Map<String, AbstractNWBRoadElement> getFeatureAttributes(final FeatureIterator feautureIterator,
310                 String shapeIdentifier) throws NetworkException
311         {
312             Map<String, AbstractNWBRoadElement> roadMap = new LinkedHashMap<>();
313             while (feautureIterator.hasNext())
314             {
315                 Feature feature = feautureIterator.next();
316                 // geometry is always first
317                 if (shapeIdentifier.equals("NWB_wegvakken"))
318                 {
319                     NWBRoadElement road = getPropertiesNWB(feature);
320                     roadMap.put(road.getRoadId(), road);
321                 }
322                 else if (shapeIdentifier.equals("NWB_rijstroken"))
323                 {
324                     NWBDrivingLane road = getPropertiesDrivingLanes(feature);
325                     roadMap.put(road.getRoadId(), road);
326                 }
327                 else if (shapeIdentifier.equals("NWB_mengstroken"))
328                 {
329                     NWBDrivingLane road = getPropertiesSpecialLanes(feature);
330                     roadMap.put(road.getRoadId(), road);
331                 }
332             }
333             return roadMap;
334         }
335 
336         /**
337          * @param feature Feature;
338          * @return one road element with properties
339          * @throws NetworkException on network exception
340          */
341         private NWBRoadElement getPropertiesNWB(final Feature feature) throws NetworkException
342         {
343             Geometry theGeom = (Geometry) feature.getDefaultGeometryProperty().getValue();
344             Coordinate[] coordinates = theGeom.getCoordinates();
345             Property property = feature.getProperty("WVK_ID");
346             String roadId = property.getValue().toString();
347             property = feature.getProperty("JTE_ID_BEG");
348             String junctionIdBegin = property.getValue().toString();
349             property = feature.getProperty("JTE_ID_END");
350             String junctionIdEnd = property.getValue().toString();
351             property = feature.getProperty("ADMRICHTNG");
352             String adminDirection = property.getValue().toString();
353             property = feature.getProperty("RIJRICHTNG");
354             String drivingDirection = property.getValue().toString();
355             property = feature.getProperty("BEGINKM");
356             Double beginKM = parseDouble(property);
357             property = feature.getProperty("EINDKM");
358             Double endKM = parseDouble(property);
359             property = feature.getProperty("BEGAFSTAND");
360             Double beginDistance = parseDouble(property);
361             property = feature.getProperty("ENDAFSTAND");
362             Double endDistance = parseDouble(property);
363 
364             OTSNode startNode;
365             if (this.network.containsNode(junctionIdBegin))
366                 startNode = (OTSNode) this.network.getNode(junctionIdBegin);
367             else
368                 startNode = new OTSNode(this.network, junctionIdBegin, new OTSPoint3D(coordinates[0]));
369             OTSNode endNode;
370             if (this.network.containsNode(junctionIdEnd))
371                 endNode = (OTSNode) this.network.getNode(junctionIdEnd);
372             else
373                 endNode = new OTSNode(this.network, junctionIdEnd, new OTSPoint3D(coordinates[coordinates.length - 1]));
374             NWBRoadElement road = new NWBRoadElement(theGeom, startNode, endNode, roadId, beginDistance, endDistance,
375                     junctionIdBegin, junctionIdEnd, adminDirection, drivingDirection, beginKM, endKM);
376             if (getSimulator() instanceof AnimatorInterface)
377             {
378                 try
379                 {
380                     System.out.println(startNode);
381                     new NodeAnimation(startNode, this.simulator);
382                     new NodeAnimation(endNode, this.simulator);
383                     OTSLink link = new OTSLink(this.network, UUID.randomUUID().toString(), startNode, endNode, network.getLinkType(LinkType.DEFAULTS.ROAD),
384                             new OTSLine3D(coordinates));
385                     new LinkAnimation(link, this.simulator, 2.0f);
386                 }
387                 catch (RemoteException | NamingException | OTSGeometryException exception)
388                 {
389                     exception.printStackTrace();
390                 }
391             }
392             return road;
393         }
394 
395         /**
396          * @param feature Feature;
397          * @return info on one driving lane
398          * @throws NetworkException on network exception
399          */
400         private NWBDrivingLane getPropertiesDrivingLanes(final Feature feature) throws NetworkException
401         {
402             Geometry theGeom = (Geometry) feature.getDefaultGeometryProperty().getValue();
403             Coordinate[] coordinates = theGeom.getCoordinates();
404 
405             Property property = feature.getProperty("WVK_ID");
406             String roadId = property.getValue().toString();
407 
408             property = feature.getProperty("KANTCODE");
409             String sideCode = property.getValue().toString();
410 
411             property = feature.getProperty("BEGAFSTAND");
412             Double beginDistance = parseDouble(property);
413 
414             property = feature.getProperty("ENDAFSTAND");
415             Double endDistance = parseDouble(property);
416 
417             property = feature.getProperty("OMSCHR");
418             String laneDescription = property.getValue().toString();
419             String[] lanes = laneDescription.split("->");
420             Integer startNumberOfLanes = Integer.parseInt(lanes[0].trim());
421             Integer endNumberOfLanes = Integer.parseInt(lanes[1].trim());
422 
423             String junctionIdBegin = roadId + "_" + beginDistance;
424             String junctionIdEnd = roadId + "_" + endDistance;
425 
426             OTSNode startNode;
427             if (this.network.containsNode(junctionIdBegin))
428                 startNode = (OTSNode) this.network.getNode(junctionIdBegin);
429             else
430                 startNode = new OTSNode(this.network, junctionIdBegin, new OTSPoint3D(coordinates[0]));
431             OTSNode endNode;
432             if (this.network.containsNode(junctionIdEnd))
433                 endNode = (OTSNode) this.network.getNode(junctionIdEnd);
434             else
435                 endNode = new OTSNode(this.network, junctionIdEnd, new OTSPoint3D(coordinates[coordinates.length - 1]));
436             NWBDrivingLane lane = new NWBDrivingLane(theGeom, startNode, endNode, roadId, beginDistance, endDistance,
437                     startNumberOfLanes, endNumberOfLanes, sideCode);
438             if (getSimulator() instanceof AnimatorInterface)
439             {
440                 try
441                 {
442                     new NodeAnimation(startNode, this.simulator);
443                     new NodeAnimation(endNode, this.simulator);
444                     OTSLink link = new OTSLink(this.network, UUID.randomUUID().toString(), startNode, endNode, network.getLinkType(LinkType.DEFAULTS.ROAD),
445                             new OTSLine3D(coordinates));
446                     new LinkAnimation(link, this.simulator, 1.0f);
447                 }
448                 catch (RemoteException | NamingException | OTSGeometryException exception)
449                 {
450                     exception.printStackTrace();
451                 }
452             }
453             return lane;
454         }
455 
456         /**
457          * @param feature Feature;
458          * @return info on one special lane
459          * @throws NetworkException on network exception
460          */
461         private NWBDrivingLane getPropertiesSpecialLanes(final Feature feature) throws NetworkException
462         {
463             Geometry theGeom = (Geometry) feature.getDefaultGeometryProperty().getValue();
464             Coordinate[] coordinates = theGeom.getCoordinates();
465 
466             Property property = feature.getProperty("WVK_ID");
467             String roadId = property.getValue().toString();
468 
469             property = feature.getProperty("KANTCODE");
470             String sideCode = property.getValue().toString();
471 
472             property = feature.getProperty("BEGAFSTAND");
473             Double beginDistance = parseDouble(property);
474 
475             property = feature.getProperty("ENDAFSTAND");
476             Double endDistance = parseDouble(property);
477 
478             property = feature.getProperty("OMSCHR");
479             String laneType = property.getValue().toString();
480 
481             property = feature.getProperty("AANT_MSK");
482             String laneDescription = property.getValue().toString();
483             String[] lanes = laneDescription.split("->");
484             Integer startNumberOfLanes = Integer.parseInt(lanes[0].trim().substring(0, 1));
485             Integer endNumberOfLanes = Integer.parseInt(lanes[1].trim().substring(0, 1));
486 
487             String junctionIdBegin = roadId + "_" + beginDistance;
488             String junctionIdEnd = roadId + "_" + endDistance;
489             OTSNode startNode;
490             if (this.network.containsNode(junctionIdBegin))
491                 startNode = (OTSNode) this.network.getNode(junctionIdBegin);
492             else
493                 startNode = new OTSNode(this.network, junctionIdBegin, new OTSPoint3D(coordinates[0]));
494             OTSNode endNode;
495             if (this.network.containsNode(junctionIdEnd))
496                 endNode = (OTSNode) this.network.getNode(junctionIdEnd);
497             else
498                 endNode = new OTSNode(this.network, junctionIdEnd, new OTSPoint3D(coordinates[coordinates.length - 1]));
499             NWBDrivingLane lane = new NWBDrivingLane(theGeom, startNode, endNode, roadId, beginDistance, endDistance,
500                     startNumberOfLanes, endNumberOfLanes, sideCode);
501             if (getSimulator() instanceof AnimatorInterface)
502             {
503                 try
504                 {
505                     new NodeAnimation(startNode, this.simulator);
506                     new NodeAnimation(endNode, this.simulator);
507                     OTSLink link = new OTSLink(this.network, UUID.randomUUID().toString(), startNode, endNode, network.getLinkType(LinkType.DEFAULTS.ROAD),
508                             new OTSLine3D(coordinates));
509                     new LinkAnimation(link, this.simulator, 1.0f);
510                 }
511                 catch (RemoteException | NamingException | OTSGeometryException exception)
512                 {
513                     exception.printStackTrace();
514                 }
515             }
516             return lane;
517         }
518 
519         /**
520          * @param property Property;
521          * @return a double
522          */
523         private Double parseDouble(final Property property)
524         {
525             if (property.getValue() != null)
526             {
527                 if (property.getValue().toString() != null)
528                 {
529                     return Double.parseDouble(property.getValue().toString());
530                 }
531             }
532             return Double.NaN;
533         }
534 
535         /** {@inheritDoc} */
536         @Override
537         public OTSNetwork getNetwork()
538         {
539             return this.network;
540         }
541 
542         /** {@inheritDoc} */
543         @Override
544         public final String toString()
545         {
546             return "TestXMLModel [simulator=" + this.simulator + "]";
547         }
548 
549         /** {@inheritDoc} */
550         @Override
551         public Serializable getSourceId()
552         {
553             return "GisNDWImport";
554         }
555 
556     }
557 
558 }