You are here

public function IpGeoLocAPI::centerOfLocations in IP Geolocation Views & Maps 8

Calculate the center of the supplied locations using one of two algorithms.

The first algorithm returns the center of the rectangle whose horizontal sides pass through the top and bottom locations in the set, while its vertical sides pass through the left-most and right-most locations.

The second algorithm returns the center of gravity of all supplied locations. The second algorithn is therefore sensitive to location clusters. This may be what you want, or it may be what you want to avoid.

Parameters

array $locations: Array of location objects each with latitude and longitude.

bool $center_of_gravity: If TRUE use the center of gravity algorithm.

Return value

array containing latitude and longitude of the center

File

src/Services/IpGeoLocAPI.php, line 609

Class

IpGeoLocAPI
Class IpGeoAPI to interact with other modules.

Namespace

Drupal\ip_geoloc\Services

Code

public function centerOfLocations(array $locations, $center_of_gravity = FALSE) {
  if (empty($locations)) {
    return [
      NULL,
      NULL,
    ];
  }
  if ($center_of_gravity) {

    // Because longitude turns back on itself, cannot simply average coords.
    $count = 0;
    $x = $y = $z = 0.0;
    foreach ($locations as $location) {
      if (isset($location->lon)) {
        $lng = $location->lon;
        $lat = $location->lat;
      }
      elseif (isset($location->longitude)) {
        $lng = $location->longitude;
        $lat = $location->latitude;
      }
      else {
        continue;
      }
      $lng = deg2rad($lng);
      $lat = deg2rad($lat);

      // Convert to Cartesian coords and total the 3 dimensions independently.
      $x += cos($lat) * cos($lng);
      $y += cos($lat) * sin($lng);
      $z += sin($lat);
      $count++;
    }
    $x /= $count;
    $y /= $count;
    $z /= $count;
    $center_lat = atan2($z, sqrt($x * $x + $y * $y));
    $center_lng = atan2($y, $x);
    return [
      rad2deg($center_lat),
      rad2deg($center_lng),
    ];
  }

  // Alternative method based on top & bottom lat and left & right lon.
  $top = $bottom = $left = $right = NULL;
  foreach ($locations as $location) {
    if (isset($location->lon)) {
      $lng = $location->lon;
      $lat = $location->lat;
    }
    elseif (isset($location->longitude)) {
      $lng = $location->longitude;
      $lat = $location->latitude;
    }
    else {
      continue;
    }
    if (!isset($top) || $lat > $top) {
      $top = $lat;
    }
    if (!isset($bottom) || $lat < $bottom) {
      $bottom = $lat;
    }
    if (!isset($left) || $lng < $left) {
      $left = $lng;
    }
    if (!isset($right) || $lng > $right) {
      $right = $lng;
    }
  }
  if (!isset($top) || !isset($left)) {
    return [
      NULL,
      NULL,
    ];
  }
  $center_lat = 0.5 * ($top + $bottom);
  $center_lng = 0.5 * ($left + $right);
  if ($right - $left > 180) {

    // If the angle between right and left is greater than 180, then averaging
    // is still ok, provided we flip over to the opposite end of the world.
    $center_lng = $center_lng > 0.0 ? $center_lng - 180.0 : $center_lng + 180.0;
  }
  return [
    $center_lat,
    $center_lng,
  ];
}