001: package org.geotools.shapefile;
002:
003: import java.io.*;
004: import java.net.URL;
005: import java.net.URLConnection;
006: import java.util.ArrayList;
007:
008: import com.vividsolutions.jts.geom.*;
009: import com.vividsolutions.jump.io.EndianDataInputStream;
010: import com.vividsolutions.jump.io.EndianDataOutputStream;
011:
012: /**
013: *
014: * This class represnts an ESRI Shape file.<p>
015: * You construct it with a file name, and later
016: * you can read the file's propertys, i.e. Sizes, Types, and the data itself.<p>
017: * Copyright 1998 by James Macgill. <p>
018: *
019: * Version 1.0beta1.1 (added construct with inputstream)
020: * 1.0beta1.2 (made Shape type constants public 18/Aug/98)
021: *
022: * This class supports the Shape file as set out in :-<br>
023: * <a href="http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf"><b>"ESRI(r) Shapefile - A Technical Description"</b><br>
024: * <i>'An ESRI White Paper . May 1997'</i></a><p>
025: *
026: * This code is coverd by the LGPL.
027: *
028: * <a href="mailto:j.macgill@geog.leeds.ac.uk">Mail the Author</a>
029: */
030:
031: public class Shapefile {
032:
033: static final int SHAPEFILE_ID = 9994;
034: static final int VERSION = 1000;
035:
036: public static final int NULL = 0;
037: public static final int POINT = 1;
038: public static final int POINTZ = 11;
039: public static final int POINTM = 21;
040: public static final int ARC = 3;
041: public static final int ARCM = 23;
042: public static final int ARCZ = 13;
043: public static final int POLYGON = 5;
044: public static final int POLYGONM = 25;
045: public static final int POLYGONZ = 15;
046: public static final int MULTIPOINT = 8;
047: public static final int MULTIPOINTM = 28;
048: public static final int MULTIPOINTZ = 18;
049: public static final int UNDEFINED = -1;
050: //Types 2,4,6,7 and 9 were undefined at time or writeing
051:
052: private URL baseURL;
053: private InputStream myInputStream;
054:
055: /**
056: * Creates and initialises a shapefile from a url
057: * @param url The url of the shapefile
058: */
059: public Shapefile(URL url) {
060: baseURL = url;
061: myInputStream = null;
062: try {
063: URLConnection uc = baseURL.openConnection();
064: // From: Sheldon Young [mailto:syoung@forsite-sa.com]
065: // Sent: Thursday, February 05, 2004 2:49 PM
066: // To: Martin Davis
067: // Subject: Patch for JUMP
068: //
069: //
070: // For Shapefile.java, using a 16 kb buffer instead of the
071: // default 2 kb buffer makes a 20% difference on my 15MB test case.
072: myInputStream = new BufferedInputStream(
073: uc.getInputStream(), 16 * 1024);
074: } catch (Exception e) {
075: }
076: }
077:
078: public Shapefile(InputStream IS) {
079: myInputStream = IS;
080: }
081:
082: private EndianDataInputStream getInputStream() throws IOException {
083: if (myInputStream == null) {
084: throw new IOException(
085: "Could make a connection to the URL: " + baseURL);
086: }
087: EndianDataInputStream sfile = new EndianDataInputStream(
088: myInputStream);
089: return sfile;
090: }
091:
092: private EndianDataOutputStream getOutputStream() throws IOException {
093: // System.out.println(baseURL.getFile());
094: //URLConnection connection = baseURL.openConnection();
095: //connection.setUseCaches(false);
096: // connection.setDoInput(true);
097: // connection.setDoOutput(true);
098: // connection.connect();
099: //BufferedOutputStream in = new BufferedOutputStream(connection.getOutputStream());
100: BufferedOutputStream in = new BufferedOutputStream(
101: new FileOutputStream(baseURL.getFile()));
102: EndianDataOutputStream sfile = new EndianDataOutputStream(in);
103: return sfile;
104: }
105:
106: /**
107: * Initialises a shapefile from disk.
108: * Use Shapefile(String) if you don't want to use LEDataInputStream directly (recomended)
109: * @param geometryFactory the geometry factory to use to read the shapes
110: */
111: public GeometryCollection read(GeometryFactory geometryFactory)
112: throws IOException, ShapefileException, Exception {
113: EndianDataInputStream file = getInputStream();
114: if (file == null)
115: throw new IOException(
116: "Failed connection or no content for " + baseURL);
117: ShapefileHeader mainHeader = new ShapefileHeader(file);
118: if (mainHeader.getVersion() < VERSION) {
119: System.err.println("Sf-->Warning, Shapefile format ("
120: + mainHeader.getVersion()
121: + ") older that supported (" + VERSION
122: + "), attempting to read anyway");
123: }
124: if (mainHeader.getVersion() > VERSION) {
125: System.err.println("Sf-->Warning, Shapefile format ("
126: + mainHeader.getVersion()
127: + ") newer that supported (" + VERSION
128: + "), attempting to read anyway");
129: }
130:
131: Geometry body;
132: ArrayList list = new ArrayList();
133: int type = mainHeader.getShapeType();
134: ShapeHandler handler = getShapeHandler(type);
135: if (handler == null)
136: throw new ShapeTypeNotSupportedException(
137: "Unsuported shape type:" + type);
138:
139: int recordNumber = 0;
140: int contentLength = 0;
141: try {
142: while (true) {
143: // file.setLittleEndianMode(false);
144: recordNumber = file.readIntBE();
145: contentLength = file.readIntBE();
146: try {
147: body = handler.read(file, geometryFactory,
148: contentLength);
149: list.add(body);
150: // System.out.println("Done record: " + recordNumber);
151: } catch (IllegalArgumentException r2d2) {
152: //System.out.println("Record " +recordNumber+ " has is NULL Shape");
153: list.add(new GeometryCollection(null, null, -1));
154: } catch (Exception c3p0) {
155: System.out.println("Error processing record (a):"
156: + recordNumber);
157: System.out.println(c3p0.getMessage());
158: c3p0.printStackTrace();
159: list.add(new GeometryCollection(null, null, -1));
160: }
161: // System.out.println("processing:" +recordNumber);
162: }
163: } catch (EOFException e) {
164:
165: }
166: return geometryFactory
167: .createGeometryCollection((Geometry[]) list
168: .toArray(new Geometry[] {}));
169: }
170:
171: /**
172: * Saves a shapefile to and output stream.
173: * @param geometries geometry collection to write
174: * @param ShapeFileDimentions shapefile dimension
175: */
176: //ShapeFileDimentions => 2=x,y ; 3=x,y,m ; 4=x,y,z,m
177: public void write(GeometryCollection geometries,
178: int ShapeFileDimentions) throws IOException, Exception {
179: EndianDataOutputStream file = getOutputStream();
180: ShapefileHeader mainHeader = new ShapefileHeader(geometries,
181: ShapeFileDimentions);
182: mainHeader.write(file);
183: int pos = 50; // header length in WORDS
184: //records;
185: //body;
186: //header;
187: int numShapes = geometries.getNumGeometries();
188: Geometry body;
189: ShapeHandler handler;
190:
191: if (geometries.getNumGeometries() == 0) {
192: handler = new PointHandler(); //default
193: } else {
194: handler = Shapefile.getShapeHandler(geometries
195: .getGeometryN(0), ShapeFileDimentions);
196: }
197:
198: for (int i = 0; i < numShapes; i++) {
199: body = geometries.getGeometryN(i);
200: //file.setLittleEndianMode(false);
201: file.writeIntBE(i + 1);
202: file.writeIntBE(handler.getLength(body));
203: // file.setLittleEndianMode(true);
204: pos += 4; // length of header in WORDS
205: handler.write(body, file);
206: pos += handler.getLength(body); // length of shape in WORDS
207: }
208: file.flush();
209: file.close();
210: }
211:
212: //ShapeFileDimentions => 2=x,y ; 3=x,y,m ; 4=x,y,z,m
213: public synchronized void writeIndex(GeometryCollection geometries,
214: EndianDataOutputStream file, int ShapeFileDimentions)
215: throws IOException, Exception {
216: Geometry geom;
217:
218: ShapeHandler handler;
219: int nrecords = geometries.getNumGeometries();
220: ShapefileHeader mainHeader = new ShapefileHeader(geometries,
221: ShapeFileDimentions);
222:
223: if (geometries.getNumGeometries() == 0) {
224: handler = new PointHandler(); //default
225: } else {
226: handler = Shapefile.getShapeHandler(geometries
227: .getGeometryN(0), ShapeFileDimentions);
228: }
229:
230: // mainHeader.fileLength = 50 + 4*nrecords;
231:
232: mainHeader.writeToIndex(file);
233: int pos = 50;
234: int len = 0;
235:
236: //file.setLittleEndianMode(false);
237:
238: for (int i = 0; i < nrecords; i++) {
239: geom = geometries.getGeometryN(i);
240: len = handler.getLength(geom);
241:
242: file.writeIntBE(pos);
243: file.writeIntBE(len);
244: pos = pos + len + 4;
245: }
246: file.flush();
247: file.close();
248: }
249:
250: /**
251: * Returns a string for the shape type of index.
252: * @param index An int coresponding to the shape type to be described
253: * @return A string descibing the shape type
254: */
255: public static String getShapeTypeDescription(int index) {
256: switch (index) {
257: case (NULL):
258: return ("Null");
259: case (POINT):
260: return ("Points");
261: case (POINTZ):
262: return ("Points Z");
263: case (POINTM):
264: return ("Points M");
265: case (ARC):
266: return ("Arcs");
267: case (ARCM):
268: return ("ArcsM");
269: case (ARCZ):
270: return ("ArcsM");
271: case (POLYGON):
272: return ("Polygon");
273: case (POLYGONM):
274: return ("PolygonM");
275: case (POLYGONZ):
276: return ("PolygonZ");
277: case (MULTIPOINT):
278: return ("Multipoint");
279: case (MULTIPOINTM):
280: return ("MultipointM");
281: case (MULTIPOINTZ):
282: return ("MultipointZ");
283: default:
284: return ("Undefined");
285: }
286: }
287:
288: public static ShapeHandler getShapeHandler(Geometry geom,
289: int ShapeFileDimentions) throws Exception {
290: return getShapeHandler(getShapeType(geom, ShapeFileDimentions));
291: }
292:
293: public static ShapeHandler getShapeHandler(int type)
294: throws Exception {
295:
296: switch (type) {
297: case Shapefile.POINT:
298: return new PointHandler();
299: case Shapefile.POINTZ:
300: return new PointHandler(Shapefile.POINTZ);
301: case Shapefile.POINTM:
302: return new PointHandler(Shapefile.POINTM);
303: case Shapefile.POLYGON:
304: return new PolygonHandler();
305: case Shapefile.POLYGONM:
306: return new PolygonHandler(Shapefile.POLYGONM);
307: case Shapefile.POLYGONZ:
308: return new PolygonHandler(Shapefile.POLYGONZ);
309: case Shapefile.ARC:
310: return new MultiLineHandler();
311: case Shapefile.ARCM:
312: return new MultiLineHandler(Shapefile.ARCM);
313: case Shapefile.ARCZ:
314: return new MultiLineHandler(Shapefile.ARCZ);
315: case Shapefile.MULTIPOINT:
316: return new MultiPointHandler();
317: case Shapefile.MULTIPOINTM:
318: return new MultiPointHandler(Shapefile.MULTIPOINTM);
319: case Shapefile.MULTIPOINTZ:
320: return new MultiPointHandler(Shapefile.MULTIPOINTZ);
321: }
322: return null;
323: }
324:
325: //ShapeFileDimentions => 2=x,y ; 3=x,y,m ; 4=x,y,z,m
326: public static int getShapeType(Geometry geom,
327: int ShapeFileDimentions) throws ShapefileException {
328:
329: if ((ShapeFileDimentions != 2) && (ShapeFileDimentions != 3)
330: && (ShapeFileDimentions != 4)) {
331: throw new ShapefileException(
332: "invalid ShapeFileDimentions for getShapeType - expected 2,3,or 4 but got "
333: + ShapeFileDimentions
334: + " (2=x,y ; 3=x,y,m ; 4=x,y,z,m)");
335: //ShapeFileDimentions = 2;
336: }
337:
338: if (geom instanceof Point) {
339: switch (ShapeFileDimentions) {
340: case 2:
341: return Shapefile.POINT;
342: case 3:
343: return Shapefile.POINTM;
344: case 4:
345: return Shapefile.POINTZ;
346: }
347: }
348: if (geom instanceof MultiPoint) {
349: switch (ShapeFileDimentions) {
350: case 2:
351: return Shapefile.MULTIPOINT;
352: case 3:
353: return Shapefile.MULTIPOINTM;
354: case 4:
355: return Shapefile.MULTIPOINTZ;
356: }
357: }
358: if ((geom instanceof Polygon) || (geom instanceof MultiPolygon)) {
359: switch (ShapeFileDimentions) {
360: case 2:
361: return Shapefile.POLYGON;
362: case 3:
363: return Shapefile.POLYGONM;
364: case 4:
365: return Shapefile.POLYGONZ;
366: }
367: }
368: if ((geom instanceof LineString)
369: || (geom instanceof MultiLineString)) {
370: switch (ShapeFileDimentions) {
371: case 2:
372: return Shapefile.ARC;
373: case 3:
374: return Shapefile.ARCM;
375: case 4:
376: return Shapefile.ARCZ;
377: }
378: }
379: return Shapefile.UNDEFINED;
380: }
381:
382: public synchronized void readIndex(InputStream is)
383: throws IOException {
384: EndianDataInputStream file = null;
385: try {
386: BufferedInputStream in = new BufferedInputStream(is);
387: file = new EndianDataInputStream(in);
388: } catch (Exception e) {
389: System.err.println(e);
390: }
391: ShapefileHeader head = new ShapefileHeader(file);
392:
393: int pos = 0, len = 0;
394: //file.setLittleEndianMode(false);
395: file.close();
396: }
397: }
|