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.UNITS;
15  import org.djunits.value.vdouble.scalar.Frequency;
16  import org.djunits.value.vdouble.scalar.Length;
17  import org.djunits.value.vdouble.scalar.Speed;
18  import org.geotools.data.FileDataStoreFinder;
19  import org.geotools.data.shapefile.ShapefileDataStore;
20  import org.geotools.data.simple.SimpleFeatureCollection;
21  import org.geotools.data.simple.SimpleFeatureIterator;
22  import org.geotools.data.simple.SimpleFeatureSource;
23  import org.geotools.geometry.jts.JTSFactoryFinder;
24  import org.opengis.feature.Property;
25  import org.opengis.feature.simple.SimpleFeature;
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.LinkType;
32  import org.opentrafficsim.core.network.LongitudinalDirectionality;
33  import org.opentrafficsim.core.network.Network;
34  import org.opentrafficsim.core.network.NetworkException;
35  import org.opentrafficsim.core.network.OTSNode;
36  import org.opentrafficsim.draw.network.LinkAnimation;
37  import org.opentrafficsim.draw.road.LaneAnimation;
38  import org.opentrafficsim.draw.road.ShoulderAnimation;
39  import org.opentrafficsim.road.network.lane.CrossSectionLink;
40  import org.opentrafficsim.road.network.lane.Lane;
41  import org.opentrafficsim.road.network.lane.NoTrafficLane;
42  import org.opentrafficsim.road.network.lane.Shoulder;
43  import org.opentrafficsim.road.network.lane.changing.LaneKeepingPolicy;
44  import org.opentrafficsim.road.network.lane.changing.OvertakingConditions;
45  
46  import com.vividsolutions.jts.geom.Coordinate;
47  import com.vividsolutions.jts.geom.Geometry;
48  import com.vividsolutions.jts.geom.GeometryFactory;
49  import com.vividsolutions.jts.geom.LineString;
50  import com.vividsolutions.jts.geom.Point;
51  
52  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
53  
54  /**
55   * <p>
56   * Copyright (c) 2013-2019 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: 2019-01-06 01:35:05 +0100 (Sun, 06 Jan 2019) $, @version $Revision: 4831 $, by $Author: averbraeck $,
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, OTSNode> 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, OTSNode> nodes = new HashMap<>();
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                     OTSNode node = new OTSNode(network, nr, new OTSPoint3D(coordinate));
138                     nodes.put(nr, node);
139                 }
140             }
141         }
142         catch (Exception problem)
143         {
144             problem.printStackTrace();
145         }
146         finally
147         {
148             iterator.close();
149             storeNodes.dispose();
150         }
151         System.out.println("aantal knopen (353): geteld " + nodes.size());
152         return nodes;
153     }
154 
155     /**
156      * @param number String; number string
157      * @return boolean: true if the number refers to a Centroid; false otherwise
158      */
159     public static boolean inspectNodeCentroid(final String number)
160     {
161         boolean isCentroid = false;
162         String[] names = removeQuotes(number).split(":");
163         String name = names[0];
164         if (name.charAt(0) == 'C')
165         {
166             isCentroid = true;
167         }
168         return isCentroid;
169     }
170 
171     /**
172      * @param network Network; the network
173      * @param shapeFileName String; the nodes shapefile to read
174      * @param links Map&lt;String,Link&gt;; : returns the file with real links
175      * @param nodes Map&lt;String,OTSNode&gt;; the map of nodes to retrieve start and end node
176      * @param simulator OTSSimulatorInterface; simulator for the animation registration
177      * @throws IOException on error
178      */
179     public static void readLinks(final Network network, final String shapeFileName, final Map<String, Link> links,
180             final Map<String, OTSNode> nodes, final OTSSimulatorInterface simulator) throws IOException
181     {
182         /*-
183          * the_geom class com.vividsolutions.jts.geom.MultiLineString MULTILINESTRING ((232250.38755446894 ...
184          * LINKNR class java.lang.Long 1
185          * NAME class java.lang.String 
186          * DIRECTION class java.lang.Long 1
187          * LENGTH class java.lang.Double 1.80327678
188          * ANODE class java.lang.Long 684088
189          * BNODE class java.lang.Long 1090577263
190          * LINKTAG class java.lang.String 967536
191          * WEGTYPEAB class java.lang.String mvt
192          * TYPEWEGVAB class java.lang.String asw 2x2 (8600)
193          * TYPEWEG_AB class java.lang.String 12 Autosnelweg 2x2
194          * SPEEDAB class java.lang.Double 120.0
195          * CAPACITYAB class java.lang.Double 8600.0
196          * ...
197          */
198 
199         URL url;
200         if (new File(shapeFileName).canRead())
201         {
202             url = new File(shapeFileName).toURI().toURL();
203         }
204         else
205         {
206             url = ShapeFileReader.class.getResource(shapeFileName);
207         }
208 
209         ShapefileDataStore storeLinks = (ShapefileDataStore) FileDataStoreFinder.getDataStore(url);
210         SimpleFeatureSource featureSourceLinks = storeLinks.getFeatureSource();
211         SimpleFeatureCollection featureCollectionLinks = featureSourceLinks.getFeatures();
212         SimpleFeatureIterator iterator = featureCollectionLinks.features();
213 
214         try
215         {
216             while (iterator.hasNext())
217             {
218                 SimpleFeature feature = iterator.next();
219                 GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
220                 Geometry geometry = (Geometry) feature.getAttribute("the_geom");
221                 Coordinate[] coords = geometry.getCoordinates();
222                 LineString line = geometryFactory.createLineString(coords);
223                 String nr = String.valueOf(feature.getAttribute("LINKNR"));
224                 String nrBA = nr + "_BA";
225                 String name = String.valueOf(feature.getAttribute("NAME"));
226                 // the reason to use String.valueOf(...) is that the .dbf files sometimes use double,
227                 // but also represent LENGTH by a string ....
228                 double lengthIn = Double.parseDouble(String.valueOf(feature.getAttribute("LENGTH")));
229                 Length length = new Length(lengthIn, KILOMETER);
230                 short direction = (short) Long.parseLong(String.valueOf(feature.getAttribute("DIRECTION")));
231                 String lNodeA = String.valueOf(feature.getAttribute("ANODE"));
232                 String lNodeB = String.valueOf(feature.getAttribute("BNODE"));
233                 // long lNodeB = NodeCentroidNumber(String.valueOf(feature.getAttribute("BNODE")));
234                 String linkTag = (String) feature.getAttribute("LINKTAG");
235                 String wegtype = (String) feature.getAttribute("WEGTYPEAB");
236                 String typeWegVak = (String) feature.getAttribute("TYPEWEGVAB");
237                 String typeWeg = (String) feature.getAttribute("TYPEWEG_AB");
238                 Double speedIn = Double.parseDouble(String.valueOf(feature.getAttribute("SPEEDAB")));
239                 Speed speed = new Speed(speedIn, KM_PER_HOUR);
240                 double capacityIn = Double.parseDouble(String.valueOf(feature.getAttribute("CAPACITYAB")));
241                 Frequency capacity = new Frequency(capacityIn, PER_HOUR);
242                 // new DoubleScalar.Abs<LengthUnit>(shpLink.getLength(), KILOMETER);
243                 // create the link or connector to a centroid....
244                 OTSNode nodeA = nodes.get(lNodeA);
245                 OTSNode nodeB = nodes.get(lNodeB);
246 
247                 if (nodeA != null && nodeB != null)
248                 {
249                     CrossSectionLink linkAB = null;
250                     CrossSectionLink linkBA = null;
251                     linkAB = new CrossSectionLink(network, nr, nodeA, nodeB, LinkType.ROAD,
252                             new OTSLine3D(new OTSPoint3D[] { nodeA.getPoint(), nodeB.getPoint() }), simulator,
253                             LaneKeepingPolicy.KEEP_RIGHT);
254                     animate(linkAB, typeWegVak, simulator);
255                     linkBA = new CrossSectionLink(network, nrBA, nodeB, nodeA, LinkType.ROAD,
256                             new OTSLine3D(new OTSPoint3D[] { nodeB.getPoint(), nodeA.getPoint() }), simulator,
257                             LaneKeepingPolicy.KEEP_RIGHT);
258                     animate(linkBA, typeWegVak, simulator);
259                     if (direction == 1)
260                     {
261                         links.put(nr, linkAB);
262                     }
263                     else if (direction == 2)
264                     {
265                         links.put(nrBA, linkBA);
266                     }
267                     else if (direction == 3)
268                     {
269                         links.put(nr, linkAB);
270                         links.put(nrBA, linkBA);
271                     }
272 
273                 }
274                 else
275                 {
276                     System.out.println("Node lNodeA=" + lNodeA + " or lNodeB=" + lNodeB + " not found for linknr=" + nr
277                             + ", name=" + name);
278                 }
279             }
280 
281         }
282         catch (Exception problem)
283         {
284             problem.printStackTrace();
285         }
286         finally
287         {
288             iterator.close();
289             storeLinks.dispose();
290         }
291 
292     }
293 
294     /**
295      * @param shapeFileName String; the areas shapefile to read
296      * @throws IOException on error
297      */
298     public static void shapeFileInfo(final String shapeFileName) throws IOException
299     {
300         URL url;
301         if (new File(shapeFileName).canRead())
302         {
303             url = new File(shapeFileName).toURI().toURL();
304         }
305         else
306         {
307             url = ShapeFileReader.class.getResource(shapeFileName);
308         }
309         ShapefileDataStore store = (ShapefileDataStore) FileDataStoreFinder.getDataStore(url);
310 
311         SimpleFeatureSource featureSource = store.getFeatureSource();
312         SimpleFeatureCollection featureCollection = featureSource.getFeatures();
313         SimpleFeatureIterator iterator = featureCollection.features();
314         try
315         {
316             while (iterator.hasNext())
317             {
318                 SimpleFeature feature = iterator.next();
319                 Collection<Property> areaProperties = feature.getProperties();
320                 for (Property p : areaProperties)
321                 {
322                     System.out.println(p.getName() + " " + p.getValue().getClass() + " " + p.getValue().toString());
323                 }
324                 return;
325             }
326         }
327         catch (Exception problem)
328         {
329             problem.printStackTrace();
330         }
331         finally
332         {
333             iterator.close();
334             store.dispose();
335         }
336     }
337 
338     /**
339      * @param name String; the name with quotes
340      * @return name without quotes
341      */
342     public static String removeQuotes(final String name)
343     {
344         String newName = name;
345         if (newName.length() >= 2 && newName.charAt(0) == '"' && newName.charAt(newName.length() - 1) == '"')
346         {
347             newName = newName.substring(1, newName.length() - 1);
348         }
349         return newName;
350     }
351 
352     /**
353      * @param link CrossSectionLink; the link
354      * @param wegType String; wegtype
355      * @param simulator SimulatorInterface.TimeDoubleUnit; animator
356      * @throws NamingException in case of context error
357      * @throws RemoteException in case of context error
358      * @throws NetworkException on network inconsistency
359      */
360     private static void animate(final CrossSectionLink link, final String wegType,
361             final SimulatorInterface.TimeDoubleUnit simulator) 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 int; aantal stroken per zijde
417      * @param spits int; aantal spitsstroken
418      * @param link CrossSectionLink; link
419      * @param simulator SimulatorInterface.TimeDoubleUnit; animator
420      * @throws NetworkException on network inconsistency
421      */
422     private static void addNLanes(final int n, final int spits, final CrossSectionLink link,
423             final SimulatorInterface.TimeDoubleUnit 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 m05 = new Length(0.5, METER);
428         Length m10 = new Length(1.0, METER);
429         Length m35 = new Length(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(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                 // TODO Lane doesn't take direction anymore, this is given in the LaneType
442                 String lr = i < 0 ? "L" : "R";
443                 //
444                 Lane laneEM = new NoTrafficLane(link, lr + "." + "EM", new Length(i * 0.75, METER), new Length(i * 0.75, METER),
445                         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 = new Lane(link, "lane." + lr + "." + j, new Length(lat, METER), new Length(lat, METER), m35, m35,
452                             null, speedLimit, new OvertakingConditions.LeftAndRight());
453                     new LaneAnimation(lane, simulator, Color.GRAY, false);
454                     lat += i * 1.75;
455                 }
456                 // spitsstroken
457                 for (int j = 0; j < spits; j++)
458                 {
459                     lat += i * 1.75;
460                     Lane lane = new NoTrafficLane(link, "extra." + lr + "." + j, new Length(lat, METER), new Length(lat, METER),
461                             m35, m35);
462                     new LaneAnimation(lane, simulator, Color.LIGHT_GRAY, false);
463                     lat += i * 1.75;
464                 }
465                 Lane laneEO = new NoTrafficLane(link, lr + "." + "EO", new Length(lat + i * 0.25, METER),
466                         new Length(lat + i * 0.25, METER), m05, m05);
467                 new LaneAnimation(laneEO, simulator, Color.LIGHT_GRAY, false);
468                 lat += i * 0.5;
469                 Shoulder sO = new Shoulder(link, lr + "." + "sO", new Length(lat, METER), m10);
470                 new ShoulderAnimation(sO, simulator, Color.GREEN);
471             }
472         }
473         catch (NamingException | RemoteException | OTSGeometryException ne)
474         {
475             //
476         }
477     }
478 
479     /**
480      * @param n int; aantal stroken per zijde
481      * @param middenberm boolean; aanwezig of niet
482      * @param link CrossSectionLink; link
483      * @param simulator SimulatorInterface.TimeDoubleUnit; animator
484      * @throws NetworkException on network inconsistency
485      */
486     private static void addCityStreetLanes(final int n, final boolean middenberm, final CrossSectionLink link,
487             final SimulatorInterface.TimeDoubleUnit simulator) throws NetworkException
488     {
489         // 2 x n lanes, grass underneath, lines between lanes, barrier in center
490         // lane is 3.0 meters wide. gap in middle is one meter. outside 0.5 meters on both sides
491         Length m10 = new Length(1.0, METER);
492         Length m30 = new Length(3.0, METER);
493         Speed speedLimit = new Speed(100, KM_PER_HOUR);
494 
495         try
496         {
497             if (middenberm)
498             {
499                 Shoulder sM = new Shoulder(link, "sM", new Length(0.0, METER), m10);
500                 new ShoulderAnimation(sM, simulator, Color.GREEN);
501             }
502             for (int i = -1; i <= 1; i += 2)
503             {
504                 // TODO Lane doesn't take direction anymore, this is given in the LaneType
505                 LongitudinalDirectionality dir =
506                         (i < 0) ? LongitudinalDirectionality.DIR_PLUS : LongitudinalDirectionality.DIR_MINUS;
507                 double lat = middenberm ? 0.5 : 0.0;
508                 for (int j = 0; j < n; j++)
509                 {
510                     lat += i * 1.5;
511                     String lr = i < 0 ? "L" : "R";
512                     Lane lane = new Lane(link, "lane." + lr + "." + j, new Length(lat, METER), new Length(lat, METER), m30, m30,
513                             null, speedLimit, new OvertakingConditions.LeftAndRight());
514                     new LaneAnimation(lane, simulator, Color.DARK_GRAY, false);
515                     lat += i * 1.5;
516                 }
517             }
518         }
519         catch (NamingException | RemoteException | ArrayIndexOutOfBoundsException | OTSGeometryException ne)
520         {
521             ne.printStackTrace();
522         }
523     }
524 
525     /**
526      * @param link CrossSectionLink; link
527      * @param simulator SimulatorInterface.TimeDoubleUnit; animator
528      * @throws NetworkException on network inconsistency
529      */
530     private static void addCityStreet(final CrossSectionLink link, final SimulatorInterface.TimeDoubleUnit simulator)
531             throws NetworkException
532     {
533         Length m60 = new Length(6.0, METER);
534         Speed speedLimit = new Speed(100, KM_PER_HOUR);
535 
536         try
537         {
538             Lane lane = new Lane(link, "lane", new Length(0.0, METER), new Length(0.0, METER), m60, m60, null, speedLimit,
539                     new OvertakingConditions.LeftAndRight());
540             new LaneAnimation(lane, simulator, Color.DARK_GRAY, false);
541         }
542         catch (NamingException | RemoteException | OTSGeometryException ne)
543         {
544             //
545         }
546     }
547 
548 }