24
\$\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\$
  • \$\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 '16 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\$ – TheBikingViking Jul 5 '16 at 22:25
  • \$\begingroup\$ Yes, I forgot the first char. Thanks! \$\endgroup\$ – Luis Mendo Jul 5 '16 at 22:27
  • 3
    \$\begingroup\$ "Rather dangerous"? Mate, the real danger is once you get here. \$\endgroup\$ – Andrew Grimm Jul 6 '16 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\$ – TheBikingViking Jul 6 '16 at 20:12

14 Answers 14

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.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You can compress "NEWS" to ‘€Ã‘ or even '€Ã if lowercase is allowed. \$\endgroup\$ – Adnan Jul 7 '16 at 11:09
  • \$\begingroup\$ @Adnan: Nice! Didn't think of compressing since the string was so small already. \$\endgroup\$ – Emigna Jul 7 '16 at 11:13
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.

| improve this answer | |
\$\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
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ There's a typo in your explanation on the second to last line: bnase. \$\endgroup\$ – TheBikingViking Jul 31 '16 at 23:19
  • 1
    \$\begingroup\$ @TheBikingViking Thank you! Corrected \$\endgroup\$ – Luis Mendo Jul 31 '16 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.

| improve this answer | |
\$\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
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ @LuisMendo same as yours, but comma separated lists, and no quotes \$\endgroup\$ – busukxuan Jul 6 '16 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

| improve this answer | |
\$\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>
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ ah I just see @Neil had the same idea, but some shorter parametrizing \$\endgroup\$ – Titus Jul 6 '16 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"
| improve this answer | |
\$\endgroup\$
  • 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\$ – TheBikingViking Jul 6 '16 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.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ That is unfortunately verbose. \$\endgroup\$ – Steven H. Jul 5 '16 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 moving to codidact.com Jul 6 '16 at 3:30
  • \$\begingroup\$ Do note that this is C# 6.0 \$\endgroup\$ – Yytsi Jul 6 '16 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 moving to codidact.com Jul 6 '16 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 '16 at 11:40
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};}
| improve this answer | |
\$\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.

| improve this answer | |
\$\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).

| improve this answer | |
\$\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
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Befunge, 122 114 111 Bytes (110 characters)

~"N"-v
 v"N"_"S"
v>,&.&.&.~"W"-
_"E"v"W"
+:v >,&"´"\-&"Z"*&
v\_\.00..@.-\"<".-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

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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