Java tutorial
/** * Copyright (c) 2015 Bosch Software Innovations GmbH and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.hawkbit.ui.rollout.groupschart.client; import java.util.List; import com.github.gwtd3.api.D3; import com.github.gwtd3.api.arrays.Array; import com.github.gwtd3.api.core.Selection; import com.github.gwtd3.api.core.UpdateSelection; import com.github.gwtd3.api.core.Value; import com.github.gwtd3.api.functions.DatumFunction; import com.github.gwtd3.api.svg.Arc; import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style; import com.google.gwt.user.client.ui.DockLayoutPanel; /** * Draws a pie chart using D3. The slices are based on the list of Longs and on * the total target count. The total target count represents 100% of the pie. If * the sum in the list of Longs is less than the total target count a slice for * unassigned targets will be displayed. * */ // Exception squid - non Java 8 compatible GWT code that runs on browser @SuppressWarnings({ "squid:TrailingCommentCheck", "squid:S1604" }) public class GroupsPieChartWidget extends DockLayoutPanel { private static final String ATTR_VISIBILITY = "visibility"; private static final String ATTR_TRANSFORM = "transform"; private List<Long> groupTargetCounts; private Long totalTargetCount; private long unassignedTargets; private Selection svg; private Selection pieGroup; private Selection infoText; private Arc arc; /** * Initializes the pie chart */ public GroupsPieChartWidget() { super(Style.Unit.PX); init(); } private void init() { initChart(); draw(); } /** * Updates the pie chart with new data * * @param groupTargetCounts * list of target counts * @param totalTargetCount * total count of targets that are represented by the pie */ public void update(final List<Long> groupTargetCounts, final Long totalTargetCount) { this.groupTargetCounts = groupTargetCounts; this.totalTargetCount = totalTargetCount; if (groupTargetCounts != null) { long sum = 0; for (Long targetCount : groupTargetCounts) { sum += targetCount; } unassignedTargets = totalTargetCount - sum; } draw(); } private static PieArc getPie(Long count, Long total, double startAngle) { final Double percentage = count.doubleValue() / total.doubleValue(); return new PieArc(startAngle, startAngle + percentage * 2 * Math.PI); } private void draw() { if (svg == null || groupTargetCounts == null || totalTargetCount == null) { return; } final Array<Arc> dataArray = Array.create(); PieArc pie = getPie(unassignedTargets, totalTargetCount, 0); dataArray.push(pie.getArc()); double lastAngle = pie.getEndAngle(); for (int i = 0; i < groupTargetCounts.size(); i++) { final PieArc arcEntry = getPie(groupTargetCounts.get(i), totalTargetCount, lastAngle); dataArray.push(arcEntry.getArc()); lastAngle = arcEntry.getEndAngle(); } UpdateSelection pies = pieGroup.selectAll(".pie").data(dataArray); pies.enter().append("path").classed("pie", true).on(BrowserEvents.MOUSEOVER, new DatumFunction<Void>() { @Override public Void apply(Element context, Value d, int index) { Array<Double> point = arc.centroid(d.as(Arc.class), index); double x = point.getNumber(0); double y = point.getNumber(1); if (index == 0) { updateHoverText("Unassigned: " + unassignedTargets, x, y); } else { updateHoverText(index + ": " + groupTargetCounts.get(index - 1), x, y); } return null; } }).on(BrowserEvents.MOUSEOUT, new DatumFunction<Void>() { @Override public Void apply(Element context, Value d, int index) { infoText.attr(ATTR_VISIBILITY, "hidden"); return null; } }); pies.exit().remove(); pies.attr("d", arc); } private void updateHoverText(final String displayText, final double x, final double y) { final Selection text = infoText.select("text"); final Selection background = infoText.select("rect"); text.html(displayText); final double textWidth = getTextWidth(text.node()); final double textHeight = getTextHeight(text.node()); background.attr("width", textWidth * 1.1); background.attr("height", textHeight); moveSelection(background, -textWidth * 1.1 / 2.0, -textHeight * 0.8); moveSelection(infoText, x, y); infoText.attr(ATTR_VISIBILITY, "visible"); } private static void moveSelection(Selection sel, double x, double y) { sel.attr(ATTR_TRANSFORM, "translate(" + x + ", " + y + ")"); } private static final native double getTextWidth(Element e)/*-{ return e.getBBox().width; }-*/; private static final native double getTextHeight(Element e)/*-{ return e.getBBox().height; }-*/; private void initChart() { arc = D3.svg().arc().innerRadius(0).outerRadius(90); int height = 200; int width = 260; svg = D3.select(this).append("svg").attr("width", width).attr("height", height).append("g"); moveSelection(svg, (float) width / 2, (float) height / 2); pieGroup = svg.append("g"); infoText = svg.append("g").attr(ATTR_VISIBILITY, "hidden").classed("pie-info", true); infoText.append("rect"); infoText.append("text").attr("text-anchor", "middle"); } private static class PieArc { private double startAngle; private double endAngle; public PieArc(double startAngle, double endAngle) { this.startAngle = startAngle; this.endAngle = endAngle; } public double getEndAngle() { return endAngle; } public Arc getArc() { return Arc.constantArc().startAngle(startAngle).endAngle(endAngle); } } }