View Javadoc

1   /*
2    * @(#) MapFileXMLParser.java Sep 1, 2002
3    * 
4    * Copyright (c) 2002-2004 Javel b.v. Delft, the Netherlands All rights
5    * reserved.
6    * 
7    * This software is proprietary information of Javel b.v. The code is published
8    * under the General Public License
9    */
10  
11  package nl.javel.gisbeans.map.mapfile;
12  
13  import java.awt.Color;
14  import java.awt.Dimension;
15  import java.awt.Font;
16  import java.awt.geom.Rectangle2D;
17  import java.io.IOException;
18  import java.net.URL;
19  import java.util.ArrayList;
20  import java.util.Iterator;
21  
22  import nl.javel.gisbeans.io.esri.ShapeFile;
23  import nl.javel.gisbeans.map.AbstractAttribute;
24  import nl.javel.gisbeans.map.Attribute;
25  import nl.javel.gisbeans.map.AttributeInterface;
26  import nl.javel.gisbeans.map.Image;
27  import nl.javel.gisbeans.map.ImageInterface;
28  import nl.javel.gisbeans.map.Layer;
29  import nl.javel.gisbeans.map.LayerInterface;
30  import nl.javel.gisbeans.map.Legend;
31  import nl.javel.gisbeans.map.LegendInterface;
32  import nl.javel.gisbeans.map.Map;
33  import nl.javel.gisbeans.map.MapInterface;
34  import nl.javel.gisbeans.map.ReferenceMap;
35  import nl.javel.gisbeans.map.ReferenceMapInterface;
36  import nl.javel.gisbeans.map.Scalebar;
37  import nl.javel.gisbeans.map.ScalebarInterface;
38  import nl.javel.gisbeans.map.StaticAttribute;
39  import nl.tudelft.simulation.language.io.URLResource;
40  
41  import org.apache.xerces.parsers.DOMParser;
42  import org.jdom.Element;
43  import org.jdom.Namespace;
44  import org.jdom.input.DOMBuilder;
45  import org.w3c.dom.Document;
46  import org.xml.sax.SAXException;
47  import org.xml.sax.SAXParseException;
48  import org.xml.sax.helpers.DefaultHandler;
49  
50  /***
51   * This class parses XML-mapfiles and constructs appropriate map objects. <br>
52   * Validation is set with the system property
53   * nl.javel.gisbeans.map.mapfile.validate =[true|false] and the xmlschema is set
54   * with the systemproperty nl.javel.gisbeans.map.mapfile.schema = [file]
55   * <p>
56   * (c) copyright 2002-2004 <a href="http://www.javel.nl">Javel b.v. </a>, the
57   * Netherlands.
58   * <p>
59   * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
60   * License (GPL) </a>, no warranty <br>
61   * 
62   * @version 2.0 28.04.04
63   *          <p>
64   * @author <a href="mailto:paul.jacobs@javel.nl">Paul Jacobs </a><a
65   *         href="http://www.tbm.tudelft.nl/webstaf/peterja/index.htm">Peter
66   *         Jacobs </a>
67   */
68  public final class MapFileXMLParser
69  {
70  	/*** the default mapfile */
71  	public static final URL MAPFILE_SCHEMA = URLResource
72  			.getResource("/nl/javel/gisbeans/map/mapfile/mapfile.xsd");
73  
74  	/***
75  	 * parses a Mapfile URL to a mapFile.
76  	 * 
77  	 * @param url the mapfile url.
78  	 * @return MapInterface the parsed mapfile.
79  	 * @throws IOException on failure.
80  	 */
81  	public static MapInterface parseMapFile(final URL url) throws IOException
82  	{
83  		try
84  		{
85  			Map map = new Map();
86  			DOMParser domParser = new DOMParser();
87  			domParser
88  					.setFeature("http://xml.org/sax/features/validation", true);
89  			domParser.setFeature(
90  					"http://apache.org/xml/features/validation/schema", true);
91  			domParser
92  					.setProperty(
93  							"http://apache.org/xml/properties/schema/external-schemaLocation",
94  							"http://www.javel.nl/gisbeans "
95  									+ MAPFILE_SCHEMA.toExternalForm());
96  			domParser.setErrorHandler(new ValidHandler());
97  			domParser.parse(url.toExternalForm());
98  			Document document = domParser.getDocument();
99  			DOMBuilder builder = new DOMBuilder();
100 			org.jdom.Document jdomDocument = builder.build(document);
101 			Element xmlMapFileElement = jdomDocument.getRootElement();
102 			Element element = null;
103 			Namespace nameSpace = xmlMapFileElement.getNamespace();
104 			map.setName(xmlMapFileElement.getChildText("name"));
105 			if ((element = xmlMapFileElement.getChild("units")) != null)
106 			{
107 				map.setUnits(parseUnits(element));
108 			}
109 			double[] extent = new double[4];
110 			extent[MapInterface.MINX] = new Double(xmlMapFileElement.getChild(
111 					"extent").getChildText("minX")).doubleValue();
112 			extent[MapInterface.MINY] = new Double(xmlMapFileElement.getChild(
113 					"extent").getChildText("minY")).doubleValue();
114 			extent[MapInterface.MAXX] = new Double(xmlMapFileElement.getChild(
115 					"extent").getChildText("maxX")).doubleValue();
116 			extent[MapInterface.MAXY] = new Double(xmlMapFileElement.getChild(
117 					"extent").getChildText("maxY")).doubleValue();
118 			map.setExtent(new Rectangle2D.Double(extent[0], extent[1],
119 					(extent[2] - extent[0]), (extent[3] - extent[1])));
120 			if ((element = xmlMapFileElement.getChild("image", nameSpace)) != null)
121 			{
122 				map.setImage(parseImage(element, nameSpace));
123 			}
124 			if ((element = xmlMapFileElement
125 					.getChild("referenceMap", nameSpace)) != null)
126 			{
127 				map.setReferenceMap(parseReferenceMap(element));
128 			}
129 			java.util.List layerElements = xmlMapFileElement.getChildren(
130 					"layer", nameSpace);
131 			ArrayList layers = new ArrayList();
132 			for (Iterator iterator = layerElements.iterator(); iterator
133 					.hasNext();)
134 			{
135 				Element layerElement = (Element) iterator.next();
136 				layers.add(parseLayer(layerElement));
137 			}
138 			map.setLayers(layers);
139 			return map;
140 		} catch (Exception exception)
141 		{
142 			throw new IOException(exception.getMessage());
143 		}
144 	}
145 
146 	/***
147 	 * returns the columnNumber
148 	 * 
149 	 * @param columnNames the names
150 	 * @param columnName the name
151 	 * @return the number
152 	 */
153 	private static int getColumnNumber(String[] columnNames, String columnName)
154 	{
155 		for (int i = 0; i < columnNames.length; i++)
156 		{
157 			if (columnNames[i].equalsIgnoreCase(columnName))
158 				return i;
159 		}
160 		return -1;
161 	}
162 
163 	/***
164 	 * parses a xml-element representing an attribute
165 	 * 
166 	 * @param element The j-dom element
167 	 * @param layer the layer.
168 	 * @return AttributeInterface value
169 	 * @throws IOException
170 	 */
171 	private static AttributeInterface parseAttribute(Element element,
172 			LayerInterface layer) throws IOException
173 	{
174 		AbstractAttribute result = null;
175 		if (element.getChild("textColumn") != null)
176 		{
177 			int textColumnNumber = MapFileXMLParser.getColumnNumber(layer
178 					.getDataSource().getColumnNames(), element
179 					.getChildText("textColumn"));
180 			int angleColumnNumber = -1;
181 			if (element.getChild("degreesColumn") != null)
182 			{
183 				angleColumnNumber = MapFileXMLParser.getColumnNumber(layer
184 						.getDataSource().getColumnNames(), element
185 						.getChildText("degreesColumn"));
186 				result = new Attribute(layer, Attribute.DEGREES,
187 						angleColumnNumber, textColumnNumber);
188 			} else if (element.getChild("radiansColumn") != null)
189 			{
190 				angleColumnNumber = MapFileXMLParser.getColumnNumber(layer
191 						.getDataSource().getColumnNames(), element
192 						.getChildText("radiansColumn"));
193 				result = new Attribute(layer, Attribute.RADIANS,
194 						angleColumnNumber, textColumnNumber);
195 			} else
196 			{
197 				result = new Attribute(layer, Attribute.RADIANS, -1,
198 						textColumnNumber);
199 			}
200 		} else
201 		{
202 			double angle = 0.0;
203 			if (element.getChild("degrees") != null)
204 			{
205 				angle = Math.toRadians(new Double(element
206 						.getChildText("degrees")).doubleValue());
207 			}
208 			if (element.getChild("radians") != null)
209 			{
210 				angle = new Double(element.getChildText("radians"))
211 						.doubleValue();
212 			}
213 			String value = "";
214 			if (element.getChild("text") != null)
215 			{
216 				value = element.getChildText("text");
217 			}
218 			result = new StaticAttribute(layer, angle, value);
219 		}
220 		if (element.getChild("minScale") != null)
221 			result.setMinScale(new Integer(element.getChildText("minScale"))
222 					.intValue());
223 		else
224 			result.setMinScale(layer.getMinScale());
225 		if (element.getChild("maxScale") != null)
226 			result.setMaxScale(new Integer(element.getChildText("maxScale"))
227 					.intValue());
228 		else
229 			result.setMaxScale(layer.getMaxScale());
230 		if (element.getChild("position") != null)
231 			result.setPosition(parsePosition(element.getChild("position")));
232 		if (element.getChild("font") != null)
233 		{
234 			result.setFont(parseFont(element.getChild("font")));
235 			result.setFontColor(parseColor(element.getChild("font").getChild(
236 					"fontColor")));
237 		}
238 		return result;
239 	}
240 
241 	/***
242 	 * parses a xml-element representing a boolean
243 	 * 
244 	 * @param element The j-dom element
245 	 * @return Boolean value
246 	 * @throws IOException
247 	 */
248 	private static boolean parseBoolean(Element element) throws IOException
249 	{
250 		try
251 		{
252 			boolean result = false;
253 			if (element.getText().equals("true"))
254 				result = true;
255 			return result;
256 		} catch (Exception exception)
257 		{
258 			throw new IOException(exception.getMessage());
259 		}
260 	}
261 
262 	/***
263 	 * parses a xml-element representing a Color
264 	 * 
265 	 * @param element The j-dom element
266 	 * @return Color of element
267 	 * @throws IOException
268 	 */
269 	private static Color parseColor(Element element) throws IOException
270 	{
271 		try
272 		{
273 			int r = new Integer(element.getChildText("r")).intValue();
274 			int g = new Integer(element.getChildText("g")).intValue();
275 			int b = new Integer(element.getChildText("b")).intValue();
276 			if (element.getChildText("a") != null)
277 			{
278 				int a = new Integer(element.getChildText("a")).intValue();
279 				return new java.awt.Color(r, g, b, a);
280 			}
281 			return new java.awt.Color(r, g, b);
282 		} catch (Exception exception)
283 		{
284 			throw new IOException(exception.getMessage());
285 		}
286 	}
287 
288 	/***
289 	 * parses a xml-element representing a Dimension
290 	 * 
291 	 * @param element The j-dom element
292 	 * @return Dimension of element
293 	 * @throws IOException
294 	 */
295 	private static Dimension parseDimension(Element element) throws IOException
296 	{
297 		try
298 		{
299 			int height = new Integer(element.getChildText("height")).intValue();
300 			int width = new Integer(element.getChildText("width")).intValue();
301 			return new Dimension(width, height);
302 		} catch (Exception exception)
303 		{
304 			throw new IOException(exception.getMessage());
305 		}
306 	}
307 
308 	/***
309 	 * parses a xml-element representing a double[]
310 	 * 
311 	 * @param element The j-dom element
312 	 * @return Value of extent
313 	 * @throws IOException
314 	 */
315 	private static double[] parseExtent(Element element) throws IOException
316 	{
317 		try
318 		{
319 			double[] result = new double[4];
320 			result[MapInterface.MINX] = new Double(element.getChildText("minX"))
321 					.doubleValue();
322 			result[MapInterface.MINY] = new Double(element.getChildText("minY"))
323 					.doubleValue();
324 			result[MapInterface.MAXY] = new Double(element.getChildText("maxX"))
325 					.doubleValue();
326 			result[MapInterface.MAXY] = new Double(element.getChildText("maxY"))
327 					.doubleValue();
328 			return result;
329 		} catch (Exception exception)
330 		{
331 			throw new IOException(exception.getMessage());
332 		}
333 	}
334 
335 	/***
336 	 * parses a xml-element representing a font
337 	 * 
338 	 * @param element The j-dom element
339 	 * @return the font
340 	 * @throws IOException
341 	 */
342 	private static Font parseFont(Element element) throws IOException
343 	{
344 		try
345 		{
346 			String fontName = element.getChildText("fontName");
347 			int fontSize = new Integer(element.getChildText("fontSize"))
348 					.intValue();
349 			return new Font(fontName, Font.TRUETYPE_FONT, fontSize);
350 		} catch (Exception exception)
351 		{
352 			throw new IOException(exception.getMessage());
353 		}
354 	}
355 
356 	/***
357 	 * parses a xml-element representing the Image
358 	 * 
359 	 * @param element The j-dom element
360 	 * @param nameSpace The gisbeans namespace
361 	 * @return Value of image
362 	 * @throws IOException
363 	 */
364 	private static ImageInterface parseImage(org.jdom.Element element,
365 			Namespace nameSpace) throws IOException
366 	{
367 		ImageInterface result = new Image();
368 		try
369 		{
370 			if (element.getChild("backgroundColor") != null)
371 				result.setBackgroundColor(parseColor(element
372 						.getChild("backgroundColor")));
373 			if (element.getChild("legend", nameSpace) != null)
374 				result.setLegend(parseLegend(element.getChild("legend",
375 						nameSpace)));
376 			if (element.getChild("scalebar", nameSpace) != null)
377 				result.setScalebar(parseScalebar(element.getChild("scalebar",
378 						nameSpace)));
379 			if (element.getChild("size") != null)
380 				result.setSize(parseDimension(element.getChild("size")));
381 			return result;
382 		} catch (Exception exception)
383 		{
384 			throw new IOException(exception.getMessage());
385 		}
386 	}
387 
388 	/***
389 	 * parses a xml-element representing a Layer
390 	 * 
391 	 * @param element The j-dom element
392 	 * @return Layer of element
393 	 * @throws IOException
394 	 */
395 	private static LayerInterface parseLayer(Element element)
396 			throws IOException
397 	{
398 		LayerInterface result = new Layer();
399 		try
400 		{
401 			if (element.getChild("color") != null)
402 				result.setColor(parseColor(element.getChild("color")));
403 			if (element.getChild("data").getChild("shapeFile") != null)
404 			{
405 				String resource = element.getChild("data").getChildText(
406 						"shapeFile");
407 				ShapeFile dataSource = new ShapeFile(URLResource
408 						.getResource(resource));
409 				if (element.getAttribute("cache") != null
410 						&& element.getAttribute("cache").getValue().equals(
411 								"false"))
412 				{
413 					dataSource.setCache(false);
414 				} else
415 				{
416 					dataSource.setCache(true);
417 				}
418 				result.setDataSource(dataSource);
419 			}
420 			if (element.getChild("data").getChild("SDI") != null)
421 			{
422 				throw new IOException("SDI not yet supported");
423 			}
424 			if (element.getChild("maxScale") != null)
425 				result
426 						.setMaxScale(new Integer(element
427 								.getChildText("maxScale")).intValue());
428 			if (element.getChild("minScale") != null)
429 				result
430 						.setMinScale(new Integer(element
431 								.getChildText("minScale")).intValue());
432 			result.setName(element.getChildText("name"));
433 			if (element.getChild("outlineColor") != null)
434 				result.setOutlineColor(parseColor(element
435 						.getChild("outlineColor")));
436 			if (element.getChild("status") != null)
437 				result.setStatus(parseBoolean(element.getChild("status")));
438 			if (element.getChild("transform") != null)
439 				result
440 						.setTransform(parseBoolean(element
441 								.getChild("transform")));
442 			java.util.List attributesElements = element
443 					.getChildren("attribute");
444 			ArrayList attributes = new ArrayList();
445 			for (Iterator iterator = attributesElements.iterator(); iterator
446 					.hasNext();)
447 			{
448 				Element attributeElement = (Element) iterator.next();
449 				attributes.add(parseAttribute(attributeElement, result));
450 			}
451 			result.setAttributes(attributes);
452 			return result;
453 		} catch (Exception exception)
454 		{
455 			exception.printStackTrace();
456 			throw new IOException(exception.getMessage());
457 		}
458 	}
459 
460 	/***
461 	 * parses a xml-element representing a Legend
462 	 * 
463 	 * @param element The j-dom element
464 	 * @return Legend of element
465 	 * @throws IOException
466 	 */
467 	private static LegendInterface parseLegend(Element element)
468 			throws IOException
469 	{
470 		LegendInterface result = new Legend();
471 		try
472 		{
473 			if (element.getChild("backgroundColor") != null)
474 				result.setOutlineColor(parseColor(element
475 						.getChild("backgroundColor")));
476 			if (element.getChild("embed") != null)
477 				result.setEmbed(parseBoolean(element.getChild("embed")));
478 			if (element.getChild("font") != null)
479 			{
480 				result.setFont(parseFont(element.getChild("font")));
481 				result.setFontColor(parseColor(element.getChild("font")
482 						.getChild("fontColor")));
483 			}
484 			if (element.getChild("outlineColor") != null)
485 				result.setOutlineColor(parseColor(element
486 						.getChild("outlineColor")));
487 			if (element.getChild("position") != null)
488 				result.setPosition(parsePosition(element.getChild("position")));
489 			if (element.getChild("size") != null)
490 				result.setSize(parseDimension(element.getChild("size")));
491 			if (element.getChild("status") != null)
492 				result.setStatus(parseBoolean(element.getChild("status")));
493 			return result;
494 		} catch (Exception exception)
495 		{
496 			throw new IOException(exception.getMessage());
497 		}
498 	}
499 
500 	/***
501 	 * parses a xml-element representing a Position
502 	 * 
503 	 * @param element The j-dom element
504 	 * @return Position of element
505 	 */
506 	private static int parsePosition(org.jdom.Element element)
507 	{
508 		if (element.getText().equals("ul"))
509 			return MapInterface.UL;
510 		if (element.getText().equals("uc"))
511 			return MapInterface.UC;
512 		if (element.getText().equals("ur"))
513 			return MapInterface.UR;
514 		if (element.getText().equals("cl"))
515 			return MapInterface.CL;
516 		if (element.getText().equals("cc"))
517 			return MapInterface.CC;
518 		if (element.getText().equals("cr"))
519 			return MapInterface.CR;
520 		if (element.getText().equals("ll"))
521 			return MapInterface.LL;
522 		if (element.getText().equals("lc"))
523 			return MapInterface.LC;
524 		if (element.getText().equals("lr"))
525 			return MapInterface.LR;
526 		return MapInterface.UL;
527 	}
528 
529 	/***
530 	 * parses a xml-element representing a ReferenceMap
531 	 * 
532 	 * @param element The j-dom element
533 	 * @return ReferenceMap of element
534 	 * @throws IOException
535 	 */
536 	private static ReferenceMapInterface parseReferenceMap(
537 			org.jdom.Element element) throws IOException
538 	{
539 		ReferenceMapInterface result = new ReferenceMap();
540 		try
541 		{
542 			result.setImage(new java.net.URL(element.getChildText("image")));
543 			result.setExtent(parseExtent(element.getChild("extent")));
544 			if (element.getChild("outlineColor") != null)
545 				result.setOutlineColor(parseColor(element
546 						.getChild("outlineColor")));
547 			if (element.getChild("size") != null)
548 				result.setSize(parseDimension(element.getChild("size")));
549 			if (element.getChild("status") != null)
550 				result.setStatus(parseBoolean(element.getChild("status")));
551 			return result;
552 		} catch (Exception exception)
553 		{
554 			throw new IOException(exception.getMessage());
555 		}
556 	}
557 
558 	/***
559 	 * parses a xml-element representing a Scalebar
560 	 * 
561 	 * @param element The j-dom element
562 	 * @return Scalebar of element
563 	 * @throws IOException
564 	 */
565 	private static ScalebarInterface parseScalebar(org.jdom.Element element)
566 			throws IOException
567 	{
568 		ScalebarInterface result = new Scalebar();
569 		try
570 		{
571 			if (element.getChild("backgroundColor") != null)
572 				result.setBackgroundColor(parseColor(element
573 						.getChild("backgroundColor")));
574 			if (element.getChild("color") != null)
575 				result.setColor(parseColor(element.getChild("color")));
576 			if (element.getChild("embed") != null)
577 				result.setEmbed(parseBoolean(element.getChild("embed")));
578 			if (element.getChild("font") != null)
579 			{
580 				result.setFont(parseFont(element.getChild("font")));
581 				result.setFontColor(parseColor(element.getChild("font")
582 						.getChild("fontColor")));
583 			}
584 			if (element.getChild("intervals") != null)
585 				result.setIntervals(new Integer(element
586 						.getChildText("intervals")).intValue());
587 			if (element.getChild("position") != null)
588 				result.setPosition(parsePosition(element.getChild("position")));
589 			if (element.getChild("size") != null)
590 				result.setSize(parseDimension(element.getChild("size")));
591 			if (element.getChild("status") != null)
592 				result.setStatus(parseBoolean(element.getChild("status")));
593 			if (element.getChild("units") != null)
594 				result.setUnits(parseUnits(element.getChild("units")));
595 			return result;
596 		} catch (Exception exception)
597 		{
598 			throw new IOException(exception.getMessage());
599 		}
600 	}
601 
602 	/***
603 	 * parses a xml-element representing a Units
604 	 * 
605 	 * @param element The j-dom element
606 	 * @return Units of element
607 	 */
608 	private static int parseUnits(org.jdom.Element element)
609 	{
610 		if (element.getText().equals("feet"))
611 			return MapInterface.FEET;
612 		if (element.getText().equals("dd"))
613 			return MapInterface.DD;
614 		if (element.getText().equals("inches"))
615 			return MapInterface.INCHES;
616 		if (element.getText().equals("kilometers"))
617 			return MapInterface.KILOMETERS;
618 		if (element.getText().equals("meters"))
619 			return MapInterface.METERS;
620 		if (element.getText().equals("miles"))
621 			return MapInterface.MILES;
622 		return MapInterface.METERS;
623 	}
624 
625 	/***
626 	 * The validationHandler.
627 	 */
628 	private static class ValidHandler extends DefaultHandler
629 	{
630 		/***
631 		 * format the exception with line number, column number, etc.
632 		 * 
633 		 * @param exception
634 		 * @return
635 		 */
636 		private String formatError(SAXParseException exception)
637 		{
638 			return "SAXParseException: \nsystemId=" + exception.getSystemId()
639 					+ "\npublicId=" + exception.getSystemId() + "\nMessage="
640 					+ exception.getMessage() + "\nline,col="
641 					+ exception.getLineNumber() + ","
642 					+ exception.getColumnNumber();
643 		}
644 
645 		/***
646 		 * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
647 		 */
648 		public void error(SAXParseException e) throws SAXException
649 		{
650 			throw new SAXException(formatError(e));
651 		}
652 
653 		/***
654 		 * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
655 		 */
656 		public void fatalError(SAXParseException e) throws SAXException
657 		{
658 			throw new SAXException(formatError(e));
659 		}
660 	}
661 }