001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/io/shpapi/shape_new/ShapePolyline.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2006 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: bezema $
064     * 
065     * @version $Revision: 6259 $, $Date: 2007-03-20 10:15:15 +0100 (Di, 20 Mär 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. "
124                              + "This will probably lead to followup errors, "
125                              + "better check your input data. Stack Trace:", e );
126            }
127        }
128    
129        /**
130         * Creates a new PolylineZ from deegree Curve.
131         * 
132         * @param c
133         */
134        public ShapePolyline( Curve c ) {
135            this( Arrays.asList( new Curve[] { c } ) );
136        }
137    
138        private int numPoints() {
139            int num = 0;
140    
141            for ( ShapePoint[] ps : points ) {
142                num += ps.length;
143            }
144    
145            return num;
146        }
147    
148        /*
149         * (non-Javadoc)
150         * 
151         * @see org.deegree.io.shpapi.Shape#getByteLength()
152         */
153        public int getByteLength() {
154            int numPoints = numPoints();
155    
156            int len = 44 + 4 * points.length + 16 * numPoints;
157    
158            if ( isM ) {
159                len += 8 * numPoints + 16;
160            }
161    
162            if ( isZ ) {
163                len += 16 * numPoints + 32;
164            }
165    
166            return len;
167        }
168    
169        protected int readPolyline( byte[] bytes, int offset ) {
170            int off = offset;
171            envelope = new ShapeEnvelope( false, false );
172            off = envelope.read( bytes, off );
173    
174            int numParts = ByteUtils.readLEInt( bytes, off );
175            off += 4;
176    
177            int numPoints = ByteUtils.readLEInt( bytes, off );
178            off += 4;
179    
180            points = new ShapePoint[numParts][];
181            int[] partStart = new int[numParts];
182    
183            for ( int i = 0; i < numParts; ++i ) {
184                partStart[i] = ByteUtils.readLEInt( bytes, off );
185                off += 4;
186            }
187    
188            for ( int i = 0; i < numParts; ++i ) {
189    
190                // calculate number of points for current part
191                int len;
192                if ( i == numParts - 1 ) {
193                    len = numPoints - partStart[i];
194                } else {
195                    len = partStart[i + 1] - partStart[i];
196                }
197    
198                points[i] = new ShapePoint[len];
199                for ( int j = 0; j < len; ++j ) {
200                    points[i][j] = new ShapePoint( bytes, off );
201                    off += 16;
202                }
203            }
204    
205            return off;
206        }
207    
208        protected int readPolylineZ( byte[] bytes, int offset ) {
209            isZ = true;
210    
211            int off = readPolyline( bytes, offset );
212    
213            int numPoints = numPoints();
214    
215            double zmin, zmax, mmin, mmax;
216            zmin = ByteUtils.readLEDouble( bytes, off );
217            off += 8;
218            zmax = ByteUtils.readLEDouble( bytes, off );
219            off += 8;
220    
221            double[] zVals = new double[numPoints];
222            for ( int i = 0; i < numPoints; ++i ) {
223                zVals[i] = ByteUtils.readLEDouble( bytes, off );
224                off += 8;
225            }
226    
227            mmin = ByteUtils.readLEDouble( bytes, off );
228            off += 8;
229            mmax = ByteUtils.readLEDouble( bytes, off );
230            off += 8;
231    
232            double[] mVals = new double[numPoints];
233            for ( int i = 0; i < numPoints; ++i ) {
234                mVals[i] = ByteUtils.readLEDouble( bytes, off );
235                off += 8;
236            }
237    
238            int i = 0;
239            for ( ShapePoint[] ps : points ) {
240                for ( ShapePoint p : ps ) {
241                    p.extend( zVals[i], mVals[i] );
242                    ++i;
243                }
244            }
245    
246            envelope.extend( zmin, zmax, mmin, mmax );
247    
248            return off;
249        }
250    
251        protected int readPolylineM( byte[] bytes, int offset ) {
252            isM = true;
253    
254            int off = readPolyline( bytes, offset );
255    
256            int numPoints = numPoints();
257    
258            double mmin, mmax;
259            mmin = ByteUtils.readLEDouble( bytes, off );
260            off += 8;
261            mmax = ByteUtils.readLEDouble( bytes, off );
262            off += 8;
263    
264            envelope.extend( mmin, mmax );
265    
266            double[] mVals = new double[numPoints];
267            for ( int i = 0; i < numPoints; ++i ) {
268                mVals[i] = ByteUtils.readLEDouble( bytes, off );
269                off += 8;
270            }
271    
272            int i = 0;
273            for ( ShapePoint[] ps : points ) {
274                for ( ShapePoint p : ps ) {
275                    p.extend( mVals[i] );
276                    ++i;
277                }
278            }
279    
280            return off;
281        }
282    
283        /*
284         * (non-Javadoc)
285         * 
286         * @see org.deegree.io.shpapi.Shape#read(byte[], int)
287         */
288        public int read( byte[] bytes, int offset ) {
289            int off = offset;
290    
291            int type = ByteUtils.readLEInt( bytes, off );
292            off += 4;
293    
294            if ( type == ShapeFile.NULL ) {
295                return off;
296            }
297    
298            if ( type == ShapeFile.POLYLINE ) {
299                isZ = false;
300                isM = false;
301                return readPolyline( bytes, off );
302            }
303    
304            if ( type == ShapeFile.POLYLINEZ ) {
305                isZ = true;
306                isM = false;
307                return readPolylineZ( bytes, off );
308            }
309    
310            if ( type == ShapeFile.POLYLINEM ) {
311                isZ = false;
312                isM = true;
313                return readPolylineM( bytes, off );
314            }
315    
316            return -1;
317        }
318    
319        protected int writePolyline( byte[] bytes, int offset ) {
320            int off = envelope.write( bytes, offset );
321    
322            ByteUtils.writeLEInt( bytes, off, points.length );
323            off += 4;
324    
325            int numPoints = numPoints();
326            ByteUtils.writeLEInt( bytes, off, numPoints );
327            off += 4;
328    
329            int pos = 0;
330            for ( ShapePoint[] ps : points ) {
331                ByteUtils.writeLEInt( bytes, off, pos );
332                off += 4;
333                pos += ps.length;
334            }
335    
336            for ( ShapePoint[] ps : points ) {
337                for ( ShapePoint p : ps ) {
338                    ByteUtils.writeLEDouble( bytes, off, p.x );
339                    off += 8;
340                    ByteUtils.writeLEDouble( bytes, off, p.y );
341                    off += 8;
342                }
343            }
344    
345            return off;
346        }
347    
348        protected int writePolylineZ( byte[] bytes, int offset ) {
349            int off = writePolyline( bytes, offset );
350    
351            ByteUtils.writeLEDouble( bytes, off, envelope.zmin );
352            off += 8;
353            ByteUtils.writeLEDouble( bytes, off, envelope.zmax );
354            off += 8;
355    
356            for ( ShapePoint[] ps : points ) {
357                for ( ShapePoint p : ps ) {
358                    ByteUtils.writeLEDouble( bytes, off, p.z );
359                    off += 8;
360                }
361            }
362    
363            ByteUtils.writeLEDouble( bytes, off, envelope.mmin );
364            off += 8;
365            ByteUtils.writeLEDouble( bytes, off, envelope.mmax );
366            off += 8;
367    
368            for ( ShapePoint[] ps : points ) {
369                for ( ShapePoint p : ps ) {
370                    ByteUtils.writeLEDouble( bytes, off, p.m );
371                    off += 8;
372                }
373            }
374    
375            return off;
376        }
377    
378        protected int writePolylineM( byte[] bytes, int offset ) {
379            int off = writePolyline( bytes, offset );
380    
381            ByteUtils.writeLEDouble( bytes, off, envelope.mmin );
382            off += 8;
383            ByteUtils.writeLEDouble( bytes, off, envelope.mmax );
384            off += 8;
385    
386            for ( ShapePoint[] ps : points ) {
387                for ( ShapePoint p : ps ) {
388                    ByteUtils.writeLEDouble( bytes, off, p.m );
389                    off += 8;
390                }
391            }
392    
393            return off;
394        }
395    
396        /*
397         * (non-Javadoc)
398         * 
399         * @see org.deegree.io.shpapi.Shape#write(byte[], int)
400         */
401        public int write( byte[] bytes, int offset ) {
402            if ( isZ ) {
403                ByteUtils.writeLEInt( bytes, offset, ShapeFile.POLYLINEZ );
404                return writePolylineZ( bytes, offset + 4 );
405            }
406            if ( isM ) {
407                ByteUtils.writeLEInt( bytes, offset, ShapeFile.POLYLINEM );
408                return writePolylineM( bytes, offset + 4 );
409            }
410            ByteUtils.writeLEInt( bytes, offset, ShapeFile.POLYLINE );
411            return writePolyline( bytes, offset + 4 );
412        }
413    
414        /*
415         * (non-Javadoc)
416         * 
417         * @see org.deegree.io.shpapi.shape_new.Shape#getType()
418         */
419        public int getType() {
420            if ( isZ ) {
421                return ShapeFile.POLYLINEZ;
422            }
423            if ( isM ) {
424                return ShapeFile.POLYLINEM;
425            }
426            return ShapeFile.POLYLINE;
427        }
428    
429        /*
430         * (non-Javadoc)
431         * 
432         * @see org.deegree.io.shpapi.shape_new.Shape#getEnvelope()
433         */
434        public ShapeEnvelope getEnvelope() {
435            return envelope;
436        }
437    
438        /**
439         * This creates a MultiCurve object.
440         * 
441         * @see org.deegree.io.shpapi.shape_new.Shape#getGeometry()
442         */
443        public Geometry getGeometry()
444                                throws ShapeGeometryException {
445            if ( points == null ) {
446                return null;
447            }
448            try {
449                Curve[] cs = new Curve[points.length];
450    
451                for ( int i = 0; i < points.length; ++i ) {
452                    Position[] ps = new Position[points[i].length];
453                    for ( int k = 0; k < points[i].length; ++k ) {
454                        ps[k] = GeometryFactory.createPosition( points[i][k].x, points[i][k].y,
455                                                                points[i][k].z );
456                    }
457                    cs[i] = GeometryFactory.createCurve( ps, null );
458                }
459    
460                return GeometryFactory.createMultiCurve( cs, null );
461            } catch ( GeometryException e ) {
462                throw new ShapeGeometryException( "MultiCurve could not be constructed"
463                                                  + " from ShapePolyline.", e );
464            }
465        }
466    
467        @Override
468        public String toString() {
469            try {
470                return WKTAdapter.export( getGeometry() ).toString();
471            } catch ( GeometryException e ) {
472                return "(unknown)";
473            }
474        }
475    
476    }
477    
478    /***************************************************************************************************
479     * <code>
480     Changes to this class. What the people have been up to:
481    
482     $Log$
483     Revision 1.1  2007/02/26 14:26:49  schmitz
484     Added a new implementation of the ShapeFile API that implements the Z and M variants of the datatypes as well.
485     Also added some basic tests for the API as well as a new version of the GML/Shape converters.
486    
487     </code>
488     **************************************************************************************************/