001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2002, Centre for Computational Geography
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.data.shapefile.shp;
018:
019: import java.io.IOException;
020: import java.nio.ByteBuffer;
021: import java.nio.ByteOrder;
022: import java.nio.MappedByteBuffer;
023: import java.nio.channels.FileChannel;
024:
025: import org.geotools.data.shapefile.Lock;
026: import org.geotools.data.shapefile.StreamLogging;
027: import org.geotools.resources.NIOUtilities;
028:
029: import com.vividsolutions.jts.geom.Envelope;
030: import com.vividsolutions.jts.geom.Geometry;
031: import com.vividsolutions.jts.geom.GeometryCollection;
032:
033: /**
034: * ShapefileWriter allows for the storage of geometries in esris shp format.
035: * During writing, an index will also be created. To create a ShapefileWriter,
036: * do something like<br>
037: * <code>
038: * GeometryCollection geoms;
039: * File shp = new File("myshape.shp");
040: * File shx = new File("myshape.shx");
041: * ShapefileWriter writer = new ShapefileWriter(
042: * shp.getChannel(),shx.getChannel()
043: * );
044: * writer.write(geoms,ShapeType.ARC);
045: * </code>
046: * This example assumes that each shape in the collection is a LineString.
047: *
048: * @see org.geotools.data.shapefile.ShapefileDataStore
049: * @author jamesm
050: * @author aaime
051: * @author Ian Schneider
052: *
053: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/plugin/shapefile/src/main/java/org/geotools/data/shapefile/shp/ShapefileWriter.java $
054: */
055: public class ShapefileWriter {
056:
057: FileChannel shpChannel;
058: FileChannel shxChannel;
059: ByteBuffer shapeBuffer;
060: ByteBuffer indexBuffer;
061: ShapeHandler handler;
062: ShapeType type;
063: int offset;
064: int lp;
065: int cnt;
066: private StreamLogging shpLogger = new StreamLogging(
067: "SHP Channel in ShapefileWriter");
068: private StreamLogging shxLogger = new StreamLogging(
069: "SHX Channel in ShapefileWriter");
070:
071: /** Creates a new instance of ShapeFileWriter
072: * @throws IOException */
073: public ShapefileWriter(FileChannel shpChannel,
074: FileChannel shxChannel, Lock lock) throws IOException {
075: this .shpChannel = shpChannel;
076: this .shxChannel = shxChannel;
077: shpLogger.open();
078: shxLogger.open();
079: }
080:
081: // private void allocateBuffers(int geomCnt, int fileLength) throws IOException {
082: // if (shpChannel instanceof FileChannel) {
083: // FileChannel shpc = (FileChannel) shpChannel;
084: // FileChannel shxc = (FileChannel) shxChannel;
085: // shapeBuffer = shpc.map(FileChannel.MapMode.READ_WRITE,0, fileLength);
086: // indexBuffer = shxc.map(FileChannel.MapMode.READ_WRITE,0, 100 + 8 * geomCnt);
087: // indexBuffer.order(ByteOrder.BIG_ENDIAN);
088: // } else {
089: // throw new RuntimeException("Can only handle FileChannels - fix me!");
090: // }
091: // }
092:
093: /**
094: * Allocate some buffers for writing.
095: */
096: private void allocateBuffers() {
097: shapeBuffer = ByteBuffer.allocateDirect(16 * 1024);
098: indexBuffer = ByteBuffer.allocateDirect(100);
099: }
100:
101: /**
102: * Make sure our buffer is of size.
103: */
104: private void checkShapeBuffer(int size) {
105: if (shapeBuffer.capacity() < size) {
106: if (shapeBuffer != null)
107: NIOUtilities.clean(shapeBuffer);
108: shapeBuffer = ByteBuffer.allocateDirect(size);
109: }
110: }
111:
112: /**
113: * Drain internal buffers into underlying channels.
114: */
115: private void drain() throws IOException {
116: shapeBuffer.flip();
117: indexBuffer.flip();
118: while (shapeBuffer.remaining() > 0)
119: shpChannel.write(shapeBuffer);
120: while (indexBuffer.remaining() > 0)
121: shxChannel.write(indexBuffer);
122: shapeBuffer.flip().limit(shapeBuffer.capacity());
123: indexBuffer.flip().limit(indexBuffer.capacity());
124: }
125:
126: private void writeHeaders(GeometryCollection geometries,
127: ShapeType type) throws IOException {
128: // ShapefileHeader header = new ShapefileHeader();
129: // Envelope bounds = geometries.getEnvelopeInternal();
130: // header.write(shapeBuffer, type, geometries.getNumGeometries(), fileLength / 2,
131: // bounds.getMinX(),bounds.getMinY(), bounds.getMaxX(),bounds.getMaxY()
132: // );
133: // header.write(indexBuffer, type, geometries.getNumGeometries(), 50 + 4 * geometries.getNumGeometries(),
134: // bounds.getMinX(),bounds.getMinY(), bounds.getMaxX(),bounds.getMaxY()
135: // );
136: int fileLength = 100;
137: //int largestShapeSize = 0;
138: for (int i = geometries.getNumGeometries() - 1; i >= 0; i--) {
139: // shape length + record (2 ints)
140: int size = handler.getLength(geometries.getGeometryN(i)) + 8;
141: fileLength += size;
142: // if (size > largestShapeSize)
143: // largestShapeSize = size;
144: }
145: writeHeaders(geometries.getEnvelopeInternal(), type, geometries
146: .getNumGeometries(), fileLength);
147: }
148:
149: /**
150: * Write the headers for this shapefile including the bounds, shape type, the
151: * number of geometries and the total fileLength (in actual bytes, NOT
152: * 16 bit words).
153: */
154: public void writeHeaders(Envelope bounds, ShapeType type,
155: int numberOfGeometries, int fileLength) throws IOException {
156:
157: try {
158: handler = type.getShapeHandler();
159: } catch (ShapefileException se) {
160: throw new RuntimeException("unexpected Exception", se);
161: }
162: if (shapeBuffer == null)
163: allocateBuffers();
164: ShapefileHeader header = new ShapefileHeader();
165: header.write(shapeBuffer, type, numberOfGeometries,
166: fileLength / 2, bounds.getMinX(), bounds.getMinY(),
167: bounds.getMaxX(), bounds.getMaxY());
168: header.write(indexBuffer, type, numberOfGeometries,
169: 50 + 4 * numberOfGeometries, bounds.getMinX(), bounds
170: .getMinY(), bounds.getMaxX(), bounds.getMaxY());
171:
172: offset = 50;
173: this .type = type;
174: cnt = 0;
175:
176: shpChannel.position(0);
177: shxChannel.position(0);
178: drain();
179: }
180:
181: /**
182: * Allocate internal buffers and position the channels to the beginning
183: * or the record section of the shapefile. The headers MUST be rewritten
184: * after this operation, or the file may be corrupt...
185: */
186: public void skipHeaders() throws IOException {
187: if (shapeBuffer == null)
188: allocateBuffers();
189: shpChannel.position(100);
190: shxChannel.position(100);
191: }
192:
193: /**
194: * Write a single Geometry to this shapefile. The Geometry must be
195: * compatable with the ShapeType assigned during the writing of the headers.
196: */
197: public void writeGeometry(Geometry g) throws IOException {
198: if (shapeBuffer == null)
199: throw new IOException("Must write headers first");
200: lp = shapeBuffer.position();
201: int length = handler.getLength(g);
202:
203: // must allocate enough for shape + header (2 ints)
204: checkShapeBuffer(length + 8);
205:
206: length /= 2;
207:
208: shapeBuffer.order(ByteOrder.BIG_ENDIAN);
209: shapeBuffer.putInt(++cnt);
210: shapeBuffer.putInt(length);
211: shapeBuffer.order(ByteOrder.LITTLE_ENDIAN);
212: shapeBuffer.putInt(type.id);
213: handler.write(shapeBuffer, g);
214:
215: assert (length * 2 == (shapeBuffer.position() - lp) - 8);
216:
217: lp = shapeBuffer.position();
218:
219: // write to the shx
220: indexBuffer.putInt(offset);
221: indexBuffer.putInt(length);
222: offset += length + 4;
223:
224: drain();
225: assert (shapeBuffer.position() == 0);
226: }
227:
228: /**
229: * Close the underlying Channels.
230: */
231: public void close() throws IOException {
232: if (shpChannel != null && shpChannel.isOpen()) {
233: shpLogger.close();
234: shpChannel.close();
235: }
236: if (shxChannel != null && shxChannel.isOpen()) {
237: shxChannel.close();
238: shxLogger.close();
239: }
240: shpChannel = null;
241: shxChannel = null;
242: handler = null;
243: if (indexBuffer instanceof MappedByteBuffer) {
244: NIOUtilities.clean(indexBuffer);
245: }
246: if (shapeBuffer instanceof MappedByteBuffer) {
247: NIOUtilities.clean(shapeBuffer);
248: }
249: indexBuffer = null;
250: shapeBuffer = null;
251: }
252:
253: /**
254: * Bulk write method for writing a collection of (hopefully) like geometries
255: * of the given ShapeType.
256: */
257: public void write(GeometryCollection geometries, ShapeType type)
258: throws IOException, ShapefileException {
259: handler = type.getShapeHandler();
260:
261: writeHeaders(geometries, type);
262:
263: lp = shapeBuffer.position();
264: for (int i = 0, ii = geometries.getNumGeometries(); i < ii; i++) {
265: Geometry g = geometries.getGeometryN(i);
266:
267: writeGeometry(g);
268: }
269:
270: close();
271: }
272:
273: }
|