001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/shpapi/shape_new/ShapePolyline.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2008 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014    
015     This library is distributed in the hope that it will be useful,
016     but WITHOUT ANY WARRANTY; without even the implied warranty of
017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
018     Lesser General Public License for more details.
019    
020     You should have received a copy of the GNU Lesser General Public
021     License along with this library; if not, write to the Free Software
022     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
023    
024     Contact:
025    
026     Andreas Poth
027     lat/lon GmbH
028     Aennchenstr. 19
029     53177 Bonn
030     Germany
031     E-Mail: poth@lat-lon.de
032    
033     Prof. Dr. Klaus Greve
034     Department of Geography
035     University of Bonn
036     Meckenheimer Allee 166
037     53115 Bonn
038     Germany
039     E-Mail: greve@giub.uni-bonn.de
040    
041     ---------------------------------------------------------------------------*/
042    package org.deegree.io.shpapi.shape_new;
043    
044    import java.util.Arrays;
045    import java.util.List;
046    
047    import org.deegree.framework.log.ILogger;
048    import org.deegree.framework.log.LoggerFactory;
049    import org.deegree.model.spatialschema.ByteUtils;
050    import org.deegree.model.spatialschema.Curve;
051    import org.deegree.model.spatialschema.Geometry;
052    import org.deegree.model.spatialschema.GeometryException;
053    import org.deegree.model.spatialschema.GeometryFactory;
054    import org.deegree.model.spatialschema.LineString;
055    import org.deegree.model.spatialschema.Position;
056    import org.deegree.model.spatialschema.WKTAdapter;
057    
058    /**
059     * <code>ShapePolyline</code> corresponds to the Polyline, PolylineM and PolylineZ shapes of the
060     * shapefile spec.
061     * 
062     * @author <a href="mailto:schmitz@lat-lon.de">Andreas Schmitz</a>
063     * @author last edited by: $Author: apoth $
064     * 
065     * @version $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $
066     */
067    public class ShapePolyline implements Shape {
068    
069        private static final ILogger LOG = LoggerFactory.getLogger( ShapePolyline.class );
070    
071        protected boolean isM, isZ;
072    
073        private ShapeEnvelope envelope;
074    
075        protected ShapePoint[][] points;
076    
077        /**
078         * Empty constructor. Should be used in concert with read().
079         */
080        public ShapePolyline() {
081            // all default
082        }
083    
084        /**
085         * Creates a new Polyline/M/Z.
086         * 
087         * @param z
088         * @param m
089         */
090        public ShapePolyline( boolean z, boolean m ) {
091            isM = m;
092            isZ = z;
093        }
094    
095        /**
096         * Creates a new PolylineZ from deegree Curves.
097         * 
098         * @param cs
099         */
100        public ShapePolyline( List<Curve> cs ) {
101            isZ = true;
102            try {
103                points = new ShapePoint[cs.size()][];
104                int partNum = 0;
105                for ( Curve c : cs ) {
106    
107                    if ( envelope == null ) {
108                        envelope = new ShapeEnvelope( c.getEnvelope() );
109                    }
110                    LineString ls = c.getAsLineString();
111    
112                    points[partNum] = new ShapePoint[ls.getNumberOfPoints()];
113    
114                    ShapePoint p;
115                    for ( int i = 0; i < ls.getNumberOfPoints(); i++ ) {
116                        p = new ShapePoint( ls.getPositionAt( i ) );
117                        points[partNum][i] = p;
118                        envelope.fit( p.x, p.y, p.z );
119                    }
120                    ++partNum;
121                }
122            } catch ( GeometryException e ) {
123                LOG.logError( "Something was wrong with a Curve object. " + "This will probably lead to followup errors, "
124                              + "better check your input data. Stack Trace:", e );
125            }
126        }
127    
128        /**
129         * Creates a new PolylineZ from deegree Curve.
130         * 
131         * @param c
132         */
133        public ShapePolyline( Curve c ) {
134            this( Arrays.asList( new Curve[] { c } ) );
135        }
136    
137        private int numPoints() {
138            int num = 0;
139    
140            for ( ShapePoint[] ps : points ) {
141                num += ps.length;
142            }
143    
144            return num;
145        }
146    
147        /*
148         * (non-Javadoc)
149         * 
150         * @see org.deegree.io.shpapi.Shape#getByteLength()
151         */
152        public int getByteLength() {
153            int numPoints = numPoints();
154    
155            int len = 44 + 4 * points.length + 16 * numPoints;
156    
157            if ( isM ) {
158                len += 8 * numPoints + 16;
159            }
160    
161            if ( isZ ) {
162                len += 16 * numPoints + 32;
163            }
164    
165            return len;
166        }
167    
168        protected int readPolyline( byte[] bytes, int offset ) {
169            int off = offset;
170            envelope = new ShapeEnvelope( false, false );
171            off = envelope.read( bytes, off );
172    
173            int numParts = ByteUtils.readLEInt( bytes, off );
174            off += 4;
175    
176            int numPoints = ByteUtils.readLEInt( bytes, off );
177            off += 4;
178    
179            points = new ShapePoint[numParts][];
180            int[] partStart = new int[numParts];
181    
182            for ( int i = 0; i < numParts; ++i ) {
183                partStart[i] = ByteUtils.readLEInt( bytes, off );
184                off += 4;
185            }
186    
187            for ( int i = 0; i < numParts; ++i ) {
188    
189                // calculate number of points for current part
190                int len;
191                if ( i == numParts - 1 ) {
192                    len = numPoints - partStart[i];
193                } else {
194                    len = partStart[i + 1] - partStart[i];
195                }
196    
197                points[i] = new ShapePoint[len];
198                for ( int j = 0; j < len; ++j ) {
199                    points[i][j] = new ShapePoint( bytes, off );
200                    off += 16;
201                }
202            }
203    
204            return off;
205        }
206    
207        protected int readPolylineZ( byte[] bytes, int offset ) {
208            isZ = true;
209    
210            int off = readPolyline( bytes, offset );
211    
212            int numPoints = numPoints();
213    
214            double zmin, zmax, mmin, mmax;
215            zmin = ByteUtils.readLEDouble( bytes, off );
216            off += 8;
217            zmax = ByteUtils.readLEDouble( bytes, off );
218            off += 8;
219    
220            double[] zVals = new double[numPoints];
221            for ( int i = 0; i < numPoints; ++i ) {
222                zVals[i] = ByteUtils.readLEDouble( bytes, off );
223                off += 8;
224            }
225    
226            mmin = ByteUtils.readLEDouble( bytes, off );
227            off += 8;
228            mmax = ByteUtils.readLEDouble( bytes, off );
229            off += 8;
230    
231            double[] mVals = new double[numPoints];
232            for ( int i = 0; i < numPoints; ++i ) {
233                mVals[i] = ByteUtils.readLEDouble( bytes, off );
234                off += 8;
235            }
236    
237            int i = 0;
238            for ( ShapePoint[] ps : points ) {
239                for ( ShapePoint p : ps ) {
240                    p.extend( zVals[i], mVals[i] );
241                    ++i;
242                }
243            }
244    
245            envelope.extend( zmin, zmax, mmin, mmax );
246    
247            return off;
248        }
249    
250        protected int readPolylineM( byte[] bytes, int offset ) {
251            isM = true;
252    
253            int off = readPolyline( bytes, offset );
254    
255            int numPoints = numPoints();
256    
257            double mmin, mmax;
258            mmin = ByteUtils.readLEDouble( bytes, off );
259            off += 8;
260            mmax = ByteUtils.readLEDouble( bytes, off );
261            off += 8;
262    
263            envelope.extend( mmin, mmax );
264    
265            double[] mVals = new double[numPoints];
266            for ( int i = 0; i < numPoints; ++i ) {
267                mVals[i] = ByteUtils.readLEDouble( bytes, off );
268                off += 8;
269            }
270    
271            int i = 0;
272            for ( ShapePoint[] ps : points ) {
273                for ( ShapePoint p : ps ) {
274                    p.extend( mVals[i] );
275                    ++i;
276                }
277            }
278    
279            return off;
280        }
281    
282        /*
283         * (non-Javadoc)
284         * 
285         * @see org.deegree.io.shpapi.Shape#read(byte[], int)
286         */
287        public int read( byte[] bytes, int offset ) {
288            int off = offset;
289    
290            int type = ByteUtils.readLEInt( bytes, off );
291            off += 4;
292    
293            if ( type == ShapeFile.NULL ) {
294                return off;
295            }
296    
297            if ( type == ShapeFile.POLYLINE ) {
298                isZ = false;
299                isM = false;
300                return readPolyline( bytes, off );
301            }
302    
303            if ( type == ShapeFile.POLYLINEZ ) {
304                isZ = true;
305                isM = false;
306                return readPolylineZ( bytes, off );
307            }
308    
309            if ( type == ShapeFile.POLYLINEM ) {
310                isZ = false;
311                isM = true;
312                return readPolylineM( bytes, off );
313            }
314    
315            return -1;
316        }
317    
318        protected int writePolyline( byte[] bytes, int offset ) {
319            int off = envelope.write( bytes, offset );
320    
321            ByteUtils.writeLEInt( bytes, off, points.length );
322            off += 4;
323    
324            int numPoints = numPoints();
325            ByteUtils.writeLEInt( bytes, off, numPoints );
326            off += 4;
327    
328            int pos = 0;
329            for ( ShapePoint[] ps : points ) {
330                ByteUtils.writeLEInt( bytes, off, pos );
331                off += 4;
332                pos += ps.length;
333            }
334    
335            for ( ShapePoint[] ps : points ) {
336                for ( ShapePoint p : ps ) {
337                    ByteUtils.writeLEDouble( bytes, off, p.x );
338                    off += 8;
339                    ByteUtils.writeLEDouble( bytes, off, p.y );
340                    off += 8;
341                }
342            }
343    
344            return off;
345        }
346    
347        protected int writePolylineZ( byte[] bytes, int offset ) {
348            int off = writePolyline( bytes, offset );
349    
350            ByteUtils.writeLEDouble( bytes, off, envelope.zmin );
351            off += 8;
352            ByteUtils.writeLEDouble( bytes, off, envelope.zmax );
353            off += 8;
354    
355            for ( ShapePoint[] ps : points ) {
356                for ( ShapePoint p : ps ) {
357                    ByteUtils.writeLEDouble( bytes, off, p.z );
358                    off += 8;
359                }
360            }
361    
362            ByteUtils.writeLEDouble( bytes, off, envelope.mmin );
363            off += 8;
364            ByteUtils.writeLEDouble( bytes, off, envelope.mmax );
365            off += 8;
366    
367            for ( ShapePoint[] ps : points ) {
368                for ( ShapePoint p : ps ) {
369                    ByteUtils.writeLEDouble( bytes, off, p.m );
370                    off += 8;
371                }
372            }
373    
374            return off;
375        }
376    
377        protected int writePolylineM( byte[] bytes, int offset ) {
378            int off = writePolyline( bytes, offset );
379    
380            ByteUtils.writeLEDouble( bytes, off, envelope.mmin );
381            off += 8;
382            ByteUtils.writeLEDouble( bytes, off, envelope.mmax );
383            off += 8;
384    
385            for ( ShapePoint[] ps : points ) {
386                for ( ShapePoint p : ps ) {
387                    ByteUtils.writeLEDouble( bytes, off, p.m );
388                    off += 8;
389                }
390            }
391    
392            return off;
393        }
394    
395        /*
396         * (non-Javadoc)
397         * 
398         * @see org.deegree.io.shpapi.Shape#write(byte[], int)
399         */
400        public int write( byte[] bytes, int offset ) {
401            if ( isZ ) {
402                ByteUtils.writeLEInt( bytes, offset, ShapeFile.POLYLINEZ );
403                return writePolylineZ( bytes, offset + 4 );
404            }
405            if ( isM ) {
406                ByteUtils.writeLEInt( bytes, offset, ShapeFile.POLYLINEM );
407                return writePolylineM( bytes, offset + 4 );
408            }
409            ByteUtils.writeLEInt( bytes, offset, ShapeFile.POLYLINE );
410            return writePolyline( bytes, offset + 4 );
411        }
412    
413        /*
414         * (non-Javadoc)
415         * 
416         * @see org.deegree.io.shpapi.shape_new.Shape#getType()
417         */
418        public int getType() {
419            if ( isZ ) {
420                return ShapeFile.POLYLINEZ;
421            }
422            if ( isM ) {
423                return ShapeFile.POLYLINEM;
424            }
425            return ShapeFile.POLYLINE;
426        }
427    
428        /*
429         * (non-Javadoc)
430         * 
431         * @see org.deegree.io.shpapi.shape_new.Shape#getEnvelope()
432         */
433        public ShapeEnvelope getEnvelope() {
434            return envelope;
435        }
436    
437        /**
438         * This creates a MultiCurve object.
439         * 
440         * @see org.deegree.io.shpapi.shape_new.Shape#getGeometry()
441         */
442        public Geometry getGeometry()
443                                throws ShapeGeometryException {
444            if ( points == null ) {
445                return null;
446            }
447            try {
448                Curve[] cs = new Curve[points.length];
449    
450                for ( int i = 0; i < points.length; ++i ) {
451                    Position[] ps = new Position[points[i].length];
452                    for ( int k = 0; k < points[i].length; ++k ) {
453                        if ( isZ ) {
454                            ps[k] = GeometryFactory.createPosition( points[i][k].x, points[i][k].y, points[i][k].z );
455                        } else {
456                            ps[k] = GeometryFactory.createPosition( points[i][k].x, points[i][k].y );
457                        }
458                    }
459                    cs[i] = GeometryFactory.createCurve( ps, null );
460                }
461    
462                return GeometryFactory.createMultiCurve( cs, null );
463            } catch ( GeometryException e ) {
464                throw new ShapeGeometryException( "MultiCurve could not be constructed" + " from ShapePolyline.", e );
465            }
466        }
467    
468        @Override
469        public String toString() {
470            try {
471                return WKTAdapter.export( getGeometry() ).toString();
472            } catch ( GeometryException e ) {
473                return "(unknown)";
474            }
475        }
476    
477    }