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