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.nio.ByteBuffer;
020:
021: import com.vividsolutions.jts.geom.Coordinate;
022: import com.vividsolutions.jts.geom.CoordinateSequence;
023: import com.vividsolutions.jts.geom.Envelope;
024: import com.vividsolutions.jts.geom.GeometryFactory;
025: import com.vividsolutions.jts.geom.LineString;
026: import com.vividsolutions.jts.geom.MultiLineString;
027: import org.geotools.geometry.jts.coordinatesequence.CSBuilder;
028: import org.geotools.geometry.jts.coordinatesequence.CSBuilderFactory;
029:
030: /*
031: * $Id: MultiLineHandler.java 28944 2008-01-25 15:56:58Z acuster $
032: * @author aaime
033: * @author Ian Schneider
034: */
035: /**
036: * The default JTS handler for shapefile. Currently uses the default JTS
037: * GeometryFactory, since it doesn't seem to matter.
038: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/plugin/shapefile/src/main/java/org/geotools/data/shapefile/shp/MultiLineHandler.java $
039: */
040: public class MultiLineHandler implements ShapeHandler {
041: final ShapeType shapeType;
042:
043: GeometryFactory geometryFactory = new GeometryFactory();
044:
045: CSBuilder builder = CSBuilderFactory.getDefaultBuilder();
046:
047: double[] x;
048:
049: double[] y;
050:
051: double[] z;
052:
053: /** Create a MultiLineHandler for ShapeType.ARC */
054: public MultiLineHandler() {
055: shapeType = ShapeType.ARC;
056: }
057:
058: /**
059: * Create a MultiLineHandler for one of: <br>
060: * ShapeType.ARC,ShapeType.ARCM,ShapeType.ARCZ
061: *
062: * @param type
063: * The ShapeType to use.
064: * @throws ShapefileException
065: * If the ShapeType is not correct (see constructor).
066: */
067: public MultiLineHandler(ShapeType type) throws ShapefileException {
068: if ((type != ShapeType.ARC) && (type != ShapeType.ARCM)
069: && (type != ShapeType.ARCZ)) {
070: throw new ShapefileException(
071: "MultiLineHandler constructor - expected type to be 3,13 or 23");
072: }
073:
074: shapeType = type;
075: }
076:
077: /**
078: * Get the type of shape stored
079: * (ShapeType.ARC,ShapeType.ARCM,ShapeType.ARCZ)
080: */
081: public ShapeType getShapeType() {
082: return shapeType;
083: }
084:
085: /** */
086: public int getLength(Object geometry) {
087: MultiLineString multi = (MultiLineString) geometry;
088:
089: int numlines;
090: int numpoints;
091: int length;
092:
093: numlines = multi.getNumGeometries();
094: numpoints = multi.getNumPoints();
095:
096: if (shapeType == ShapeType.ARC) {
097: length = 44 + (4 * numlines) + (numpoints * 16);
098: } else if (shapeType == ShapeType.ARCM) {
099: length = 44 + (4 * numlines) + (numpoints * 16) + 8 + 8
100: + (8 * numpoints);
101: } else if (shapeType == ShapeType.ARCZ) {
102: length = 44 + (4 * numlines) + (numpoints * 16) + 8 + 8
103: + (8 * numpoints) + 8 + 8 + (8 * numpoints);
104: } else {
105: throw new IllegalStateException(
106: "Expected ShapeType of Arc, got " + shapeType);
107: }
108:
109: return length;
110: }
111:
112: private Object createNull() {
113: return geometryFactory
114: .createMultiLineString((LineString[]) null);
115: }
116:
117: public Object readOld(ByteBuffer buffer, ShapeType type) {
118: if (type == ShapeType.NULL) {
119: return createNull();
120: }
121: int dimensions = shapeType == ShapeType.ARCZ ? 3 : 2;
122: //read bounding box (not needed)
123: buffer.position(buffer.position() + 4 * 8);
124:
125: int numParts = buffer.getInt();
126: int numPoints = buffer.getInt(); //total number of points
127:
128: int[] partOffsets = new int[numParts];
129:
130: //points = new Coordinate[numPoints];
131: for (int i = 0; i < numParts; i++) {
132: partOffsets[i] = buffer.getInt();
133: }
134:
135: LineString[] lines = new LineString[numParts];
136: // Coordinate[] coords = new Coordinate[numPoints];
137: prepareCoordinateArrays(numPoints);
138:
139: for (int t = 0; t < numPoints; t++) {
140: x[t] = buffer.getDouble();
141: y[t] = buffer.getDouble();
142: // coords[t] = new Coordinate(buffer.getDouble(),
143: // buffer.getDouble());
144: }
145:
146: if (dimensions == 3) {
147: //z min, max
148: buffer.position(buffer.position() + 2 * 8);
149: for (int t = 0; t < numPoints; t++) {
150: // coords[t].z = buffer.getDouble(); //z value
151: z[t] = buffer.getDouble();
152: }
153: }
154:
155: int offset = 0;
156: int start = 0;
157: int finish;
158: int length;
159:
160: for (int part = 0; part < numParts; part++) {
161: if (part == (numParts - 1)) {
162: finish = numPoints;
163: } else {
164: finish = partOffsets[part + 1];
165: }
166:
167: length = finish - start;
168: start = finish;
169:
170: // Lines may be either composed of 0, 2 or more coordinates, but not
171: // just one.
172: // since some tools produce such files, we work around it by
173: // duplicating the single
174: // coordinate
175: // Coordinate[] points = null;
176: if (length == 1)
177: // points = new Coordinate[2];
178: builder.start(2, dimensions);
179: else
180: // points = new Coordinate[length];
181: builder.start(length, dimensions);
182:
183: for (int i = 0; i < length; i++) {
184: builder.setOrdinate(x[offset], 0, i);
185: builder.setOrdinate(y[offset], 1, i);
186: if (dimensions == 3)
187: builder.setOrdinate(z[offset], 2, i);
188: // points[i] = coords[offset];
189: offset++;
190: }
191:
192: if (length == 1) {
193: // points[1] = (Coordinate) points[0].clone();
194: builder.setOrdinate(x[offset - 1], 0, 1);
195: builder.setOrdinate(y[offset - 1], 1, 1);
196: if (dimensions == 3)
197: builder.setOrdinate(z[offset - 1], 2, 1);
198: }
199:
200: lines[part] = geometryFactory.createLineString(builder
201: .end());
202: }
203:
204: return geometryFactory.createMultiLineString(lines);
205: }
206:
207: public Object read(ByteBuffer buffer, ShapeType type) {
208: if (type == ShapeType.NULL) {
209: return createNull();
210: }
211: int dimensions = (shapeType == ShapeType.ARCZ) ? 3 : 2;
212: //read bounding box (not needed)
213: buffer.position(buffer.position() + 4 * 8);
214:
215: int numParts = buffer.getInt();
216: int numPoints = buffer.getInt(); //total number of points
217:
218: int[] partOffsets = new int[numParts];
219:
220: //points = new Coordinate[numPoints];
221: for (int i = 0; i < numParts; i++) {
222: partOffsets[i] = buffer.getInt();
223: }
224: // read the first two coordinates and start building the coordinate
225: // sequences
226: CoordinateSequence[] lines = new CoordinateSequence[numParts];
227: int finish, start = 0;
228: int length = 0;
229: boolean clonePoint = false;
230: for (int part = 0; part < numParts; part++) {
231: start = partOffsets[part];
232:
233: if (part == (numParts - 1)) {
234: finish = numPoints;
235: } else {
236: finish = partOffsets[part + 1];
237: }
238:
239: length = finish - start;
240: if (length == 1) {
241: length = 2;
242: clonePoint = true;
243: } else {
244: clonePoint = false;
245: }
246:
247: builder.start(length, dimensions);
248: for (int i = 0; i < length; i++) {
249: builder.setOrdinate(buffer.getDouble(), 0, i);
250: builder.setOrdinate(buffer.getDouble(), 1, i);
251: }
252:
253: if (clonePoint) {
254: builder.setOrdinate(builder.getOrdinate(0, 0), 0, 1);
255: builder.setOrdinate(builder.getOrdinate(1, 0), 1, 1);
256: }
257:
258: lines[part] = builder.end();
259: }
260:
261: // if we have another coordinate, read and add to the coordinate
262: // sequences
263: if (dimensions == 3) {
264: //z min, max
265: buffer.position(buffer.position() + 2 * 8);
266: for (int part = 0; part < numParts; part++) {
267: start = partOffsets[part];
268:
269: if (part == (numParts - 1)) {
270: finish = numPoints;
271: } else {
272: finish = partOffsets[part + 1];
273: }
274:
275: length = finish - start;
276: if (length == 1) {
277: length = 2;
278: clonePoint = true;
279: } else {
280: clonePoint = false;
281: }
282:
283: for (int i = 0; i < length; i++) {
284: builder.setOrdinate(lines[part],
285: buffer.getDouble(), 2, i);
286: }
287:
288: }
289: }
290:
291: // Prepare line strings and return the multilinestring
292: LineString[] lineStrings = new LineString[numParts];
293: for (int part = 0; part < numParts; part++) {
294: lineStrings[part] = geometryFactory
295: .createLineString(lines[part]);
296: }
297:
298: return geometryFactory.createMultiLineString(lineStrings);
299: }
300:
301: /**
302: * @param numPoints
303: */
304: private void prepareCoordinateArrays(int numPoints) {
305: if (x == null || x.length < numPoints) {
306: x = new double[numPoints];
307: y = new double[numPoints];
308: z = new double[numPoints];
309: }
310: }
311:
312: public void write(ByteBuffer buffer, Object geometry) {
313: MultiLineString multi = (MultiLineString) geometry;
314:
315: Envelope box = multi.getEnvelopeInternal();
316: buffer.putDouble(box.getMinX());
317: buffer.putDouble(box.getMinY());
318: buffer.putDouble(box.getMaxX());
319: buffer.putDouble(box.getMaxY());
320:
321: int numParts = multi.getNumGeometries();
322:
323: buffer.putInt(numParts);
324: int npoints = multi.getNumPoints();
325: buffer.putInt(npoints);
326:
327: LineString[] lines = new LineString[numParts];
328: int idx = 0;
329:
330: for (int i = 0; i < numParts; i++) {
331: lines[i] = (LineString) multi.getGeometryN(i);
332: buffer.putInt(idx);
333: idx = idx + lines[i].getNumPoints();
334: }
335:
336: Coordinate[] coords = multi.getCoordinates();
337:
338: for (int t = 0; t < npoints; t++) {
339: buffer.putDouble(coords[t].x);
340: buffer.putDouble(coords[t].y);
341: }
342:
343: if (shapeType == ShapeType.ARCZ) {
344: double[] zExtreame = JTSUtilities.zMinMax(coords);
345:
346: if (Double.isNaN(zExtreame[0])) {
347: buffer.putDouble(0.0);
348: buffer.putDouble(0.0);
349: } else {
350: buffer.putDouble(zExtreame[0]);
351: buffer.putDouble(zExtreame[1]);
352: }
353:
354: for (int t = 0; t < npoints; t++) {
355: double z = coords[t].z;
356:
357: if (Double.isNaN(z)) {
358: buffer.putDouble(0.0);
359: } else {
360: buffer.putDouble(z);
361: }
362: }
363: }
364:
365: if (shapeType == ShapeType.ARCZ) {
366: buffer.putDouble(-10E40);
367: buffer.putDouble(-10E40);
368:
369: for (int t = 0; t < npoints; t++) {
370: buffer.putDouble(-10E40);
371: }
372: }
373: }
374:
375: }
376:
377: /*
378: * $Log: MultiLineHandler.java,v $ Revision 1.4 2003/11/13 22:10:35 jmacgill
379: * cast a null to avoid ambigous call with JTS1.4
380: *
381: * Revision 1.3 2003/07/23 23:41:09 ianschneider more testing updates
382: *
383: * Revision 1.2 2003/07/23 00:59:59 ianschneider Lots of PMD fix ups
384: *
385: * Revision 1.1 2003/05/14 17:51:20 ianschneider migrated packages
386: *
387: * Revision 1.3 2003/04/30 23:19:45 ianschneider Added construction of multi
388: * geometries for default return values, even if only one geometry. This could
389: * have effects through system.
390: *
391: * Revision 1.2 2003/03/30 20:21:09 ianschneider Moved buffer branch to main
392: *
393: * Revision 1.1.2.3 2003/03/12 15:30:14 ianschneider made ShapeType final for
394: * handlers - once they're created, it won't change.
395: *
396: * Revision 1.1.2.2 2003/03/07 00:36:41 ianschneider
397: *
398: * Added back the additional ShapeType parameter in ShapeHandler.read.
399: * ShapeHandler's need return their own special "null" shape if needed. Fixed
400: * the ShapefileReader to not throw exceptions for "null" shapes. Fixed
401: * ShapefileReader to accomodate junk after the last valid record. The theory
402: * goes, if the shape number is proper, that is, one greater than the previous,
403: * we consider that a valid record and attempt to read it. I suppose, by chance,
404: * the junk could coincide with the next record number. Stupid ESRI. Fixed some
405: * record-length calculations which resulted in writing of bad shapefiles.
406: *
407: * Revision 1.1.2.1 2003/03/06 01:16:34 ianschneider
408: *
409: * The initial changes for moving to java.nio. Added some documentation and
410: * improved exception handling. Works for reading, may work for writing as of
411: * now.
412: *
413: * Revision 1.1 2003/02/27 22:35:50 aaime New shapefile module, initial commit
414: *
415: * Revision 1.2 2003/01/22 18:31:05 jaquino Enh: Make About Box configurable
416: *
417: * Revision 1.3 2002/10/30 22:36:11 dblasby Line reader now returns
418: * LINESTRING(..) if there is only one part to the arc polyline.
419: *
420: * Revision 1.2 2002/09/09 20:46:22 dblasby Removed LEDatastream refs and
421: * replaced with EndianData[in/out]putstream
422: *
423: * Revision 1.1 2002/08/27 21:04:58 dblasby orginal
424: *
425: * Revision 1.2 2002/03/05 10:23:59 jmacgill made sure geometries were created
426: * using the factory methods
427: *
428: * Revision 1.1 2002/02/28 00:38:50 jmacgill Renamed files to more intuitve
429: * names
430: *
431: * Revision 1.3 2002/02/13 00:23:53 jmacgill First semi working JTS version of
432: * Shapefile code
433: *
434: * Revision 1.2 2002/02/11 18:42:45 jmacgill changed read and write statements
435: * so that they produce and take Geometry objects instead of specific MultiLine
436: * objects changed parts[] array name to partOffsets[] for clarity and
437: * consistency with ShapePolygon
438: *
439: * Revision 1.1 2002/02/11 16:54:43 jmacgill added shapefile code and
440: * directories
441: *
442: */
|