/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53115 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.model.spatialschema;
import java.io.Serializable;
import org.deegree.model.crs.CoordinateSystem;
/**
* default implementation of the SurfacePatch interface from
* package jago.model. the class is abstract because it should be
* specialized by derived classes <code>Polygon</code> for example
*
* ------------------------------------------------------------
* @version 11.6.2001
* @author Andreas Poth
*/
abstract class SurfacePatchImpl implements GenericSurface, Serializable {
/** Use serialVersionUID for interoperability. */
private final static long serialVersionUID = 7641735268892225180L;
protected CoordinateSystem crs = null;
protected Envelope envelope = null;
protected Point centroid = null;
protected SurfaceInterpolation interpolation = null;
protected Position[] exteriorRing = null;
protected Position[][] interiorRings = null;
protected double area = 0;
protected boolean valid = false;
/**
* Creates a new SurfacePatchImpl object.
*
* @param interpolation
* @param exteriorRing
* @param interiorRings
* @param crs
*
* @throws GeometryException
*/
protected SurfacePatchImpl( SurfaceInterpolation interpolation,
Position[] exteriorRing, Position[][] interiorRings,
CoordinateSystem crs ) throws GeometryException {
this.crs = crs;
if ( ( exteriorRing == null ) || ( exteriorRing.length < 3 ) ) {
throw new GeometryException( "The exterior ring doesn't contains enough point!" );
}
// check, if the exteriorRing of the polygon is closed
// and if the interiorRings (if !=null) are closed
if ( !exteriorRing[0].equals( exteriorRing[exteriorRing.length - 1] ) ) {
throw new GeometryException( "The exterior ring isn't closed!" );
}
if ( interiorRings != null ) {
for ( int i = 0; i < interiorRings.length; i++ ) {
if ( !interiorRings[i][0].equals( interiorRings[i][interiorRings[i].length - 1] ) ) {
throw new GeometryException( "The interior ring " + i + " isn't closed!" );
}
}
}
this.interpolation = interpolation;
this.exteriorRing = exteriorRing;
this.interiorRings = interiorRings;
setValid( false );
}
/**
* invalidates the calculated parameters of the Geometry
*/
protected void setValid(boolean valid) {
this.valid = valid;
}
/**
* returns true if the calculated parameters of the Geometry are valid
* and false if they must be recalculated
*/
protected boolean isValid() {
return valid;
}
/**
*
*/
private void calculateEnvelope() {
// double[] min = new double[exteriorRing[0].getCoordinateDimension()];
// for ( int j = 0; j < exteriorRing[0].getCoordinateDimension(); j++ ) {
// min[j] = exteriorRing[0].getAsArray()[j];
// }
double[] min = exteriorRing[0].getAsArray().clone();
double[] max = min.clone();
for ( int i = 1; i < exteriorRing.length; i++ ) {
double[] pos = exteriorRing[i].getAsArray();
for ( int j = 0; j < exteriorRing[i].getCoordinateDimension(); j++ ) {
if ( pos[j] < min[j] ) {
min[j] = pos[j];
} else if ( pos[j] > max[j] ) {
max[j] = pos[j];
}
}
}
envelope = new EnvelopeImpl( new PositionImpl( min ), new PositionImpl( max ), crs );
}
/**
* The interpolation determines the surface interpolation mechanism
* used for this SurfacePatch. This mechanism uses the control
* points and control parameters defined in the various subclasses
* to determine the position of this SurfacePatch.
*/
public SurfaceInterpolation getInterpolation() {
return interpolation;
}
/**
* returns the bounding box of the surface patch
*/
public Envelope getEnvelope() {
if (!isValid()) {
calculateParam();
}
return envelope;
}
/**
* returns a reference to the exterior ring of the surface
*/
public Position[] getExteriorRing() {
return exteriorRing;
}
/**
* returns a reference to the interior rings of the surface
*/
public Position[][] getInteriorRings() {
return interiorRings;
}
/**
* returns the length of all boundaries of the surface
* in a reference system appropriate for measuring distances.
*/
public double getPerimeter() {
return -1;
}
/**
* returns the coordinate system of the surface patch
*/
public CoordinateSystem getCoordinateSystem() {
return crs;
}
/**
*
*
* @param other
*
* @return
*/
public boolean equals( Object other ) {
if ( ( other == null ) || !( other instanceof SurfacePatchImpl ) ) {
return false;
}
// Assuming Interpolation can be null (not checked by Constructor)
if ( getInterpolation() != null ) {
if ( !getInterpolation().equals( ( (SurfacePatch)other ).getInterpolation() ) ) {
return false;
}
} else {
if ( ( (SurfacePatch)other ).getInterpolation() != null ) {
return false;
}
}
// Assuming envelope cannot be null (always calculated)
if ( !envelope.equals( ( (SurfacePatch)other ).getEnvelope() ) ) {
return false;
}
// Assuming exteriorRing cannot be null (checked by Constructor)
// if ( !Arrays.equals( exteriorRing, ( (SurfacePatch)other ).getExteriorRing() ) ) {
// TODO
// correct comparing of each point considering current tolerance level
// }
// Assuming either can have interiorRings set to null (not checked
//by Constructor)
if ( interiorRings != null ) {
if ( ( (SurfacePatch)other ).getInteriorRings() == null ) {
return false;
}
if ( interiorRings.length != ( (SurfacePatch)other ).getInteriorRings().length ) {
return false;
}
for ( int i = 0; i < interiorRings.length; i++ ) {
//TODO
// correct comparing of each point considering current tolerance level
}
} else {
if ( ( (SurfacePatch)other ).getInteriorRings() != null ) {
return false;
}
}
return true;
}
/**
* The operation "centroid" shall return the mathematical centroid for this
* Geometry. The result is not guaranteed to be on the object.
*/
public Point getCentroid() {
if (!isValid()) {
calculateParam();
}
return centroid;
}
/**
* The operation "area" shall return the area of this GenericSurface.
* The area of a 2 dimensional geometric object shall be a numeric
* measure of its surface area Since area is an accumulation (integral)
* of the product of two distances, its return value shall be in a unit
* of measure appropriate for measuring distances squared.
*/
public double getArea() {
if (!isValid()) {
calculateParam();
}
return area;
}
/**
* calculates the centroid and area of the surface patch. this
* method is only valid for the two-dimensional case.
*/
private void calculateCentroidArea() {
Position centroid_ = calculateCentroid( exteriorRing );
double varea = calculateArea( exteriorRing );
double x = centroid_.getX();
double y = centroid_.getY();
x *= varea;
y *= varea;
if ( interiorRings != null ) {
for ( int i = 0; i < interiorRings.length; i++ ) {
double dum = -1 * calculateArea( interiorRings[i] );
Position temp = calculateCentroid( interiorRings[i] );
x += ( temp.getX() * dum );
y += ( temp.getY() * dum );
varea += dum;
}
}
area = varea;
centroid = new PointImpl( x / varea, y / varea, crs );
}
/**
* calculates the centroid and the area of the surface patch
*/
protected void calculateParam() {
calculateEnvelope();
calculateCentroidArea();
setValid( true );
}
/**
* calculates the area of the surface patch <p></p> taken from gems iv
* (modified)<p></p> this method is only valid for the two-dimensional case.
*/
private double calculateArea( Position[] point ) {
int i;
int j;
double ai;
double atmp = 0;
for ( i = point.length - 1, j = 0; j < point.length; i = j, j++ ) {
double xi = point[i].getX() - point[0].getX();
double yi = point[i].getY() - point[0].getY();
double xj = point[j].getX() - point[0].getX();
double yj = point[j].getY() - point[0].getY();
ai = ( xi * yj ) - ( xj * yi );
atmp += ai;
}
return Math.abs( atmp / 2 );
}
/**
* calculates the centroid of the surface patch <p> taken from gems iv
* (modified)<p></p> this method is only valid for the two-dimensional case.
*/
protected Position calculateCentroid( Position[] point ) {
int i;
int j;
double ai;
double x;
double y;
double atmp = 0;
double xtmp = 0;
double ytmp = 0;
// move points to the origin of the coordinate space
// (to solve precision issues)
double transX = point [0].getX();
double transY = point [0].getY();
for ( i = point.length - 1, j = 0; j < point.length; i = j, j++ ) {
double x1 = point[i].getX() - transX;
double y1 = point[i].getY() - transY;
double x2 = point[j].getX() - transX;
double y2 = point[j].getY() - transY;
ai = ( x1 * y2 ) - ( x2 * y1 );
atmp += ai;
xtmp += ( ( x2 + x1 ) * ai );
ytmp += ( ( y2 + y1 ) * ai );
}
if ( atmp != 0 ) {
x = xtmp / ( 3 * atmp ) + transX;
y = ytmp / ( 3 * atmp ) + transY;
} else {
x = point[0].getX();
y = point[0].getY();
}
return new PositionImpl( x, y );
}
/**
*
*
* @return
*/
public String toString() {
String ret = "SurfacePatch: ";
ret = "interpolation = " + interpolation + "\n";
ret += "exteriorRing = \n";
for ( int i = 0; i < exteriorRing.length; i++ ) {
ret += ( exteriorRing + "\n" );
}
ret += ( "interiorRings = " + interiorRings + "\n" );
ret += ( "envelope = " + envelope + "\n" );
return ret;
}
}/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: SurfacePatchImpl.java,v $
Revision 1.13 2006/11/27 09:07:51 poth
JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code.
Revision 1.12 2006/11/23 09:20:49 bezema
updated the copying of position values
Revision 1.11 2006/09/18 16:41:34 bezema
added the crs to the envelope creation
Revision 1.10 2006/08/28 14:22:56 poth
bug fix coordinate dimension
Revision 1.9 2006/07/12 14:46:15 poth
comment footer added
********************************************************************** */