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