    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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    }