null
not permitted).
+ * @param key the ranges of each group to be generated (null
not permitted).
+ * @param key the scaling factor for the weights (null
not permitted).
+ */
+ public Weighted_PyramidDataset(GroupName[] groupNames, double[][] groupRanges, double scalingFactor) {
+ ParamChecks.nullNotPermitted(groupNames, "groupNames");
+ ParamChecks.nullNotPermitted(groupRanges, "groupRanges");
+ ParamChecks.nullNotPermitted(scalingFactor, "scalingFactor");
+ this.dataMap = new HashMapnull
not permitted).
+ * @param values the raw observations. (null
not permitted).
+ * @param weightings the weights associated with the values, i.e.
+ * weight i indicates the number of times the value i appears (null
not permitted).
+ */
+ public void addSeries(String[] keys, double[][] values, double[][] weightings) {
+ ParamChecks.nullNotPermitted(keys, "key");
+ ParamChecks.nullNotPermitted(values, "values");
+ ParamChecks.nullNotPermitted(weightings, "weightings");
+ if(values.length != 2 || weightings.length != 2) {
+ throw new IllegalArgumentException(
+ "You must provide a pair of series!");
+ }
+ if(values[0].length != weightings[0].length || values[1].length != weightings[1].length) {
+ throw new IllegalArgumentException(
+ "The length of weightings array must be the same as the values array for each series!");
+ }
+
+ // Create and add the two series to the dataMap
+ for (int s = 0; s < 2; s++) {
+ // for each series create a new bucket to store the variable sums
+ Mapnull
not permitted and
+ * zero-length array not permitted).
+ *
+ * @return The minimum value.
+ */
+ private double getMinimum(double[] values) {
+ if (values == null || values.length < 1) {
+ throw new IllegalArgumentException(
+ "Null or zero length 'values' argument.");
+ }
+ double min = Double.MAX_VALUE;
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] < min) {
+ min = values[i];
+ }
+ }
+ return min;
+ }
+
+ /**
+ * Returns the maximum value in an array of values.
+ *
+ * @param values the values (null
not permitted and
+ * zero-length array not permitted).
+ *
+ * @return The maximum value.
+ */
+ private double getMaximum(double[] values) {
+ if (values == null || values.length < 1) {
+ throw new IllegalArgumentException(
+ "Null or zero length 'values' argument.");
+ }
+ double max = -Double.MAX_VALUE;
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] > max) {
+ max = values[i];
+ }
+ }
+ return max;
+ }
+
+ /**
+ * Tests this dataset for equality with an arbitrary object.
+ *
+ * @param obj the object to test against (null
permitted).
+ *
+ * @return A boolean.
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof Weighted_PyramidDataset)) {
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Returns a clone of the dataset.
+ *
+ * @return A clone of the dataset.
+ *
+ * @throws CloneNotSupportedException if the object cannot be cloned.
+ */
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ Weighted_PyramidDataset clone = (Weighted_PyramidDataset) super.clone();
+ return clone;
+ }
+
+
+ public double[][] getDataArray() {
+ double [][] data = new double[dataMap.keySet().size()][groupNames.length];
+ int i=0;
+ for (Map+ * Title: JAS-mine + *
+ *+ * Description: Java Agent-based Simulation library + *
+ *+ * Copyright (C) 2020 Kostas Manios + *
+ * + * This work is based on "Weighted_HistogramSimulationPlotter.java" by Ross Richardson + * + * 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; either version 2.1 of the License, or (at your option) + * any later version. + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * @author Kostas Manios + * + *
+ */
+public class Weighted_PyramidPlotter extends JInternalFrame implements EventListener {
+
+ /**
+ * Default values
+ */
+ public static String DEFAULT_TITLE = "Population Chart";
+ public static String DEFAULT_XAXIS = "Age Group";
+ public static String DEFAULT_YAXIS = "Population";
+ public static String DEFAULT_LEFT_CAT = "Males";
+ public static String DEFAULT_RIGHT_CAT = "Females";
+ public static Boolean DEFAULT_REVERSE_ORDER = false;
+ private static int MAXIMUM_VISIBLE_CATEGORIES = 20;
+
+ /**
+ * Variables
+ */
+
+ private static final long serialVersionUID = 1L;
+
+ private JFreeChart chart;
+
+ private WeightedArraySource[] sources;
+
+ private Weighted_PyramidDataset dataset;
+
+ private String xaxis;
+
+ private String yaxis;
+
+ private final String[] catNames = new String[2];
+
+ private GroupName[] groupNames;
+
+ private double[][] groupRanges; // These need to be doubles for the DatasetUtilities.createCategoryDataset method
+
+ private double scalingFactor; // This scales the sample (e.g. to the whole population)
+
+
+ /**
+ * Constructor for pyramid objects, showing only the latest data as time moves forward.
+ * Default values are used for all parameters: title, x-axis, y-axis, category names, age group names/ranges, reverseOrder
+ * It generates one age group per unique age, whose title is that age.
+ *
+ */
+ public Weighted_PyramidPlotter() {
+ this(DEFAULT_TITLE, DEFAULT_XAXIS, DEFAULT_YAXIS, DEFAULT_LEFT_CAT, DEFAULT_RIGHT_CAT);
+ }
+
+ /**
+ * Constructor for pyramid objects, showing only the latest data as time moves forward.
+ * Default values are used for the following parameters: x-axis, y-axis, category names, age group names/ranges, reverseOrder
+ * It generates one age group per unique age, whose title is that age.
+ *
+ * @param title - title of the chart
+ *
+ */
+ public Weighted_PyramidPlotter(String title) {
+ this(title, DEFAULT_XAXIS, DEFAULT_YAXIS, DEFAULT_LEFT_CAT, DEFAULT_RIGHT_CAT);
+ }
+
+ /**
+ * Constructor for pyramid objects, showing only the latest data as time moves forward.
+ * Default values are used for the following parameters: category names, age group names/ranges, reverseOrder
+ * It generates one age group per unique age, whose title is that age.
+ *
+ * @param title - title of the chart
+ * @param xaxis - name of the x-axis
+ * @param yaxis - name of the y-axis
+ *
+ *
+ public PopulationPyramidPlotter(String title, String xaxis, String yaxis) {
+ this(title, xaxis, yaxis, DEFAULT_LEFT_CAT, DEFAULT_RIGHT_CAT);
+ }
+ */
+
+ /**
+ * Constructor for pyramid objects, showing only the latest data as time moves forward.
+ * Default values are used for the following parameters: age group names/ranges, reverseOrder
+ * It generates one age group per unique age, whose title is that age.
+ *
+ * @param title - title of the chart
+ * @param xaxis - name of the x-axis
+ * @param yaxis - name of the y-axis
+ * @param leftCat - the name of the left category
+ * @param rightCat - the name of the right category
+ *
+ */
+ public Weighted_PyramidPlotter(String title, String xaxis, String yaxis, String leftCat, String rightCat) {
+ // fix the titles and prepare the plotter, leaving the groups null
+ fixTitles(title, xaxis, yaxis, leftCat, rightCat);
+ preparePlotter();
+ }
+
+ /**
+ * Constructor for pyramid objects, showing only the latest data as time moves forward.
+ * It generates groups names and ranges using the start/end/step values provided.
+ * Default values are used for the following parameters: x-axis, y-axis, category names, age group names/ranges, reverseOrder
+ *
+ * @param start - the minimum accepted value in groups
+ * @param end - the maximum accepted value in groups
+ * @param step - the step used to separate value into groups
+ *
+ */
+ public Weighted_PyramidPlotter(int start, int end, int step) {
+ this(DEFAULT_TITLE, DEFAULT_XAXIS, DEFAULT_YAXIS, DEFAULT_LEFT_CAT, DEFAULT_RIGHT_CAT, start, end, step, DEFAULT_REVERSE_ORDER);
+ }
+
+ /**
+ * Constructor for pyramid objects, showing only the latest data as time moves forward.
+ * It generates groups names and ranges using the start/end/step and order values provided.
+ * Default values are used for the following parameters: x-axis, y-axis, category names, age group names/ranges
+ *
+ * @param start - the minimum accepted value in groups
+ * @param end - the maximum accepted value in groups
+ * @param step - the step used to separate value into groups
+ * @param reverseOrder - if true, it will reverse the groups
+ *
+ */
+ public Weighted_PyramidPlotter(int start, int end, int step, Boolean reverseOrder) {
+ this(DEFAULT_TITLE, DEFAULT_XAXIS, DEFAULT_YAXIS, DEFAULT_LEFT_CAT, DEFAULT_RIGHT_CAT, start, end, step, reverseOrder);
+ }
+
+ /**
+ * Constructor for pyramid objects, showing only the latest data as time moves forward.
+ * It generates groups names and ranges using the start/end/step values provided.
+ * Descending order is used by default.
+ *
+ * @param title - title of the chart
+ * @param xaxis - name of the x-axis
+ * @param yaxis - name of the y-axis
+ * @param leftCat - the name of the left category
+ * @param rightCat - the name of the right category
+ * @param start - the minimum accepted value in groups
+ * @param end - the maximum accepted value in groups
+ * @param step - the step used to separate value into groups
+ * @param reverseOrder - if true, it will reverse the groups
+ *
+ */
+ public Weighted_PyramidPlotter(String title, String xaxis, String yaxis, String leftCat, String rightCat, int start, int end, int step, Boolean reverseOrder) {
+ if (step == 0) return;
+ fixTitles(title, xaxis, yaxis, leftCat, rightCat);
+
+ // Create the groups based on the range, and save them to "this"
+ GroupDetails gd = makeGroupsFromRange(start, end, step, reverseOrder);
+ this.groupNames = gd.groupNames;
+ this.groupRanges = gd.groupRanges;
+
+ preparePlotter();
+ }
+
+ /**
+ * Constructor for pyramid objects, showing only the latest data as time moves forward.
+ * It generates groups based on the names and ranges provided.
+ * Default values are used for the following parameters: title, x-axis, y-axis, category names
+ *
+ * @param groupNames - an array of the name of each group
+ * @param groupRanges - an array of the min/max values of each group
+ *
+ */
+ public Weighted_PyramidPlotter(String[] groupNames, double[][] groupRanges) {
+ this(DEFAULT_TITLE, DEFAULT_XAXIS, DEFAULT_YAXIS, DEFAULT_LEFT_CAT, DEFAULT_RIGHT_CAT, groupNames, groupRanges);
+ }
+
+ /**
+ * Constructor for pyramid objects, showing only the latest data as time moves forward.
+ * It generates groups based on the names and ranges provided.
+ *
+ * @param title - title of the chart
+ * @param xaxis - name of the x-axis
+ * @param yaxis - name of the y-axis
+ * @param leftCat - the name of the left category
+ * @param rightCat - the name of the right category
+ * @param groupNames - an array of the name of each group
+ * @param groupRanges - an array of the min/max values of each group
+ *
+ */
+ public Weighted_PyramidPlotter(String title, String xaxis, String yaxis, String leftCat, String rightCat, String[] groupNames, double[][] groupRanges) {
+ fixTitles(title, xaxis, yaxis, leftCat, rightCat);
+
+ // Fix names
+ this.groupNames = groupNames==null ? null : getGroupNamesFromStrings(groupNames);
+ this.groupRanges = groupRanges;
+
+ preparePlotter();
+ }
+
+ // The function that prepares the titles
+ private void fixTitles(String title, String xaxis, String yaxis, String leftCat, String rightCat)
+ {
+ this.setTitle(title);
+ this.xaxis = xaxis;
+ this.yaxis = yaxis;
+ this.catNames[0] = leftCat; this.catNames[1] = rightCat;
+ }
+
+ // the function that calculates groups from a range
+ private GroupDetails makeGroupsFromRange(int start, int end, int step, Boolean reverseOrder) {
+ // First we calculate the optimal (visually at least!) number of groups, so that
+ // the last group ends with "max" and its size is "(0.5 * step) < size < (1.5*step)"
+ int noOfGroups = (int)Math.max(Math.round((double)(end - start) / (double)step) + (Math.abs(step)==1?1:0), 1);
+ // *Note: should we enforce equal groups sizes?
+
+ // Then, if required, we reverse the order
+ if (reverseOrder) {
+ int temp = start;
+ start = end;
+ end = temp;
+ step = -step;
+ }
+
+ // Then we calculate the group ranges & names
+ String[] groupNames = new String[noOfGroups];
+ double[][] groupRanges = new double[noOfGroups][2];
+
+ // asc checks whether we are ascending or descending
+ Boolean asc = start <= end;
+ for (int i = 0; i < noOfGroups; i++) {
+ // The range needs to always be stored in ascending order, hence the extended use of "asc" here. Sorry! :)
+ //