Home > CodeIgniter, Google Map, PHP > CodeIgniter Library to Create Google Maps Encoded Polylines

CodeIgniter Library to Create Google Maps Encoded Polylines

This class is used to encode a number of coordinates into an encoded polylines to be used in Google Maps. The code is originally from Jim Hribar http://www.jimhribar.com/cgi-bin/moin.cgi/PolylineEncoder and is rewritten by Gabriel Svennerberg www.svennerberg.com into a class and modified by me into CodeIgniter library. The code is written using the methods and algorithms found in the work of Mark MacClure http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline

To use the library, put the class file into system/application/library. Then load the library from your controller class. While loading the library, the only required argument is points which is an array containing the coordinates for the polyline. You can add additional parameters as described below.

numLevels and zoomFactor are optional

This parameter defines how many different levels of magnification the polyline has and the change in magnification between those levels. Default values for these are

numLevels = 18
zoomFactor = 2

Make sure to use the same numLevels and zoomFactor in your Javascript or the lines won’t display properly.

verySmall

This parameter indicates the length of a barely visible object at the highest zoom level. The default value is 0.00001. By lowering this number you can decrease the number of coordinates used.

forceEndpoints

This parameter indicates whether or not the endpoints should be visible at all zoom levels. This parameter is optional with a default value of true.  Probably should stay true regardless.

After defining the required and optional parameter, we could call the main methods of the class to create the encoded polylines.

dpEncode()

The returns value of the method is an associative array with the encoded polyline:

array["Points"] = The encoded coordinates
array["Levels"] = Encoded level
array["PointsLiteral"] = The encoded coordinates as a literal
array["NumLevels"] = Returns the value for NumLevels
array["ZoomFactor"] = Returns the value for ZoomFactor

There is another method which returns an array with the supplied (unencoded) coordinates:

getPoints()

Here is an example of CI controller that uses the library.

<?php
class Mapservices extends Controller {
    function Mapservices()
    {
        parent::Controller();
    }

    function gettrack()
    {
        $points=array();
        $query = $this->db->get(‘tracks’);
        foreach ($query->result() as $row)
        {
            $points[]=array(
                $row->lat,
                $row->lon
            );
        }

        $this->load->library(‘Gmaputils’,$points);
        $a = $this->gmaputils->dpEncode();

        $data[‘mapsapi’]="enter your maps api key";
        $data[‘encodedPoints’]=$a[‘Points’];
        $data[‘encodedLevels’]=$a[‘Levels’];

        $this->load->view(‘index’, $data);
    }
}
?>

In the above example, I load $points array variable with an array of latitude and longitude values from a table called tracks. After filling up the array, I initialized the library with that array of points array as the parameter. Then I called the dpEncode() method and store the result into a temporary array $a. Lastly I loaded the view called index supplied with data of encoded polylines.

Here is an example of that view file to display the encoded polylines.

<head>
<script src="http://maps.google.com/maps?file=api&amp;amp;v=2&amp;amp;key=<?=$mapsapi?>" type="text/javascript"></script>
<script>
function init()
{
    var map = new GMap2(document.getElementById(‘map’));
    var indonesia = new GLatLng(-0.615222552406841,110.830078125);
    map.setCenter(indonesia, 4);

    encodedPolyline = new GPolyline.fromEncoded({
        color: "#FF0000",
        weight: 5,
        points: <?=encodedPoints?>,
        levels: <?=encodedLevels?>,
        zoomFactor: 2,
        numLevels: 18
    });

    map.addOverlay(encodedPolyline);
}
</script>
</head>
<body onload="init()">
    <div id="map"></div>
</body>

Here is the library code

<?php if (!defined(‘BASEPATH’)) exit(‘No direct script access allowed’);

/**
 * Google Maps Polylines Library for Code Igniter
 * Written by Akhmad Daniel Sembiring
 * Copyright 2009
 * http://vitraining.com
 *
 * Released as free code, however,
 * You must keep the copyright information.
 *
 *
 * Version 1.0
 */

class Gmaputils {

    // The constructor
    function __construct(array $points=array(), $numLevels = 18, $zoomFactor = 2, $verySmall = 0.000001, $forceEndpoints = true) {
        $this->points = $points;
        $this->numLevels = $numLevels;
        $this->zoomFactor = $zoomFactor;
        $this->verySmall = $verySmall;
        $this->forceEndpoints = $forceEndpoints;

        for($i = 0; $i < $this->numLevels; $i++){
            $this->zoomLevelBreaks[$i] = $this->verySmall*pow($this->zoomFactor, $this->numLevels-$i-1);
        }
    }

    protected $points;
    protected $numLevels;
    protected $zoomFactor;
    protected $verySmall;
    protected $forceEndpoints;
    protected $zoomLevelBreaks;

    // Returns the supplied coordinates
    public function getPoints() {
        return $this->points;
    }

    // The main method which is called to perform the encoding
    // Returns an associative array containing the encoded points, levels,
    // an escaped string literal containing the encoded points
    // It also returns the zoomFactor and numLevels
    public function dpEncode() {

        ////daniel
        $dists=;
        $absMaxDist = ;

        if(count($this->points) > 2) {

            $stack[] = array(0, count($this->points)-1);

            while(count($stack) > 0) {
                $current = array_pop($stack);
                $maxDist = 0;
                $absMaxDist = 0;

                for($i = $current[0]+1; $i < $current[1]; $i++) {
                    $temp = self::distance($this->points[$i], $this->points[$current[0]], $this->points[$current[1]]);
                    if($temp > $maxDist) {
                        $maxDist = $temp;
                        $maxLoc = $i;
                        if($maxDist > $absMaxDist) {
                            $absMaxDist = $maxDist;
                        }
                    }
                }

                if($maxDist > $this->verySmall) {
                    $dists[$maxLoc] = $maxDist;
                    array_push($stack, array($current[0], $maxLoc));
                    array_push($stack, array($maxLoc, $current[1]));
                }
            }
        }

        $encodedPoints = self::createEncodings($this->points, $dists);
        $encodedLevels = self::encodeLevels($this->points, $dists, $absMaxDist);
        $encodedPointsLiteral = str_replace(\\,"\\\\",$encodedPoints);

        $polyline["Points"] = $encodedPoints;
        $polyline["Levels"] = $encodedLevels;
        $polyline["PointsLiteral"] = $encodedPointsLiteral;
        $polyline["ZoomFactor"] = $this->zoomFactor;
        $polyline["NumLevels"] = $this->numLevels;

        return $polyline;
    }

    protected function computeLevel($dd) {
        if($dd > $this->verySmall) {
            $lev = 0;
            while($dd < $this->zoomLevelBreaks[$lev]) {
                $lev++;
            }
        }

        return $lev;
    }

    protected function distance($p0, $p1, $p2) {
        if($p1[0] == $p2[0] &amp;&amp; $p1[1] == $p2[1]) {
            $out = sqrt(pow($p2[0]-$p0[0],2) + pow($p2[1]-$p0[1],2));
        } else {
            $u = (($p0[0]-$p1[0])*($p2[0]-$p1[0]) + ($p0[1]-$p1[1]) * ($p2[1]-$p1[1])) / (pow($p2[0]-$p1[0],2) + pow($p2[1]-$p1[1],2));
            if($u <= 0) {
                $out = sqrt(pow($p0[0]$p1[0],2) + pow($p0[1]$p1[1],2));
            }
            if($u >= 1) {
                $out = sqrt(pow($p0[0]$p2[0],2) + pow($p0[1]$p2[1],2));
            }
            if(0 < $u &amp;&amp; $u < 1) {
                $out = sqrt(pow($p0[0]-$p1[0]-$u*($p2[0]-$p1[0]),2) + pow($p0[1]-$p1[1]-$u*($p2[1]-$p1[1]),2));
            }
        }

        return $out;
    }

    protected static function encodeSignedNumber($num) {
       $sgn_num = $num << 1;

       if ($num < 0) {
           $sgn_num = ~($sgn_num);
       }

       return self::encodeNumber($sgn_num);
    }

    protected static function createEncodings($points, $dists) {
        $plat = ;
        $plng = ;
        $encoded_points = "";

        for($i=0; $i<count($points); $i++) {
            if(isset($dists[$i]) || $i == 0 || $i == count($points)-1) {
                $point = $points[$i];
                $lat = $point[0];
                $lng = $point[1];
                $late5 = floor($lat * 1e5);
                $lnge5 = floor($lng * 1e5);
                $dlat = $late5$plat;
                $dlng = $lnge5$plng;
                $plat = $late5;
                $plng = $lnge5;
                $encoded_points .= self::encodeSignedNumber($dlat) . self::encodeSignedNumber($dlng);
            }
        }

        return $encoded_points;
    }

    protected function encodeLevels($points, $dists, $absMaxDist) {
        $encoded_levels = "";

        if($this->forceEndpoints) {
            $encoded_levels .= self::encodeNumber($this->numLevels-1);
        } else {
            $encoded_levels .= self::encodeNumber($this->numLevels – self::computeLevel($absMaxDist)-1);
        }

        for($i=1; $i<count($points)-1; $i++) {
            if(isset($dists[$i])) {
                $encoded_levels .= self::encodeNumber($this->numLevels – self::computeLevel($dists[$i])-1);
            }
        }

        if($this->forceEndpoints) {
            $encoded_levels .= self::encodeNumber($this->numLevels -1);
        } else {
            $encoded_levels .= self::encodeNumber($this->numLevels – self::computeLevel($absMaxDist)-1);
        }

        return $encoded_levels;
    }

    protected static function encodeNumber($num) {
        $encodeString = "";

        while($num >= 0×20) {
            $nextValue = (0×20 | ($num &amp; 0x1f)) + 63;
            $encodeString .= chr($nextValue);
            $num >>= 5;
        }

        $finalValue = $num + 63;
        $encodeString .= chr($finalValue);

        return $encodeString;
    }
}
?>

  • Share/Bookmark
  1. No comments yet.
  1. No trackbacks yet.
This site uses a Hackadelic PlugIn, Hackadelic SEO Table Of Contents 1.6.0.