Hi Larry, hi Michaël,

I had a look at the decimation code in Java2DConverter.
This is awesome! Congratulations! :-)

But way not go step further and streamline the model to view
coordination transform. Why to create all this temporary
Coordinate[] stuff? In the end all what matters is a PathIterator
that can handle an AffineTransform coming from Java2D.
Instead of transform the data to a temporary Coordinate array
and used this to construct GeneralPaths we can write a PathIterator
that transforms and decimates the data on-the-fly.
All we have to do is to concatenate the model to view transform to the
incoming matrix.

To archive this we must add a

AffineTransform getModelToViewTransform();

method to the Java2DConverter.PointConverter interface. This
does not hurt because Viewport already implements it.

To see what I mean, look at the DirectPolygonShape that I've
attached. It handles the Polygon case of Java2DConverter.
Add this class to the sources and change the toShape(Polygon)
method to look as follow:

  private Shape toShape(Polygon polygon) throws
  NoninvertibleTransformException
  {
    return new DirectPolygonShape(
      polygon,
      pointConverter.getModelToViewTransform(),
      1d / (2d*pointConverter.getScale()));
  }

Speaking of performance. On my computer
(very old 1.2 GHz AMD/Athlon-T-Bird running GNU/Linux, Java 6)
the burluc layer is rendered in full extend without the streamling
in about 9.x seconds, with the streamling in about 8.x secs, with
x varying a bit. It's just a second, but it's a second! ;-)

What do you think?

Regards, Sascha
/*
 * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI 
 * for visualizing and manipulating spatial features with geometry and attributes.
 *
 * Copyright (C) 2003 Vivid Solutions
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 * For more information, contact:
 *
 * Vivid Solutions
 * Suite #1A
 * 2328 Government Street
 * Victoria BC  V8T 5G5
 * Canada
 *
 * (250)385-6040
 * www.vividsolutions.com
 */

package com.vividsolutions.jump.workbench.ui.renderer.java2D;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Polygon;

import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import java.util.Iterator;
import java.util.NoSuchElementException;

// for more accurate (float instead of int) rendering.
// From larry becker's SkyJUMP code to OpenJUMP [mmichaud]
// Streamlined [s-l-teichmann]
public class DirectPolygonShape 
implements   Shape 
{
	private Polygon         polygon;
	private AffineTransform model2view;
	private double          halfPixel;

	protected DirectPolygonShape(){
	}
	
	/**
	 * @param polygon    the JTS polygon
	 * @param model2view the affine transform from model to view
	 * @param halfPixel  line segments shorter than halfPixel are ignored.
	 */
	public DirectPolygonShape(
		Polygon         polygon,
		AffineTransform model2view,
		double          halfPixel
		) {
		this.polygon    = polygon;
		this.model2view = model2view;
		this.halfPixel  = halfPixel;
	}

	/**
	 * helper PathIterator that iterates over PathIterators
	 */
	public final class PathIteratorsIterator 
	implements         PathIterator
	{
    private Iterator     iterators;
    private boolean      done;
		private PathIterator current;

    public PathIteratorsIterator(Iterator iterators) {
			this.iterators = iterators;

			if (!iterators.hasNext())
				done = true;
			else
				current = (PathIterator)iterators.next();
    }

    public int getWindingRule() {
			return WIND_EVEN_ODD;
    }

    public boolean isDone() {
			return done;
    }

    public void next() {
			if (done)
				return;

			if (current.isDone()) {
				if (iterators.hasNext())
					current = (PathIterator)iterators.next();
				else
					done = true;
			}
			else
				current.next();
    }

    public int currentSegment(float [] coords) {
			return current.currentSegment(coords);
    }

    public int currentSegment(double [] coords) {
			return current.currentSegment(coords);
    }
	} // class PathIteratorsIterator

	/**
	 * Implements a PathIterator and Larry's decimator on-the-fly
	 */
  public static final class AffinePolygonPath 
	implements                PathIterator
	{
		private int             iterate;
		private Coordinate []   points;
		private Point2D.Double  tmp;
		private AffineTransform xform;
		private double          halfPixel;
		private Coordinate      p0;
		private int             segmentType;
		
		public AffinePolygonPath(
			Coordinate []   points, 
			AffineTransform xform, 
			double          halfPixel
		){
			this.xform     = xform;
			this.points    = points;
			this.halfPixel = halfPixel;
			tmp            = new Point2D.Double();
			internalNext();
		}

		/** Math.abs() is a known for being slow ... */
		private static final double abs(double x) {
			return x < 0d ? -x : x;
		}

		private final void internalNext() {
			for (;;) {
				// issue SEG_CLOSE at end
				if (iterate >= points.length) {
					segmentType = SEG_CLOSE;
					break;
				}

				// issue first two and last
				if (iterate < 2 || iterate == points.length-1) {
					Coordinate pi = points[iterate];
					tmp.x = pi.x;
					tmp.y = pi.y;
					xform.transform(tmp, tmp);
					p0 = pi;
				}
				else { // distance lesser than halfPixel?
					Coordinate pi = points[iterate];
					if (abs(p0.x-pi.x) > halfPixel || abs(p0.y-pi.y) > halfPixel) {
						tmp.x = pi.x;
						tmp.y = pi.y;
						xform.transform(tmp, tmp);
						p0 = pi;
					}
					else { // yes: try next
						++iterate;
						continue;
					}
				}

				segmentType = iterate == 0
					? SEG_MOVETO
					: SEG_LINETO;
				break;
			} // for (;;)
		}

		public int currentSegment(double[] coords) {
			coords[0] = tmp.x;
			coords[1] = tmp.y;
			return segmentType;
		}


		public int currentSegment(float[] coords) {
			coords[0] = (float)tmp.x;
			coords[1] = (float)tmp.y;
			return segmentType;
		}

		public int getWindingRule() {
			return GeneralPath.WIND_EVEN_ODD;
		}

		public boolean isDone() {
			return segmentType == SEG_CLOSE;
		}

		public void next() {
			++iterate;
			internalNext();
		}	
	} // class AffinePolygonPath

	public Rectangle getBounds() {
			/[EMAIL PROTECTED] Implement this java.awt.Shape method*/
			throw new java.lang.UnsupportedOperationException(
					"Method getBounds() not yet implemented.");
	}

	public Rectangle2D getBounds2D() {
			/[EMAIL PROTECTED] Implement this java.awt.Shape method*/
			throw new java.lang.UnsupportedOperationException(
					"Method getBounds() not yet implemented.");
	}

	public boolean contains(double x, double y) {
			/[EMAIL PROTECTED] Implement this java.awt.Shape method*/
			throw new java.lang.UnsupportedOperationException(
					"Method contains() not yet implemented.");
	}

	public boolean contains(Point2D p) {
			/[EMAIL PROTECTED] Implement this java.awt.Shape method*/
			throw new java.lang.UnsupportedOperationException(
					"Method contains() not yet implemented.");
	}

	public boolean intersects(double x, double y, double w, double h) {
			/[EMAIL PROTECTED] Implement this java.awt.Shape method*/
			throw new java.lang.UnsupportedOperationException(
					"Method intersects() not yet implemented.");
	}

	public boolean intersects(Rectangle2D r) {
			/[EMAIL PROTECTED] Implement this java.awt.Shape method*/
			throw new java.lang.UnsupportedOperationException(
					"Method intersects() not yet implemented.");
	}

	public boolean contains(double x, double y, double w, double h) {
			/[EMAIL PROTECTED] Implement this java.awt.Shape method*/
			throw new java.lang.UnsupportedOperationException(
					"Method contains() not yet implemented.");
	}

	public boolean contains(Rectangle2D r) {
			/[EMAIL PROTECTED] Implement this java.awt.Shape method*/
			throw new java.lang.UnsupportedOperationException(
					"Method contains() not yet implemented.");
	}

	public PathIterator getPathIterator(AffineTransform xform) {

		if (xform == null)
			xform = model2view;
		else {
			xform = new AffineTransform(xform);
			xform.concatenate(model2view);
		}

		final AffineTransform at = xform;

		return new PathIteratorsIterator(new Iterator() {

			int ring;

			public boolean hasNext() {
				return ring < polygon.getNumInteriorRing() + 1;
			}

			public Object next() {
				if (!hasNext())
					throw new NoSuchElementException();
				Object x = ring == 0
					? new AffinePolygonPath(polygon.getExteriorRing().getCoordinates(),        at, halfPixel)
					: new AffinePolygonPath(polygon.getInteriorRingN(ring-1).getCoordinates(), at, halfPixel);
				++ring;
				return x;
			}

			public void remove() {
				throw new UnsupportedOperationException();
			}
		});

	}

	public PathIterator getPathIterator(AffineTransform at, double flatness) {
		// since we don't support curved geometries, can simply delegate to the simple method
		return getPathIterator(at);
	}
}
// end of file
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Jump-pilot-devel mailing list
Jump-pilot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel

Reply via email to