/* * [Population.java] * * Summary: Simple World Population Clock Applet. * * Copyright: (c) 2012-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2012-03-07 initial version * 1.1 2012-12-24 add more population data * 1.2 2013-01-30 revised population estimates. */ package com.mindprod.population; import com.mindprod.common18.BigDate; import com.mindprod.common18.Build; import com.mindprod.common18.CMPAboutJBox; import com.mindprod.common18.Common18; import com.mindprod.common18.FontFactory; import com.mindprod.common18.HybridJ; import com.mindprod.common18.JEButton; import com.mindprod.common18.VersionCheck; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.Timer; import java.awt.Color; import java.awt.Container; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.TimeZone; import static java.lang.System.*; /** * Simple World Population Clock Applet. *

* It works by interpolating monthly populations projections from http://www.census.gov/population/popclockworld.html * * @author Roedy Green, Canadian Mind Products * @version 1.2 2013-01-30 revised population estimates. * @since 2012-03-07 */ public final class Population extends JApplet implements ActionListener { /** * true if debugging, and want extra output */ private static final boolean DEBUGGING = false; /** * height of Applet box in pixels. Does not include surrounding frame. */ private static final int APPLET_HEIGHT = 150; /** * Width of Applet box in pixels. */ private static final int APPLET_WIDTH = 340; private static final int FIRST_COPYRIGHT_YEAR = 2012; /** * how ofter to update the world population clock in milliseconds. */ private static final int FREQUENCY = 400; /** * undisplayed copyright notice */ private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 2012-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; private static final String RELEASE_DATE = "2013-01-30"; private static final String TITLE_STRING = "World Population Clock"; /** * embedded version string. */ private static final String VERSION_STRING = "1.2"; /** * raw time data from http://www.census.gov/population/international/data/idb/informationGateway.php * http://en.wikipedia.org/wiki/World_population_estimates */ private static final String[] timestampStrings = { // last checked 2014-03-23 "2012-03-01", // 6_997_564_595 "2012-04-01", // 7_004_110_246 "2012-05-01", // 7_010_444_747 "2012-06-01", // 7_016_990_398 "2012-07-01", // 7_017_543_964 "2012-08-01", // 7_024_140_935 "2012-09-01", // 7_030_737_906 "2012-10-01", // 7_037_122_072 "2012-11-01", // 7_043_719_043 "2012-12-01", // 7_050_103_209 "2013-01-01", // 7_056_700_180 "2013-02-01", // 7_063_297_151 "2013-03-01", // 7_069_255_706 "2013-04-01", // 7_075_852_677 "2013-05-01", // 7_082_236_843 "2013-06-01", // 7_088_833_814 "2013-07-01", // 7_095_217_980 "2014-07-01", // 7_172_800_105 "2015-01-01", // 7_200_008_584 "2015-07-01", // 7_250_104_524 "2020-01-01", // 7_557_514_266 "2025-01-01", // 7_892_758_722 "2025-07-01", // 7_986_584_000 "2030-01-01", // 8_202_205_367 "2035-01-01", // 8_487_066_951 "2040-01-01", // 8_748_743_446 "2045-01-01", // 8_987_418_540 "2050-01-01", // 9_202_458_484 }; /** * background colour for window. */ private static final Color BACKGROUND_FOR_APP = Build.BACKGROUND_FOR_BLENDING; /** * for final result */ private static final Color BACKGROUND_FOR_POPULATION = Color.WHITE; /** * for final result */ private static final Color BACKGROUND_FOR_TIME = Color.WHITE; /** * foreground colour for window text */ private static final Color FOREGROUND_FOR_APP = Color.BLACK; /** * for final result */ private static final Color FOREGROUND_FOR_POPULATION = new Color( 0xff7f24 ); /** * for final result */ private static final Color FOREGROUND_FOR_TIME = new Color( 0x8b2500 ); /** * for titles */ private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c ); /** * population value formatter. */ private static final DecimalFormat FPOPULATION = new DecimalFormat( "0,000" ); /** * for final result */ private static final Font FONT_FOR_POPULATION; /** * for final result */ private static final Font FONT_FOR_TIME; /** * for for titles and About buttons */ private static final Font FONT_FOR_TITLE; /** * Local date formatter. */ private static final SimpleDateFormat FLOCAL = new SimpleDateFormat( "EEEE yyyy-MM-dd hh:mm:ss aa" ); /** * monthly populations estimate, data must be kept current from http://www.census.gov/population/international/data/idb/informationGateway.php */ private static final long[] populations = { 6_997_564_595L, // 2012-03-01 7_004_110_246L, // 2012-04-01 7_010_444_747L, // 2012-05-01 7_016_990_398L, // 2012-06-01 7_017_543_964L, // 2012-07-01 7_024_140_935L, // 2012-08-01 7_030_737_906L, // 2012-09-01 7_037_122_072L, // 2012-10-01 7_043_719_043L, // 2012-11-01 7_050_103_209L, // 2012-12-01 7_056_700_180L, // 2013-01-01 7_063_297_151L, // 2013-02-01 7_069_255_706L, // 2013-03-01 7_075_852_677L, // 2013-04-01 7_082_236_843L, // 2013-05-01 7_088_833_814L, // 2013-06-01 7_095_217_980L, // 2013-07-01 7_172_800_105L, // 2014-07-01 7_200_008_584L, // 2015-01-01 7_250_104_524L, // 2015-07-01 7_557_514_266L, // 2020-01-01 7_892_758_722L, // 2025-01-01 7_986_584_000L, // 2025-07-01 8_202_205_367L, // 2030-01-01 8_487_066_951L, // 2035-01-01 8_748_743_446L, // 2040-01-01 8_987_418_540L, // 2045-01-01 9_202_458_484L, // 2050-01-01 }; /** * timestamp (millis since 1970 UTC) when corresponding populations estimate is valid, */ private static final long[] timestamps; static { FONT_FOR_POPULATION = FontFactory.build( "Dialog", Font.BOLD, 30 ); FONT_FOR_TIME = FontFactory.build( "Dialog", Font.PLAIN, 16 ); FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 17 ); } static { /* get long timestamps from date strings. */ if ( timestampStrings.length != populations.length ) { throw new IllegalArgumentException( "program bug: mismatch in timestamps and population estimates" ); } final TimeZone utc = TimeZone.getTimeZone( "UTC" ); timestamps = new long[ timestampStrings.length ]; for ( int i = 0; i < timestamps.length; i++ ) { final BigDate d = new BigDate( timestampStrings[ i ] ); timestamps[ i ] = d.getTimeStamp( utc ); } } /** * about */ private JButton about; /** * first line of result as a sentence. */ private JLabel localTime; /** * title of applet */ private JLabel title; /** * annual growth in consumption as a percent */ private JLabel worldPopulation; /** * timer to keep population stats up to date */ private Timer splashTimer; /** * constructor */ public Population() { } /** * Calculate current world populations. -1 means cannot compute because tables are obsolete. * * @return current world populations */ private static long calcCurrentPopulation() { final long now = System.currentTimeMillis(); if ( DEBUGGING ) { out.println( now + " " + FLOCAL.format( now ) ); for ( long t : timestamps ) { out.println( t + " " + FLOCAL.format( t ) ); } } final int where = Arrays.binarySearch( timestamps, now ); int thisMonthIndex; int nextMonthIndex; if ( where >= 0 ) { thisMonthIndex = where; nextMonthIndex = where + 1; } else { final int insertionPoint = -( where + 1 ); thisMonthIndex = insertionPoint - 1; nextMonthIndex = insertionPoint; } if ( DEBUGGING ) { out.println( thisMonthIndex + " " + nextMonthIndex ); } if ( thisMonthIndex < 0 || nextMonthIndex >= timestamps.length ) { return -1; } // interpolate final double startPop = populations[ thisMonthIndex ]; final double endPop = populations[ nextMonthIndex ]; final double popIncrease = endPop - startPop; final double start = timestamps[ thisMonthIndex ]; final double end = timestamps[ nextMonthIndex ]; final double fraction = ( now - start ) / ( end - start ); final double popIncreaseSoFar = popIncrease * fraction; final double popNow = popIncreaseSoFar + startPop; return ( long ) ( popNow + .5 ); } /** * create gui components */ private void createComponents() { title = new JLabel( TITLE_STRING + " " + VERSION_STRING ); title.setFont( FONT_FOR_TITLE ); title.setForeground( FOREGROUND_FOR_TITLE ); // leave background app background about = new JEButton( "About" ); about.setToolTipText( "About " + TITLE_STRING + " " + VERSION_STRING ); worldPopulation = new JLabel( "", JLabel.CENTER ); worldPopulation.setFont( FONT_FOR_POPULATION ); worldPopulation.setBackground( BACKGROUND_FOR_POPULATION ); worldPopulation.setForeground( FOREGROUND_FOR_POPULATION ); localTime = new JLabel( "", JLabel.CENTER ); localTime.setFont( FONT_FOR_TIME ); localTime.setBackground( BACKGROUND_FOR_TIME ); localTime.setForeground( FOREGROUND_FOR_TIME ); } /** * hook up the listeners */ private void hookListeners() { // engage the listeners about.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { // open about box frame new CMPAboutJBox( TITLE_STRING, VERSION_STRING, "Displays current running estimate of world populations", "from U.S. Census bureau information.", "freeware", RELEASE_DATE, FIRST_COPYRIGHT_YEAR, "Roedy Green", "POPULATION", "1.8" ); } } ); } /** * layout the components * * @param contentPane where to add components */ private void layoutGridBag( Container contentPane ) { // basic layout: // 0-----0------------------------------------ 1------- // 0 -----------TITLE------------------------ ----about // 1 ----------world population---------------------- // 2 ----------local time----------------------------- // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title, new GridBagConstraints( 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 10, 10, 5, 5 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( about, new GridBagConstraints( 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 10, 5, 5, 10 ), 10, 2 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( worldPopulation, new GridBagConstraints( 0, 1, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( 0, 10, 5, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( localTime, new GridBagConstraints( 0, 2, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 0, 10, 10, 10 ), 0, 0 ) ); } /** * Allow this Applet to run as as application as well. * * @param args command line arguments ignored. */ public static void main( String args[] ) { HybridJ.fireup( new Population(), TITLE_STRING + " " + VERSION_STRING, APPLET_WIDTH, APPLET_HEIGHT ); } // end main /** * invoked whenever Timer ticks over * * @param e event not used. */ public void actionPerformed( ActionEvent e ) { final long now = System.currentTimeMillis(); // FLOCAL.setTimeZone( TimeZone.getDefault() ); local tz happens by default. localTime.setText( FLOCAL.format( now ) ); final long population = calcCurrentPopulation(); if ( population < 0 ) { worldPopulation.setText( "To function, the program needs fresh US Census data." ); } else { worldPopulation.setText( FPOPULATION.format( population ) ); } } /** * Called by the browser or Applet viewer to inform * this Applet that it is being reclaimed and that it should destroy * any resources that it has allocated. */ @Override public void destroy() { about = null; worldPopulation = null; localTime = null; title = null; if ( splashTimer != null ) { splashTimer.stop(); splashTimer = null; } } /** * Called by the browser or Applet viewer to inform * this Applet that it has been loaded into the system. */ @Override public void init() { if ( !VersionCheck.isJavaVersionOK( 1, 8, 0, this ) ) { return; } Common18.setLaf(); Container contentPane = this.getContentPane(); contentPane.setBackground( BACKGROUND_FOR_APP ); contentPane.setForeground( FOREGROUND_FOR_APP ); contentPane.setLayout( new GridBagLayout() ); createComponents(); layoutGridBag( contentPane ); hookListeners(); this.validate(); this.setVisible( true ); } // end init /** * usual Applet start */ public void start() { // use a Swing timer to keep updating the time and the population. splashTimer = new Timer( FREQUENCY, this ); splashTimer.setRepeats( true ); splashTimer.start(); } /** * usual Applet stop */ @Override public void stop() { if ( splashTimer != null ) { splashTimer.stop(); } } }