sascha@465: package de.intevation.gnv.raster;
sascha@465:
sascha@499: import com.vividsolutions.jts.geom.Coordinate;
sascha@499: import com.vividsolutions.jts.geom.Geometry;
sascha@499: import com.vividsolutions.jts.geom.GeometryFactory;
sascha@499: import com.vividsolutions.jts.geom.LineString;
sascha@499: import com.vividsolutions.jts.geom.MultiLineString;
sascha@501: import com.vividsolutions.jts.geom.Polygon;
sascha@499: import com.vividsolutions.jts.geom.TopologyException;
sascha@465:
sascha@499: import de.intevation.gnv.math.IJKey;
sascha@465:
sascha@465: import de.intevation.gnv.raster.Vectorizer.Edge;
sascha@465:
sascha@465: import de.intevation.gnv.utils.Pair;
sascha@465:
sascha@499: import gnu.trove.TIntObjectHashMap;
sascha@465:
sascha@499: import java.util.ArrayList;
sascha@499: import java.util.List;
sascha@499:
sascha@499: import org.apache.log4j.Logger;
sascha@779:
sascha@465: /**
sascha@802: * Vectorizer backend to generated iso lines in form of
sascha@802: * JTS multi linestrings.
sascha@803: *
sascha@780: * @author Sascha L. Teichmann
sascha@465: */
sascha@465: public class JTSMultiLineStringProducer
sascha@465: extends IsoProducer
sascha@465: {
sascha@465: private static Logger log = Logger.getLogger(
sascha@465: JTSMultiLineStringProducer.class);
sascha@465:
sascha@802: /**
sascha@802: * The JTS geometry factory of this producer.
sascha@802: */
sascha@465: protected GeometryFactory geometryFactory;
sascha@465:
sascha@802: /**
sascha@802: * The polygon used to clip the produced multe line strings.
sascha@802: */
sascha@499: protected Polygon clip;
sascha@499:
sascha@802: /**
sascha@802: * Constructor to create a JTSMultiLineStringProducer with
sascha@802: * a given clipping polygon and a world bounding box.
sascha@802: * @param clip The clipping polygon.
sascha@802: * @param minX Min x coord of the world.
sascha@802: * @param minY Min y coord of the world.
sascha@802: * @param maxX Max x coord of the world.
sascha@802: * @param maxY Max y coord of the world.
sascha@802: */
sascha@465: public JTSMultiLineStringProducer(
sascha@499: Polygon clip,
sascha@465: double minX, double minY,
sascha@465: double maxX, double maxY
sascha@465: ) {
sascha@465: this(
sascha@499: clip,
sascha@498: // XXX: Geotools crashes if only using a 2d packed data format!
sascha@498: JTSMultiPolygonProducer.createDefaultGeometryFactory(3),
sascha@465: minX, minY,
sascha@465: maxX, maxY);
sascha@465: }
sascha@465:
sascha@802: /**
sascha@802: * Constructor to create a JTSMultiLineStringProducer with
sascha@802: * a given clipping polygon, a geometry factory
sascha@802: * and a world bounding box.
sascha@802: * @param clip The clipping polygon.
sascha@802: * @param geometryFactory The geometry factory.
sascha@802: * @param minX Min x coord of the world.
sascha@802: * @param minY Min y coord of the world.
sascha@802: * @param maxX Max x coord of the world.
sascha@802: * @param maxY Max y coord of the world.
sascha@802: */
sascha@465: public JTSMultiLineStringProducer(
sascha@499: Polygon clip,
sascha@465: GeometryFactory geometryFactory,
sascha@465: double minX, double minY,
sascha@465: double maxX, double maxY
sascha@465: ) {
sascha@465: super(minX, minY, maxX, maxY);
sascha@499: this.clip = clip;
sascha@465: this.geometryFactory = geometryFactory;
sascha@465: }
sascha@465:
sascha@802: /**
sascha@802: * Clips a given line string against the clippin polygon. The
sascha@802: * result is stored in the given list of line strings.
sascha@802: * @param lineString The line string to be clipped.
sascha@802: * @param lineStrings The result line string list.
sascha@802: */
sascha@499: protected void clipLineString(
sascha@499: LineString lineString,
sascha@499: ArrayList lineStrings
sascha@499: ) {
sascha@499: if (clip == null) {
sascha@499: lineStrings.add(lineString);
sascha@499: return;
sascha@499: }
sascha@499:
sascha@499: if (!lineString.intersects(clip)) {
sascha@499: return;
sascha@499: }
sascha@499:
sascha@499: try {
sascha@499: Geometry result = lineString.intersection(clip);
sascha@499:
sascha@499: if (result instanceof LineString) {
sascha@499: lineStrings.add((LineString)result);
sascha@499: }
sascha@499: else if (result instanceof MultiLineString) {
sascha@499: MultiLineString mls = (MultiLineString)result;
sascha@499: for (int i = 0, N = mls.getNumGeometries(); i < N; ++i) {
sascha@499: Geometry g = mls.getGeometryN(i);
sascha@499: if (g instanceof LineString) {
sascha@499: lineStrings.add((LineString)g);
sascha@499: }
sascha@499: else {
sascha@499: log.warn("cannot handle geometry " + g.getClass());
sascha@499: }
sascha@499: }
sascha@499: }
sascha@499: else {
sascha@499: log.warn("cannot handle " + result.getClass());
sascha@499: }
sascha@499: }
sascha@499: catch (TopologyException te) {
sascha@499: log.error(te.getLocalizedMessage(), te);
sascha@499: lineStrings.add(lineString);
sascha@499: }
sascha@499: }
sascha@499:
sascha@802: /**
sascha@802: * Returns a list of pairs attribute -> multi line string.
sascha@802: * All line strings produced are grouped by there attribute
sascha@802: * which is generated with the given attribute generator.
sascha@802: * @param attributeGenerator The attribute generator.
sascha@802: * @return The list of attribute/multi line strings.
sascha@802: */
sascha@465: public List> getMultiLineStrings(
sascha@465: AttributeGenerator attributeGenerator
sascha@465: ) {
sascha@465: ArrayList> multiLineStrings =
sascha@465: new ArrayList>();
sascha@465:
sascha@465: double b1 = minX;
sascha@465: double m1 = width != 1
sascha@465: ? (maxX - minX)/(width-1)
sascha@465: : 0d;
sascha@465:
sascha@465: double b2 = minY;
sascha@465: double m2 = height != 1
sascha@465: ? (maxY - minY)/(height-1)
sascha@465: : 0d;
sascha@465:
sascha@465: ArrayList coordinates = new ArrayList();
sascha@465:
sascha@465: for (IJKey key: joinPairs()) {
sascha@465: ArrayList lineStrings = new ArrayList();
sascha@465:
sascha@465: // process complete
sascha@465: ArrayList completeList = complete.get(key);
sascha@465: if (completeList != null) {
sascha@465: for (Edge head: completeList) {
sascha@465: Edge current = head;
sascha@465: do {
sascha@465: coordinates.add(new Coordinate(
sascha@465: m1*(current.a % width) + b1,
sascha@465: m2*(current.a / width) + b2));
sascha@465: }
sascha@465: while ((current = current.next) != head);
sascha@465: // add head again to close shape
sascha@465: coordinates.add(new Coordinate(
sascha@465: m1*(head.a % width) + b1,
sascha@465: m2*(head.a / width) + b2));
sascha@499:
sascha@499: clipLineString(
sascha@465: geometryFactory.createLineString(
sascha@465: coordinates.toArray(
sascha@499: new Coordinate[coordinates.size()])),
sascha@499: lineStrings);
sascha@499:
sascha@465: coordinates.clear();
sascha@465: }
sascha@465: }
sascha@465:
sascha@465: // process open
sascha@465: TIntObjectHashMap map = commonOpen.get(key);
sascha@465:
sascha@465: if (map != null) {
sascha@465: for (Edge head: headList(map)) {
sascha@465:
sascha@465: head = Vectorizer.simplify(head, width);
sascha@465: Edge current = head, last = head;
sascha@465: do {
sascha@465: coordinates.add(new Coordinate(
sascha@465: m1*(current.a % width) + b1,
sascha@465: m2*(current.a / width) + b2));
sascha@465: last = current;
sascha@465: }
sascha@465: while ((current = current.next) != null);
sascha@465: // add b from tail
sascha@465: coordinates.add(new Coordinate(
sascha@465: m1*(last.b % width) + b1,
sascha@465: m2*(last.b / width) + b2));
sascha@499:
sascha@499: clipLineString(
sascha@465: geometryFactory.createLineString(
sascha@465: coordinates.toArray(
sascha@499: new Coordinate[coordinates.size()])),
sascha@499: lineStrings);
sascha@499:
sascha@465: coordinates.clear();
sascha@465: } // for all in common open
sascha@465: } // if map defined for key
sascha@465:
sascha@465: if (!lineStrings.isEmpty()) {
sascha@465: MultiLineString multiLineString =
sascha@465: geometryFactory.createMultiLineString(
sascha@465: lineStrings.toArray(
sascha@465: new LineString[lineStrings.size()]));
sascha@465: lineStrings.clear();
sascha@465: Object attribute = attributeGenerator != null
sascha@465: ? attributeGenerator.generateAttribute(key.i, key.j)
sascha@465: : key;
sascha@465: multiLineStrings.add(new Pair