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