The complete specification of assignment #6 can be found as part of the stream at iTunes.
Add features to the display
The current display contains only lines and labels, but could easily be extended to make it more readable. You could, for example, put a dot at each of the data points on the graph. Even better, however, would be to choose different symbols for each line so that the data would be easily distinguishable even in a black-and-white copy. For example, you could use little circles for the first entry, squares for the second, triangles for the third, diamonds for the fourth, and so on. You might also figure out what the top rank for a name is over the years and set the label for that data point in boldface.
Create a new data type to hold the possible forms of the markers:
private enum Marker { CIRCLE, SQUARE, TRIANGLE, DIAMOND }
When iterating the name entries choose a marker type and draw it the various data points:
private void drawEntry(NameSurferEntry entry) { ... int index = data.indexOf(entry); Color color = COLORS[index % N_COLORS]; Marker marker = Marker.values()[index % N_MARKERS]; drawMarker(marker, x0, y0, color); ... for (int i = 1; i < NDECADES; i++) { ... drawMarker(marker, x0, y0, color); } } private void drawMarker(Marker type, double x, double y, Color color) { GObject marker; GPen helper; double halfSize = SIZE_MARKER / 2.0; double x0 = x - halfSize - 0.5; double y0 = y - halfSize - 0.5; switch (type) { case CIRCLE: marker = new GOval(x0, y0, SIZE_MARKER, SIZE_MARKER); break; case SQUARE: marker = new GRect(x0, y0, SIZE_MARKER, SIZE_MARKER); break; case TRIANGLE: helper = new GPen(x, y0); helper.drawLine(halfSize, SIZE_MARKER); helper.drawLine(-SIZE_MARKER, 0); helper.drawLine(halfSize, -SIZE_MARKER); marker = helper; break; case DIAMOND: helper = new GPen(x, y0); helper.drawLine(halfSize, halfSize); helper.drawLine(-halfSize, halfSize); helper.drawLine(-halfSize, -halfSize); helper.drawLine(halfSize, -halfSize); marker = helper; break; default: return; } marker.setColor(color); add(marker); }
To find the name with the top rank create two new maps holding the top rank for a decade and the name of that top rank:
private TreeMap<Integer,String> topName = new TreeMap<Integer,String>(); private TreeMap<Integer,Integer> topRank = new TreeMap<Integer,Integer>();
When a new entry is added loop over every decade, if there is no entry yet in the top maps or the current top rank is lower than the one from the entry add the values for the current entry:
public void addEntry(NameSurferEntry entry) { ... String name = entry.getName(); for (int i = 0; i < NDECADES; i++) { int rank = entry.getRank(i); if (rank == 0) continue; if (!topRank.containsKey(i)) { topRank.put(i, rank); topName.put(i, name); } else if (rank < topRank.get(i)) { topRank.put(i, rank); topName.put(i, name); } } update(); }
When drawing the label check if the current name for the current decade equals the top rank and print the label in bold accordingly:
private void drawEntry(NameSurferEntry entry) { ... drawLabel(name, rank, 0, x0, y0, color); for (int i = 1; i < NDECADES; i++) { ... drawLabel(name, rank, i, x0, y0, color); } } private void drawLabel(String name, int rank, int index, double x, double y, Color color) { ... if (isTopRank(name, index)) { label.setFont(label.getFont().deriveFont(Font.BOLD)); } ... } private boolean isTopRank(String name, int index) { if (!topName.containsKey(index)) return false; return name.equals(topName.get(index)); }
The code for this assignment is available on github.