ingo@1115: /* ingo@1115: * Copyright (c) 2010 by Intevation GmbH ingo@1115: * ingo@1115: * This program is free software under the LGPL (>=v2.1) ingo@1115: * Read the file LGPL.txt coming with the software for details ingo@1115: * or visit http://www.gnu.org/licenses/ if it does not exist. ingo@1115: */ ingo@1115: 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 :