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}