Is it dark outside? Draw a sun map!

71

17

Our closest star, the sun, is quite fidgety. The times it rises and sets depend on where you are, and whether it is winter or not.

We would like to be able to deduce if the sun is shining outside without having to leave the comforts of our basements - which is why we need an up-to-date sun map (a.k.a. daylight map). You are the one to write a program that generates just that!

Rules: Your program should output an image (in a known format) or an ASCII art representation of our planet, showing (an approximation of) which parts are currently lit by the sun. Your program must be original and self-contained: you are not allowed to copy, use, include or call any code except your programming language's standard libraries.

If you still don't have a clue what I'm talking about, here's an example from Wikipedia:

Example sun map

This is a popularity contest. You should note in your answer which of the following you're trying to achieve (multiple choices possible):

  • Correctness. Note that the rules say 'an approximation of' - the better your approximation, the more points in this category. You can check your implementation against Wolfram Alpha's, Time and Date's or die.net's.

  • Functionality. For example, what about interactivity? Marking specific locations? Mapping other planets?

  • Aesthetics. Drawing continents? Bonus points. Textured continents? Bonus points. On a 3D earth? With clouds? Stars? Correct stars? Massive bonus points. And so on.

  • Using uncommon, old, or just plain wrong technology. Sure, you could whip this up in Mathematica, but have you considered using m4? SQL? Forth? x86 assembly?

  • Fun. Want to use a Dymaxion-projection map? Go ahead!

  • Short code. This is the Code Golf SE, after all.

Have fun!

Wander Nauta

Posted 2014-03-16T15:44:23.817

Reputation: 3 039

1Surely this is a lot easier on a 3D Earth, because you just have to position the lights correctly. – Peter Taylor – 2014-03-16T17:29:40.503

3@PeterTaylor Probably! I think you don't even have to do lighting: you could draw a 3D earth and rotate it so that the daylight part (and nothing else) faces the viewer. It wouldn't show the nighttime part of the planet, but that's not required. – Wander Nauta – 2014-03-16T18:17:10.383

30An alternative solution is simply to install windows. (In the basement, I mean.) – r3mainer – 2014-03-16T21:55:22.657

1"x86 assembly" oh boy. – qwr – 2014-03-17T00:46:34.513

2@qwr I'd take x86 assembly over m4 any day of the week for this task... – Wander Nauta – 2014-03-17T00:52:19.723

3Also: "the sun is quite fidgety because it rises and sets at different times." Definitely the sun's fault :P – qwr – 2014-03-17T01:08:00.300

2@qwr Ever thought about how easy time/date calculations would be if we had 10-hour days, 10-day weeks, 10-week months and 10-month years, and sun between t=0 and t=5? But no, the sun has to go and show its ugly face at different places at different times and take too long to go around the earth. No complaints division either. Disgusting. – Wander Nauta – 2014-03-17T01:15:54.120

1Even if the times formed neat multiples of each other, and if there weren't other astronomical bodies to complicate things, and if the mean axis of rotation of Earth were perpendicular to the ecliptic, it still wouldn't be as simple as you wish because gyroscopic motion is complicated. – Peter Taylor – 2014-03-17T10:34:52.850

@PeterTaylor Without a doubt. But a man can dream, can't he? – Wander Nauta – 2014-03-17T10:49:35.610

2

Obligatory xkcd link: http://xkcd.com/now

– ntoskrnl – 2014-03-17T20:50:12.363

@ntoskrnl the xkcd thing would be a great answer for this if mousing over the earth changed the daylight ring on the outside. Make it so ;) – bazzargh – 2014-03-17T22:07:22.687

Answers

89

Haskell - low quality code

I was extremely tired when I wrote this.

I might have gone too far with projections idea, anyway, here's the projection the program uses. Basically like projecting earth onto a cube and then unfolding it. Besides, in this projection, the shadow is made of straight lines.
The program uses current date/time, and outputs a PPM file on stdout.

import Data.Time.Clock
import Data.Time.Calendar
import Control.Applicative
import Data.Fixed
import Data.Maybe

earth :: [[Int]]
earth = [[256],[256],[256],[256],[64,1,1,2,1,5,14,16,152],[56,19,3,27,1,6,50,1,2,1,90],[53,6,1,11,2,36,26,1,2,1,16,2,1,1,2,1,24,4,66],[47,2,5,14,4,35,22,7,54,2,1,3,60],[38,1,2,2,3,1,6,1,2,1,2,7,6,1,1,33,24,3,3,1,56,2,60],[34,2,1,4,2,1,3,1,1,3,3,2,15,3,3,29,57,5,19,1,2,11,17,1,1,1,34],[40,3,10,2,1,8,16,27,54,3,18,19,18,1,36],[33,6,5,3,2,3,1,3,2,2,1,5,16,21,1,2,53,2,10,1,6,19,1,7,4,3,9,2,33],[32,4,1,7,1,2,3,2,1,1,3,11,14,23,53,2,10,3,1,4,2,33,7,7,29],[8,5,25,10,5,3,2,14,10,2,1,18,1,2,31,6,18,1,7,4,1,60,22],[5,18,2,12,3,5,1,3,2,2,1,3,4,2,3,8,11,18,30,13,9,2,7,3,2,72,1,6,8],[4,36,2,1,1,4,3,7,1,4,3,9,8,15,34,18,2,2,2,17,1,78,4],[4,1,1,27,3,1,1,24,6,3,1,1,1,3,6,13,13,1,20,15,1,4,1,104,1],[3,31,1,24,1,2,4,8,10,9,12,6,18,7,3,7,1,1,2,99,3,2,2],[7,50,2,2,2,1,2,1,3,2,1,2,10,7,15,1,20,7,2,111,7,1],[4,35,1,15,9,1,1,3,4,1,12,5,34,8,3,110,10],[4,9,1,2,1,37,12,6,16,3,34,8,3,96,5,6,13],[6,6,1,1,8,32,12,6,3,1,49,9,4,2,1,86,1,3,4,2,19],[9,2,1,1,11,31,11,11,40,1,8,1,2,4,5,83,12,3,20],[8,1,16,33,9,11,39,2,8,1,2,3,3,83,13,5,19],[28,33,5,12,40,2,7,3,6,62,1,19,13,5,20],[27,36,2,15,34,3,2,2,6,71,1,22,11,2,22],[30,21,1,11,2,16,33,3,1,4,2,72,1,24,1,1,9,1,23],[31,21,1,26,39,4,1,98,1,1,33],[31,42,7,1,40,100,1,1,33],[33,25,2,15,4,4,35,102,36],[33,23,2,1,2,14,8,1,36,27,1,9,1,61,3,1,33],[33,26,5,14,42,10,1,11,2,2,2,7,3,5,1,9,1,44,38],[33,26,1,2,1,9,2,1,45,7,1,2,2,9,8,6,2,6,1,53,4,2,33],[33,26,1,4,1,6,44,8,6,2,3,7,9,5,3,56,1,1,4,3,33],[33,37,45,8,7,2,3,6,2,4,3,6,4,53,43],[33,36,46,6,6,1,4,1,2,2,3,16,3,47,1,5,8,2,34],[34,34,46,7,11,1,3,2,2,16,3,45,6,2,8,1,35],[34,33,48,5,11,1,4,1,4,16,2,49,3,2,6,2,35],[35,32,54,8,17,60,5,2,4,4,35],[36,30,50,12,18,60,8,2,1,1,38],[38,27,50,15,16,61,6,2,41],[38,25,51,18,3,4,6,62,6,1,42],[39,1,1,17,2,3,51,93,49],[40,1,1,11,9,2,49,31,1,10,2,50,49],[40,1,2,9,10,2,48,33,1,10,2,49,49],[41,1,2,8,11,1,47,34,2,10,5,44,50],[42,1,2,7,58,36,1,11,2,1,8,36,51],[46,6,58,36,2,15,7,34,2,1,49],[46,6,12,2,43,38,2,14,7,2,1,12,1,15,55],[46,6,5,2,7,2,41,38,2,14,10,10,4,10,59],[47,6,3,3,10,3,38,37,3,12,11,8,6,9,2,1,57],[49,10,51,38,3,9,13,7,8,9,9,2,48],[51,7,51,40,2,7,15,6,9,1,1,8,8,2,48],[55,7,47,41,1,6,17,4,12,8,8,1,49],[57,5,47,42,1,2,20,4,13,8,9,1,47],[59,3,8,1,38,43,22,4,13,1,2,4,10,2,46],[60,2,6,5,38,41,1,4,18,3,17,3,10,2,46],[61,2,1,1,2,3,1,7,34,45,18,2,18,1,60],[63,1,2,13,33,44,22,1,12,1,16,3,45],[66,14,33,43,22,1,13,1,14,1,1,1,46],[66,18,30,4,1,1,5,30,34,1,2,2,9,3,50],[66,19,43,27,34,2,2,1,7,3,52],[65,20,43,26,36,2,1,2,5,5,51],[65,21,42,24,39,3,4,7,2,1,1,1,1,1,44],[56,1,7,23,41,16,1,6,41,2,4,6,7,1,44],[64,25,39,16,1,5,42,3,4,5,2,1,8,1,2,1,37],[64,29,35,22,43,3,1,1,2,3,2,1,1,1,2,1,1,2,1,7,6,1,27],[63,31,35,20,45,2,11,1,9,7,4,2,26],[64,32,34,19,67,1,2,6,1,2,28],[65,31,34,12,1,6,48,4,18,6,31],[65,31,34,19,54,2,1,2,2,1,10,2,2,1,30],[66,29,36,14,1,3,57,1,19,2,28],[66,29,36,14,1,4,63,1,42],[67,27,36,15,1,4,63,5,3,2,33],[67,26,37,20,5,2,53,2,1,4,4,2,33],[68,25,37,20,4,3,52,9,3,3,32],[70,23,36,20,3,4,53,11,1,4,31],[71,22,37,17,5,4,51,18,31],[71,22,37,16,7,3,50,20,30],[71,21,39,15,6,3,5,1,42,24,29],[71,20,40,15,6,3,47,26,28],[71,17,43,15,6,3,46,28,27],[71,16,45,13,8,1,48,27,27],[71,16,45,12,58,28,26],[71,16,45,12,58,28,26],[70,16,47,10,59,28,26],[70,15,49,9,60,27,26],[70,14,50,7,62,7,6,13,27],[70,13,51,6,63,6,8,1,1,9,28],[70,10,138,10,28],[69,12,139,7,29],[69,11,141,5,19,3,8],[69,8,167,3,9],[69,8,166,1,1,1,10],[70,5,149,2,16,2,12],[69,6,166,3,12],[68,6,166,2,14],[68,5,166,3,14],[68,6,182],[67,6,183],[68,4,184],[68,4,6,2,176],[69,4,183],[70,5,20,1,160],[256],[256],[256],[256],[256],[256],[78,1,1,1,109,1,65],[75,2,115,1,23,1,39],[72,3,80,1,1,5,20,42,32],[74,1,70,1,4,21,5,52,2,1,25],[67,1,2,2,1,4,64,28,4,62,21],[69,9,34,1,1,1,1,1,1,1,2,48,3,69,15],[50,1,5,1,16,5,34,130,14],[32,1,1,2,4,1,3,1,4,29,32,128,18],[20,1,1,54,32,128,20],[17,49,34,137,19],[9,1,2,54,20,4,6,143,17],[16,51,18,5,10,135,21],[11,1,4,54,25,140,21],[12,66,4,155,19],[12,231,13],[0,6,9,5,2,234],[0,256],[0,256]]
main = do
    header
    mapM_ line [0..299]
    where
        header = do
            putStrLn "P3"
            putStrLn "# Some PPM readers expect a comment here"
            putStrLn "400 300"
            putStrLn "2"
        line y = mapM_ (\x -> pixel x y >>= draw) [0..399]
            where
                draw (r, g, b) = putStrLn $ (show r) ++ " " ++ (show g) ++ " " ++ (show b)
                pixel x y = fromMaybe (return (1, 1, 1)) $
                    mapRegion (\x y -> (50, -x, y)) (x - 50) (y - 50)
                    <|> mapRegion (\x y -> (-x, -50, y)) (x - 150) (y - 50)
                    <|> mapRegion (\x y -> (-x, y, 50)) (x - 150) (y - 150)
                    <|> mapRegion (\x y -> (-50, y, -x)) (x - 250) (y - 150)
                    <|> mapRegion (\x y -> (y, 50, -x)) (x - 250) (y - 250)
                    <|> mapRegion (\x y -> (y, -x, -50)) (x - 350) (y - 250)
                    where
                        mapRegion f x y = if x >= -50 && y >= -50 && x < 50 && y < 50 then
                            Just $ fmap (worldMap . shade) getCurrentTime
                            else Nothing
                                where
                                    t (x, y, z) = (atan2 y z) / pi
                                    p (x, y, z) = asin (x / (sqrt $ x*x+y*y+z*z)) / pi * 2
                                    rotate o (x, y, z) = (x, y * cos o + z * sin o, z * cos o - y * sin o)
                                    tilt o (x, y, z) = (x * cos o - y * sin o, x * sin o + y * cos o, z)
                                    shade c = ((t $ rotate yearAngle $ tilt 0.366 $ rotate (dayAngle - yearAngle) $ f x y)) `mod'` 2 > 1
                                        where
                                            dayAngle = fromIntegral (fromEnum $ utctDayTime c) / 43200000000000000 * pi + pi / 2
                                            yearAngle = (fromIntegral $ toModifiedJulianDay $ utctDay c) / 182.624 * pi + 2.5311
                                    worldMap c = case (c, index (t $ f x y) (p $ f x y)) of
                                            (False, False) -> (0, 0, 0)
                                            (False, True) -> (0, 0, 1)
                                            (True, False) -> (2, 1, 0)
                                            (True, True) -> (0, 1, 2)
                                            where
                                                index x y = index' (earth !! (floor $ (y + 1) * 63)) (floor $ (x + 1) * 127) True
                                                    where
                                                        index' [] _ p = False
                                                        index' (x:d) n p
                                                            | n < x = p
                                                            | otherwise = index' d (n - x) (not p)

That's right - triangular where-code, nested cases, invalid IO usage.

mniip

Posted 2014-03-16T15:44:23.817

Reputation: 9 396

That's a work of twisted genius. One suggestion, fromIntegral (fromEnum $ utctDayTime c) is neater as (realToFrac $ utctDayTime c). (I only learned this while writing my answer) – bazzargh – 2014-03-17T10:08:05.493

8I could watch that .gif all day. – MikeTheLiar – 2014-03-17T15:20:33.320

mnlip, I agree with @mikeTheLiar. You should embed that gif right at the top of your answer, you'd get all the votes. – bazzargh – 2014-03-17T15:49:20.740

1I voted solely on the wacky gif. That's just a twisted way to look at the world. – Allen Gould – 2014-03-17T19:25:48.323

ever so slightly trippy – Pharap – 2014-03-17T22:05:19.380

Delightful! Now somebody needs to turn this into a wall clock. – egid – 2014-03-18T03:27:34.080

61

Haskell, in the 'because it's there' category

I was curious so I wrote one. The formulas are reasonably accurate[1], but then I go and use some ascii art instead of a proper Plate Carrée map, because it looked nicer (the way I convert pixels to lat/long only works correctly for Plate Carrée)

import Data.Time
d=pi/180
tau=2*pi
m0=UTCTime(fromGregorian 2000 1 1)(secondsToDiffTime(12*60*60))
dark lat long now =
  let
    time=(realToFrac$diffUTCTime now m0)/(60*60*24)
    hour=(realToFrac$utctDayTime now)/(60*60)
    mnlong=280.460+0.9856474*time
    mnanom=(357.528+0.9856003*time)*d
    eclong=(mnlong+1.915*sin(mnanom)+0.020*sin(2*mnanom))*d
    oblqec=(23.439-0.0000004*time)*d
    ra=let num=cos(oblqec)*sin(eclong)
           den=cos(eclong) in
       if den<0 then atan(num/den)+pi else atan(num/den)
    dec=asin(sin(oblqec)*sin(eclong))
    gmst =6.697375+0.0657098242*time+hour
    lmst=(gmst*15*d)+long
    ha=(lmst-ra)
    el=asin(sin(dec)*sin(lat)+cos(dec)*cos(lat)*cos(ha))
  in
  el<=0

td x = fromIntegral x :: Double
keep="NSEW"++['0'..'9']
pixel p dk=if dk && p`notElem`keep then if p==' ' then '#' else '%' else p
showMap t= do
  let w=length(worldmap!!0)
      h=length worldmap
  putStrLn (worldmap!!0)
  putStrLn (worldmap!!1)
  mapM_(\y->do
           mapM_(\x->let
                    lat=(0.5-td y/td h)*pi
                    long=(0.5-td x/td w)*tau
                    in
                     putStr [pixel ((worldmap!!(y+2))!!x) (dark lat long t)]) [0..(w-1)]
           putStrLn "") [0..(h-4)]
  putStrLn (last worldmap)

main = do {t<-getCurrentTime; showMap t}

worldmap=[
 "180 150W  120W  90W   60W   30W  000   30E   60E   90E   120E  150E 180",
 "|    |     |     |     |     |    |     |     |     |     |     |     |",
 "+90N-+-----+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+",
 "|          . _..::__:  ,-\"-\"._       |7       ,     _,.__             |",
 "|  _.___ _ _<_>`!(._`.`-.    /        _._     `_ ,_/  '  '-._.---.-.__|",
 "|.{     \" \" `-==,',._\\{  \\  / {)     / _ \">_,-' `                mt-2_|",
 "+ \\_.:--.       `._ )`^-. \"'      , [_/(                       __,/-' +",
 "|'\"'     \\         \"    _L       oD_,--'                )     /. (|   |",
 "|         |           ,'         _)_.\\\\._<> 6              _,' /  '   |",
 "|         `.         /          [_/_'` `\"(                <'}  )      |",
 "+30N       \\\\    .-. )          /   `-'\"..' `:._          _)  '       +",
 "|   `        \\  (  `(          /         `:\\  > \\  ,-^.  /' '         |",
 "|             `._,   \"\"        |           \\`'   \\|   ?_)  {\\         |",
 "|                `=.---.       `._._       ,'     \"`  |' ,- '.        |",
 "+000               |    `-._        |     /          `:`<_|h--._      +",
 "|                  (        >       .     | ,          `=.__.`-'\\     |",
 "|                   `.     /        |     |{|              ,-.,\\     .|",
 "|                    |   ,'          \\   / `'            ,\"     \\     |",
 "+30S                 |  /             |_'                |  __  /     +",
 "|                    | |                                 '-'  `-'   \\.|",
 "|                    |/                                        \"    / |",
 "|                    \\.                                            '  |",
 "+60S                                                                  +",
 "|                     ,/           ______._.--._ _..---.---------._   |",
 "|    ,-----\"-..?----_/ )      _,-'\"             \"                  (  |",
 "|.._(                  `-----'                                      `-|",
 "+90S-+-----+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+",
 "Map 1998 Matthew Thomas. Freely usable as long as this line is included"]

Example output, from a more interesting time of year (we're near the equinox, so Wander Nauta's rectangular blobs are fairly accurate :) ) - this is for Jan 16 13:55:51 UTC 2014:

180 150W  120W  90W   60W   30W  000   30E   60E   90E   120E  150E 180
|    |     |     |     |     |    |     |     |     |     |     |     |
%90N%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%##########%#%%%%%%%%##%%%%%%%#######%7#######%#####%%%%%#############%
%##%%%%%#%#%%%%%%%%%%%%%%####%########%%%#####%%#%%%##%##%%%%%%%%%%%%%%
%%%#####%#%#%%%%%%%%%%%##%##%#%%#####%#%#%%%%%%#%################%%%2%%
%#%%%%%%%#######%%%#%%%%%#%%######, [_/(         ##############%%%%%%#%
%%%%#####%#########%####%%#####  oD_,--'            ####%#####%%#%%###%
%#########%###########%%#####    _)_.\\._<> 6        ######%%%#%##%###%
%#########%%#########%######    [_/_'` `"(             ###%%%##%######%
%30N#######%%####%%%#%#####     /   `-'"..' `:._       ###%%##%#######%
%###%########%##%##%%#####     /         `:\  > \  ,-^. #%%#%#########%
%#############%%%%###%%###     |           \`'   \|   ?_)##%%#########%
%################%%%%%%%#      `._._       ,'     "`  |' %%#%%########%
%000###############%####`-._        |     /          `:`<_%%%%%%######%
%##################%####    >       .     | ,          `=.%%%%%%%#####%
%###################%%#    /        |     |{|              %%%%%#####%%
%####################%#  ,'          \   / `'            ,"#####%#####%
%30S#################%  /             |_'                |  %%##%#####%
%####################% |                                 '-'##%%%###%%%
%####################|/                                      ##%####%#%
%####################\.                                       #####%##%
%60S################                                          ########%
%##################   ,/           ______._.--._ _..---.-------%%%%###%
%####%%%%%%%%%%%%%--_/ )      _,-'"             "                ##%##%
%%%%%###########       `-----'                                    ##%%%
%90S%%%%%%%%%----+-----+-----+----+-----+-----+-----+-----+-----+----%%
Map 1998 Matthew Thomas. Freely usable as long as this line is included

[1] they're the same as you'll find elsewhere, except without the extra work to keep degrees between 0 and 360, hours between 0 and 24, and radians between 0 and 2pi. I think those are holdovers from the days we used slide rules; trig functions work just fine outside those ranges...

bazzargh

Posted 2014-03-16T15:44:23.817

Reputation: 2 476

7Brilliant! I love that you can still see the map through the 'dark'. Also, the math looks solid. Could you add the date you used for the example, so other people can compare their solutions to yours? – Wander Nauta – 2014-03-17T00:24:02.607

I see that you've added the date, thanks! – Wander Nauta – 2014-03-17T00:26:17.537

1Yep. I like that in the image you can clearly see that it's winter in the Northern Hemisphere, made it easier to believe that date! I'm glad you posted an answer before me, saved me endless faff trying to do a golfed version, there's no way I'll beat yours for that. – bazzargh – 2014-03-17T00:29:19.500

39

Animation

.

Bash, 882* characters

This is my second entry, this time in the Aesthetics, Weird tech, Fun and Short code categories. It's inspired by Ram Narasimhan's entry and Peter Taylor's comment.

The script first generates a low-res texture of the world, bundled as base64-encoded data. It then generates 24 PovRay-scenes containing a sphere with that texture, each one rotated to 'face the sun'. Finally, the frames are combined into a GIF animation using ImageMagick. This means you'll have to have both PovRay and ImageMagick installed for the script to work - feel free to ignore this entry if you think that should disqualify it.

Like Ram's entry, and my first entry, this does not account for seasonal change, which means it's not very precise. It is, however, shorter, prettier and more precise than my first entry - and unlike Ram's entry, the map data and the code for generating the GIF animation are included.

                               echo '
                    iVBO  Rw0KGgoAAAA       NS
              UhE  U g      AAAEgAAAA                     kAQMAAAAQFe4lAAAABlB
    MVEUAFFwAbxKgAD63 AAAA   AWJLR0                  QAiAUdSAAAAAlwSFlzAAALEwAACx
 MB AJqcGAAAAAd0SU1FB9  4DE  hUWI   op      Fp5MAAADDSURBVBhXrcYhTsNQGADgr3ShE4Qi
    h4BeYQFBgqAJN8Lh    +r                jBb rArIJHPobgAgkzgeSQkVHT7MWThAHzq44
           /j/jezy6jSH  M6fB           gd  9T Nbxdl99R4Q+XpdNRISj4dlFRCz
            oI11FxIpup4uIRDe5           fokp0Y2W25jQFDfrGNGsDNsoqBaGj34D2
             bA7TcAwnmRoDZM             5tLkePUJb6uIT2rEq7hKaUhUHCXWpv7Q
             PqEv1rsuoc7X              RbV Bn2d   kGTYKMQ3C7H8z2+wc/eMd S
              QW39v8kAAA               AA      SUVOR K5CYII='|base64 \
               -di>t;for                X in     {0..23};do R=$((90-(\
                $X*15)                )); echo "camera{location <0,
                 0,                   -5> angle 38 }    light_source{
                  <0,0,               -1000> rgb < 2,2,   2>} sphere
                    {<0              ,0,0> 1 pigment      {
                      /**/            image_map{\"t\"        map_type
                        1}}                rotate           <0,$R,0>
                        }">s               ;povray             +Is +H300\
                        +Of$X.png          +W400
                        mogrify            -fill                     white    \
                        -annotate           +0+10                    "$X:00" \
                         -gravity           south                    f$X.png
                         done;              convert                -delay     \
                         100                -loop                 0 $(ls f*  \
                         |sort               -V)                  ani.gif
                        exit;

As a bonus, here's a GIF that uses NASA's Blue Marble image instead of the space-saving 1-bit texture, i.e. what the result would have looked like without any size restriction: http://i.imgur.com/AnahEIu.gif

*: 882 characters not counting decorative whitespace, 1872 characters total.

Wander Nauta

Posted 2014-03-16T15:44:23.817

Reputation: 3 039

5+1 for making it all self-contained. And also for creating the self-referential code which itself looks like a world map. Nice work. – Ram Narasimhan – 2014-03-19T00:49:32.240

1argh! multiple entries. Now I'm losing my excuse for not doing a mad one... – bazzargh – 2014-03-19T01:29:02.603

1That's sick! Love it. – pandubear – 2014-03-19T08:04:36.953

Heh, I realize now that I could've used the formatting of the code itself as the source for the world map (with spaces = ocean, everything else = land) and actually get better resolution with less characters. Oh well... – Wander Nauta – 2014-03-19T11:21:51.827

2This looks like Minecraft. – Kaz Wolfe – 2014-10-20T20:57:25.820

^ I was going to say... – Joe Z. – 2014-12-08T16:37:16.177

25

I decided to kick off the contest with an entry of my own, in the short code category. It's 923 characters long, not counting newlines.

C: 923 characters

Here's the code:

i;j;w=160;f=40;t;b;p;s;e;k;d=86400;q=599;
char* m="M('+z EDz :!#\"!*!8S$[\"!$!#\"\")\"!3R)V$'!!()1M./!F)\"!!!!)'/GE5@\"\"!&%.3&,Y$D\"!!%$)5i\"\"\"F\"%&&6%!e'A#!#!!#&$5&!f&A'$*\"5&!c-#'3''8\"$!!#\"U'\"=5$'8#$$\"S(#=7!*5\"!\"#['!A@6#!^H=!#6bH;!!!\"6_!!I;<&!&\"!!$\"F\"!I8;&\"#\"$&#\"C#\"I7<%#!\"/\"BP5=$*,\"=#\"$!L4A%&\"\"G\"\"\"#M1@)*F\"%P/@,!N#!S(E;!@W'E=!!!<Y&D7!&!\"$7\\$D8!)$4_$C8!('&#&!!a&@9!&(%$&g$>9!$*#(%h\">:!!-\"(%&!b!$&5:!\"+\"(!!#$!!!c+5<-!'!'!#!e)5:.!(!&!\"\"e,:25!!!\"!\"\"h-;07#\"$h.9/:\"\"$!!#\"a17-;'!\"$!!\"$!X46,<\"%\"&$\\45,>#&!$$#!W45,C!!!'!\"!$!V26,H\"#!$!\"!\"!S17-#!A!!#\"!_07,\"#A&!\"`.7+#\"A*.!Q.7*$\">/^-9)$\"=0^*<)$!>1]*<(D1])>&E2\\)>&F&!)\\)@#G$%(\\'w%]'x#,\"P%z .\"P%z .!R$z -\"S$z b#z c#z d#z 3";
main(){
t=(time(0)%d*160)/d;
printf("P2\n%d 62\n5\n",w);
for(;i<q;i++){
for(j=m[i]-' ';j>0;j--){
p=k%w,s=(t-f),e=(t+f);
printf("%c ","1324"[b*2+((p>s&&p<e)||(p>s+w&&p<e+w)||(p>s-w&&p<e-w))]);
k++;
}
b=!b;
}
}

Here's how it works:

A crude bitmap of the world* is run-length encoded as a string. Every character in the string represents a run of either land or sea pixels. Long runs of sea are split into a run of sea, then 0 land pixels, then another run of sea, to avoid including unprintable characters in the string. The Python script I wrote to convert PBM files into this format is here.

I then use time() to find out how many seconds have passed in Greenwich since midnight, 1 January 1970. I modulo that to find out how many seconds have passed there today, using that information to position the light portion of the map more-or-less accordingly (I hope).

Correctness is a joke. There's no math at all. The code assumes the earth is a cylinder (block-shaped day/night), that the sun is directly above the equator (no summer/winter), and that you like the color gray (no color).

On the plus side, I do draw continents.

The output is in Portable Graymap (PGM) format, which can then be converted to PNG by something like ImageMagick or the GIMP.

Here's an example output, converted to PNG (larger version):

Example output

*: The entire world except Antarctica, but who lives there anyway...

Wander Nauta

Posted 2014-03-16T15:44:23.817

Reputation: 3 039

1Nice output, it would be better if the dark area was curved though – qwr – 2014-03-17T00:46:03.790

1Yep! Having the output be curved means you would have to do some trigonometry though, which would make it much longer. (Or I guess you could just round the correct corners to make it look right-ish...) – Wander Nauta – 2014-03-17T00:50:39.280

1@WanderNauta There'll be some severely pissed off penguins that will feel cheated that you didn't take them into consideration, bro... – WallyWest – 2014-03-17T10:43:50.190

@WallyWest If you live on the poles, like your penguins, you don't need this program - midnight sun and all. – Wander Nauta – 2014-03-17T10:48:46.140

22

Haskell - it's Hammer time.

enter image description here

I did another one. Adapted from my previous version, this one uses an oblique Hammer projection to show both poles at the same time (in fact you're seeing the whole earth in each frame). Just for added weirdness, instead of using a bitmap directly, I sampled the earth along a spiral to give approximately equal area coverage; this is what allows me to distort the earth and rotate it easily. Hammer's projection is equal area too; my idea was that pairing these two things would lead to less distortion when I fill in the gaps. I display a graticule on the projection too, using Bresenham's algorithm to draw the lines. The globe and terminator line both move over the course of the day.

Edited to change the image to a higher resolution but (deliberately) coarser underlying bitmap, so you can see the effect of the spiral. This uses 5000 points (sampled from ~260000), equivalent to a 50x100 bitmap, but giving more resolution to the equator than to the poles.

To use the code, compile with ghc, run with an optional numeric parameter which is the hour offset; files are generated like 'earth0.pgm', 'earth1.pgm'.

import System.Environment
import Data.List (intercalate,unfoldr)
import qualified Data.Set as Set
import Data.List.Split
import Data.List
import Data.Maybe (catMaybes)
import qualified Data.Map as Map
import Data.Time
import Debug.Trace
d=pi/180
tau=2*pi
m0=UTCTime(fromGregorian 2000 1 1)(secondsToDiffTime(12*60*60))
dark::Double->Double->UTCTime->Bool
dark lat long now =
  let
    time=(realToFrac$diffUTCTime now m0)/(60*60*24)
    hour=(realToFrac$utctDayTime now)/(60*60)
    mnlong=280.460+0.9856474*time
    mnanom=(357.528+0.9856003*time)*d
    eclong=(mnlong+1.915*sin(mnanom)+0.020*sin(2*mnanom))*d
    oblqec=(23.439-0.0000004*time)*d
    ra=let num=cos(oblqec)*sin(eclong)
           den=cos(eclong) in
       if den<0 then atan(num/den)+pi else atan(num/den)
    dec=asin(sin(oblqec)*sin(eclong))
    gmst =6.697375+0.0657098242*time+hour
    lmst=(gmst*15*d)+long
    ha=(lmst-ra)
    el=asin(sin(dec)*sin(lat)+cos(dec)*cos(lat)*cos(ha))
  in
  el<=0
infill(open, known)= 
  if null open then known else infill gen
  where
    neighbours (x,y)=catMaybes $ map ((flip Map.lookup) known) [(x+1,y),(x-1,y),(x,y+1),(x,y-1),(x+1,y+1),(x-1,y+1),(x-1,y-1),(x-1,y-1)] 
    vote a= if null a then Nothing
             else Just ((sum a)`div`(length a))
    fill x (open',  known')=
      case vote (neighbours x) of
        Nothing->(x:open',known')
        Just c->(open',(x,c):known')
    gen=(\(o,k)->(o,Map.fromList k))$foldr fill ([], Map.toList known) open
mpoint (a,b)=case a of Nothing->Nothing;Just c->Just(c,b)
grid w h n g lut= map (\y->map (\x->if Set.member (x,y) g then 3 else case Map.lookup (x,y) lut of Nothing->7;Just c->c) [1..w]) [1..h]
unknowns w h lut=concatMap (\y->concatMap (\x->let z=1-(2*x//w-1)^2-(2*y//h-1)^2 in case Map.lookup (x,y) lut of Nothing->if z<0 then [] else [(x,y)];_->[]) [1..w]) [1..h]
main=do
  args <- getArgs
  let off = if null args then 0 else read(args!!0)
  actual <- getCurrentTime
  let now=((fromIntegral off)*60*60) `addUTCTime` actual
  let tod=realToFrac(utctDayTime now)/86400+0.4
  let s=5000
  let w=800
  let h=400
  let n=6
  -- pbm <- readFile "earth.pbm"
  -- let bits=ungrid s$parsepbm pbm
  let bits=[0,23,4,9,1,3,1,2,6,10,1,10,4,1,3,7,10,7,4,2,2,1,2,6,12,1,1,2,1,5,4,1,8,1,3,
            1,21,7,2,2,35,1,4,3,2,2,2,2,16,1,25,1,2,8,1,4,1,2,13,3,2,1,26,1,1,10,3,3,8,
            2,3,6,1,3,25,2,1,10,15,5,1,6,2,3,30,10,15,19,32,11,16,20,35,11,1,2,14,22,27,
            1,8,14,16,22,2,1,22,1,1,2,1,1,2,1,2,1,3,16,14,25,1,2,21,1,6,1,2,1,1,2,3,17,
            14,26,1,2,1,1,26,1,1,3,3,1,1,19,13,28,4,1,26,6,6,21,11,35,40,21,11,37,41,20,
            2,4,4,1,1,39,19,1,6,1,16,19,2,4,5,40,18,2,7,1,17,19,1,1,1,1,1,2,3,46,7,1,5,
            4,25,16,3,1,1,3,5,44,1,4,5,4,3,6,4,1,19,22,5,46,2,3,4,6,2,9,22,22,2,50,1,5,
            2,1,1,6,1,8,24,15,5,1,2,51,2,5,1,1,1,5,1,10,23,14,9,55,1,4,2,17,16,1,4,14,9,
            57,4,1,3,17,13,20,11,54,2,1,3,1,2,20,12,18,13,47,4,3,8,21,10,17,15,44,5,1,1,
            4,1,3,2,22,10,15,16,46,4,3,1,2,2,25,9,17,15,47,1,1,3,30,9,18,13,46,2,1,4,25,
            2,1,11,16,13,46,8,24,2,2,9,16,11,45,12,22,1,3,7,17,10,45,12,21,1,3,7,19,8,
            43,12,25,6,19,8,41,12,25,5,20,7,40,11,25,4,20,6,40,5,3,2,48,6,38,3,54,4,30,
            1,6,2,55,2,29,1,5,1,53,3,28,1,55,3,49,1,30,2,76,1,284,3,4,1,15,1,17,10,1,9,
            7,1,13,21,4,4,1,2,6,17,2,8,3,63]
  let t(phi,lambda)=unitsphere$rx (-pi/4)$rz (-tod*4*pi)$sphereunit(phi, lambda)
  let hmr=(fmap (\(x,y)->(floor((fl w)*(x+4)/8),floor((fl h)*(y+2)/4)))).hammer.t
  let g=graticule hmr n
  let lut = Map.fromList$ catMaybes $map mpoint$map (\((lat,long),bit)->(hmr(lat,long),bit*4+2-if dark lat long now then 2 else 0))  $zip (spiral s) (rld bits)
  -- let lut = Map.fromList$ catMaybes $map mpoint$map (\((lat,long),bit)->(hmr(lat,long),bit))$zip (spiral s) (rld bits)
  let lut' = infill ((unknowns w h lut), lut)
  let pgm = "P2\n"++((show w)++" "++(show h)++" 7\n")++(intercalate "\n" $ map (intercalate " ")$chunksOf 35 $ map show(concat$grid w h n g lut'))++"\n"
  writeFile ("earth"++(show off)++".pgm") pgm

fl=fromIntegral
spiral::Int->[(Double,Double)]
spiral n=map (\k-> let phi=acos(((2*(fl k))-1)/(fl n)-1) in rerange(pi/2-phi,sqrt((fl n)*pi)*phi)) [1..n]
rld::[Int]->[Int]
rld bits=concat$rld' (head bits) (tail bits)
  where
   rld' bit []=[]
   rld' bit (run:xs) = (replicate run bit):(rld' (case bit of 1->0;_->1) xs)
rle::[Int]->[Int]
rle bits=(head bits):(map length$group bits)
sample::Int->Int->Int->[(Int,Int)]
sample n w h = map (\(phi, theta)->((floor((fl h)*((phi-(pi/2))/pi)))`mod`h, (floor((fl w)*(theta-pi)/(tau)))`mod`w )) $ spiral n
ungrid::Int->[[Int]]->[Int]
ungrid n g = rle $ map (\(y, x)->(g!!y)!!x) (sample n w h)
  where w = length$head g
        h = length g
parsepbm::[Char]->[[Int]]
parsepbm pbm=
    let header = lines pbm
        format = head header
        [width, height] = map read$words (head$drop 1 header)
        rest = drop 2 header
        d = ((map read).concat.(map words)) rest
    in chunksOf width d
rerange(phi,lambda)
 | abs(phi)>pi = rerange(phi - signum(phi)*tau, lambda)
 | abs(phi)>pi/2 = rerange(phi-signum(phi)*pi, lambda+pi)
 | abs(lambda)>pi = rerange(phi, lambda - signum(lambda)*tau)
 | otherwise = (phi, lambda)
laea(phi,lambda)=if isInfinite(z) then Nothing else Just (z*cos(phi)*sin(lambda),z*sin(phi)) where z=4/sqrt(1+cos(phi)*cos(lambda))
hammer(phi,lambda)=case laea(phi, lambda/2) of Nothing->Nothing; Just(x,y)->Just (x, y/2)
bresenham :: (Int, Int)->(Int, Int)->[(Int, Int)]
bresenham p0@(x0,y0) p1@(x1,y1)
  | abs(dx)>50||abs(dy)>50=[]
  | x0>x1 = map h$ bresenham (h p0) (h p1)
  | y0>y1 = map v$ bresenham (v p0) (v p1)
  | (x1-x0) < (y1-y0) = map f$ bresenham (f p0) (f p1)
  | otherwise = unfoldr (\(x,y,d)->if x>x1 then Nothing else Just((x,y),(if 2*(d+dy)<dx then(x+1,y,d+dy)else(x+1,y+1,d+dy-dx)))) (x0,y0,0)
      where 
        h(x,y)=(-x,y)
        v(x,y)=(x,-y)
        f(x,y)=(y,x)
        dx=x1-x0
        dy=y1-y0
globe n k= 
  (concatMap (\m->map (meridian m) [k*(1-n)..k*(n-1)]) [k*(1-2*n),k*(2-2*n)..k*2*n])
  ++(concatMap (\p->map (parallel p) [k*(-2*n)..k*2*n]) [k*(1-n),k*(2-n)..k*(n-1)])
  where
  meridian m p=(radians(p,m),radians(p+1,m))
  parallel p m=(radians(p,m),radians(p,m+1))
  radians(p,m)=rerange((p//(k*n))*pi/2,(m//(k*n))*pi/2)
graticule f n=Set.fromList $ concatMap (\(a,b)->case (f a,f b) of (Nothing,_)->[];(_,Nothing)->[];(Just c,Just d)->bresenham c d) (globe n 4)
rx theta (x,y,z) = (x, y*(cos theta)-z*(sin theta), y*(sin theta)+z*(cos theta))
ry theta (x,y,z) = (z*(sin theta)+x*(cos theta), y, z*(cos theta)-x*(sin theta))
rz theta (x,y,z) = (x*(cos theta)-y*(sin theta), x*(sin theta)+y*(cos theta), z)
sphereunit (phi, theta) = (rz theta (ry (-phi) (1,0,0)))
unitsphere (x,y,z) = (asin z, atan2 y x)
x//y=(fromIntegral x)/(fromIntegral y)    

bazzargh

Posted 2014-03-16T15:44:23.817

Reputation: 2 476

3This is... madness. Absolute madness. I love it. – Wander Nauta – 2014-03-27T20:14:49.677

1It tooks me so long to get the transform right on this one. The spiral bitmap alone is hilarious. – bazzargh – 2014-03-27T20:16:55.543

re "it does scale up quite well" - do you mean you could easily make a higher-resolution GIF? – Wander Nauta – 2014-03-27T20:23:25.600

Yes. quite a lot larger. I took that code out for a bit while I messed with the graticule, when I get time a bit later I'll put it back in and upload a better image – bazzargh – 2014-03-27T20:25:55.297

1there you go - nice big image of crazytown – bazzargh – 2014-03-28T02:27:49.580

"Stop! Hammer(-Aitoff projection) time." – Joe Z. – 2014-12-08T16:38:28.510

21

C, using pnm images

Late answer, focusing on correctness and aesthetics. The output is a blend of two input images (day.pnm and night.pnm), including a stripe of twilight. I'm using images based on NASAs blue marble here.

The code uses my own img.h for clarity (just imagine it being included verbatim in the .c for strict rule compliance...). Everything in there is implemented via C macros. The animations are built with imagemagicks convert from multiple frames - the program itself will only output static images. Code is below.

Now: (Aug 13, ~13:00 CEST)

output

One day: (Jan 1st)

oneday

One year: (12:00 UTC)

oneyear

sun.c

  #include <math.h>
  #include <time.h>

  #include "img.h"

  #ifndef M_PI
  #define M_PI 3.14159265359
  #endif

  double deg2rad(double x) {return x / 180.0 * M_PI;}
  double rad2deg(double x) {return x * 180.0 / M_PI;}

  double  sind(double x) {return  sin(deg2rad(x));}
  double  cosd(double x) {return  cos(deg2rad(x));}
  double asind(double x) {return rad2deg(asin(x));}

  double elevation(double latitude, double longitude, int yday, int hour, int min, int sec)
  {
     double fd = (hour + (min + sec / 60.0) / 60.0) / 24.0;
     double fyd = 360.0 * (yday + fd) / 366.0;

     double m = fyd - 3.943;
     double ta = -1.914 * sind(m) + 2.468 * sind(2 * m + 205.6);
     double hourangle = (fd - 0.5) * 360.0 + longitude + ta;
     double decl = 0.396 - 22.913 * cosd(fyd) + 4.025 * sind(fyd) - 0.387 * cosd(2 * fyd) + 0.052 * sind(2 * fyd) - 0.155 * cosd(3 * fyd) + 0.085 * sind(3 * fyd);

     return asind(cosd(hourangle) * cosd(decl) * cosd(latitude) + sind(decl) * sind(latitude));
  }

  int main(int argc, char* argv[])
  {
     Image day, night, out;
     int x, y;
     time_t t = time(0);
     struct tm* utc = gmtime(&t);
     int yday = utc->tm_yday, hour = utc->tm_hour, min = utc->tm_min, sec = utc->tm_sec;

     imgLoad(day, "day.pnm");
     imgLoad(night, "night.pnm");
     imgLoad(out, "day.pnm");
     for(y = 0; y < day.height; ++y)
     {
        double latitude = 90.0 - 180.0 * (y + 0.5) / day.height;
        for(x = 0; x < day.width; ++x)
        {
           double longitude = -180.0 + 360.0 * (x + 0.5) / day.width;
           double elev = elevation(latitude, longitude, yday, hour, min, sec);
           double nf = elev > -0.8 ? 0.0 : elev > -6.0 ? 0.5 : 1.0;
           double df = 1.0 - nf;
           Color dc = imgGetColor(day, x, y);
           Color nc = imgGetColor(night, x, y);
           imgDotC3(out, x, y, df * dc.r + nf * nc.r, df * dc.g + nf * nc.g, df * dc.b + nf * nc.b);
        }
     }
     imgSave(out, "out.pnm");
  }

img.h

  #include <stdlib.h>
  #include <stdio.h>
  #include <string.h>

  typedef struct
  {
     unsigned char r;
     unsigned char g;
     unsigned char b;
  } Color;

  typedef struct
  {
     Color* data;
     int width;
     int height;
     Color c;
  } Image;

  #define imgCreate(img, w, h)           {\
                                            int length;\
                                            (img).width = (w);\
                                            (img).height = (h);\
                                            length = (img).width * (img).height * sizeof(Color);\
                                            (img).data = malloc(length);\
                                            memset((img).data, 0, length);\
                                            (img).c.r = (img).c.g = (img).c.b = 0;\
                                         }

  #define imgDestroy(img)                {\
                                            free((img).data);\
                                            (img).width = 0;\
                                            (img).height = 0;\
                                            (img).c.r = (img).c.g = (img).c.b = 0;\
                                         }

  #define imgSetColor(img, ur, ug, ub)   {\
                                            (img).c.r = (ur);\
                                            (img).c.g = (ug);\
                                            (img).c.b = (ub);\
                                         }

  #define imgDot(img, x, y)              {\
                                            (img).data[(int)(x) + (int)(y) * (img).width] = (img).c;\
                                         }

  #define imgDotC3(img, x, y, ur, ug, ub) {\
                                            (img).data[(int)(x) + (int)(y) * (img).width].r = (ur);\
                                            (img).data[(int)(x) + (int)(y) * (img).width].g = (ug);\
                                            (img).data[(int)(x) + (int)(y) * (img).width].b = (ub);\
                                         }

  #define imgDotC(img, x, y, c)          {\
                                            (img).data[(int)(x) + (int)(y) * (img).width] = (c);\
                                         }

  #define imgGetColor(img, x, y)         ((img).data[(int)(x) + (int)(y) * (img).width])

  #define imgLine(img, x, y, xx, yy)     {\
                                            int x0 = (x), y0 = (y), x1 = (xx), y1 = (yy);\
                                            int dx =  abs(x1 - x0), sx = x0 < x1 ? 1 : -1;\
                                            int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;\
                                            int err = dx + dy, e2;\
                                            \
                                            for(;;)\
                                            {\
                                               imgDot((img), x0, y0);\
                                               if (x0 == x1 && y0 == y1) break;\
                                               e2 = 2 * err;\
                                               if (e2 >= dy) {err += dy; x0 += sx;}\
                                               if (e2 <= dx) {err += dx; y0 += sy;}\
                                            }\
                                         }

  #define imgSave(img, fname)            {\
                                            FILE* f = fopen((fname), "wb");\
                                            fprintf(f, "P6 %d %d 255\n", (img).width, (img).height);\
                                            fwrite((img).data, sizeof(Color), (img).width * (img).height, f);\
                                            fclose(f);\
                                         }

  #define imgLoad(img, fname)            {\
                                            FILE* f = fopen((fname), "rb");\
                                            char buffer[16];\
                                            int index = 0;\
                                            int field = 0;\
                                            int isP5 = 0;\
                                            unsigned char c = ' ';\
                                            while(field < 4)\
                                            {\
                                               do\
                                               {\
                                                  if(c == '#') while(c = fgetc(f), c != '\n');\
                                               } while(c = fgetc(f), isspace(c) || c == '#');\
                                               index = 0;\
                                               do\
                                               {\
                                                  buffer[index++] = c;\
                                               } while(c = fgetc(f), !isspace(c) && c != '#' && index < 16);\
                                               buffer[index] = 0;\
                                               switch(field)\
                                               {\
                                                  case 0:\
                                                     if (strcmp(buffer, "P5") == 0) isP5 = 1;\
                                                     else if (strcmp(buffer, "P6") == 0) isP5 = 0;\
                                                     else fprintf(stderr, "image format \"%s\" unsupported (not P5 or P6)\n", buffer), exit(1);\
                                                     break;\
                                                  case 1:\
                                                     (img).width = atoi(buffer);\
                                                     break;\
                                                  case 2:\
                                                     (img).height = atoi(buffer);\
                                                     break;\
                                                  case 3:\
                                                     index = atoi(buffer);\
                                                     if (index != 255) fprintf(stderr, "image format unsupported (not 255 values per channel)\n"), exit(1);\
                                                     break;\
                                               }\
                                               field++;\
                                            }\
                                            imgCreate((img), (img).width, (img).height);\
                                            if (isP5)\
                                            {\
                                               int length = (img).width * (img).height;\
                                               for(index = 0; index < length; ++index)\
                                               {\
                                                  (img).data[index].r = (img).data[index].g = (img).data[index].b = fgetc(f);\
                                               }\
                                            }\
                                            else\
                                            {\
                                               fread((img).data, sizeof(Color), (img).width * (img).height, f);\
                                            }\
                                            fclose(f);\
                                         }

Manuel Kasten

Posted 2014-03-16T15:44:23.817

Reputation: 3 226

Very pretty! I like it. – Wander Nauta – 2014-08-13T13:00:52.713

18

R: Using ggplot2 and Map projection

enter image description here

Inspired by @mniip's post, I decided to try using R's mapproj package, wherein we can orient the globe by specifying where the North Pole should be when computing the projection.

Based on current GMT time, I compute the longitude where is it currently noon and make that point the map's center. We are looking at Earth from the "Sun's point of view" so whatever is visible is in daylight.

Much of the code is just aesthetics. The only part I had to figure out was to compute the "noon Longitude," that is the longitude value where it was noon given any GMT time.

library(ggplot2);library(maps);library(ggmap)
world <- map_data("world")# a lat-long dataframe from the maps package
worldmap <- ggplot(world, aes(x=long, y=lat, group=group)) + 
  geom_path(color="orange") + 
  theme(panel.background= element_rect("black"),  
        axis.text.y=element_blank(),
        axis.ticks=element_blank(),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        panel.grid.major = element_line(colour="blue", size=0.75),
        panel.grid.minor = element_line(colour="blue")
  )  

#Create a function that takes in the current GMT time
print_3d_coordmap <- function (current_gmt_time) {
  curr_gmt_mins <- as.POSIXlt(current_gmt_time)$hour*60 + as.POSIXlt(current_gmt_time)$min
  noon_longitude <- 180 - (curr_gmt_mins * 360/1440)
  #centered at wherever longitude where it is Noon now on (lat:equator)  
  worldmap + coord_map("ortho", orientation=c(0, noon_longitude, 0))
}

#test it out
print_3d_coordmap(Sys.time() + 7*60*60) # my location is 7 hours behind UTC

I then used the R animation package to generate 24 images and stitched them into one GIF.

Ram Narasimhan

Posted 2014-03-16T15:44:23.817

Reputation: 281

Looks great! Does it correctly handle summer and winter, though? I don't know R that well, but it looks like your equator is always at the center of the image. – Wander Nauta – 2014-03-18T09:33:33.770

Yes, you are correct. I did a quick and dirty implementation using the equator as the only latitude. (Focused on the animation instead.) The project has lots of features that I am not using. If there is a reference that shows how to vary latitudes with seasons, I will be happy to try it out. – Ram Narasimhan – 2014-03-18T19:06:46.667

Here's a reference in R, Ram - in fact this is where I translated the formulas in my entry from http://stackoverflow.com/questions/8708048/position-of-the-sun-given-time-of-day-latitude-and-longitude

– bazzargh – 2014-03-18T20:15:30.147

@bazzargh Thanks! Looks like I have a lot of learning about azimuth angles to do. – Ram Narasimhan – 2014-03-19T00:46:15.153

9

JavaScript – by Martin Kleppe (http://aem1k.com/)

I want to stress that this is not my work, but the work of Martin Kleppe. I just think it fits so perfectly that it should not be missing here:

Online Demo (or just paste it into the console)

eval(z='p="<"+"pre>"/*        ######## */;for(y in n="zw24l6k\
4e3t4jnt4qj24xh2 x/*    *############### */42kty24wrt413n243n\
9h243pdxt41csb yz/*  #################### */43iyb6k43pk7243nm\
r24".split(4)){/*     *#################*   */for(a in t=pars\
eInt(n[y],36)+/*          ###############*   */(e=x=r=[]))for\
(r=!r,i=0;t[a/*               ############*   */]>i;i+=.05)wi\
th(Math)x-= /*                #############    */.05,0<cos(o=\
new Date/1e3/*                #########*       */-x/PI)&&(e[~\
~(32*sin(o)*/*                     ####*       */sin(.5+y/7))\
+60] =-~ r);/*                         *###    */for(x=0;122>\
x;)p+="   *#"/*                        #####  */[e[x++]+e[x++\
]]||(S=("eval"/*                      *##### */+"(z=\'"+z.spl\
it(B = "\\\\")./*      ###*           ####  */join(B+B).split\
(Q="\'").join(B+Q/*                  ###* */)+Q+")//m1k")[x/2\
+61*y-1]).fontcolor/*               ##   */(/\\w/.test(S)&&"#\
03B");document.body.innerHTML=p+=B+"\\n"}setTimeout(z)')//m1k\

Ingo Bürk

Posted 2014-03-16T15:44:23.817

Reputation: 2 674

2If it's not your work, you should turn your answer into a community wiki. – Kyle Kanos – 2014-08-13T14:28:14.183

1Thanks for the tip, never noticed that checkbox before. Done! – Ingo Bürk – 2014-08-13T14:31:14.337