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