1 package nl.tudelft.simulation.dsol.animation.gis.esri;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.io.IOException;
6 import java.net.URL;
7 import java.util.ArrayList;
8 import java.util.List;
9
10 import javax.xml.parsers.DocumentBuilder;
11 import javax.xml.parsers.DocumentBuilderFactory;
12 import javax.xml.parsers.ParserConfigurationException;
13
14 import org.djutils.draw.bounds.Bounds2d;
15 import org.djutils.io.URLResource;
16 import org.w3c.dom.DOMException;
17 import org.w3c.dom.Document;
18 import org.w3c.dom.Element;
19 import org.w3c.dom.Node;
20 import org.w3c.dom.NodeList;
21 import org.xml.sax.SAXException;
22
23 import nl.tudelft.simulation.dsol.animation.gis.GisMapInterface;
24 import nl.tudelft.simulation.dsol.animation.gis.LayerInterface;
25 import nl.tudelft.simulation.dsol.animation.gis.MapImageInterface;
26 import nl.tudelft.simulation.dsol.animation.gis.MapUnits;
27 import nl.tudelft.simulation.dsol.animation.gis.map.Feature;
28 import nl.tudelft.simulation.dsol.animation.gis.map.GisMap;
29 import nl.tudelft.simulation.dsol.animation.gis.map.Layer;
30 import nl.tudelft.simulation.dsol.animation.gis.map.MapImage;
31 import nl.tudelft.simulation.dsol.animation.gis.transform.CoordinateTransform;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public final class EsriFileXmlParser
48 {
49
50 public static final URL MAPFILE_SCHEMA = URLResource.getResource("/resources/mapfile.xsd");
51
52
53 private EsriFileXmlParser()
54 {
55
56 }
57
58
59
60
61
62
63
64 public static GisMapInterface parseMapFile(final URL url) throws IOException
65 {
66 return parseMapFile(url, new CoordinateTransform.NoTransform());
67 }
68
69
70
71
72
73
74
75
76 public static GisMapInterface parseMapFile(final URL url, final CoordinateTransform coordinateTransform) throws IOException
77 {
78 try
79 {
80 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
81 DocumentBuilder builder = factory.newDocumentBuilder();
82 Document document = builder.parse(url.openStream());
83 document.getDocumentElement().normalize();
84 Element root = document.getDocumentElement();
85
86 GisMapInterface map = new GisMap();
87
88
89 map.setName(nodeText(root, "name"));
90
91
92 if (nodeTagExists(root, "units"))
93 {
94 map.setUnits(parseUnits(nodeText(root, "units")));
95 }
96
97
98 map.setExtent(parseExtent(nodeTagItem(root, "extent", 0), coordinateTransform));
99
100
101 if (nodeTagExists(root, "image"))
102 {
103 map.setImage(parseImage(nodeTagItem(root, "image", 0)));
104 }
105
106
107 map.setLayers(parseLayers(root.getElementsByTagName("layer"), coordinateTransform));
108
109 return map;
110 }
111 catch (DOMException | SAXException | ParserConfigurationException exception)
112 {
113 throw new IOException(exception);
114 }
115 }
116
117
118
119
120
121
122 @SuppressWarnings("checkstyle:needbraces")
123 private static MapUnits parseUnits(final String units)
124 {
125 if (units.equals("feet"))
126 return MapUnits.FEET;
127 if (units.equals("dd"))
128 return MapUnits.DECIMAL_DEGREES;
129 if (units.equals("inches"))
130 return MapUnits.INCHES;
131 if (units.equals("kilometers"))
132 return MapUnits.KILOMETERS;
133 if (units.equals("meters"))
134 return MapUnits.METERS;
135 if (units.equals("miles"))
136 return MapUnits.MILES;
137 return MapUnits.METERS;
138 }
139
140
141
142
143
144
145
146
147 private static Bounds2d parseExtent(final Node node, final CoordinateTransform coordinateTransform) throws IOException
148 {
149 try
150 {
151 double minX = nodeDouble(node, "minX");
152 double minY = nodeDouble(node, "minY");
153 double maxX = nodeDouble(node, "maxX");
154 double maxY = nodeDouble(node, "maxY");
155
156 double[] p = coordinateTransform.doubleTransform(minX, minY);
157 double[] q = coordinateTransform.doubleTransform(maxX, maxY);
158 minX = Math.min(p[0], q[0]);
159 minY = Math.min(p[1], q[1]);
160 maxX = Math.max(p[0], q[0]);
161 maxY = Math.max(p[1], q[1]);
162 return new Bounds2d(minX, maxX, minY, maxY);
163 }
164 catch (Exception exception)
165 {
166 throw new IOException(exception);
167 }
168 }
169
170
171
172
173
174
175
176 @SuppressWarnings("checkstyle:needbraces")
177 private static MapImageInterface parseImage(final Node node) throws IOException
178 {
179 Element element = (Element) node;
180 MapImageInterface mapImage = new MapImage();
181 try
182 {
183 if (nodeTagExists(node, "backgroundColor"))
184 mapImage.setBackgroundColor(parseColor(nodeTagItem(element, "backgroundColor", 0)));
185 if (nodeTagExists(node, "size"))
186 mapImage.setSize(parseDimension(nodeTagItem(element, "size", 0)));
187 return mapImage;
188 }
189 catch (Exception exception)
190 {
191 throw new IOException(exception);
192 }
193 }
194
195
196
197
198
199
200
201 private static Color parseColor(final Node node) throws IOException
202 {
203 try
204 {
205 int r = nodeInt(node, "r");
206 int g = nodeInt(node, "g");
207 int b = nodeInt(node, "b");
208 if (nodeTagExists(node, "a"))
209 {
210 int a = nodeInt(node, "a");
211 return new Color(r, g, b, a);
212 }
213 return new Color(r, g, b);
214 }
215 catch (Exception exception)
216 {
217 throw new IOException(exception.getMessage());
218 }
219 }
220
221
222
223
224
225
226
227 private static Dimension parseDimension(final Node node) throws IOException
228 {
229 try
230 {
231 int width = nodeInt(node, "width");
232 int height = nodeInt(node, "height");
233 return new Dimension(width, height);
234 }
235 catch (Exception exception)
236 {
237 throw new IOException(exception.getMessage());
238 }
239 }
240
241
242
243
244
245
246
247
248 private static List<LayerInterface> parseLayers(final NodeList layerNodeList, final CoordinateTransform coordinateTransform)
249 throws IOException
250 {
251 try
252 {
253 List<LayerInterface> layerList = new ArrayList<>();
254 for (int i = 0; i < layerNodeList.getLength(); i++)
255 {
256 Node layerNode = layerNodeList.item(i);
257 LayerInterface layer = new Layer();
258 layerList.add(layer);
259 layer.setName(nodeText(layerNode, "name"));
260 Feature feature = new Feature();
261 layer.addFeature(feature);
262
263 Node dataNode = nodeTagItem(layerNode, "data", 0);
264 if (nodeTagExists(dataNode, "shapeFile"))
265 {
266 String resourceName = nodeText(dataNode, "shapeFile");
267 URL resource = URLResource.getResource(resourceName);
268 if (resource == null)
269 {
270 throw new IOException("Cannot locate shapeFile: " + resourceName);
271 }
272 ShapeFileReader dataSource = new ShapeFileReader(resource, coordinateTransform, layer.getFeatures());
273 dataSource.populateShapes();
274 }
275
276
277
278
279
280
281
282
283
284
285
286 if (nodeTagExists(layerNode, "fillColor"))
287 {
288 feature.setFillColor(parseColor(nodeTagItem(layerNode, "fillColor", 0)));
289 }
290 if (nodeTagExists(layerNode, "outlineColor"))
291 {
292 feature.setOutlineColor(parseColor(nodeTagItem(layerNode, "outlineColor", 0)));
293 }
294 if (nodeTagExists(layerNode, "display"))
295 {
296 layer.setDisplay(nodeBoolean(layerNode, "display"));
297 }
298 if (nodeTagExists(layerNode, "transform"))
299 {
300 layer.setTransform(nodeBoolean(layerNode, "transform"));
301 }
302 }
303 return layerList;
304 }
305 catch (Exception exception)
306 {
307 throw new IOException(exception.getMessage());
308 }
309 }
310
311
312
313
314
315
316
317
318 private static boolean nodeTagExists(final Node node, final String tag) throws IOException
319 {
320 try
321 {
322 Element element = (Element) node;
323 return element.getElementsByTagName(tag).getLength() > 0;
324 }
325 catch (Exception exception)
326 {
327 throw new IOException(exception);
328 }
329 }
330
331
332
333
334
335
336
337
338
339
340 private static Node nodeTagItem(final Node node, final String tag, final int item) throws IOException
341 {
342 try
343 {
344 Element element = (Element) node;
345 return element.getElementsByTagName(tag).item(item);
346 }
347 catch (Exception exception)
348 {
349 throw new IOException(exception);
350 }
351 }
352
353
354
355
356
357
358
359
360 private static String nodeText(final Node node, final String tag) throws IOException
361 {
362 try
363 {
364 Element element = (Element) node;
365 return element.getElementsByTagName(tag).item(0).getTextContent();
366 }
367 catch (Exception exception)
368 {
369 throw new IOException(exception);
370 }
371 }
372
373
374
375
376
377
378
379
380 private static double nodeDouble(final Node node, final String tag) throws IOException
381 {
382 try
383 {
384 return Double.parseDouble(nodeText(node, tag));
385 }
386 catch (Exception exception)
387 {
388 throw new IOException(exception);
389 }
390 }
391
392
393
394
395
396
397
398
399 private static int nodeInt(final Node node, final String tag) throws IOException
400 {
401 try
402 {
403 return Integer.parseInt(nodeText(node, tag));
404 }
405 catch (Exception exception)
406 {
407 throw new IOException(exception);
408 }
409 }
410
411
412
413
414
415
416
417
418 private static boolean nodeBoolean(final Node node, final String tag) throws IOException
419 {
420 try
421 {
422 String b = nodeText(node, tag).toLowerCase();
423 return b.startsWith("t") || b.startsWith("y") || b.equals("1");
424 }
425 catch (Exception exception)
426 {
427 throw new IOException(exception);
428 }
429 }
430 }