23
\$\begingroup\$

Background

Countless generations of children have wondered where they would end up if they dug a hole directly downwards. It turns out that this would, unsurprisingly, be rather dangerous, but anyway...

Antipodes are points that are directly opposite each other on the Earth's surface. This means that if a line was drawn between the two points, it would pass through the centre of the Earth.

Challenge

Write a program or function that, given a point, finds its antipode.

In this challenge, points are represented using the longitude-latitude system and degrees, arc minutes and arc seconds. To find the antipode, swap the directions of each ordinate (N <-> S and W <-> E) and subtract the longitude ordinate from 180 degrees.

Example:
Take the point N 50 26 23 W 4 18 29. Swap the directions to give S 50 26 23 E 4 18 29. Subtract the longitude ordinate from 180 0 0 to give 175 41 31, leaving the antipode coordinates as S 50 26 23 E 175 41 31.

Rules

Input

A set of latitude-longitude coordinates, in any reasonable format, where each ordinate contains a direction, a number of degrees, a number of arc minutes, and a number of arc seconds.

Output

The latitude-longitude coordinates of the antipode, in any reasonable format, where each ordinate contains a direction, a number of degrees, a number of arc minutes, and a number of arc seconds.

Take reasonable to mean that each part of the coordinate can be distinguished unambiguously.

Specs

  • The direction for the latitude ordinate is N or S, and that for the longitude ordinate is W or E.
  • All coordinate values are integers. The degree value will be between 0 and 90 for latitude, and between 0 and 180 for longitude. The arc minute and arc second values for both ordinates will be between 0 and 59.
  • If all the values for an ordinate are 0, either direction is acceptable.
  • There is no need to zero-pad any values.
  • No latitude ordinate will ever be larger than 90 degrees, and no longitude ordinate will ever be larger than 180 degrees.
  • Standard loopholes apply.

Test cases

N 50 26 23 W 4 18 29 -> S 50 26 23 E 175 41 31

S 43 9 9 E 0 0 5     -> N 43 9 9 W 179 59 55

N 0 0 0 E 0 0 0      -> S/N 0 0 0 W/E 180 0 0 (either direction fine in each case)

S 1 2 3 W 4 5 6      -> N 1 2 3 E 175 54 54

S 9 21 43 W 150 7 59 -> N 9 21 43 E 29 52 1

S 27 40 2 W 23 0 0   -> N 27 40 2 E 157 0 0

N 0 58 37 W 37 0 0   -> S 0 58 37 E 143 0 0

Useful links

This is , so the shortest answer in bytes wins!

\$\endgroup\$
8
  • \$\begingroup\$ Is this a reasonable format? Four inputs: array of 3 numbers, char, array of three numbers; separated by newlines \$\endgroup\$
    – Luis Mendo
    Jul 5, 2016 at 22:02
  • \$\begingroup\$ @LuisMendo Unless I am misunderstanding you, I can only see three inputs; I assume you would want an extra char? I would say that that is a reasonable format. By reasonable, I essentially mean that each part of the coordinate can be distinguished unambiguously. \$\endgroup\$ Jul 5, 2016 at 22:25
  • \$\begingroup\$ Yes, I forgot the first char. Thanks! \$\endgroup\$
    – Luis Mendo
    Jul 5, 2016 at 22:27
  • 3
    \$\begingroup\$ "Rather dangerous"? Mate, the real danger is once you get here. \$\endgroup\$ Jul 6, 2016 at 4:05
  • 1
    \$\begingroup\$ @busukxuan Apologies for not responding sooner. I would say that both of those formats are not reasonable, since the spec requires N, S, E, or W as a direction, while the redundant 0 introduces ambiguity as to which value represents which component of the ordinate. \$\endgroup\$ Jul 6, 2016 at 20:12

14 Answers 14

6
\$\begingroup\$

JavaScript (ES6), 88 bytes

(a,b,c,d,e,f,g,h)=>[a<'S'?'S':'N',b,c,d,e<'W'?'W':'E',!(g|h)+179-f,g|h&&!h+59-g,h&&60-h]

Takes 8 parameters and returns an array as the result. Expects a to be one of N or S and similarly e to be one of W or E. Calculations:

  • f needs to be subtracted from 179 if either g or h is nonzero but 180 if both g and h are zero (because there is no borrow), thus !(g|h) is added to the 179.
  • g needs to be zero if both g and h are zero, thus g|h&&, otherwise it needs to be subtracted from 59 if h is nonzero but 60 if h is zero (because there is no borrow), thus !h is added to the 59.
  • h needs to be zero if it was already zero, otherwise it is simply subtracted from 60.

Another way to look at this is to notice that subtracting in binary is achieved by adding the 1s complement plus an additional 1. Translated to this problem, we take 179-f, 59-g and 59-h and add 1. 59-h + 1 is 60-h, but if this is 60 then it carries, so the desired result is zero if h was originally zero. We add 1 to 59-g if there's a carry from h, i.e. if h was originally zero. Again we have to allow for a carry, which this time happens if both g and h are zero, and we add 1 to 179-f in this case.

\$\endgroup\$
6
\$\begingroup\$

MATL, 51 bytes

'NS'tPXEii'WE'tPXE648e3i60:qXJZA-JYAtn3-?2:&)wJZAwh

Input format:

'N'
[50 26 23]
'W'
[4 18 29]

Output format:

S
50 26 23
E
175  41  31

Try it online!

Explanation

'NS'tPXE     % Take input (string) implicitly. Exchange 'N' and 'S'
i            % Take input (array)
i'WE'tPXE    % Take input (string). Exchange 'W' and 'E'
648e3        % Push 648000
i            % Take input (array)
60:qXJ       % Push [0 1 ... 59]. Copy into clipboard J
ZA           % Convert second input array from base 60 to decimal
-            % Subtract
JYA          % Convert to base 60
tn3-?        % If length exceeds 3
  2:&)       %   Split into first two elements and then the rest
  w          %   Swap
  JZA        %   Convert from base 60 to decimal
  wh         %   Swap, concatenate
\$\endgroup\$
2
  • \$\begingroup\$ There's a typo in your explanation on the second to last line: bnase. \$\endgroup\$ Jul 31, 2016 at 23:19
  • 1
    \$\begingroup\$ @TheBikingViking Thank you! Corrected \$\endgroup\$
    – Luis Mendo
    Jul 31, 2016 at 23:27
4
\$\begingroup\$

Racket, 199 bytes

(λ(l)(cons(cons(if(eq?(caar l)'n)'s'n)(cdar l))(cons(if(eq?(cadr l)'e)'w'e)(list (- 180(+(caddr l)(sgn(foldl + 0(cdddr l)))))(modulo(- 60(+(fourth l)(sgn(fifth l))))60)(modulo(- 60(fifth l))60)))))

That's horribly long. There's probably some things I could do to shorten it further, but shudders I'm completely done.

Takes a cons pair of two lists: one for the latitude and one for the longitude. Each list has the direction (as a lowercase Racket symbol) as its first item and following it the degrees, arc-minutes, and arc-seconds. Outputs in the same format

Racket will interpret this pair of two lists as a single list with another list as its first element. This is perfectly fine, since you can still access both latitude and longitude as if they were two lists in a pair.

Usage:

>    (
     (λ(l)(cons(cons(if(eq?(caar l)'n)'s'n)(cdar l))(cons(if(eq?(cadr l)'e)'w'e)(list (- 180(+(caddr l)(sgn(foldl + 0(cdddr l)))))(modulo(- 60(+(fourth l)(sgn(fifth l))))60)(modulo(- 60(fifth l))60)))))
     (cons (list 's 43 9 9) (list 'e 0 0 5)))
'((n 43 9 9) w 179 59 55)

Which can be interpreted by future code as '((n 43 9 9) (w 179 59 55)), two lists.

\$\endgroup\$
4
\$\begingroup\$

Pyth, 41 45 43 35 bytes

K60-"NS"ww-"EW"wA.D-*3^K3iEKK+.DGKH

Uses base-60 conversion to turn degrees and minutes into seconds.

I/O format:

N
[1,2,3]
E
[4,5,6]

It prints a line every time you input a line, so to have a good-looking format, you can either use the CLI and pipe the input, or more conveniently, use the online Pyth implementation.

In pseudocode:

K60                                              K = 60
   -"NS"w                                        "NS".remove(input())
         w                                       print(input())
          -"EW"w                                 "EW".remove(input())
                A.D                              G,H = divmod(
                   -*3^K3                          3*K**3 -
                         iEK                         baseToDec(input(),K)),
                            K                      K)
                             +.DGKH              divmod(G,K)+H
\$\endgroup\$
1
  • \$\begingroup\$ @LuisMendo same as yours, but comma separated lists, and no quotes \$\endgroup\$
    – busukxuan
    Jul 6, 2016 at 0:35
4
\$\begingroup\$

Python 2, 140 122 bytes

Updated:

def f(c):x=([180,0,0],[179,59,60])[1<sum(c[6:8])];print['NS'['N'in c]]+c[1:4]+['EW'['E'in c]]+map(lambda x,y:x-y,x,c[5:8])

This uses a slightly different approach: setting the values to subtract from the longitude based on the minutes and seconds. If there are 0 mins and seconds, it subtracts 180 from the degrees, and if there are >0 mins and secs, it subtracts 179, 59, and 60 from the d, m, s respectively.

Original:

def f(c):v=divmod;m,s=v((180-(c[5]+c[6]/60.+c[7]/3600.))*3600,60);d,m=v(m,60);print['NS'['N'in c]]+c[1:4]+['EW'['E'in c]]+map(round,[d,m,s])

Takes input as a list: f(['N', 0, 0, 0, 'E', 0, 0, 0]), converts the longitude to decimal degrees, subtracts from 180, then converts back to degrees, minutes, seconds and re-constructs the list, flipping the directions in the process.

Un-golfed:

def f(c):
    minutes,seconds=divmod((180-(c[5]+c[6]/60.+c[7]/3600.))*3600,60)
    degrees,minutes=divmod(minutes,60)
    print ['NS'['N'in c]]+c[1:4]+['EW'['E'in c]]+map(round,[degrees,minutes,seconds])

Try it

\$\endgroup\$
3
\$\begingroup\$

JavaScript, 138 137 129 124 112 110

2 bytes improvement inspired by @Neil´s code

f=a=>a.map((v,i,a)=>i%4?i>6?v&&60-v:i>5?(59-v+!a[7])%60:i>4?179-v+!(a[7]|a[6]):v:'NEWS'[3-'NEWS'.indexOf(v)]);
  • input = output = array containing 2x(1 uppercase char and 3 int)
  • tested in Firefox

ungolfed

f=a=>a.map((v,i,a)=>
i%4?
    i>6?v&&60-v              // 7: invert seconds - old: (60-v)%60
    :i>5?(59-v+!a[7])%60     // 6: invert minutes, increase if seconds are 0
    :i>4?179-v+!(a[7]|a[6])  // 5: invert degrees, increase if seconds and minutes are 0
    :v                       // 1,2,3: unchanged
:'NEWS'[3-'NEWS'.indexOf(v)] // 0,4: swap directions
);

tests

<table id=out border=1><tr><th>in</th><th>out<th>expected</th><th>ok?</th></tr></table>
<script>
addR=(r,s)=>{var d=document.createElement('td');d.appendChild(document.createTextNode(s));r.appendChild(d)}
test=(x,e,f)=>{var y,r=document.createElement('tr');addR(r,x);addR(r,y=('function'==typeof f)?f(x):f);addR(r,e);addR(r,e.toString()==y.toString()?'Y':'N');document.getElementById('out').appendChild(r)}

samples=[
'N',50,26,23,'W',4,18,29,   'S',50,26,23,'E',175,41,31,
'S',43,9,9,'E',0,0,5,       'N',43,9,9,'W',179,59,55,
'N',0,0,0,'E',0,0,0,        'S',0,0,0,'W',180,0,0,
'S',1,2,3,'W',4,5,6,        'N',1,2,3,'E',175,54,54,
'S',9,21,43,'W',150,7,59,   'N',9,21,43,'E',29,52,1,
'S',27,40,2,'W',23,0,0,     'N',27,40,2,'E',157,0,0,
'N',0,58,37,'W',37,0,0,     'S',0,58,37,'E',143,0,0,
];
while (samples.length)
{
    x=samples.splice(0,8);
    e=samples.splice(0,8);
    test(x,e,h);
    test(e,x,h);
}
</script>
\$\endgroup\$
1
  • \$\begingroup\$ ah I just see @Neil had the same idea, but some shorter parametrizing \$\endgroup\$
    – Titus
    Jul 6, 2016 at 15:19
2
\$\begingroup\$

Python 3, 131 130 bytes

def f(w,x,y,z):S=60;d=divmod;a,b,c=y;l,m=d(648e3-c-S*b-S*S*a,S);return w,"N" if x=="S" else "S",d(l,S)+(m,),"E" if z=="W" else "W"

Formats: angles are tuples of the form (deg,min,sec), directions are of the form N. Outputs a quadruple of 2 angles, each followed by its direction.

Ungolfed version:

def f(latitude,NS,longitude,EW):
    degree,minute,second=longitude
    minute,second=divmod(648000-second-60*minute-60*60*degree,60)
    return latitude, "N" if NS=="S" else "S", divmod(minute,60)+(second,), "E" if EW=="W" else "W"
\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can get this down to 120 bytes by doing def f(w,x,y,z):S=60;d=divmod;a,b,c=y;l,m=d(648e3-c-S*b-S*S*a,S);return w,["S","N"][x=="S"],d(l,S)+(m,),["W","E"][z=="W"] This works by using the fact that True and False can be interpreted as 1 and 0 , and hence a if b else c is equivalent to [c, a][b]. On a side note, I think that your original code has a typo; should x=="W" be z=="W"? \$\endgroup\$ Jul 6, 2016 at 19:31
2
\$\begingroup\$

C#, 310 269 bytes

float[]t(string[]a,int n)=>a.Skip(n).Take(3).Select(float.Parse).ToArray();string A(string i){var s=i.Split(' ');var w=t(s,5);float a=180-(w[0]+w[1]/60+w[2]/3600),b=a%1*60;return(s[0][0]>82?"N":"S")+$" {string.Join(" ",t(s,1))} {(s[4][0]<70?'W':'E')} {a} {b} "+b%1*60;}

The input is one string. You can try it on .NetFiddle.

Code

float[]t(string[]a,int n)=>a.Skip(n).Take(3).Select(float.Parse).ToArray();
string A(string i) {
    var s=i.Split(' ');var w=t(s,5);float a=180-(w[0]+w[1]/60+w[2]/3600),b=a%1*60;
    return (s[0][0]>82?"N":"S")
        +$" {string.Join(" ",t(s,1))} {(s[4][0]<70?'W':'E')} {a} {b} "+b%1*60;
}

If I don't take a string as input but a char, float[], char, float[], I can do:

C#, 167 166 165 163 152 148 147 139 bytes

(s,n,e,w)=>{float a=180-(w[0]+w[1]/60+w[2]/3600),b=a%1*60;return(s>82?"N":"S")+$" {string.Join(" ",n)} {(e<70?'W':'E')} {a} {b} "+b%1*60;};

Code

(s,n,e,w) => {
    float a=180-(w[0]+w[1]/60+w[2]/3600),b=a%1*60;
    return(s>82?"N":"S")+$" {string.Join(" ",n)} {(e<70?'W':'E')} {a} {b} "+b%1*60;
};

Also I can remove the 3600 and use instead to move to 164 characters and 166 bytes. Should I use it?


C#, 150 bytes

(s,n,m,p,e,w,x,y)=>{var t=new TimeSpan(180,0,0)-new TimeSpan(w,x,y);return(s>82?"N":"S")+$" {n} {m} {p} {(e<70?'W':'E')} {t.TotalHours} {t:%m\\ s}";};

Code

(s,n,m,p,e,w,x,y) => {
    var z=new TimeSpan(180,0,0)-new TimeSpan(w,x,y);
     return(s>82?"N":"S")+$" {n} {m} {p} {(e<70?'W':'E')} {z.TotalHours} {z:%m\\ s}";
};

A more .NET way! I delegate all the logic to the .NET struct TimeSpan and I abuse the string formatting logic. Input is char, int, int, int, char, int, int, int. I share this one to give some ideas. Maybe someone will improve in a better way than me.

\$\endgroup\$
7
  • \$\begingroup\$ That is unfortunately verbose. \$\endgroup\$
    – Steven H.
    Jul 5, 2016 at 23:41
  • \$\begingroup\$ @Fawful I post another version with different arguments. Less verbose isn't? I still can't get rid of string.Join()... \$\endgroup\$
    – aloisdg
    Jul 6, 2016 at 3:30
  • \$\begingroup\$ Do note that this is C# 6.0 \$\endgroup\$
    – Yytsi
    Jul 6, 2016 at 9:12
  • \$\begingroup\$ @TuukkaX since it is the lastest version of C#, I didn't specify it. I will if I use some un release feature like C#7 feature. \$\endgroup\$
    – aloisdg
    Jul 6, 2016 at 11:11
  • 1
    \$\begingroup\$ Well, I don't see a return, I had to add it myself... Using your test a('N', new float[] { 50, 26, 23 }, 'W', new float[] { 4, 18, 29 }) I get S 50 26 23 E 175.6919 41.51642 30.98511 - would you consider this a pass? \$\endgroup\$
    – onedaywhen
    Jul 6, 2016 at 11:40
1
\$\begingroup\$

05AB1E, 37 34 bytes

`60©3L<Rm*O3°648*s-®‰`s®‰s‚˜)'€Ã‡

Explained

`                                      # split input to 4 lines with longitude on top
 60©3L<Rm*O                            # convert longitude to seconds
            3°648*                     # longitude 180 0 0 in seconds
                  s-                   # subtract our longitude from this
                    ®‰`                # get seconds part
                       s®‰             # get minute and degrees part
                          s‚˜)         # join to list
                              '€Ã‡    # translate directions

Try it online

Edit: Saved 3 bytes thanks to Adnan.

\$\endgroup\$
2
  • \$\begingroup\$ You can compress "NEWS" to ‘€Ã‘ or even '€Ã if lowercase is allowed. \$\endgroup\$
    – Adnan
    Jul 7, 2016 at 11:09
  • \$\begingroup\$ @Adnan: Nice! Didn't think of compressing since the string was so small already. \$\endgroup\$
    – Emigna
    Jul 7, 2016 at 11:13
0
\$\begingroup\$

Java, 199 177 or 151 or 134 Bytes

177 Bytes solution which prints result

void t(int...a){int i=648000-(a[5]*3600+a[6]*60+a[7]);System.out.println(a[0]=='N'?"S":"N"+" "+a[1]+" "+a[2]+" "+a[3]+" "+(a[4]=='E'?"W":"E")+" "+i/3600+" "+i/60%60+" "+i%60);}

151 Bytes solution which returns result as array

Object[]y(int...a){int i=648000-(a[5]*3600+a[6]*60+a[7]);return new Object[]{a[0]=='N'?"S":"N",a[1],a[2],a[3],a[4]=='E'?"W":"E",i/3600,i/60%60,i%60};}

134 Bytes if we used lambda notation

a->{int i=648000-a[5]*3600-a[6]*60-a[7];return new Object[]{a[0]=='N'?"S":"N",a[1],a[2],a[3],a[4]=='E'?"W":"E",i/3600,i/60%60,i%60};}
\$\endgroup\$
0
\$\begingroup\$

C, 188

main(int c,char**v){int n=*v[1]==78?83:78,e=*v[5]==69?87:69,b=648000-(atoi(v[6])*3600+atoi(v[7])*60+atoi(v[8]));printf("%c %s %s %s %c %d %d %d\n",n,v[2],v[3],v[4],e,b/3600,b/60%60,b%60);}

Requires 8 program arguments, will fail otherwise. Pass arguments like N 50 26 23 W 4 18 29. Direction indicators N, S, E, W, must be capitalized.

\$\endgroup\$
0
\$\begingroup\$

PostGIS, 123 bytes

The Tool-For-The-Job is disappointingly verbose, partly because of SQL wordiness and partly due to the need to cast to geometry and back:

CREATE FUNCTION f(p geography)
RETURNS geography
AS 'SELECT ST_Affine(p::geometry,1,0,0,-1,180,0)::geography' LANGUAGE sql;

On the other hand, this single function will transform points, collections of points, or more complex shapes (such as the entire coastline of Australia and New Zealand).

\$\endgroup\$
0
\$\begingroup\$

PHP, 148 bytes

same approach as my JavaScript answer, look there for a breakdown

  • +16 function overhead
  • +17 for $ signs
  • +12 for parantheses (different operator precedence)
  • +4 for foreach because PHP´s array_map has a huge overhead
  • +1 for (60-$v)%60 because $v&&60-$v casts to boolean in PHP
  • -8 by working on the original
  • -4 by using chr, ord and some arithmetics on the direction swap

function f(&$a){foreach($a as$i=>$v)$a[$i]=$i%4?($i>6?(60-$v)%60:($i>5?(59-$v+!$a[7])%60:($i>4?179-$v+!($a[7]|$a[6]):$v))):chr(($i?156:161)-ord($v));}
  • function works on original
  • format: array with 2x (1 uppercase char, 3 int)

tests

function out($a){if(!is_array($a))return$a;$r=[];foreach($a as$v)$r[]=out($v);return'['.join(',',$r).']';}
function cmp($a,$b){if(is_numeric($a)&&is_numeric($b))return 1e-2<abs($a-$b);if(is_array($a)&&is_array($b)&&count($a)==count($b)){foreach($a as $v){$w = array_shift($b);if(cmp($v,$w))return true;}return false;}return strcmp($a,$b);}
$samples=[
N,50,26,23,W,4,18,29,   S,50,26,23,E,175,41,31,
S,43,9,9,E,0,0,5,       N,43,9,9,W,179,59,55,
N,0,0,0,E,0,0,0,        S,0,0,0,W,180,0,0,
S,1,2,3,W,4,5,6,        N,1,2,3,E,175,54,54,
S,9,21,43,W,150,7,59,   N,9,21,43,E,29,52,1,
S,27,40,2,W,23,0,0,     N,27,40,2,E,157,0,0,
N,0,58,37,W,37,0,0,     S,0,58,37,E,143,0,0,
];
while ($samples)
{
    $xx=$x=array_splice($samples,0,8);
    $ee=$e=array_splice($samples,0,8);
    func($x); test($xx,$ee,$x);
    func($e); test($ee,$xx,$e);
}

abandoned ideas

  • no loop approach with carry flags: +5
  • loop backwards with carry flags: +7
  • I could turn this into a program for PHP<5.4, but never mind: -3
\$\endgroup\$
0
\$\begingroup\$

Befunge, 122 114 111 Bytes (110 characters)

~"N"-v
 v"N"_"S"
v>,&.&.&.~"W"-
_"E"v"W"
+:v >,&"´"\-&"Z"*&
v\_\[email protected]\"<".-1\ <
>1-.:"Z"/"<"\-\"Z"%:#^_\..@

Input format:

N 50 26 23W 4 18 29

Notice the longitude direction and the arc seconds must be huddled together

You may test the code here

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.