class Decimator {

	public final Geometry decimateTransformGeneralize(Geometry geometry,
			MathTransform transform) throws TransformException {
		if (geometry instanceof GeometryCollection) {
			GeometryCollection collection = (GeometryCollection) geometry;
			final int length = collection.getNumGeometries();
			boolean cloned = false;
			Class elementType = null;
            Geometry[] elements = null;
			for (int i = 0; i < length; i++) {
				Geometry source = collection.getGeometryN(i);
                Geometry generalized = decimateTransformGeneralize(source,
						transform);
                
                // lazily handle the case where we need to deep clone
                if(generalized != source) {
                    cloned = true;
                    if(elements == null) {
                        elements = new Geometry[collection.getNumGeometries()];
                        for (int j = 0; j < i; j++) {
                            Geometry element = collection.getGeometryN(j);
                            elements[j] = element;
                            accumulateGeometryType(elementType, element);
                        }
                    }
                }
                if(cloned) {
                    elements[i] = generalized;
                    elementType = accumulateGeometryType(elementType, generalized);
                }
			}
			if(cloned) {
			    if(elementType == Point.class) {
			        Point[] points = new Point[elements.length];
			        System.arraycopy(elements, 0, points, 0, elements.length);
                    return collection.getFactory().createMultiPoint(points);
                } else if (elementType == LineString.class) {
                    LineString[] lines = new LineString[elements.length];
                    System.arraycopy(elements, 0, lines, 0, elements.length);
                    return collection.getFactory().createMultiLineString(lines);
                } else if (elementType == Polygon.class) {
                    Polygon[] polys = new Polygon[elements.length];
                    System.arraycopy(elements, 0, polys, 0, elements.length);
                    return collection.getFactory().createMultiPolygon(polys);
                } else {
                    return collection.getFactory().createGeometryCollection(elements);
			    }
			} else {
			    return collection;
			}
		} else if (geometry instanceof Point) {
			LiteCoordinateSequence seq = (LiteCoordinateSequence) ((Point) geometry)
					.getCoordinateSequence();
            decimateTransformGeneralize(seq, transform, false, spanx, spany);
            return geometry;
		} else if (geometry instanceof Polygon) {
			Polygon polygon = (Polygon) geometry;
            LinearRing shell = (LinearRing) decimateTransformGeneralize(polygon.getExteriorRing(),
                    transform);
            boolean cloned = shell != polygon.getExteriorRing();
            final int length = polygon.getNumInteriorRing();
            LinearRing[] holes = cloned ? new LinearRing[length] : null;
			for (int i = 0; i < length; i++) {
                LineString hole = polygon.getInteriorRingN(i);
                LinearRing generalized = (LinearRing) decimateTransformGeneralize(hole,
						transform);
                cloned |= generalized != hole;
                if (cloned) {
                    if (holes == null) {
                        holes = new LinearRing[length];
                        for (int j = 0; j < i; j++) {
                            holes[j] = (LinearRing) polygon.getInteriorRingN(j);
                        }
                    }
                    holes[i] = generalized;
                }
            }

            if (cloned) {
                return polygon.getFactory().createPolygon(shell, holes);
            } else {
                return polygon;
			}
		} else if (geometry instanceof LineString) {
            double spanx = this.spanx;
            double spany = this.spany;
			LineString ls = (LineString) geometry;
            if (ls instanceof CurvedGeometry<?>) {
                CurvedGeometry<LineString> curved = (CurvedGeometry<LineString>) ls;
                ls = curved.linearize(Math.min(Math.abs(spanx), Math.abs(spany)));
                // do not generalize further, we already got a good representation
                spanx = -1;
                spany = -1;
            }
            LiteCoordinateSequence seq = LiteCoordinateSequenceFactory.lite(ls
                    .getCoordinateSequence());
            boolean loop = ls instanceof LinearRing;
            if (!loop && seq.size() > 1) {
                double x0 = seq.getOrdinate(0, 0);
                double y0 = seq.getOrdinate(0, 1);
                double x1 = seq.getOrdinate(seq.size() - 1, 0);
                double y1 = seq.getOrdinate(seq.size() - 1, 1);
                loop = Math.abs(x0 - x1) < EPS && Math.abs(y0 - y1) < EPS;
            }
            decimateTransformGeneralize(seq, transform, loop, spanx, spany);
            return ls;
        } else {
            return geometry;
		}
	}

	public  Decimator(MathTransform screenToWorld, Rectangle paintArea, double pixelDistance);
	public  Decimator(MathTransform screenToWorld, Rectangle paintArea);
	public  Decimator(MathTransform screenToWorld);
	public  Decimator(double spanx, double spany);

}