You are here

class GmapPolylineToolbox in GMap Module 7.2

Hierarchy

Expanded class hierarchy of GmapPolylineToolbox

2 files declare their use of GmapPolylineToolbox
gmap_polyutil.inc in ./gmap_polyutil.inc
Encoded polyline utilities.
oopmigration.test in tests/oopmigration.test
unit tests for refactoring steps

File

lib/Drupal/gmap/GmapPolylineToolbox.php, line 19
Contains GmapPolylineToolbox.php Encoded polyline utilities.

Namespace

Drupal\gmap
View source
class GmapPolylineToolbox {
  const GMAP_DP_EPSILON = 1.0E-5;
  const GMAP_ZOOM_LEVELS = 18;
  const GMAP_ZOOM_FACTOR = 2;
  private $latlonNumber;
  private $latlonLevels;
  private $encodedlatlonNumber;
  private $encoded;
  private $startPoint;
  private $endPoint;
  private $distance;
  private $measurePoint;

  /**
   * @var array
   */
  private $points;

  /**
   * @var array
   */
  private $pointWeights;
  private $weight;
  protected static $levels;

  /**
   * @var static Singleton instance
   */
  protected static $gmapInstance;

  /**
   * Do not change.
   */
  private function __construct() {
    self::$levels = $this
      ->getZoomLevels();
  }

  /**
   * Getting instance.
   * @return array
   *   GmapPolylineToolbox SingleTon instance
   */
  public static function getInstance() {
    if (is_null(self::$gmapInstance)) {
      self::$gmapInstance = new self();
    }
    return self::$gmapInstance;
  }

  /**
   * The following  methods will encode numbers.
   *
   * So that they may be used in "Encoded Polylines" on Google Maps.
   * The encoding is described here:
   *   http://code.google.com/apis/maps/documentation/polylinealgorithm.html
   *
   * Numbers for latitudes/longitudes and levels are encoded slightly
   * differently--when generating Encoded Polylines, latitudes and
   * longitudes are encoded with gmap_polyutil_encode_signed(),
   * and "levels" are encoded using gmap_polyutil_encode_unsigned().
   *
   * former gmap_polyutil_encode_latlon($x)
   */
  public function setLatLonNumber($x) {
    $this->latlonNumber = $x;
    return $this;
  }

  /**
   * Former gmap_polyutil_encode_latlon($x).
   *
   * @return string
   *   LatLon.
   */
  public function getEncodedLatLon() {
    $this->latlonNumber = round($this->latlonNumber * 100000.0) << 1;
    if ($this->latlonNumber < 0) {
      $this->latlonNumber = ~$this->latlonNumber;
    }
    $this->encodedlatlonNumber = $this
      ->setLatLonNumber($this->latlonNumber)
      ->getEncode();
    return $this->encodedlatlonNumber;
  }

  /**
   * Former gmap_polyutil_encode_latlon($x).
   *
   * @return string
   *   Levels.
   */
  public function getEncodedLevels() {
    $this->latlonLevels = $this
      ->setLatLonNumber(abs($this->latlonNumber))
      ->getEncode();
    return $this->latlonLevels;
  }

  /**
   * Former _gmap_polyutil_encode($x).
   *
   * @return string
   *   Encoded.
   */
  public function getEncode() {
    $this->encoded = '';
    while ($this->latlonNumber >= 32) {
      $this->encoded .= chr((32 | $this->latlonNumber & 31) + 63);
      $this->latlonNumber >>= 5;
    }
    $this->encoded .= chr(($this->latlonNumber & 31) + 63);
    return $this->encoded;
  }

  /**
   * Points set from former gmap_polyutil_dist($p1, $p2).
   *
   * @param string $p1
   *   Input 1.
   *
   * @param string $p2
   *   Input 2.
   *
   * @return object
   *   $this.
   */
  public function setLinePoints($p1, $p2) {
    $this->startPoint = $p1;
    $this->endPoint = $p2;
    return $this;
  }

  /**
   * Distance in two dimensions.
   *
   * √((x1-x0)^2 + (y1-y0)^2)
   * former gmap_polyutil_dist($p1, $p2)
   */
  public function getDist() {
    $this->distance = sqrt(pow($this->endPoint[0] - $this->startPoint[0], 2) + pow($this->endPoint[1] - $this->startPoint[1], 2));
    return $this->distance;
  }

  /**
   * Set $this->measurePoint to value.
   *
   * @param string $q
   *   Input string.
   *
   * @return object
   *   $this
   */
  public function setMeasurePoint($q) {
    $this->measurePoint = $q;
    return $this;
  }

  /**
   * Distance between a point and a line segment.
   *
   * former gmap_polyutil_point_line_dist()
   *
   * @return float
   *   Distance.
   */
  public function getPointLineDist() {
    if ($this->startPoint[0] == $this->endPoint[0] && $this->startPoint[1] == $this->endPoint[1]) {

      // lp1 and lp2 are the same point--they don't define a line--so we return
      // the distance between two points.
      return $this
        ->setLinePoints($this->measurePoint, $this->startPoint)
        ->getDist();
    }

    // Use the dot product to find where q lies with respect to the line segment
    // p1p2. For more information, see:
    // http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/
    // http://www.codeguru.com/forum/printthread.php?t=194400
    $u = (($this->endPoint[1] - $this->startPoint[1]) * ($this->measurePoint[1] - $this->startPoint[1]) + ($this->endPoint[0] - $this->startPoint[0]) * ($this->measurePoint[0] - $this->startPoint[0])) / (pow($this->endPoint[1] - $this->startPoint[1], 2) + pow($this->endPoint[0] - $this->startPoint[0], 2));

    // Point is not alongside segment, it is further off in $p1's direction.
    if ($u <= 0) {
      return $this
        ->setLinePoints($this->measurePoint, $this->startPoint)
        ->getDist();
    }
    elseif ($u >= 1) {
      return $this
        ->setLinePoints($this->measurePoint, $this->endPoint)
        ->getDist();
    }
    else {

      // Point is alongside segment.
      // Calculate distance between q and the nearest point on the line segment.
      // Use $u to calculate the nearest point on the line segment:
      // p1 + u*(p2 - p1) => [p1x + u*(p2x - p1x), p1y + u*(p2y - p1y)]
      return $this
        ->setLinePoints($this->measurePoint, array(
        $this->startPoint[0] + $u * ($this->endPoint[0] - $this->startPoint[0]),
        $this->startPoint[1] + $u * ($this->endPoint[1] - $this->startPoint[1]),
      ))
        ->getDist();
    }
  }

  /**
   * Former gmap_polyutil_dp_encode($points).
   *
   * @param array $points
   *   An array of coordinate pairs.
   *
   * @return object
   *   $this
   */
  public function setPoints(array $points) {
    $this->points = $points;
    return $this;
  }

  /**
   * Implementation of the Douglas-Peucker polyline simplification algorithm.
   *
   * See:
   * http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/algorithm.html
   *
   * former gmap_polyutil_dp_encode($points)
   *
   * @return array
   *   An array of keys => weights; the keys correspond with indices of points
   *   in the $points array. Some points may be insignificant according to the
   *   algorithm - they will not have entries in the return array. The "weights"
   *   are actually the point's distance from the line segment that it
   *   subdivides.
   */
  public function getDPEncode() {
    $this->pointWeights = array();
    $max_i = 0;
    if (count($this->points) > 2) {

      // The 'stack' holds line segments to be simplified.
      $stack[] = array(
        0,
        count($this->points) - 1,
      );
      while (count($stack) > 0) {

        // Take a line segment to look at.
        $segment = array_pop($stack);

        // Figure out which subdividing point is the furthest off the line segment.
        $max_dist = 0;
        for ($i = $segment[0] + 1; $i < $segment[1]; $i++) {
          $dist = $this
            ->setMeasurePoint($this->points[$i])
            ->setLinePoints($this->points[$segment[0]], $this->points[$segment[1]])
            ->getPointLineDist();
          if ($dist > $max_dist) {
            $max_dist = $dist;
            $max_i = $i;
          }
        }

        // If the subdividing point found above is significantly off the line,
        // segment then we want to simplify further. Add sub-segments to the stack.
        if ($max_dist > self::GMAP_DP_EPSILON) {
          $this->pointWeights[$max_i] = $max_dist;
          array_push($stack, array(
            $segment[0],
            $max_i,
          ));
          array_push($stack, array(
            $max_i,
            $segment[1],
          ));
        }
      }
    }

    // The first and last points of the line should always be visible.
    $levels = $this
      ->getZoomLevels();
    $this->pointWeights[0] = $levels[0];
    $this->pointWeights[count($this->points) - 1] = $levels[0];
    return $this->pointWeights;
  }

  /**
   * Simplify a set of points and generate an "Encoded Polyline" for GMaps.
   *
   * former gmap_polyutil_polyline($points)
   *
   * @return array
   *   An array containing the point and zoom information necessary to display
   *   encoded polylines on Google Maps:
   *   'points', 'levels', 'numLevels', and 'zoomFactor'.
   */
  public function getPolyline() {
    $points_encoded = '';
    $levels_encoded = '';

    // Simplify the line.
    $weights = $this
      ->getDPEncode();
    $previous = array(
      0,
      0,
    );
    foreach ($this->points as $i => $p) {
      if (isset($weights[$i])) {

        // Encode each simplified point,
        // the deltas between points are encoded, rather than the point values themselves.
        $points_encoded .= $this
          ->setLatLonNumber($p[0] - $previous[0])
          ->getEncodedLatLon() . $this
          ->setLatLonNumber($p[1] - $previous[1])
          ->getEncodedLatLon();
        $levels_encoded .= $this
          ->setLatLonNumber($this
          ->setWeight($weights[$i])
          ->getZoomLevel())
          ->getEncodedLevels();
        $previous = $p;
      }
    }
    return array(
      'points' => $points_encoded,
      'levels' => $levels_encoded,
      'numLevels' => self::GMAP_ZOOM_LEVELS,
      'zoomFactor' => self::GMAP_ZOOM_FACTOR,
    );
  }

  /**
   * Build a logarithmic scale of zoom levels.
   *
   * former _gmap_polyutil_zoom_levels()
   *
   * @return mixed
   *   Zoom levels.
   */
  public function getZoomLevels() {
    if (!isset(self::$levels)) {
      for ($i = 0; $i < self::GMAP_ZOOM_LEVELS; $i++) {
        self::$levels[$i] = self::GMAP_DP_EPSILON * pow(self::GMAP_ZOOM_FACTOR, self::GMAP_ZOOM_LEVELS - $i - 1);
      }
    }
    return self::$levels;
  }

  /**
   * Setting a weight.
   *
   * former _gmap_polyutil_get_zoom_level($weight).
   *
   * @param int $weight
   *   Weight for setting.
   *
   * @return object
   *   $this.
   */
  public function setWeight($weight) {
    $this->weight = $weight;
    return $this;
  }

  /**
   * Place points in levels.
   *
   * former _gmap_polyutil_get_zoom_level($weight)
   *
   * Place points in levels based on their "weight" -- a value
   * derived from distance calculations in the
   * simplification algorithm, gmap_polyutil_dp_encode().
   *
   * @return int
   *   Zoom level.
   */
  public function getZoomLevel() {
    $i = 0;
    while (self::$levels[$i] > $this->weight) {
      $i++;
    }
    return self::GMAP_ZOOM_LEVELS - $i - 1;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
GmapPolylineToolbox::$distance private property
GmapPolylineToolbox::$encoded private property
GmapPolylineToolbox::$encodedlatlonNumber private property
GmapPolylineToolbox::$endPoint private property
GmapPolylineToolbox::$gmapInstance protected static property
GmapPolylineToolbox::$latlonLevels private property
GmapPolylineToolbox::$latlonNumber private property
GmapPolylineToolbox::$levels protected static property
GmapPolylineToolbox::$measurePoint private property
GmapPolylineToolbox::$points private property
GmapPolylineToolbox::$pointWeights private property
GmapPolylineToolbox::$startPoint private property
GmapPolylineToolbox::$weight private property
GmapPolylineToolbox::getDist public function Distance in two dimensions.
GmapPolylineToolbox::getDPEncode public function Implementation of the Douglas-Peucker polyline simplification algorithm.
GmapPolylineToolbox::getEncode public function Former _gmap_polyutil_encode($x).
GmapPolylineToolbox::getEncodedLatLon public function Former gmap_polyutil_encode_latlon($x).
GmapPolylineToolbox::getEncodedLevels public function Former gmap_polyutil_encode_latlon($x).
GmapPolylineToolbox::getInstance public static function Getting instance.
GmapPolylineToolbox::getPointLineDist public function Distance between a point and a line segment.
GmapPolylineToolbox::getPolyline public function Simplify a set of points and generate an "Encoded Polyline" for GMaps.
GmapPolylineToolbox::getZoomLevel public function Place points in levels.
GmapPolylineToolbox::getZoomLevels public function Build a logarithmic scale of zoom levels.
GmapPolylineToolbox::GMAP_DP_EPSILON constant
GmapPolylineToolbox::GMAP_ZOOM_FACTOR constant
GmapPolylineToolbox::GMAP_ZOOM_LEVELS constant
GmapPolylineToolbox::setLatLonNumber public function The following methods will encode numbers.
GmapPolylineToolbox::setLinePoints public function Points set from former gmap_polyutil_dist($p1, $p2).
GmapPolylineToolbox::setMeasurePoint public function Set $this->measurePoint to value.
GmapPolylineToolbox::setPoints public function Former gmap_polyutil_dp_encode($points).
GmapPolylineToolbox::setWeight public function Setting a weight.
GmapPolylineToolbox::__construct private function Do not change.