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