Skip to content

Instantly share code, notes, and snippets.

@lth2h
Last active April 20, 2016 15:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lth2h/1251391586dbf1bad7c3 to your computer and use it in GitHub Desktop.
Save lth2h/1251391586dbf1bad7c3 to your computer and use it in GitHub Desktop.
#!/usr/bin/perl
use strict;
use warnings;
my $debug = 1;
## GC code is
# http://support.groundspeak.com/index.php?pg=kb.page&id=221
# How are they generated?
# Each geocache listing is assigned a number based on the order in which it is posted - e.g. Beverly, one of the oldest geocaches, is number 40. Originally, the geocaching.com database translated this number into a hexadecimal code (1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10) with a "GC" tagged onto the front (GC28 for Beverly).
# Sometime around April 2003, the database reached ID=65535 or GCFFFF - the maximum of four digit hexadecimal. The programmer's used the solution of changing to a base 31 code: 0-9, A-Z with some characters left out. The waypoints were originally limited to six characters because most GPS units only allow six characters per waypoint. In December 2006, the database hit 512401 geocache records, which meant that they had already had GCZZZZ, the maximum geocache under the base 31 method. The programmer's solution was that next geocache would be GC10000 (seven digits). That geocache is now a memorial to the rollover.
# All that is simply very interesting trivia as all you really need to know is that the GC***** is a unique code:a way to identify the individual geocaches.
# http://forums.groundspeak.com/GC/index.php?showtopic=292618
# What I found out for now:
# 0 - 65535 GC0 - GCFFFF (Hexadecimal, 4 digits)
# 65536 - 512400 GCG000 - GCZZZZ (base31, 4 digits)
# 512401 - 28218031 GC10000 - GCZZZZZ (base31, 5 digits)
# jholly, on 21 March 2012 - 02:36 PM, said:
# The characters used for the base-31 conversion are 0123456789ABCDEFGHJKMNPQRTVWXYZ (the letters ILOSU are omitted)
# Ah ok, this is the information I need! Thank you very much! :-) I didn't find it with google...
## SOOOOO....
# 0 0
# 1 1
# 2 2
# 3 3
# 4 4
# 5 5
# 6 6
# 7 7
# 8 8
# 9 9
# a 10
# b 11
# c 12
# d 13
# e 14
# f 15
# g 16
# h 17
# j 18
# k 19
# m 20
# n 21
# p 22
# q 23
# r 24
# t 25
# v 26
# w 27
# x 28
# y 29
# z 30
# Write out the hexadecimal in its expanded form. For example, 1A2B3C becomes 1*16^5+10*16^4+2*16^3+11*16^2+3*16^1+12*16^0.
# GC
# YCT4 => 465130 --
# GC
# Y 29*31^3 = 863939
# C 12*31^2 = 11532
# T 25*31^1 = 775
# 4*31^0 = 4
########### 876250 - 411120 = 465130
# GC4YT8Y = 4171205
#
# 4 4*31^4 3694084
# Y 29*31^3 863939
# T 25*31^2 24025
# 8 8*31^1 248
# Y 29*31^0 29
############## 4582325 - 411120 = 4171205
# GC590DT = 4475032
#
# 5 5*31^4 4617605
# 9 9*31^3 268119
# 0 0*31^2 0
# D 13*31^1 403
# T 25*31^0 25
############## 4886152 - 411120 = 4475032
## GCG123 = 66562
# G 16*31^3 476656
# 1 1*31^2 961
# 2 2*31^1 62
# 3 3*31^0 3
############## 477682 - 411120 = 66562
# GCFFFF 65535
# 4 digit below G000 => hex
# hex(FFFF) = 65535
# F 15*16^3
# F 15*16^2
# F 15*16^1
# F 15*16^0
# 65535 == 65535
# http://geolex.locusprime.net/
# GCxxxx – The GCxxxx waypoint identifier is just that, an identifier. It has little other significance, beyond acting as a unique common identifier. Some GPSr units can only accept waypoints of six characters or less, so the generated ID has a maximum length of six. The GCxxxx ID is actually derived from the sequential ID number assigned to each cache page when it's submitted for publishing. The first 2 characters are always “GC”. Originally, the next four characters were created by simply converting the ID number to base-16. But it became apparent that that scheme couldn't create enough IDs for the fast growing sport. In April of 2003, a new scheme was put into place. All cache IDs after CGFFFF would be created using a base-31 scheme. The characters used for the base-31 conversion are 0123456789ABCDEFGHJKMNPQRTVWXYZ (the letters ILOSU are omitted). When converting from a base-31 waypoint ID back to its original sequential ID, you have to subtract 411120, to account for the value change between GCFFFF under the old system, and GCG000 under the new system. Update: When the end of the 6 character identifiers (GCZZZZ) was approaching, it was decided to scrap the 6 character limit. At that point, many of the newer models of GPSrs could handle longer waypoint names, and off-site software existed to allow users to customize their waypoints to accommodate their particular GPS unit.
use Data::Dumper;
my $offset = 411120;
my @base31;
foreach (qw(0 1 2 3 4 5 6 7 8 9 A B C D E F G H J K M N P Q R T V W X Y Z)) {
push (@base31, $_);
}
my %decimal;
@decimal{@base31} = (0..$#base31); #magic from http://www.perlmonks.org/?node_id=75723
chomp(my $gc = uc($ARGV[0]));
# chomp($gc);
die(usage()) unless (length($gc) > 0);
$gc =~ s/^[gGcC]{2}//;
if (!($gc=~ m/[0123456789ABCDEFGHJKMNPQRTVWXYZ]{1,5}/)) {
die("$gc must be between 1 and 5 characters and cannot contain the letters I,L,O,S, or U.\n");
}
print "GC: $gc\n" if $debug;
if (length($gc) == 5) {
print "Lenght 5, base31\n" if $debug;
print convert_base31();
print "\n";
exit;
} elsif (length($gc) <4) {
print "Lenght less than 4, hex\n" if $debug;
print convert_hex();
print "\n";
exit;
}
print "Must be lenght 4, either hex or base31\n" if $debug;
# lenght 4 could be either hex or base31
if ($gc =~ m/^[0123456789ABCDEF]{4}$/i) {
print "has only hex chars, guess it is hex\n" if $debug;
print convert_hex();
print "\n";
exit;
} else {
print "has non-hex chars, must be base31\n" if $debug;
print convert_base31();
print "\n";
exit;
}
print "Something went wrong\n";
sub convert_hex {
my $rv = hex($gc);
return $rv;
}
sub convert_base31 {
my $rv;
my $reverse = reverse($gc);
print "REVERSE: $reverse\n" if $debug;
my @chars = split("", $reverse);
for (my $i=0; $i <= $#chars; $i++) {
if ($debug) {
print "I: $i, char:" . $chars[$i] . "\n";
print $decimal{$chars[$i]} . "*31**$i = ";
print $decimal{$chars[$i]} * (31**$i);
print "\n" if $debug;
}
$rv += $decimal{$chars[$i]}*(31**$i);
}
print "RV: $rv\n" if $debug;
#can't return until we subtract the offset
$rv -= $offset;
return $rv;
}
sub usage {
print "$0 <GC code>\n";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment