View Javadoc

1   /*
2    * Shapefile.java
3    * 
4    * Created on October 11, 2002 Last edited on October 11, 2002
5    */
6   package nl.javel.gisbeans.io.esri;
7   
8   import java.awt.geom.GeneralPath;
9   import java.awt.geom.Point2D;
10  import java.awt.geom.Rectangle2D;
11  import java.io.IOException;
12  import java.net.URL;
13  import java.net.URLConnection;
14  import java.util.ArrayList;
15  import java.util.Iterator;
16  import java.util.List;
17  import java.util.logging.Logger;
18  
19  import nl.javel.gisbeans.geom.GisObject;
20  import nl.javel.gisbeans.geom.SerializableGeneralPath;
21  import nl.javel.gisbeans.io.DataSourceInterface;
22  import nl.javel.gisbeans.io.EndianInterface;
23  import nl.javel.gisbeans.io.ObjectEndianInputStream;
24  import nl.javel.gisbeans.map.MapInterface;
25  import nl.tudelft.simulation.language.d2.Shape;
26  
27  /***
28   * This class reads ESRI-shapefiles and returns the shape object
29   * 
30   * @author <a href="mailto:peter.jacobs@javel.nl">Peter Jacobs </a> <br>
31   *         <a href="mailto:paul.jacobs@javel.nl">Paul Jacobs </a>
32   * 
33   * @since JDK 1.2
34   * @version 1.0
35   */
36  public class ShapeFile implements DataSourceInterface
37  {
38  	/*** the URLS */
39  	private URL shpFile = null, shxFile = null, dbfFile = null;
40  
41  	/*** the number of shapes */
42  	private int numShapes = 0;
43  
44  	/*** the type */
45  	private int type = MapInterface.POLYGON;
46  
47  	/*** our DBF reader */
48  	private DbfReader dbfReader;
49  
50  	/*** the NULLSHAPE as defined by ESRI */
51  	public static final int NULLSHAPE = 0;
52  
53  	/*** the POINT as defined by ESRI */
54  	public static final int POINT = 1;
55  
56  	/*** the POLYLINE as defined by ESRI */
57  	public static final int POLYLINE = 3;
58  
59  	/*** the POLYGON as defined by ESRI */
60  	public static final int POLYGON = 5;
61  
62  	/*** the MULTIPOINT as defined by ESRI */
63  	public static final int MULTIPOINT = 8;
64  
65  	/*** the POINTZ as defined by ESRI */
66  	public static final int POINTZ = 11;
67  
68  	/*** the POLYLINEZ as defined by ESRI */
69  	public static final int POLYLINEZ = 13;
70  
71  	/*** the POLYGONZ as defined by ESRI */
72  	public static final int POLYGONZ = 15;
73  
74  	/*** the MULTIPOINTZ as defined by ESRI */
75  	public static final int MULTIPOINTZ = 18;
76  
77  	/*** the POINM as defined by ESRI */
78  	public static final int POINTM = 21;
79  
80  	/*** the POLYLINEM as defined by ESRI */
81  	public static final int POLYLINEM = 23;
82  
83  	/*** the POLYGONM as defined by ESRI */
84  	public static final int POLYGONM = 25;
85  
86  	/*** the MULTIPOINTM as defined by ESRI */
87  	public static final int MULTIPOINTM = 28;
88  
89  	/*** the MULTIPATCH as defined by ESRI */
90  	public static final int MULTIPATCH = 31;
91  
92  	/*** may we cache parsed data?.. */
93  	private boolean cache = true;
94  
95  	/*** the cachedContent */
96  	private ArrayList cachedContent = null;
97  
98  	/***
99  	 * constructs a new ESRI ShapeFile
100 	 * 
101 	 * @param url URL may or may not end with their extension.
102 	 * @throws IOException throws an IOException if the shxFile is not
103 	 *         accessable
104 	 */
105 	public ShapeFile(final java.net.URL url) throws IOException
106 	{
107 		String fileName = url.toString();
108 		if (fileName.endsWith(".shp") || fileName.endsWith(".shx")
109 				|| fileName.endsWith(".dbf"))
110 		{
111 			fileName = fileName.substring(0, fileName.length() - 4);
112 		}
113 		this.shpFile = new URL(fileName + ".shp");
114 		this.shxFile = new URL(fileName + ".shx");
115 		this.dbfFile = new URL(fileName + ".dbf");
116 		try
117 		{
118 			URLConnection connection = this.shxFile.openConnection();
119 			connection.connect();
120 			this.numShapes = (connection.getContentLength() - 100) / 8;
121 			this.dbfReader = new DbfReader(this.dbfFile);
122 		} catch (IOException exception)
123 		{
124 			throw new IOException("Can't read " + this.shxFile.toString());
125 		}
126 	}
127 
128 	/***
129 	 * @return Returns the cache.
130 	 */
131 	public boolean isCache()
132 	{
133 		return this.cache;
134 	}
135 
136 	/***
137 	 * @param cache The cache to set.
138 	 */
139 	public void setCache(boolean cache)
140 	{
141 		this.cache = cache;
142 		this.dbfReader.setCache(cache);
143 	}
144 
145 	/***
146 	 * @see DataSourceInterface#getColumnNames()
147 	 */
148 	public String[] getColumnNames()
149 	{
150 		return this.dbfReader.getColumnNames();
151 	}
152 
153 	/***
154 	 * @see nl.javel.gisbeans.io.DataSourceInterface#getAttributes()
155 	 */
156 	public String[][] getAttributes() throws IOException
157 	{
158 		return this.dbfReader.getRows();
159 	}
160 
161 	/***
162 	 * @see DataSourceInterface#getDataSource()
163 	 */
164 	public URL getDataSource()
165 	{
166 		return this.shpFile;
167 	}
168 
169 	/***
170 	 * @see DataSourceInterface#getNumShapes()
171 	 */
172 	public int getNumShapes()
173 	{
174 		return this.numShapes;
175 	}
176 
177 	/***
178 	 * getter for a specific shape at a certain index point in shapefile
179 	 * 
180 	 * @param index the index of the shape
181 	 * @return Object shape
182 	 * @throws IOException on IOfailure
183 	 */
184 	public synchronized GisObject getShape(int index) throws IOException
185 	{
186 		if (index > this.numShapes || index < 0)
187 		{
188 			throw new IndexOutOfBoundsException("Index =" + index
189 					+ " number of shapes in layer :" + this.numShapes);
190 		}
191 
192 		//May we use the cache?
193 		if (this.cache && this.cachedContent != null)
194 		{
195 			return (GisObject) this.cachedContent.get(index);
196 		}
197 
198 		ObjectEndianInputStream indexInput = new ObjectEndianInputStream(
199 				this.shxFile.openStream());
200 		indexInput.skipBytes(8 * index + 100);
201 		int offset = 2 * indexInput.readInt();
202 		indexInput.close();
203 		ObjectEndianInputStream shapeInput = new ObjectEndianInputStream(
204 				this.shpFile.openStream());
205 		shapeInput.skipBytes(offset);
206 		Object shape = this.readShape(shapeInput);
207 		shapeInput.close();
208 		return new GisObject(shape, this.dbfReader.getRow(index));
209 	}
210 
211 	/***
212 	 * getter for all shapes in a shapefile
213 	 * 
214 	 * @return HashMap (Object shape)
215 	 * @throws IOException on IOfailure
216 	 */
217 	public synchronized List getShapes() throws IOException
218 	{
219 		//May we use the cache?
220 		if (this.cache && this.cachedContent != null)
221 		{
222 			return this.cachedContent;
223 		}
224 
225 		ObjectEndianInputStream shapeInput = new ObjectEndianInputStream(
226 				this.shpFile.openStream());
227 
228 		shapeInput.skipBytes(100);
229 		ArrayList results = new ArrayList(this.numShapes);
230 		String[][] attributes = this.dbfReader.getRows();
231 		for (int i = 0; i < this.numShapes; i++)
232 		{
233 			results
234 					.add(new GisObject(this.readShape(shapeInput),
235 							attributes[i]));
236 		}
237 		shapeInput.close();
238 
239 		//May we use the cache?
240 		if (this.cache)
241 		{
242 			this.cachedContent = results;
243 		}
244 		return results;
245 	}
246 
247 	/***
248 	 * getter for all shapes intersecting with a certain extent
249 	 * 
250 	 * @param extent the extent to get
251 	 * @return HashMap (Object shape)
252 	 * @throws IOException on IOfailure
253 	 */
254 	public synchronized List getShapes(Rectangle2D extent) throws IOException
255 	{
256 		//May we use the cache?
257 		if (!this.cache)
258 		{
259 			if (this.cachedContent == null)
260 			{
261 				this.getShapes();
262 			}
263 			List result = new ArrayList();
264 			for (Iterator i = this.cachedContent.iterator(); i.hasNext();)
265 			{
266 				GisObject shape = (GisObject) i.next();
267 				if (shape.getShape() instanceof SerializableGeneralPath)
268 				{
269 					if (Shape.overlaps(extent, ((SerializableGeneralPath) shape
270 							.getShape()).getBounds2D()))
271 					{
272 						result.add(shape);
273 					}
274 				} else if (shape.getShape() instanceof Point2D)
275 				{
276 					if (extent.contains((Point2D) shape.getShape()))
277 					{
278 						result.add(shape);
279 					}
280 				} else
281 				{
282 					Logger.getLogger("nl.javel.gisbeans").severe(
283 							"unknown shape in cached content " + shape);
284 				}
285 			}
286 			return result;
287 		}
288 
289 		ObjectEndianInputStream shapeInput = new ObjectEndianInputStream(
290 				this.shpFile.openStream());
291 
292 		shapeInput.skipBytes(100);
293 		ArrayList results = new ArrayList();
294 
295 		String[][] attributes = this.dbfReader.getRows();
296 		for (int i = 0; i < this.numShapes; i++)
297 		{
298 			shapeInput.setEncode(EndianInterface.BIG_ENDIAN);
299 			int shapeNumber = shapeInput.readInt();
300 			int contentLength = shapeInput.readInt();
301 			shapeInput.setEncode(EndianInterface.LITTLE_ENDIAN);
302 			int type = shapeInput.readInt();
303 			if (type != 0 && type != 1 && type != 11 && type != 21)
304 			{
305 				double minX = shapeInput.readDouble();
306 				double minY = shapeInput.readDouble();
307 				double width = shapeInput.readDouble() - minX;
308 				double height = shapeInput.readDouble() - minY;
309 				Rectangle2D bounds = new Rectangle2D.Double(minX, minY, width,
310 						height);
311 				if (Shape.overlaps(extent, bounds))
312 				{
313 					results.add(new GisObject(this.readShape(shapeInput,
314 							shapeNumber, contentLength, type, false),
315 							attributes[i]));
316 				} else
317 				{
318 					shapeInput.skipBytes((2 * contentLength) - 36);
319 				}
320 			} else if (type != 0)
321 			{
322 
323 				Point2D temp = (Point2D) this.readShape(shapeInput,
324 						shapeNumber, contentLength, type, false);
325 				if (extent.contains(temp))
326 				{
327 					results.add(new GisObject(temp, attributes[i]));
328 				}
329 			}
330 		}
331 		shapeInput.close();
332 		return results;
333 	}
334 
335 	/***
336 	 * getter for all shapes intersecting with a certain extent
337 	 * 
338 	 * @param attribute the attribute
339 	 * @param columnName the name of the dbfColumn
340 	 * @throws IOException on IO exception
341 	 * @return the list of shapes
342 	 */
343 	public synchronized List getShapes(String attribute, String columnName)
344 			throws IOException
345 	{
346 		ArrayList result = new ArrayList();
347 		int[] shapeNumbers = this.dbfReader
348 				.getRowNumbers(attribute, columnName);
349 		for (int i = 0; i < shapeNumbers.length; i++)
350 		{
351 			result.add(this.getShape(i));
352 		}
353 		return result;
354 	}
355 
356 	/***
357 	 * getter for the type
358 	 * 
359 	 * @return int
360 	 */
361 	public int getType()
362 	{
363 		return this.type;
364 	}
365 
366 	/***
367 	 * reads a shape
368 	 * 
369 	 * @param input the inputStream
370 	 * @return the shape
371 	 * @throws IOException on IOException
372 	 */
373 	private Object readShape(ObjectEndianInputStream input) throws IOException
374 	{
375 		return readShape(input, -1, -1, -1, true);
376 	}
377 
378 	/***
379 	 * @param input the inputstream
380 	 * @param shapeNumber the number
381 	 * @param contentLength the length of the content
382 	 * @param type
383 	 * @param skipBox
384 	 * @return the shape
385 	 * @throws IOException
386 	 */
387 	private Object readShape(ObjectEndianInputStream input, int shapeNumber,
388 			int contentLength, int type, boolean skipBox) throws IOException
389 	{
390 		input.setEncode(EndianInterface.BIG_ENDIAN);
391 		if (shapeNumber == -1)
392 			shapeNumber = input.readInt();
393 
394 		if (contentLength == -1)
395 			contentLength = input.readInt();
396 
397 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
398 		if (type == -1)
399 			type = input.readInt();
400 		switch (type)
401 		{
402 			case 0 :
403 				return readNullShape(input);
404 			case 1 :
405 				return readPoint(input);
406 			case 3 :
407 				return readPolyLine(input, skipBox);
408 			case 5 :
409 				return readPolygon(input, skipBox);
410 			case 8 :
411 				return readMultiPoint(input, skipBox);
412 			case 11 :
413 				return readPointZ(input, contentLength);
414 			case 13 :
415 				return readPolyLineZ(input, contentLength, skipBox);
416 			case 15 :
417 				return readPolygonZ(input, contentLength, skipBox);
418 			case 18 :
419 				return readMultiPointZ(input, contentLength, skipBox);
420 			case 21 :
421 				return readPointM(input, contentLength);
422 			case 23 :
423 				return readPolyLineM(input, contentLength, skipBox);
424 			case 25 :
425 				return readPolygonM(input, contentLength, skipBox);
426 			case 28 :
427 				return readMultiPointM(input, contentLength, skipBox);
428 			case 31 :
429 				return readMultiPatch(input, contentLength, skipBox);
430 			default :
431 				throw new IOException(
432 						"Unknown shape type or shape type not supported");
433 		}
434 	}
435 
436 	/***
437 	 * reads a nullshape
438 	 * 
439 	 * @param input the inputStream
440 	 * @return a nullobject
441 	 * @throws IOException on IOException
442 	 */
443 	private synchronized Object readNullShape(ObjectEndianInputStream input)
444 			throws IOException
445 	{
446 		if (input != null)
447 			throw new IOException("readNullShape inputStream is is not null");
448 		return null;
449 	}
450 
451 	/***
452 	 * reads a Point
453 	 * 
454 	 * @param input the inputStream
455 	 * @return the java2D PointShape
456 	 * @throws IOException on IOException
457 	 */
458 	private synchronized Object readPoint(ObjectEndianInputStream input)
459 			throws IOException
460 	{
461 		this.type = MapInterface.POINT;
462 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
463 		return new Point2D.Double(input.readDouble(), input.readDouble());
464 	}
465 
466 	/***
467 	 * reads a PolyLine
468 	 * 
469 	 * @param input the inputStream
470 	 * @param skipBox whether to skip the box
471 	 * @return the java2D PointShape
472 	 * @throws IOException on IOException
473 	 */
474 	private synchronized Object readPolyLine(ObjectEndianInputStream input,
475 			boolean skipBox) throws IOException
476 	{
477 		this.type = MapInterface.LINE;
478 		if (skipBox == true)
479 			input.skipBytes(32);
480 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
481 		int numParts = input.readInt();
482 		int numPoints = input.readInt();
483 		int[] partBegin = new int[numParts + 1];
484 
485 		for (int i = 0; i < partBegin.length - 1; i++)
486 		{
487 			partBegin[i] = input.readInt();
488 
489 		}
490 		partBegin[partBegin.length - 1] = numPoints;
491 
492 		SerializableGeneralPath result = new SerializableGeneralPath(
493 				GeneralPath.WIND_NON_ZERO, numPoints);
494 		for (int i = 0; i < numParts; i++)
495 		{
496 			result.moveTo((float) input.readDouble(), (float) input
497 					.readDouble());
498 			for (int ii = (partBegin[i] + 1); ii < partBegin[i + 1]; ii++)
499 			{
500 				result.lineTo((float) input.readDouble(), (float) input
501 						.readDouble());
502 			}
503 		}
504 		return result;
505 	}
506 
507 	/***
508 	 * reads a Polygon
509 	 * 
510 	 * @param input the inputStream
511 	 * @param skipBox whether to skip the box
512 	 * @return the java2D PointShape
513 	 * @throws IOException on IOException
514 	 */
515 	private synchronized Object readPolygon(ObjectEndianInputStream input,
516 			boolean skipBox) throws IOException
517 	{
518 		this.type = MapInterface.POLYGON;
519 		if (skipBox == true)
520 			input.skipBytes(32);
521 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
522 		int numParts = input.readInt();
523 		int numPoints = input.readInt();
524 		int[] partBegin = new int[numParts + 1];
525 
526 		for (int i = 0; i < partBegin.length - 1; i++)
527 		{
528 			partBegin[i] = input.readInt();
529 		}
530 		partBegin[partBegin.length - 1] = numPoints;
531 
532 		SerializableGeneralPath result = new SerializableGeneralPath(
533 				GeneralPath.WIND_NON_ZERO, numPoints);
534 		for (int i = 0; i < numParts; i++)
535 		{
536 			result.moveTo((float) input.readDouble(), (float) input
537 					.readDouble());
538 			for (int ii = (partBegin[i] + 1); ii < partBegin[i + 1]; ii++)
539 			{
540 				result.lineTo((float) input.readDouble(), (float) input
541 						.readDouble());
542 			}
543 		}
544 
545 		return result;
546 	}
547 
548 	/***
549 	 * reads a readMultiPoint
550 	 * 
551 	 * @param input the inputStream
552 	 * @param skipBox whether to skip the box
553 	 * @return the java2D PointShape
554 	 * @throws IOException on IOException
555 	 */
556 	private synchronized Object readMultiPoint(ObjectEndianInputStream input,
557 			boolean skipBox) throws IOException
558 	{
559 		this.type = MapInterface.POINT;
560 		if (skipBox == true)
561 			input.skipBytes(32);
562 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
563 		Point2D[] result = new Point2D.Double[input.readInt()];
564 		for (int i = 0; i < result.length; i++)
565 		{
566 			result[i] = (Point2D) readPoint(input);
567 		}
568 
569 		return result;
570 	}
571 
572 	/***
573 	 * reads a readPointZ
574 	 * 
575 	 * @param input the inputStream
576 	 * @param contentLength the contentLength
577 	 * @return the java2D PointShape
578 	 * @throws IOException on IOException
579 	 */
580 	private synchronized Object readPointZ(ObjectEndianInputStream input,
581 			int contentLength) throws IOException
582 	{
583 		this.type = MapInterface.POINT;
584 		Object point = this.readPoint(input);
585 		input.skipBytes((contentLength * 2) - 20);
586 
587 		return point;
588 	}
589 
590 	/***
591 	 * reads a readPolyLineZ
592 	 * 
593 	 * @param input the inputStream
594 	 * @param contentLength the contentLength
595 	 * @param skipBox whether to skip the box
596 	 * @return the java2D PointShape
597 	 * @throws IOException on IOException
598 	 */
599 	private synchronized Object readPolyLineZ(ObjectEndianInputStream input,
600 			int contentLength, boolean skipBox) throws IOException
601 	{
602 		this.type = MapInterface.LINE;
603 		if (skipBox == true)
604 			input.skipBytes(32);
605 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
606 		int numParts = input.readInt();
607 		int numPoints = input.readInt();
608 		int byteCounter = 44;
609 		int[] partBegin = new int[numParts + 1];
610 
611 		for (int i = 0; i < partBegin.length - 1; i++)
612 		{
613 			partBegin[i] = input.readInt();
614 			byteCounter += 4;
615 		}
616 		partBegin[partBegin.length - 1] = numPoints;
617 
618 		SerializableGeneralPath result = new SerializableGeneralPath(
619 				GeneralPath.WIND_NON_ZERO, numPoints);
620 		for (int i = 0; i < numParts; i++)
621 		{
622 			result.moveTo((float) input.readDouble(), (float) input
623 					.readDouble());
624 			byteCounter += 16;
625 			for (int ii = (partBegin[i] + 1); ii < partBegin[i + 1]; ii++)
626 			{
627 				result.lineTo((float) input.readDouble(), (float) input
628 						.readDouble());
629 				byteCounter += 16;
630 			}
631 		}
632 		input.skipBytes((contentLength * 2) - byteCounter);
633 
634 		return result;
635 	}
636 
637 	/***
638 	 * reads a readPolygonZ
639 	 * 
640 	 * @param input the inputStream
641 	 * @param contentLength the contentLength
642 	 * @param skipBox whether to skip the box
643 	 * @return the java2D PointShape
644 	 * @throws IOException on IOException
645 	 */
646 	private synchronized Object readPolygonZ(ObjectEndianInputStream input,
647 			int contentLength, boolean skipBox) throws IOException
648 	{
649 		this.type = MapInterface.POLYGON;
650 		if (skipBox == true)
651 			input.skipBytes(32);
652 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
653 		int numParts = input.readInt();
654 		int numPoints = input.readInt();
655 		int byteCounter = 44;
656 		int[] partBegin = new int[numParts + 1];
657 		for (int i = 0; i < partBegin.length - 1; i++)
658 		{
659 			partBegin[i] = input.readInt();
660 			byteCounter += 4;
661 		}
662 		partBegin[partBegin.length - 1] = numPoints;
663 
664 		SerializableGeneralPath result = new SerializableGeneralPath(
665 				GeneralPath.WIND_NON_ZERO, numPoints);
666 		for (int i = 0; i < numParts; i++)
667 		{
668 			result.moveTo((float) input.readDouble(), (float) input
669 					.readDouble());
670 			byteCounter += 16;
671 			for (int ii = (partBegin[i] + 1); ii < partBegin[i + 1]; ii++)
672 			{
673 				result.lineTo((float) input.readDouble(), (float) input
674 						.readDouble());
675 				byteCounter += 16;
676 			}
677 		}
678 		input.skipBytes((contentLength * 2) - byteCounter);
679 
680 		return result;
681 	}
682 
683 	/***
684 	 * reads a readMultiPointZ
685 	 * 
686 	 * @param input the inputStream
687 	 * @param contentLength the contentLength
688 	 * @param skipBox whether to skip the box
689 	 * @return the java2D PointShape
690 	 * @throws IOException on IOException
691 	 */
692 	private synchronized Object readMultiPointZ(ObjectEndianInputStream input,
693 			int contentLength, boolean skipBox) throws IOException
694 	{
695 		this.type = MapInterface.POINT;
696 		if (skipBox == true)
697 			input.skipBytes(32);
698 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
699 		Point2D[] result = new Point2D.Double[input.readInt()];
700 		int byteCounter = 40;
701 		for (int i = 0; i < result.length; i++)
702 		{
703 			result[i] = (Point2D) readPoint(input);
704 			byteCounter += 16;
705 		}
706 		input.skipBytes((contentLength * 2) - byteCounter);
707 
708 		return result;
709 	}
710 
711 	/***
712 	 * reads a readPointM
713 	 * 
714 	 * @param input the inputStream
715 	 * @param contentLength the contentLength
716 	 * @return the java2D PointShape
717 	 * @throws IOException on IOException
718 	 */
719 	private synchronized Object readPointM(ObjectEndianInputStream input,
720 			int contentLength) throws IOException
721 	{
722 		this.type = MapInterface.POINT;
723 		Object point = this.readPoint(input);
724 		input.skipBytes((contentLength * 2) - 20);
725 		return point;
726 	}
727 
728 	/***
729 	 * reads a readPolyLineM
730 	 * 
731 	 * @param input the inputStream
732 	 * @param contentLength the contentLength
733 	 * @param skipBox whether to skip the box
734 	 * @return the java2D PointShape
735 	 * @throws IOException on IOException
736 	 */
737 	private synchronized Object readPolyLineM(ObjectEndianInputStream input,
738 			int contentLength, boolean skipBox) throws IOException
739 	{
740 		this.type = MapInterface.LINE;
741 		if (skipBox == true)
742 		{
743 			input.skipBytes(32);
744 		}
745 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
746 		int numParts = input.readInt();
747 		int numPoints = input.readInt();
748 		int byteCounter = 44;
749 		int[] partBegin = new int[numParts + 1];
750 		for (int i = 0; i < partBegin.length - 1; i++)
751 		{
752 			partBegin[i] = input.readInt();
753 			byteCounter += 4;
754 		}
755 		partBegin[partBegin.length - 1] = numPoints;
756 
757 		SerializableGeneralPath result = new SerializableGeneralPath(
758 				GeneralPath.WIND_NON_ZERO, numPoints);
759 		for (int i = 0; i < numParts; i++)
760 		{
761 			result.moveTo((float) input.readDouble(), (float) input
762 					.readDouble());
763 			byteCounter += 16;
764 			for (int ii = (partBegin[i] + 1); ii < partBegin[i + 1]; ii++)
765 			{
766 				result.lineTo((float) input.readDouble(), (float) input
767 						.readDouble());
768 				byteCounter += 16;
769 			}
770 		}
771 		input.skipBytes((contentLength * 2) - byteCounter);
772 		return result;
773 	}
774 
775 	/***
776 	 * reads a readPolyLineM
777 	 * 
778 	 * @param input the inputStream
779 	 * @param contentLength the contentLength
780 	 * @param skipBox whether to skip the box
781 	 * @return the java2D PointShape
782 	 * @throws IOException on IOException
783 	 */
784 	private synchronized Object readPolygonM(ObjectEndianInputStream input,
785 			int contentLength, boolean skipBox) throws IOException
786 	{
787 		this.type = MapInterface.POLYGON;
788 		if (skipBox == true)
789 			input.skipBytes(32);
790 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
791 		int numParts = input.readInt();
792 		int numPoints = input.readInt();
793 		int byteCounter = 44;
794 		int[] partBegin = new int[numParts + 1];
795 
796 		for (int i = 0; i < partBegin.length - 1; i++)
797 		{
798 			partBegin[i] = input.readInt();
799 			byteCounter += 4;
800 		}
801 		partBegin[partBegin.length - 1] = numPoints;
802 
803 		SerializableGeneralPath result = new SerializableGeneralPath(
804 				GeneralPath.WIND_NON_ZERO, numPoints);
805 		for (int i = 0; i < numParts; i++)
806 		{
807 			result.moveTo((float) input.readDouble(), (float) input
808 					.readDouble());
809 			byteCounter += 16;
810 			for (int ii = (partBegin[i] + 1); ii < partBegin[i + 1]; ii++)
811 			{
812 				result.lineTo((float) input.readDouble(), (float) input
813 						.readDouble());
814 				byteCounter += 16;
815 			}
816 		}
817 		input.skipBytes((contentLength * 2) - byteCounter);
818 		return result;
819 	}
820 
821 	/***
822 	 * reads a readMultiPointM
823 	 * 
824 	 * @param input the inputStream
825 	 * @param contentLength the contentLength
826 	 * @param skipBox whether to skip the box
827 	 * @return the java2D PointShape
828 	 * @throws IOException on IOException
829 	 */
830 	private synchronized Object readMultiPointM(ObjectEndianInputStream input,
831 			int contentLength, boolean skipBox) throws IOException
832 	{
833 		this.type = MapInterface.POINT;
834 		if (skipBox == true)
835 			input.skipBytes(32);
836 		input.setEncode(EndianInterface.LITTLE_ENDIAN);
837 		Point2D[] result = new Point2D.Double[input.readInt()];
838 		int byteCounter = 40;
839 		for (int i = 0; i < result.length; i++)
840 		{
841 			result[i] = (Point2D) readPoint(input);
842 			byteCounter += 16;
843 		}
844 		input.skipBytes((contentLength * 2) - byteCounter);
845 
846 		return result;
847 	}
848 
849 	/***
850 	 * reads a readMultiPointM
851 	 * 
852 	 * @param input the inputStream
853 	 * @param contentLength the contentLength
854 	 * @param skipBox whether to skip the box
855 	 * @return the java2D PointShape
856 	 * @throws IOException on IOException
857 	 */
858 	private synchronized Object readMultiPatch(ObjectEndianInputStream input,
859 			int contentLength, boolean skipBox) throws IOException
860 	{
861 		if (input != null || contentLength != 0 || skipBox != false)
862 		{
863 			throw new IOException(
864 					"Please inform <a href=\"mailto:support@javel.nl\">support@javel.nl</a> that you need MultiPatch support");
865 		}
866 		return null;
867 	}
868 }