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