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