Subversion Repositories eFlore/Projets.communes

Rev

Blame | Last modification | View Log | RSS feed

<?php
/*========================================================
/* Implementation of Douglas-Peuker in PHP.
/* 
/* Anthony Cartmell
/* ajcartmell@fonant.com
/* 
/* This software is provided as-is, with no warranty.
/* Please use and modify freely for anything you like :)
/* Version 1.2 - 18 Aug 2009  Fixes problem with line of three points.
/*                                                      Thanks to Craig Stanton http://craig.stanton.net.nz
/* Version 1.1 - 17 Jan 2007  Fixes nasty bug!
/*========================================================*/

class PolylineReducer {
        private $original_points = array();
        private $tolerance;
        private $tolerance_squared;

        public function __construct($geopoints_array) {
                foreach ($geopoints_array as $point) {
                        $this->original_points[] = new Vector($point->latitude, $point->longitude);
                }
                /*----- Include first and last points -----*/
                $this->original_points[0]->include = true;
                $this->original_points[count($this->original_points)-1]->include = true;
        }

        /**
        * Returns a list of GeoPoints for the simplest polyline that leaves
        * no original point more than $tolerance away from it.
        *
        * @param float  $tolerance
        * @return Geopoint array
        */
        public function SimplerLine($tolerance = 0.5) {
                $this->tolerance = $tolerance;
                $this->tolerance_squared = $tolerance * $tolerance;
                $this->DouglasPeucker(0, count($this->original_points) - 1);
                foreach ($this->original_points as $point) {
                        if ($point->include) {
                                $out[] = new GeoPoint($point->x, $point->y);
                        }
                }
                return $out;
        }

        /**
        * Douglas-Peuker polyline simplification algorithm. First draws single line
        * from start to end. Then finds largest deviation from this straight line, and if
        * greater than tolerance, includes that point, splitting the original line into
        * two new lines. Repeats recursively for each new line created.
        *
        * @param int $start_vertex_index
        * @param int $end_vertex_index
        */
        private function DouglasPeucker($start_vertex_index, $end_vertex_index) {
                if ($end_vertex_index <= $start_vertex_index + 1) // there is nothing to simplify
                return;

                // Make line from start to end
                $line = new Line($this->original_points[$start_vertex_index], $this->original_points[$end_vertex_index]);

                // Find largest distance from intermediate points to this line
                $max_dist_to_line_squared = 0;
                for ($index = $start_vertex_index+1; $index < $end_vertex_index; $index++) {
                        $dist_to_line_squared = $line->DistanceToPointSquared($this->original_points[$index]);
                        if ($dist_to_line_squared>$max_dist_to_line_squared) {
                                $max_dist_to_line_squared = $dist_to_line_squared;
                                $max_dist_index = $index;
                        }
                }

                // Check max distance with tolerance
                if ($max_dist_to_line_squared > $this->tolerance_squared) {// error is worse than the tolerance
                        // split the polyline at the farthest vertex from S
                        $this->original_points[$max_dist_index]->include = true;
                        // recursively simplify the two subpolylines
                        $this->DouglasPeucker($start_vertex_index,$max_dist_index);
                        $this->DouglasPeucker($max_dist_index,$end_vertex_index);
                }
                // else the approximation is OK, so ignore intermediate vertices
        }
}
?>