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