sascha@465: package de.intevation.gnv.raster;
sascha@465:
sascha@465: import de.intevation.gnv.math.IJKey;
sascha@465:
sascha@465: import de.intevation.gnv.raster.Vectorizer.Edge;
sascha@465:
sascha@779: import gnu.trove.TIntHashSet;
sascha@779: import gnu.trove.TIntObjectHashMap;
sascha@779: import gnu.trove.TObjectProcedure;
sascha@779:
sascha@779: import java.util.ArrayList;
sascha@779: import java.util.HashMap;
sascha@779: import java.util.HashSet;
sascha@779: import java.util.List;
sascha@779:
sascha@465: /**
sascha@802: * Vectorizer backend to generate iso lines as line strings
sascha@801: * and custom labels on the chart.
sascha@801: *
sascha@780: * @author Sascha L. Teichmann
sascha@465: */
sascha@465: public class IsoProducer
sascha@465: extends AbstractProducer
sascha@465: {
sascha@801: /**
sascha@801: * Iso lines separate two neighbors. This interface decouples
sascha@801: * the generation of a suitable label for these to neighbors.
sascha@801: */
sascha@465: public interface AttributeGenerator {
sascha@465:
sascha@801: /**
sascha@801: * Generate attribute show as label for two given neighbors.
sascha@801: * @param neighbor1 The first neighbor.
sascha@801: * @param neighbor2 The second neighbor.
sascha@801: * @return The label attribute.
sascha@801: */
sascha@465: Object generateAttribute(int neighbor1, int neighbor2);
sascha@465:
sascha@465: } // interface AttributeGenerator
sascha@465:
sascha@801: /**
sascha@801: * Internal map of open edge to neighbor attributes.
sascha@801: */
sascha@465: protected HashMap open;
sascha@801: /**
sascha@801: * Internal map to associate neighbors with open edges.
sascha@801: */
sascha@465: protected HashMap commonOpen;
sascha@801: /**
sascha@801: * Internal map to associate neighbors with complete rings.
sascha@801: */
sascha@465: protected HashMap> complete;
sascha@465:
sascha@801: /**
sascha@801: * Width of the index space.
sascha@801: */
sascha@465: protected int width;
sascha@801: /**
sascha@801: * Height of the index space.
sascha@801: */
sascha@465: protected int height;
sascha@465:
sascha@801: /**
sascha@801: * Constructor with a given world bounding box.
sascha@801: * @param minX Min x coord of the world.
sascha@801: * @param minY Min y coord of the world.
sascha@801: * @param maxX Max x coord of the world.
sascha@801: * @param maxY Max y coord of the world.
sascha@801: */
sascha@465: public IsoProducer(
sascha@465: double minX, double minY,
sascha@465: double maxX, double maxY
sascha@465: ) {
sascha@465: super(minX, minY, maxX, maxY);
sascha@465:
sascha@465: open = new HashMap();
sascha@465: commonOpen = new HashMap();
sascha@465: complete = new HashMap>();
sascha@465: }
sascha@465:
sascha@465: public void handleRings(
sascha@778: List rings,
sascha@778: int value,
sascha@465: int width,
sascha@465: int height
sascha@465: ) {
sascha@465: if (value == -1) {
sascha@465: return;
sascha@465: }
sascha@465: this.width = width;
sascha@465: this.height = height;
sascha@465:
sascha@465: Integer v = Integer.valueOf(value);
sascha@465:
sascha@465: for (Edge head: rings) {
sascha@465: Edge current = head;
sascha@465: do {
sascha@465: Integer neighbor = open.remove(current);
sascha@465:
sascha@465: if (neighbor != null) {
sascha@465: IJKey pair = new IJKey(value, neighbor.intValue());
sascha@465: pair.sort();
sascha@465:
sascha@465: TIntObjectHashMap co = commonOpen.get(pair);
sascha@465:
sascha@465: if (co == null) {
sascha@465: commonOpen.put(pair, co = new TIntObjectHashMap());
sascha@465: }
sascha@465:
sascha@465: Edge edge = new Edge(current);
sascha@465:
sascha@465: Edge otherA = (Edge)co.remove(edge.a);
sascha@465: if (otherA != null) {
sascha@465: otherA.chain(edge, edge.a);
sascha@465: }
sascha@465:
sascha@465: Edge otherB = (Edge)co.remove(edge.b);
sascha@465: if (otherB != null) {
sascha@465: otherB.chain(edge, edge.b);
sascha@465: }
sascha@465:
sascha@465: if (edge.isComplete()) {
sascha@465: ArrayList list = complete.get(pair);
sascha@465: if (list == null) {
sascha@465: complete.put(pair, list = new ArrayList());
sascha@465: }
sascha@465: list.add(Vectorizer.simplify(edge, width));
sascha@465: }
sascha@465: else {
sascha@465: if (otherA == null) {
sascha@465: co.put(edge.a, edge);
sascha@465: }
sascha@465: if (otherB == null) {
sascha@465: co.put(edge.b, edge);
sascha@465: }
sascha@465: }
sascha@465: }
sascha@465: else {
sascha@465: Edge edge = new Edge(current.b, current.a);
sascha@465: open.put(edge, v);
sascha@465: }
sascha@465:
sascha@465: current = current.next;
sascha@465: }
sascha@465: while (current != head);
sascha@465: } // for all rings
sascha@465:
sascha@465: } // handleRings
sascha@465:
sascha@801: /**
sascha@801: * Join the pairs of neighbors i,j to have a distinct set.
sascha@801: * @return The distinct pairs of neighbors.
sascha@801: */
sascha@465: protected HashSet joinPairs() {
sascha@465: HashSet pairs = new HashSet();
sascha@465: pairs.addAll(complete .keySet());
sascha@465: pairs.addAll(commonOpen.keySet());
sascha@465: return pairs;
sascha@465: }
sascha@465:
sascha@801: /**
sascha@801: * Filter out the head list from the open edge lists.
sascha@801: * @param map Map of end and head lists.
sascha@801: * @return list of only head lists.
sascha@801: */
sascha@465: protected static ArrayList headList(TIntObjectHashMap map) {
sascha@465: final ArrayList headList = new ArrayList();
sascha@465: map.forEachValue(new TObjectProcedure() {
sascha@465: TIntHashSet headSet = new TIntHashSet();
sascha@465: public boolean execute(Object value) {
sascha@465: Edge head = ((Edge)value).head();
sascha@465: if (headSet.add(head.a)) {
sascha@465: headList.add(head);
sascha@465: }
sascha@465: return true;
sascha@465: }
sascha@465: });
sascha@465: return headList;
sascha@465: }
sascha@465:
sascha@801: /**
sascha@801: * Reset internal data structures to save some memory.
sascha@801: */
sascha@465: public void clear() {
sascha@465: open.clear();
sascha@465: complete.clear();
sascha@465: commonOpen.clear();
sascha@465: }
sascha@465: }
sascha@465: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :