001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030/** 031 * 032 */ 033package org.openimaj.demos.sandbox.vis; 034 035import java.io.IOException; 036import java.net.MalformedURLException; 037import java.net.URL; 038import java.util.HashMap; 039 040import org.openimaj.demos.irc.WorldVis; 041import org.openimaj.image.colour.ColourMap; 042import org.openimaj.image.colour.RGBColour; 043import org.openimaj.math.geometry.point.Point2d; 044import org.openimaj.video.Video; 045import org.openimaj.video.VideoDisplay; 046import org.openimaj.vis.VideoVisualisation; 047import org.openimaj.vis.VisualisationImpl; 048import org.openimaj.vis.general.LabelledPointVisualisation; 049import org.openimaj.vis.general.LabelledPointVisualisation.LabelledDot; 050import org.openimaj.vis.world.WorldMap; 051 052import com.Ostermiller.util.ExcelCSVParser; 053import com.Ostermiller.util.LabeledCSVParser; 054 055/** 056 * A demonstration of the World visualisation. It downloads a CSV file from the 057 * web, parses it and selects some data from the table. It then steps through 058 * all the years in the particular dataset and colours the countries based on 059 * the values of the data. 060 * <p> 061 * You can supply an indicator name on the command line (when you run it, it 062 * will print out all available indicators). If you get the name wrong you'll 063 * get an NPE probably. 064 * <p> 065 * It demonstrates the {@link WorldVis} interface as well as using the 066 * {@link VideoVisualisation} API to convert a {@link VisualisationImpl} into a 067 * {@link Video} which is displayed using a standard {@link VideoDisplay}. 068 * 069 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 070 * @created 12 Jun 2013 071 */ 072public class WorldMapGDPVis 073{ 074 private static final HashMap<String, HashMap<Integer, HashMap<String, Double>>> indicators = 075 new HashMap<String, HashMap<Integer, HashMap<String, Double>>>(); 076 077 /** 078 * Main Method 079 * 080 * @param args 081 * Command-line args. You can provide an indicator name here to 082 * override the default 083 * @throws IOException 084 * If the CSV cannot be downloaded 085 * @throws MalformedURLException 086 * Should not be thrown 087 */ 088 public static void main(final String[] args) throws MalformedURLException, IOException 089 { 090 // Create a parser for a CSV file 091 // The file gives values by country for a large set of indicators over a 092 // set of years. 093 // Years are in columns: the first column is the country, the second 094 // column the indicator type. 095 // We are using the ExcelCSVParser to parse this data for us. 096 final LabeledCSVParser l = new LabeledCSVParser( 097 new ExcelCSVParser( 098 new URL( 099 "http://users.ecs.soton.ac.uk/dpd/projects/openimaj/GDPgrowth-countries.csv" 100 ).openStream() 101 )); 102 103 // Get each line in turn and make a data structure 104 while (l.getLine() != null) 105 { 106 // Get the country and indicator (data) type. 107 final String c = l.getValueByLabel("Country"); 108 final String t = l.getValueByLabel("IndicatorName"); 109 110 // The data is between years 1971 and 2011, so we'll get each of the 111 // years 112 // for every country and indicator and store it away in a big 113 // hashmap. 114 for (int year = 1971; year <= 2011; year++) 115 { 116 final String s = l.getValueByLabel("" + year); 117 if (!s.isEmpty()) 118 { 119 final Double v = Double.parseDouble(s); 120 121 HashMap<Integer, HashMap<String, Double>> yi = WorldMapGDPVis.indicators.get(t); 122 if (yi == null) 123 WorldMapGDPVis.indicators.put(t, yi = new HashMap<Integer, HashMap<String, Double>>()); 124 125 HashMap<String, Double> ci = yi.get(year); 126 if (ci == null) 127 yi.put(year, ci = new HashMap<String, Double>()); 128 129 ci.put(c, v); 130 } 131 } 132 } 133 134 // List the indicators so we can choose one. 135 System.out.println("Possible indicators: " + WorldMapGDPVis.indicators.keySet()); 136 137 // The labelled point vis is used to plot country names. 138 final LabelledPointVisualisation dpv = new LabelledPointVisualisation(); 139 140 // The world map vis to show the world 141 final WorldMap<LabelledDot> wm = new WorldMap<LabelledDot>(1280, 720, dpv); 142 143 // Set the colours (and we'll remove the long/lat axes) 144 wm.setDefaultCountryLandColour(RGBColour.BLACK); 145 wm.getAxesRenderer().setDrawXAxis(false); 146 wm.getAxesRenderer().setDrawYAxis(false); 147 148 // This is the indicator we'll use 149 String showIndicator = "Agriculture, hunting, forestry, fishing (ISIC A-B)"; 150 if (args.length > 0) 151 showIndicator = args[0]; 152 153 // Create a video from the visualisation. 154 final VideoVisualisation vv = new VideoVisualisation(wm); 155 VideoDisplay.createVideoDisplay(vv); 156 157 // Loop through all the years and update the visualisation to show 158 // the values for each year. We do this once a second. 159 for (int year = 1971; year < 2011; year++) 160 { 161 wm.clearData(); 162 WorldMapGDPVis.updateYear(wm, showIndicator, year); 163 try 164 { 165 Thread.sleep(1000); 166 } catch (final InterruptedException e) 167 { 168 e.printStackTrace(); 169 } 170 } 171 } 172 173 /** 174 * Gets the data for a specific year and updates the visualisation. 175 * 176 * @param wm 177 * The visualisation to update. 178 * @param showIndicator 179 * Which indicator to show 180 * @param year 181 * Which year to show for 182 */ 183 private static void updateYear(final WorldMap<LabelledDot> wm, 184 final String showIndicator, final int year) 185 { 186 // Get the dataset for the given indicator and year. 187 final HashMap<String, Double> set = WorldMapGDPVis.indicators.get(showIndicator).get(year); 188 189 // Calculate the max and mean. Only values above the mean will be shown 190 double max = 0; 191 double mean = 0; 192 for (final double d : set.values()) 193 { 194 max = Math.max(max, d); 195 mean += d; 196 } 197 mean /= set.values().size(); 198 199 // Loop through all the countries 200 for (final String c : set.keySet()) 201 { 202 // Get the value 203 double v = set.get(c); 204 205 // Clip the data (just in case) 206 if (v < 0) 207 v = 0; 208 209 // Get the country from the world vis (some won't work just to 210 // naming differences) 211 final String cc = wm.getCountryCodeByName(c); 212 213 // If it didn't work, we will ignore it. 214 if (cc != null) 215 { 216 // Get the position of the country (for plotting it's name 217 // label) 218 final Point2d cl = wm.getCountryLocation(cc); 219 220 // Highlight the country with a colour based on its value 221 wm.addHighlightCountry(cc, ColourMap.Autumn.apply((float) (v / max))); 222 223 // Only if it's above the mean will we plot its name (just to 224 // keep the vis tidier) 225 if (v > mean) 226 wm.addPoint(cl.getX(), cl.getY(), new LabelledDot(c + ": " + v, 1, RGBColour.WHITE)); 227 } 228 else 229 System.out.println("country " + c + " unknown"); 230 } 231 232 } 233}