/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2014, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotoolkit.display2d.ext.pie; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Point; import org.geotoolkit.display.PortrayalException; import org.geotoolkit.display.VisitFilter; import org.geotoolkit.display2d.canvas.RenderingContext2D; import org.geotoolkit.display2d.primitive.ProjectedCoverage; import org.geotoolkit.display2d.primitive.ProjectedFeature; import org.geotoolkit.display2d.primitive.ProjectedObject; import org.geotoolkit.display2d.primitive.SearchAreaJ2D; import org.geotoolkit.display2d.style.renderer.AbstractSymbolizerRenderer; import org.geotoolkit.display2d.style.renderer.SymbolizerRendererService; import org.opengis.feature.Feature; import org.opengis.filter.expression.Expression; import java.awt.*; import java.awt.geom.Arc2D; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Pie symbolizer renderer. * * @author Johann Sorel (Geomays) * @author Cédric Briançon (Geomatys) */ public class PieSymbolizerRenderer extends AbstractSymbolizerRenderer<CachedPieSymbolizer> { private static class PropsPie { private Geometry[] geometries; private Map<Object,Double> vals = new HashMap<>(); private double size = 100; } public PieSymbolizerRenderer(final SymbolizerRendererService service, CachedPieSymbolizer cache, RenderingContext2D context){ super(service, cache, context); } @Override public void portray(ProjectedObject graphic) throws PortrayalException { } @Override public void portray(Iterator<? extends ProjectedObject> ite) throws PortrayalException { final Expression size = symbol.getSource().getSize(); final Expression group = symbol.getSource().getGroup(); final Expression quarter = symbol.getSource().getQuarter(); final Expression value = symbol.getSource().getValue(); final List<PieSymbolizer.ColorQuarter> colorQuarters = symbol.getSource().getColorQuarters(); if (group == null || quarter == null || value == null) { return; } final Map<Object,PropsPie> vals = new HashMap<>(); while(ite.hasNext()) { try { final Object next = ite.next(); final Feature f; if (next instanceof ProjectedFeature) { f = ((ProjectedFeature) next).getCandidate(); } else { continue; } final Object key = group.evaluate(f); final Object quarterKey = quarter.evaluate(f); final Double valueKey = value.evaluate(f, Double.class); PropsPie propsPie = vals.get(key); if (propsPie == null) { propsPie = new PropsPie(); vals.put(key, propsPie); } propsPie.geometries = ((ProjectedFeature) next).getGeometry(null).getDisplayGeometryJTS(); if (size != null) { final Double s = size.evaluate(f, Double.class); if (s != null && !Double.isNaN(s) && s > 0) { propsPie.size = s; } } Double oldQuarter = propsPie.vals.get(quarterKey); if (oldQuarter == null) { oldQuarter = valueKey; } else { oldQuarter += valueKey; } propsPie.vals.put(quarterKey, oldQuarter); } catch (Exception ex) { throw new PortrayalException(ex); } } renderingContext.switchToDisplayCRS(); final Graphics2D g = renderingContext.getGraphics(); for (final PropsPie propsPie : vals.values()) { final double pieSize = propsPie.size; double nbTotalValue = 0; for (final Double val : propsPie.vals.values()) { if (val != null && !Double.isNaN(val)) { nbTotalValue += val; } } if (nbTotalValue != 0) { double startDegree = 0; double countOthers = 0; for (final Map.Entry<Object,Double> entryPropsVal : propsPie.vals.entrySet()) { if (entryPropsVal.getValue() == null || Double.isNaN(entryPropsVal.getValue())) { continue; } double degrees = entryPropsVal.getValue() * 360 / nbTotalValue; for (final Geometry geom : propsPie.geometries) { // Try to find the matching color for this quarter of pie Color c = null; for (final PieSymbolizer.ColorQuarter candidate : colorQuarters) { if (entryPropsVal.getKey().equals(candidate.getQuarter().evaluate(null))) { c = candidate.getColor().evaluate(null, Color.class); break; } } if (c == null) { // Not specified, so go to others group countOthers += entryPropsVal.getValue(); break; } final Point center = geom.getCentroid(); final Arc2D arc = new Arc2D.Double(center.getX() - pieSize / 2, center.getY() - pieSize / 2, pieSize, pieSize, startDegree, degrees, Arc2D.PIE); g.setPaint(c); g.fill(arc); g.setStroke(new BasicStroke(1)); g.setPaint(Color.BLACK); g.draw(arc); startDegree += degrees; } } if (countOthers > 0) { for (final Geometry geom : propsPie.geometries) { double degrees = countOthers * 360 / nbTotalValue; final Point center = geom.getCentroid(); final Arc2D arc = new Arc2D.Double(center.getX() - pieSize / 2, center.getY() - pieSize / 2, pieSize, pieSize, startDegree, degrees, Arc2D.PIE); g.setPaint(Color.GRAY); g.fill(arc); g.setStroke(new BasicStroke(1)); g.setPaint(Color.BLACK); g.draw(arc); } } } } } @Override public void portray(ProjectedCoverage graphic) throws PortrayalException { } @Override public boolean hit(ProjectedObject graphic, SearchAreaJ2D mask, VisitFilter filter) { return false; } @Override public boolean hit(ProjectedCoverage graphic, SearchAreaJ2D mask, VisitFilter filter) { return false; } }