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 **************************************************************************************************/