Skip to content

Instantly share code, notes, and snippets.

@ismailbaskin
Created February 18, 2017 14:00
Show Gist options
  • Save ismailbaskin/c57e31b15baf81ab8d7412d879072f03 to your computer and use it in GitHub Desktop.
Save ismailbaskin/c57e31b15baf81ab8d7412d879072f03 to your computer and use it in GitHub Desktop.
<?php
class PointSorter
{
/** @var SortablePoint[] */
private $points;
/** @var SortablePoint */
private $upperLeftPoint;
/**
* @param SortablePoint[] $points
* @return SortablePoint[]
*/
public function getSorted(array $points): array
{
$this->points = $points;
$this->upperLeftPoint = $this->findUpperLeftPoint($this->points);
usort($this->points, [$this, 'pointSort']);
return $this->points;
}
/**
* @param SortablePoint[] $points
* @return SortablePoint
*/
private function findUpperLeftPoint(array $points): SortablePoint
{
$top = $points[0];
for ($i = 1; $i < count($points); $i++) {
$temp = $points[$i];
if ($temp->getY() > $top->getY() || ($temp->getY() == $top->getY() && $temp->getX() < $top->getX())) {
$top = $temp;
}
}
return $top;
}
private function pointSort(SortablePoint $p1, SortablePoint $p2)
{
// Exclude the 'upper' point from the sort (which should come first).
if ($p1 == $this->upperLeftPoint) {
return -1;
}
if ($p2 == $this->upperLeftPoint) {
return 1;
}
// Find the slopes of 'p1' and 'p2' when a line is
// drawn from those points through the 'upper' point.
$m1 = $this->upperLeftPoint->slope($p1);
$m2 = $this->upperLeftPoint->slope($p2);
// 'p1' and 'p2' are on the same line towards 'upper'.
if ($m1 == $m2) {
// The point closest to 'upper' will come first.
return $p1->distance($this->upperLeftPoint) < $p2->distance($this->upperLeftPoint) ? -1 : 1;
}
// If 'p1' is to the right of 'upper' and 'p2' is the the left.
if ($m1 <= 0 && $m2 > 0) {
return -1;
}
// If 'p1' is to the left of 'upper' and 'p2' is the the right.
if ($m1 > 0 && $m2 <= 0) {
return 1;
}
// It seems that both slopes are either positive, or negative.
return $m1 > $m2 ? -1 : 1;
}
}
<?php
class SortablePoint
{
protected $x;
protected $y;
public function __construct(float $lat, float $lon)
{
$this->x = ($lon + 180) * 360;
$this->y = ($lat + 180) * 360;
}
public function getLat() : float {
return $this->y / 360 - 180;
}
public function getLon() : float {
return $this->x / 360 - 180;
}
public function distance(SortablePoint $point): float
{
return (($point->getX() - $this->getX()) ** 2 + ($point->getY() - $this->getY()) ** 2) ** 2;
}
public function getX(): float
{
return $this->x;
}
public function getY(): float
{
return $this->y;
}
public function slope(SortablePoint $point): float
{
return ($point->getY() - $this->getY()) / ($point->getX() - $this->getX());
}
}
<?php
$points = [
new SortablePoint(48.7771056, 9.1807688),
new SortablePoint(51.9226899, 4.4707867),
new SortablePoint(48.8566667, 2.3509871),
new SortablePoint(53.5538148, 9.9915752),
new SortablePoint(50.0878114, 14.4204598),
new SortablePoint(52.3738007, 4.8909347),
new SortablePoint(53.074981, 8.807081),
new SortablePoint(50.9580293, 1.8524129),
];
$manager = new PointSorter();
$sortedpoints = $manager->getSorted($points);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment