Sam Wilson

Wikimedia user link badge Mastodon user link badge GitHub user link badge

Extracting coordinates from single 60 bit hex numbers

Thanks to the wonderful people on Stackoverflow, I’m making some progress with figuring out how some geographic information is saved in a DB2 database that I’m working with.

it turns out that, rather than using the spatial functionality of DB2 (which would make sense, but considering the source of this database, I wouldn’t ask for too much), coordinate pairs are being stored as Morton codes in a 16-byte Character column.

So far, I’ve sorted out decoding the coordinate values, and can shuffle points predictably on my test map; now I’m just trying to figure out what grid that these coordinates are on. I’m fairly certain (not that I know anything about these things) that it’s somehow related to the MGA grid, because 0 east is at almost exactly 114°, which is where MGA zone 50 starts (the ‘almost’ comes from my point-and-click measurement technique).

But I don’t know where I’m going next; I’ll leave it till the morning.


My test code for this has been in PHP, and I needed something to convert big numbers between bases; hadn’t realised that the BC math library didn’t include base_convert(), so I found the one below.

/**
 * Convert an arbitrary-length number between arbitrary bases.
 * Copied, and very slightly translated, from
 * http://www.technischedaten.de/pmwiki2/pmwiki.php?n=Php.BaseConvert
 *
 * @param $value
 * @param $from_base
 * @param $to_base
 * @return string
 */
function bc_base_convert($value, $from_base, $to_base)
{
	$valid_digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
	if (min($from_base, $to_base) < 2)
	{
		trigger_error('Bad Format min: 2', E_USER_ERROR);
	}
	if (max($from_base, $to_base) > strlen($valid_digits))
	{
		trigger_error('Bad Format max: ' . strlen($valid_digits), E_USER_ERROR);
	}
	$dezi = '0';
	$level = 0;
	$result = '';
	$value = trim(strval($value), "\r\n\t +");
	$sign = ('-' === $value{0}) ? '-' : '';
	$value = ltrim($value, "-0");
	$len = strlen($value);
	for ($i = 0; $i < $len; $i++)
	{
		$wert = strpos($valid_digits, $value{$len - 1 - $i});
		if (FALSE === $wert)
		{
			trigger_error('Bad Char in input 1', E_USER_ERROR);
		}
		if ($wert >= $from_base)
		{
			trigger_error('Bad character in input 2', E_USER_ERROR);
		}
		$dezi = bcadd($dezi, bcmul(bcpow($from_base, $i), $wert));
	}
	if ($to_base == 10)
	{
		return $sign . $dezi; // Shortcut for base 10
	}
	while (1 !== bccomp(bcpow($to_base, $level++), $dezi));
	for ($i = $level - 2; $i >= 0; $i--)
	{
		$factor = bcpow($to_base, $i);
		$zahl = bcdiv($dezi, $factor, 0);
		$dezi = bcmod($dezi, $factor);
		$result .= $valid_digits{$zahl};
	}
	$result = empty($result) ? '0' : $result;
	return $sign . $result;
}