Usability

GEO-
LOca
tion

How the geolocation can improve ux?

Geolocation is widely used on the Internet to improve the usability of websites and gather information about the user’s location. It identifies the user’s geographic location and minimizes the need to ask for their country in forms or to display results that they search for, such as nearby restaurants, hotels and friends.

Geolocation is used in Responsive Checkout to detect the user’s country and fill it in automatically. Country auto-detection is based on the user’s IP address. Of course, auto-detection and auto-completion are possible only when the user’s country is supported. To detect their country, we use the GeoIP MaxMind database, which contains information about over 90,000 IP ranges. All data is stored in a file in the following format:

121.255. 0. 0|121.255.255.255|CN

122. 0. 0. 0|122. 0. 7.255|TH

As shown, each row contains three values: the beginning and end of the IP range and the code of the country matching the specified range. All of the inputs in the file are sorted in ascending order, based on the ranges, and no two ranges will share an IP address (i.e. they are distinguishable as sets). Moreover, the four numbers in the address are aligned with each other, with spaces in between; this enables all rows to be saved with a constant number of bytes (34 bytes).

Thanks to this way of saving data, we’re able to use binary search to find the desired range, even though the size of the file is a bit bigger. The other solution would be to keep the data in an SQL database. 

For the over 90,000 entries in the database, the search function finds the desired range (or finds that there is no match), using a maximum of 16 reading sessions from the file. Because the IP addresses are stored in the form of ranges, writing a function to allow them to be compared was inevitable (as shown below). This function can return one of four values, according to whether the searched IP is below, above, within or outside of the range:

/**
* Compare range with ipv4 and return 1, 0, 1 or null.
*
* @param $ipv4
* @param $data
* @return mixed
*/

private function compareRange($ipv4, $data) {
  	$ipv4From = $data['ipv4_from'];
  	$ipv4To = $data['ipv4_to'];

  	// Compare results
  	$resultFrom = strcmp($ipv4, $ipv4From);
  	$resultTo = strcmp($ipv4, $ipv4To);

  	// Check if specified ipv4 is lower than current range.
  	if ($resultFrom < 0) {
	  	return -1;
  	}

  	// Check if specified ipv4 is greater than current range.
  	if ($resultTo > 0) {
	  	return 1;
  	}

  	// Check if specified ipv4 is in range.
  	if ($resultFrom >= 0 && $resultTo <= 0) {
	 	 return 0;
  	}

  	// Not in range - fail!
  	return null;
}

The search function is classic binary, with the index dividing the range into half:

 /**
* Binary search of area code in range [$minIndex, $maxIndex].
*
* @param $file
* @param $ipv4
* @param $minIndex
* @param $maxIndex
* @return int|null
*/
private function findInFile($file, $ipv4, $minIndex, $maxIndex) {
	// If range is empty, ipv4 is not found and return null.
	if ($minIndex > $maxIndex) {
		return null;
	}

	// Calculate new index and get data.
 	$index = ($minIndex + $maxIndex) >> 1;
  	$data = $this->readData($file, $index);

	// Compare ipv4 with current data range
	$compare = $this->compareRange($ipv4, $data);

	// If we get null, ip not in any range.
  	if (is_null($compare)) {
	 	 return null;
  	}

	// Ipv4 < range in current record.
  	if ($compare < 0) {
	  	return $this->findInFile($file, $ipv4, $minIndex, $index - 1);
  	}

	// Ipv4 > range in current record.
  	if ($compare > 0) {
	  	return $this->findInFile($file, $ipv4, $index + 1, $maxIndex);
  	}

	// Ipv4 in range!
 	 return $data;
}
List of articles