View Javadoc
1   package nl.tudelft.simulation.dsol.animation.gis.osm;
2   
3   import java.awt.Color;
4   import java.io.IOException;
5   import java.io.InputStreamReader;
6   import java.io.Reader;
7   import java.net.URL;
8   import java.util.ArrayList;
9   import java.util.Iterator;
10  import java.util.LinkedHashMap;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.Set;
14  
15  import org.djutils.exceptions.Throw;
16  
17  import de.siegmar.fastcsv.reader.NamedCsvReader;
18  import de.siegmar.fastcsv.reader.NamedCsvRow;
19  import nl.tudelft.simulation.dsol.animation.gis.FeatureInterface;
20  import nl.tudelft.simulation.dsol.animation.gis.GisMapInterface;
21  import nl.tudelft.simulation.dsol.animation.gis.LayerInterface;
22  import nl.tudelft.simulation.dsol.animation.gis.Style;
23  import nl.tudelft.simulation.dsol.animation.gis.map.Feature;
24  import nl.tudelft.simulation.dsol.animation.gis.map.GisMap;
25  import nl.tudelft.simulation.dsol.animation.gis.map.Layer;
26  import nl.tudelft.simulation.dsol.animation.gis.parser.ColorParser;
27  import nl.tudelft.simulation.dsol.animation.gis.transform.CoordinateTransform;
28  
29  /**
30   * This class parses a CSV file that defines which elements of an OpenSTreetMap file need to be drawn and what format to use.
31   * <p>
32   * Copyright (c) 2020-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
33   * for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The DSOL
34   * project is distributed under a three-clause BSD-style license, which can be found at
35   * <a href="https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
36   * </p>
37   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
38   */
39  public final class OsmFileCsvParser
40  {
41      /** Utility class, no constructor. */
42      private OsmFileCsvParser()
43      {
44          // Utility class
45      }
46  
47      /**
48       * Parses a CSV file with information about the map and layers.
49       * @param csvUrl the url of the CSV file.
50       * @param osmUrl the OpenStreetMap file in pbf, osm.gz or osm.bz2 format
51       * @param mapName the human readable name of the map
52       * @return MapInterface the parsed map file.
53       * @throws IOException on failure
54       */
55      public static GisMapInterface parseMapFile(final URL csvUrl, final URL osmUrl, final String mapName) throws IOException
56      {
57          return parseMapFile(csvUrl, osmUrl, mapName, new CoordinateTransform.NoTransform());
58      }
59  
60      /**
61       * Parses a CSV file with information about the map and layers.
62       * @param csvUrl the url of the CSV file.
63       * @param osmUrl the OpenStreetMap file in pbf, osm.gz or osm.bz2 format
64       * @param mapName the human readable name of the map
65       * @param coordinateTransform the transformation of (x, y) coordinates to (x', y') coordinates.
66       * @return MapInterface the parsed map file.
67       * @throws IOException on failure
68       */
69      public static GisMapInterface parseMapFile(final URL csvUrl, final URL osmUrl, final String mapName,
70              final CoordinateTransform coordinateTransform) throws IOException
71      {
72          return parseMapFile(csvUrl, osmUrl, mapName, coordinateTransform, ',', '"');
73      }
74  
75      /**
76       * Parses a CSV file with information about the map and layers. FieldSeparateor can be, for instance \t for a tab character,
77       * and the quote character can be the double quote, the single quote, or something else.
78       * @param csvUrl the url of the CSV file.
79       * @param osmUrl the OpenStreetMap file in pbf, osm.gz or osm.bz2 format
80       * @param mapName the human readable name of the map
81       * @param coordinateTransform the transformation of (x, y) coordinates to (x', y') coordinates.
82       * @param fieldSeparator field separator, e.g., comma for csv files and tab for tsv files
83       * @param quoteCharacter e.g., single or double quote
84       * @return MapInterface the parsed map file.
85       * @throws IOException on failure reading the CSV file, the shape files, or making the layers
86       * @throws IllegalArgumentException when one of the outline or fill colors can not be parsed
87       * @throws NumberFormatException when one of colors contains an illegal number
88       */
89      public static GisMapInterface parseMapFile(final URL csvUrl, final URL osmUrl, final String mapName,
90              final CoordinateTransform coordinateTransform, final char fieldSeparator, final char quoteCharacter)
91              throws IOException
92      {
93          GisMapInterface map = new GisMap();
94          map.setName(mapName);
95          List<String> layerNames = new ArrayList<>(); // to get index # in case layers are not sorted in the file
96          List<LayerInterface> layerList = new ArrayList<>(); // to pass to Map
97          List<FeatureInterface> featuresToRead = new ArrayList<>();
98  
99          Reader reader = new InputStreamReader(csvUrl.openStream());
100         Throw.when(reader == null, IOException.class, "Cannot find CSV file with OSM shape file information at " + csvUrl);
101         NamedCsvReader csvReader =
102                 NamedCsvReader.builder().fieldSeparator(fieldSeparator).quoteCharacter(quoteCharacter).build(reader);
103         Set<String> header = csvReader.getHeader();
104         if (!header.contains("layer") || !header.contains("key") || !header.contains("value")
105                 || !header.contains("outlineColor") || !header.contains("fillColor") || !header.contains("display")
106                 || !header.contains("transform") || !header.contains("zIndex") || !header.contains("scale"))
107         {
108             throw new IOException("OSM GIS map csv-file header row did not contain all column headers\n" + header.toString());
109         }
110 
111         Iterator<NamedCsvRow> it = csvReader.iterator();
112         while (it.hasNext())
113         {
114             NamedCsvRow row = it.next();
115 
116             String layerName = row.getField("layer");
117             String key = row.getField("key");
118             String value = row.getField("value");
119             Color outlineColor = ColorParser.parse(row.getField("outlineColor"));
120             Color fillColor = ColorParser.parse(row.getField("fillColor"));
121             boolean display = row.getField("display").toLowerCase().startsWith("t");
122             boolean transform = row.getField("transform").toLowerCase().startsWith("t");
123             double zIndex = Double.parseDouble(row.getField("zIndex"));
124             double scaleThresholdMetersPerPx = Double.parseDouble(row.getField("scale"));
125             
126             if (header.contains("filter"))
127             {
128                 String filter = row.getField("filter");
129             }
130             
131             Map<String, Number> styleMap = new LinkedHashMap<>();
132             if (header.contains("style"))
133             {
134                 String styleStr = row.getField("style");
135                 String[] styles = styleStr.split(";");
136                 for(var style : styles)
137                 {
138                     String[] kv = style.strip().split("=");
139                     String k = kv[0].strip();
140                     String v = kv.length > 1 ? kv[1].strip() : "1";
141                     Number n = Double.valueOf(v);
142                     styleMap.put(k, n);
143                 }
144             }
145 
146             LayerInterface layer = null;
147             if (layerNames.contains(layerName))
148             {
149                 layer = layerList.get(layerNames.indexOf(layerName));
150             }
151             else
152             {
153                 layer = new Layer();
154                 layerList.add(layer);
155                 layer.setName(layerName);
156                 layerNames.add(layerName);
157             }
158 
159             Feature feature = new Feature(layer);
160             feature.setKey(key);
161             feature.setValue(value);
162             Style shapeStyle = new Style();
163             feature.setShapeStyle(shapeStyle);
164             shapeStyle.setOutlineColor(outlineColor);
165             shapeStyle.setFillColor(fillColor);
166             feature.setZIndex(zIndex);
167             shapeStyle.setScaleThresholdMetersPerPx(scaleThresholdMetersPerPx);
168             if (styleMap.containsKey("lineWidthPx"))
169                 shapeStyle.setLineWidthPx(styleMap.get("lineWidthPx").intValue());
170             if (styleMap.containsKey("lineWidthM"))
171                 shapeStyle.setLineWidthM(styleMap.get("lineWidthM").doubleValue());
172             layer.addFeature(feature);
173             featuresToRead.add(feature);
174             layer.setDisplay(display);
175             layer.setTransform(transform);
176         }
177         reader.close();
178         csvReader.close();
179 
180         map.setLayers(layerList);
181         OsmFileReader osmReader = new OsmFileReader(osmUrl, coordinateTransform, featuresToRead);
182         osmReader.populateShapes();
183         return map;
184     }
185 
186 }