View Javadoc
1   package org.opentrafficsim.demo.geometry.shape;
2   
3   import java.awt.Color;
4   import java.io.File;
5   import java.io.IOException;
6   import java.net.URL;
7   import java.rmi.RemoteException;
8   import java.util.Collection;
9   import java.util.LinkedHashMap;
10  import java.util.Map;
11  
12  import javax.naming.NamingException;
13  
14  import org.djunits.unit.util.UNITS;
15  import org.djunits.value.vdouble.scalar.Direction;
16  import org.djunits.value.vdouble.scalar.Frequency;
17  import org.djunits.value.vdouble.scalar.Length;
18  import org.djunits.value.vdouble.scalar.Speed;
19  import org.geotools.data.FileDataStoreFinder;
20  import org.geotools.data.shapefile.ShapefileDataStore;
21  import org.geotools.data.simple.SimpleFeatureCollection;
22  import org.geotools.data.simple.SimpleFeatureIterator;
23  import org.geotools.data.simple.SimpleFeatureSource;
24  import org.geotools.geometry.jts.JTSFactoryFinder;
25  import org.locationtech.jts.geom.Coordinate;
26  import org.locationtech.jts.geom.Geometry;
27  import org.locationtech.jts.geom.GeometryFactory;
28  import org.locationtech.jts.geom.LineString;
29  import org.locationtech.jts.geom.Point;
30  import org.opengis.feature.Property;
31  import org.opengis.feature.simple.SimpleFeature;
32  import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
33  import org.opentrafficsim.core.geometry.OTSGeometryException;
34  import org.opentrafficsim.core.geometry.OTSLine3D;
35  import org.opentrafficsim.core.geometry.OTSPoint3D;
36  import org.opentrafficsim.core.network.Link;
37  import org.opentrafficsim.core.network.LinkType;
38  import org.opentrafficsim.core.network.LongitudinalDirectionality;
39  import org.opentrafficsim.core.network.Network;
40  import org.opentrafficsim.core.network.NetworkException;
41  import org.opentrafficsim.draw.network.LinkAnimation;
42  import org.opentrafficsim.draw.road.LaneAnimation;
43  import org.opentrafficsim.draw.road.ShoulderAnimation;
44  import org.opentrafficsim.road.network.OTSRoadNetwork;
45  import org.opentrafficsim.road.network.lane.CrossSectionLink;
46  import org.opentrafficsim.road.network.lane.Lane;
47  import org.opentrafficsim.road.network.lane.NoTrafficLane;
48  import org.opentrafficsim.road.network.lane.OTSRoadNode;
49  import org.opentrafficsim.road.network.lane.Shoulder;
50  import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
51  
52  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
53  
54  /**
55   * <p>
56   * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
57   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
58   * <p>
59   * $LastChangedDate: 2020-05-08 13:45:24 +0200 (Fri, 08 May 2020) $, @version $Revision: 6465 $, by $Author: pknoppers $,
60   * initial version Sep 11, 2014 <br>
61   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
62   * @author <a href="http://www.citg.tudelft.nl">Guus Tamminga</a>
63   */
64  public final class ShapeFileReader implements UNITS
65  {
66      /** Do not instantiate this class. */
67      private ShapeFileReader()
68      {
69          // Cannot be instantiated.
70      }
71  
72      /**
73       * @param network Network; Network, the network
74       * @param shapeFileName String; the nodes shapefile to read
75       * @param numberType String; ???
76       * @param returnCentroid boolean; if true only loop through the centroid/zones (in case of mixed nodes and centroids)
77       * @param allCentroids boolean; if true: the file contains centroids (a centroid file)
78       * @return map of (shape file) nodes with nodenr as the key
79       * @throws IOException on error
80       */
81      public static Map<String, OTSRoadNode> readNodes(final Network network, final String shapeFileName, final String numberType,
82              final boolean returnCentroid, final boolean allCentroids) throws IOException
83      {
84          /*-
85           * the_geom class com.vividsolutions.jts.geom.Point POINT (190599 325650)
86           * NODENR class java.lang.Long 18
87           * NAME class java.lang.String 
88           * X class java.lang.Double 190599.0
89           * Y class java.lang.Double 325650.0
90           * ...
91           */
92  
93          URL url;
94          if (new File(shapeFileName).canRead())
95          {
96              url = new File(shapeFileName).toURI().toURL();
97          }
98          else
99          {
100             url = ShapeFileReader.class.getResource(shapeFileName);
101         }
102         ShapefileDataStore storeNodes = (ShapefileDataStore) FileDataStoreFinder.getDataStore(url);
103 
104         Map<String, OTSRoadNode> nodes = new LinkedHashMap<>();
105 
106         SimpleFeatureSource featureSourceNodes = storeNodes.getFeatureSource();
107         SimpleFeatureCollection featureCollectionNodes = featureSourceNodes.getFeatures();
108         SimpleFeatureIterator iterator = featureCollectionNodes.features();
109         try
110         {
111             while (iterator.hasNext())
112             {
113                 SimpleFeature feature = iterator.next();
114                 Coordinate coordinate = ((Point) feature.getAttribute("the_geom")).getCoordinate();
115                 String nr = removeQuotes(String.valueOf(feature.getAttribute(numberType)));
116                 boolean addThisNode = false;
117                 if (returnCentroid)
118                 {
119                     if (nr.substring(0, 1).equals("C") || allCentroids)
120                     {
121                         addThisNode = true;
122                     }
123                 }
124                 else
125                 {
126                     if (nr == null)
127                     {
128                         System.out.println("null found");
129                     }
130                     if (!nr.substring(0, 1).equals("C"))
131                     {
132                         addThisNode = true;
133                     }
134                 }
135                 if (addThisNode)
136                 {
137                     OTSRoadNode node =
138                             new OTSRoadNode(network, nr, new OTSPoint3D(coordinate), Direction.instantiateSI(Double.NaN));
139                     nodes.put(nr, node);
140                 }
141             }
142         }
143         catch (Exception problem)
144         {
145             problem.printStackTrace();
146         }
147         finally
148         {
149             iterator.close();
150             storeNodes.dispose();
151         }
152         System.out.println("aantal knopen (353): geteld " + nodes.size());
153         return nodes;
154     }
155 
156     /**
157      * @param number String; number string
158      * @return boolean: true if the number refers to a Centroid; false otherwise
159      */
160     public static boolean inspectNodeCentroid(final String number)
161     {
162         boolean isCentroid = false;
163         String[] names = removeQuotes(number).split(":");
164         String name = names[0];
165         if (name.charAt(0) == 'C')
166         {
167             isCentroid = true;
168         }
169         return isCentroid;
170     }
171 
172     /**
173      * @param network OTSRoadNetwork; the network
174      * @param shapeFileName String; the nodes shapefile to read
175      * @param links Map&lt;String,Link&gt;; : returns the file with real links
176      * @param nodes Map&lt;String,OTSNode&gt;; the map of nodes to retrieve start and end node
177      * @param simulator OTSSimulatorInterface; simulator for the animation registration
178      * @throws IOException on error
179      */
180     public static void readLinks(final OTSRoadNetwork network, final String shapeFileName, final Map<String, Link> links,
181             final Map<String, OTSRoadNode> nodes, final OTSSimulatorInterface simulator) throws IOException
182     {
183         /*-
184          * the_geom class com.vividsolutions.jts.geom.MultiLineString MULTILINESTRING ((232250.38755446894 ...
185          * LINKNR class java.lang.Long 1
186          * NAME class java.lang.String 
187          * DIRECTION class java.lang.Long 1
188          * LENGTH class java.lang.Double 1.80327678
189          * ANODE class java.lang.Long 684088
190          * BNODE class java.lang.Long 1090577263
191          * LINKTAG class java.lang.String 967536
192          * WEGTYPEAB class java.lang.String mvt
193          * TYPEWEGVAB class java.lang.String asw 2x2 (8600)
194          * TYPEWEG_AB class java.lang.String 12 Autosnelweg 2x2
195          * SPEEDAB class java.lang.Double 120.0
196          * CAPACITYAB class java.lang.Double 8600.0
197          * ...
198          */
199 
200         URL url;
201         if (new File(shapeFileName).canRead())
202         {
203             url = new File(shapeFileName).toURI().toURL();
204         }
205         else
206         {
207             url = ShapeFileReader.class.getResource(shapeFileName);
208         }
209 
210         ShapefileDataStore storeLinks = (ShapefileDataStore) FileDataStoreFinder.getDataStore(url);
211         SimpleFeatureSource featureSourceLinks = storeLinks.getFeatureSource();
212         SimpleFeatureCollection featureCollectionLinks = featureSourceLinks.getFeatures();
213         SimpleFeatureIterator iterator = featureCollectionLinks.features();
214 
215         try
216         {
217             while (iterator.hasNext())
218             {
219                 SimpleFeature feature = iterator.next();
220                 GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
221                 Geometry geometry = (Geometry) feature.getAttribute("the_geom");
222                 Coordinate[] coords = geometry.getCoordinates();
223                 LineString line = geometryFactory.createLineString(coords);
224                 String nr = String.valueOf(feature.getAttribute("LINKNR"));
225                 String nrBA = nr + "_BA";
226                 String name = String.valueOf(feature.getAttribute("NAME"));
227                 // the reason to use String.valueOf(...) is that the .dbf files sometimes use double,
228                 // but also represent LENGTH by a string ....
229                 double lengthIn = Double.parseDouble(String.valueOf(feature.getAttribute("LENGTH")));
230                 Length length = new Length(lengthIn, KILOMETER);
231                 short direction = (short) Long.parseLong(String.valueOf(feature.getAttribute("DIRECTION")));
232                 String lNodeA = String.valueOf(feature.getAttribute("ANODE"));
233                 String lNodeB = String.valueOf(feature.getAttribute("BNODE"));
234                 // long lNodeB = NodeCentroidNumber(String.valueOf(feature.getAttribute("BNODE")));
235                 String linkTag = (String) feature.getAttribute("LINKTAG");
236                 String wegtype = (String) feature.getAttribute("WEGTYPEAB");
237                 String typeWegVak = (String) feature.getAttribute("TYPEWEGVAB");
238                 String typeWeg = (String) feature.getAttribute("TYPEWEG_AB");
239                 Double speedIn = Double.parseDouble(String.valueOf(feature.getAttribute("SPEEDAB")));
240                 Speed speed = new Speed(speedIn, KM_PER_HOUR);
241                 double capacityIn = Double.parseDouble(String.valueOf(feature.getAttribute("CAPACITYAB")));
242                 Frequency capacity = new Frequency(capacityIn, PER_HOUR);
243                 // new DoubleScalar.Abs<LengthUnit>(shpLink.getLength(), KILOMETER);
244                 // create the link or connector to a centroid....
245                 OTSRoadNode nodeA = nodes.get(lNodeA);
246                 OTSRoadNode nodeB = nodes.get(lNodeB);
247 
248                 if (nodeA != null && nodeB != null)
249                 {
250                     CrossSectionLink linkAB = null;
251                     CrossSectionLink linkBA = null;
252                     linkAB = new CrossSectionLink(network, nr, nodeA, nodeB, network.getLinkType(LinkType.DEFAULTS.ROAD),
253                             new OTSLine3D(new OTSPoint3D[] {nodeA.getPoint(), nodeB.getPoint()}), LaneKeepingPolicy.KEEPRIGHT);
254                     animate(linkAB, typeWegVak, simulator);
255                     linkBA = new CrossSectionLink(network, nrBA, nodeB, nodeA, network.getLinkType(LinkType.DEFAULTS.ROAD),
256                             new OTSLine3D(new OTSPoint3D[] {nodeB.getPoint(), nodeA.getPoint()}), LaneKeepingPolicy.KEEPRIGHT);
257                     animate(linkBA, typeWegVak, simulator);
258                     if (direction == 1)
259                     {
260                         links.put(nr, linkAB);
261                     }
262                     else if (direction == 2)
263                     {
264                         links.put(nrBA, linkBA);
265                     }
266                     else if (direction == 3)
267                     {
268                         links.put(nr, linkAB);
269                         links.put(nrBA, linkBA);
270                     }
271 
272                 }
273                 else
274                 {
275                     System.out.println("Node lNodeA=" + lNodeA + " or lNodeB=" + lNodeB + " not found for linknr=" + nr
276                             + ", name=" + name);
277                 }
278             }
279 
280         }
281         catch (Exception problem)
282         {
283             problem.printStackTrace();
284         }
285         finally
286         {
287             iterator.close();
288             storeLinks.dispose();
289         }
290 
291     }
292 
293     /**
294      * @param shapeFileName String; the areas shapefile to read
295      * @throws IOException on error
296      */
297     public static void shapeFileInfo(final String shapeFileName) throws IOException
298     {
299         URL url;
300         if (new File(shapeFileName).canRead())
301         {
302             url = new File(shapeFileName).toURI().toURL();
303         }
304         else
305         {
306             url = ShapeFileReader.class.getResource(shapeFileName);
307         }
308         ShapefileDataStore store = (ShapefileDataStore) FileDataStoreFinder.getDataStore(url);
309 
310         SimpleFeatureSource featureSource = store.getFeatureSource();
311         SimpleFeatureCollection featureCollection = featureSource.getFeatures();
312         SimpleFeatureIterator iterator = featureCollection.features();
313         try
314         {
315             while (iterator.hasNext())
316             {
317                 SimpleFeature feature = iterator.next();
318                 Collection<Property> areaProperties = feature.getProperties();
319                 for (Property p : areaProperties)
320                 {
321                     System.out.println(p.getName() + " " + p.getValue().getClass() + " " + p.getValue().toString());
322                 }
323                 return;
324             }
325         }
326         catch (Exception problem)
327         {
328             problem.printStackTrace();
329         }
330         finally
331         {
332             iterator.close();
333             store.dispose();
334         }
335     }
336 
337     /**
338      * @param name String; the name with quotes
339      * @return name without quotes
340      */
341     public static String removeQuotes(final String name)
342     {
343         String newName = name;
344         if (newName.length() >= 2 && newName.charAt(0) == '"' && newName.charAt(newName.length() - 1) == '"')
345         {
346             newName = newName.substring(1, newName.length() - 1);
347         }
348         return newName;
349     }
350 
351     /**
352      * @param link CrossSectionLink; the link
353      * @param wegType String; wegtype
354      * @param simulator SimulatorInterface.TimeDoubleUnit; animator
355      * @throws NamingException in case of context error
356      * @throws RemoteException in case of context error
357      * @throws NetworkException on network inconsistency
358      */
359     private static void animate(final CrossSectionLink link, final String wegType,
360             final SimulatorInterface.TimeDoubleUnit simulator) throws NamingException, NetworkException, RemoteException
361     {
362         // leave out if center line not needed.
363         new LinkAnimation(link, simulator, 0.1f);
364         if (wegType.startsWith("asw") || wegType.startsWith("80"))
365         {
366             int spits = 0;
367             int n = 1;
368             if (wegType.contains("2x2"))
369             {
370                 n = 2;
371             }
372             if (wegType.contains("2x3"))
373             {
374                 n = 3;
375             }
376             if (wegType.contains("2x4"))
377             {
378                 n = 4;
379             }
380             if (wegType.contains("2x5"))
381             {
382                 n = 5;
383             }
384             if (wegType.contains("+ 1") || wegType.contains("+1"))
385             {
386                 spits = 1;
387             }
388             if (wegType.contains("+ 2") || wegType.contains("+2"))
389             {
390                 spits = 2;
391             }
392             addNLanes(n, spits, link, simulator);
393         }
394         if (wegType.startsWith("stads"))
395         {
396             int n = 1;
397             if (wegType.contains("2x2"))
398             {
399                 n = 2;
400             }
401             if (wegType.contains("2x3"))
402             {
403                 n = 3;
404             }
405             boolean middenberm = wegType.contains("met middenberm");
406             addCityStreetLanes(n, middenberm, link, simulator);
407         }
408         else
409         {
410             addCityStreet(link, simulator);
411         }
412     }
413 
414     /**
415      * @param n int; aantal stroken per zijde
416      * @param spits int; aantal spitsstroken
417      * @param link CrossSectionLink; link
418      * @param simulator SimulatorInterface.TimeDoubleUnit; animator
419      * @throws NetworkException on network inconsistency
420      */
421     private static void addNLanes(final int n, final int spits, final CrossSectionLink link,
422             final SimulatorInterface.TimeDoubleUnit simulator) throws NetworkException
423     {
424         // 2 x n lanes, grass underneath, lines between lanes, barrier in center
425         // lane is 3.5 meters wide. gap in middle is one meter. outside 0.5 meters on both sides
426         Length m05 = new Length(0.5, METER);
427         Length m10 = new Length(1.0, METER);
428         Length m35 = new Length(3.5, METER);
429         Speed speedLimit = new Speed(100, KM_PER_HOUR);
430 
431         try
432         {
433             // middenberm
434             Shoulder sM = new Shoulder(link, "sM", new Length(0.0, METER), m10);
435             new ShoulderAnimation(sM, simulator, Color.GREEN);
436             for (int i = -1; i <= 1; i += 2)
437             {
438                 LongitudinalDirectionality dir =
439                         (i < 0) ? LongitudinalDirectionality.DIR_PLUS : LongitudinalDirectionality.DIR_MINUS;
440                 // TODO Lane doesn't take direction anymore, this is given in the LaneType
441                 String lr = i < 0 ? "L" : "R";
442                 //
443                 Lane laneEM = new NoTrafficLane(link, lr + "." + "EM", new Length(i * 0.75, METER), new Length(i * 0.75, METER),
444                         m05, m05);
445                 new LaneAnimation(laneEM, simulator, Color.LIGHT_GRAY);
446                 double lat = 1;
447                 for (int j = 0; j < n; j++)
448                 {
449                     lat += i * 1.75;
450                     Lane lane = new Lane(link, "lane." + lr + "." + j, new Length(lat, METER), new Length(lat, METER), m35, m35,
451                             null, speedLimit);
452                     new LaneAnimation(lane, simulator, Color.GRAY);
453                     lat += i * 1.75;
454                 }
455                 // spitsstroken
456                 for (int j = 0; j < spits; j++)
457                 {
458                     lat += i * 1.75;
459                     Lane lane = new NoTrafficLane(link, "extra." + lr + "." + j, new Length(lat, METER), new Length(lat, METER),
460                             m35, m35);
461                     new LaneAnimation(lane, simulator, Color.LIGHT_GRAY);
462                     lat += i * 1.75;
463                 }
464                 Lane laneEO = new NoTrafficLane(link, lr + "." + "EO", new Length(lat + i * 0.25, METER),
465                         new Length(lat + i * 0.25, METER), m05, m05);
466                 new LaneAnimation(laneEO, simulator, Color.LIGHT_GRAY);
467                 lat += i * 0.5;
468                 Shoulder sO = new Shoulder(link, lr + "." + "sO", new Length(lat, METER), m10);
469                 new ShoulderAnimation(sO, simulator, Color.GREEN);
470             }
471         }
472         catch (NamingException | RemoteException | OTSGeometryException ne)
473         {
474             //
475         }
476     }
477 
478     /**
479      * @param n int; aantal stroken per zijde
480      * @param middenberm boolean; aanwezig of niet
481      * @param link CrossSectionLink; link
482      * @param simulator SimulatorInterface.TimeDoubleUnit; animator
483      * @throws NetworkException on network inconsistency
484      */
485     private static void addCityStreetLanes(final int n, final boolean middenberm, final CrossSectionLink link,
486             final SimulatorInterface.TimeDoubleUnit simulator) throws NetworkException
487     {
488         // 2 x n lanes, grass underneath, lines between lanes, barrier in center
489         // lane is 3.0 meters wide. gap in middle is one meter. outside 0.5 meters on both sides
490         Length m10 = new Length(1.0, METER);
491         Length m30 = new Length(3.0, METER);
492         Speed speedLimit = new Speed(100, KM_PER_HOUR);
493 
494         try
495         {
496             if (middenberm)
497             {
498                 Shoulder sM = new Shoulder(link, "sM", new Length(0.0, METER), m10);
499                 new ShoulderAnimation(sM, simulator, Color.GREEN);
500             }
501             for (int i = -1; i <= 1; i += 2)
502             {
503                 // TODO Lane doesn't take direction anymore, this is given in the LaneType
504                 LongitudinalDirectionality dir =
505                         (i < 0) ? LongitudinalDirectionality.DIR_PLUS : LongitudinalDirectionality.DIR_MINUS;
506                 double lat = middenberm ? 0.5 : 0.0;
507                 for (int j = 0; j < n; j++)
508                 {
509                     lat += i * 1.5;
510                     String lr = i < 0 ? "L" : "R";
511                     Lane lane = new Lane(link, "lane." + lr + "." + j, new Length(lat, METER), new Length(lat, METER), m30, m30,
512                             null, speedLimit);
513                     new LaneAnimation(lane, simulator, Color.DARK_GRAY);
514                     lat += i * 1.5;
515                 }
516             }
517         }
518         catch (NamingException | RemoteException | ArrayIndexOutOfBoundsException | OTSGeometryException ne)
519         {
520             ne.printStackTrace();
521         }
522     }
523 
524     /**
525      * @param link CrossSectionLink; link
526      * @param simulator SimulatorInterface.TimeDoubleUnit; animator
527      * @throws NetworkException on network inconsistency
528      */
529     private static void addCityStreet(final CrossSectionLink link, final SimulatorInterface.TimeDoubleUnit simulator)
530             throws NetworkException
531     {
532         Length m60 = new Length(6.0, METER);
533         Speed speedLimit = new Speed(100, KM_PER_HOUR);
534 
535         try
536         {
537             Lane lane = new Lane(link, "lane", new Length(0.0, METER), new Length(0.0, METER), m60, m60, null, speedLimit);
538             new LaneAnimation(lane, simulator, Color.DARK_GRAY);
539         }
540         catch (NamingException | RemoteException | OTSGeometryException ne)
541         {
542             //
543         }
544     }
545 
546 }