/* Copyright 2013 The jeo project. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.jeo.geom;
import java.util.Iterator;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateFilter;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
/**
* A sequence or path of coordinates.
* <p>
* As the path is traversed a {@link #step()} is maintained that describes how the current
* coordinate relates to the previous coordinate.
* </p>
*
* @author Justin Deoliveira, OpenGeo
*/
public abstract class CoordinatePath implements Iterator<Coordinate> {
/**
* Enumeration describing path step/state.
*/
public static enum PathStep {
MOVE_TO, LINE_TO, CLOSE, STOP;
}
/**
* Creates a coordinate iterator for the specified geometry.
* <p>
* This method calls through to {@link #create(Geometry, boolean, double, double)} with no
* generalization.
* </p>
*/
public static CoordinatePath create(Geometry g) {
if (g == null || g.isEmpty()) {
return new EmptyPath(g);
}
switch(Geom.Type.from(g)) {
case POINT:
return new PointPath((Point) g);
case LINESTRING:
return new LineStringPath((LineString)g);
case POLYGON:
return new PolygonPath((Polygon)g);
case MULTIPOINT:
case MULTILINESTRING:
case MULTIPOLYGON:
case GEOMETRYCOLLECTION:
return new GeometryCollectionPath((GeometryCollection) g);
default:
throw new IllegalArgumentException("Unsupported type: " + g);
}
}
protected PathStep step = null;
protected Coordinate prev, curr;
/**
* decimation/generalization
*/
protected boolean generalize = false;
protected double dx = Double.NaN, dy = Double.NaN;
/**
* transform
*/
protected CoordinateFilter tx;
protected CoordinatePath() {
prev = new Coordinate(Double.NaN, Double.NaN);
curr = new Coordinate(Double.NaN, Double.NaN);
}
/**
* Specifies that the coordinate path should be generalized skipping coordinates that
* are close to one another.
* <p>
* The <tt>dx</tt> and <tt>dy</tt> arguments are used as a tolerance to collapse coordinates.
* Consecutive coordinates with both horizontal and vertical distance less than <tt>dx</tt> and
* <tt>dy</tt> respectively are collapsed.
* </p>
* @param dx Horizontal generalization distance, only taken into account when
* <tt>generalization</tt> is <tt>true</tt>
* @param dy Vertical generalization distance, only taken into account when
* <tt>generalization</tt> is <tt>true</tt>
*/
public CoordinatePath generalize(double dx, double dy) {
this.generalize = true;
this.dx = dx;
this.dy = dy;
return this;
}
/**
* Applies a transformation to the coordinate path.
*
* @param tx Transformation/filter to apply to coordinates before returning.
*/
public CoordinatePath transform(CoordinateFilter tx) {
this.tx = tx;
return this;
}
public abstract Geometry geometry();
public Coordinate coordinate() {
return curr;
}
public PathStep step() {
return step;
}
@Override
public boolean hasNext() {
if (generalize && step == PathStep.LINE_TO || step == PathStep.MOVE_TO) {
while(step == PathStep.LINE_TO || step == PathStep.MOVE_TO) {
step = doNext(curr);
if (!Double.isNaN(prev.x)) {
if (Math.abs(curr.x - prev.x) < dx &&
Math.abs(curr.y - prev.y) < dy) {
continue;
}
}
break;
}
}
else {
if (step != PathStep.STOP) {
step = doNext(curr);
}
}
return step != PathStep.STOP;
}
@Override
public Coordinate next() {
if (step == PathStep.STOP) {
return null;
}
prev.x = curr.x;
prev.y = curr.y;
// apply the filter
if (tx != null) {
tx.filter(curr);
}
return curr;
}
public void reset() {
step = PathStep.MOVE_TO;
doReset();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
protected abstract PathStep doNext(Coordinate c);
protected abstract void doReset();
}