source: extensions/rv_gmaps/trunk/include/cluster_maker.php @ 18578

Last change on this file since 18578 was 18578, checked in by rvelices, 12 years ago

rv_gmaps:

  • removed earth icon from index pages
  • map icon text localized (for themes without icons)
  • minor javascript changes for maps api v3.9
  • code optimization
  • Property svn:eol-style set to LF
  • Property svn:keywords set to Author Date Id Revision
File size: 5.5 KB
Line 
1<?php
2
3function cluster_area_compare( &$a, &$b )
4{
5        $aa = bounds_lat_range($a->bounds) * bounds_lon_range($a->bounds);
6        $ab = bounds_lat_range($b->bounds) * bounds_lon_range($b->bounds);
7        return -$aa+$ab; // biggest first
8}
9
10final class Cluster
11{
12        public $bounds;
13        public $items;
14
15        function __construct($b = array(), $i = array())
16        {
17                $this->bounds = $b;
18                $this->items = $i;
19        }
20}
21
22class ClusterMaker
23{
24        private $_image_map;
25        private $_image_ranks;
26
27        var $bounds;
28        var $debug_str;
29
30        function make_clusters($images, $maxLatPrecision, $maxLonPrecision, $maxNbMarkers)
31        {
32                $this->bounds = array();
33                foreach($images as &$img)
34                {
35                        $img['lat'] = floatval($img['lat']);
36                        $img['lon'] = floatval($img['lon']);
37                        $this->bounds = bounds_add($this->bounds, $img['lat'], $img['lon'] );
38                }
39                unset($img);
40
41                $this->_image_map = $images;
42                $this->_image_ranks = array();
43                $this->debug_str = '';
44
45                $start = get_moment();
46                $total_iterations = 0;
47                $total_generations = 0;
48
49                $pending_split = array( new Cluster($this->bounds, array_keys($this->_image_map) ) );
50                $result = array();
51
52                while ( count($pending_split) )
53                {
54                        $total_generations++;
55                        $next_level_to_split = array();
56                        while ( count($pending_split) )
57                        {
58                                $current = array_shift( $pending_split );
59                                $splitted = $this->_split_cluster($current, $maxLatPrecision, $maxLonPrecision );
60                                if ( count($splitted)>1 )
61                                {
62                                        /*echo('splitted: ' . var_export($current['bounds'],true). ' in '. count($splitted)."\n" );
63                                        foreach( $splitted as $k => $split )
64                                                echo("sub $k: " . var_export($split['bounds'],true)."\n" );*/
65                                        $total_iterations += count($current->items);
66                                        $next_level_to_split = array_merge($next_level_to_split, $splitted );
67                                        if ( count($result)+count($pending_split)+count($next_level_to_split) >= $maxNbMarkers )
68                                        {
69                                                $result = array_merge($result, $pending_split, $next_level_to_split );
70                                                $pending_split = $next_level_to_split = array();
71                                                break;
72                                        }
73                                }
74                                else
75                                        $result[] = $current;
76                        }
77                        $pending_split = $next_level_to_split;
78                        usort( $pending_split, 'cluster_area_compare' );
79                }
80
81                $merged = $this->_try_merge_clusters( $result, $maxLatPrecision, $maxLonPrecision );
82                $this->debug_str = get_elapsed_time($start, get_moment()) .' gen:'.$total_generations.' alg:'.count($this->_image_map).'+'.$total_iterations;
83                if ($merged)
84                        $this->debug_str .= " merged:$merged";
85                $this->_image_map = null;
86                return $result;
87        }
88
89        function _try_merge_clusters(&$clusters, $maxLatPrecision, $maxLonPrecision)
90        {
91                $ret = 0;
92                for ($i=0; $i<count($clusters)-1; $i++)
93                {
94                        $ci = bounds_center( $clusters[$i]->bounds );
95                        for ($j=$i+1; $j<count($clusters); $j++)
96                        {
97                                $cj = bounds_center( $clusters[$j]->bounds );
98                                $rlat = abs($ci['lat']-$cj['lat']) / $maxLatPrecision;
99                                $rlon = abs($ci['lon']-$cj['lon']) / $maxLonPrecision;
100                                if ( $rlat<1 && $rlon<1
101                                                && $rlat+$rlon<1.1)
102                                {
103                                        //print_r( "i=$i; j=$j \n"); var_export( $clusters[$i]['bounds'] ); var_export( $clusters[$j]['bounds'] );
104                                        $clusters[$i]->items = array_merge( $clusters[$i]->items, $clusters[$j]->items );
105                                        if ( empty($this->_image_ranks) )
106                                                $this->_image_ranks = array_flip( array_keys($this->_image_map) );
107                                        usort( $clusters[$i]->items, array($this, '_image_rank_compare') );
108                                        $clusters[$i]->bounds = bounds_union($clusters[$i]->bounds, $clusters[$j]->bounds );
109                                        array_splice( $clusters, $j, 1);
110                                        $j--;
111                                        $ci = bounds_center( $clusters[$i]->bounds );
112                                        $ret++;
113                                }
114                        }
115                }
116                return $ret;
117        }
118
119        function _image_rank_compare($a, $b)
120        {
121                return $this->_image_ranks[$a] - $this->_image_ranks[$b];
122        }
123
124        function _split_cluster($cluster, $maxLatPrecision, $maxLonPrecision)
125        {
126                $latRange = bounds_lat_range($cluster->bounds);
127                $lonRange = bounds_lon_range($cluster->bounds);
128
129                $lat_nb_tiles = max( 1, $latRange/$maxLatPrecision );
130                $lon_nb_tiles = max( 1, $lonRange/$maxLonPrecision );
131                //echo('lat_nb '.$lat_nb_tiles.' lon_nb '.$lon_nb_tiles."\n");
132                if ($lat_nb_tiles<2 and $lon_nb_tiles<2)
133                        return array();
134
135                $ll_tile_factor = $lat_nb_tiles/$lon_nb_tiles;
136
137                if ($ll_tile_factor > 3)
138                { // 1x3
139                        $lat_step = $latRange/3;
140                        $lon_step = $lonRange;
141                }
142                elseif ($ll_tile_factor < 1/3)
143                { // 3x1
144                        $lat_step = $latRange;
145                        $lon_step = $lonRange/3;
146                }
147                elseif ($ll_tile_factor > 2)
148                { // 2x3
149                        $lat_step = max( $latRange/3, $maxLatPrecision );
150                        $lon_step = max( $lonRange/2, $maxLonPrecision );
151                }
152                elseif ($ll_tile_factor < 1/2)
153                { // 3x2
154                        $lat_step = max( $latRange/2, $maxLatPrecision );
155                        $lon_step = max( $lonRange/3, $maxLonPrecision );
156                }
157                else
158                { // 3x3
159                        $lat_step = max( $latRange/3, $maxLatPrecision );
160                        $lon_step = max( $lonRange/3, $maxLonPrecision );
161                }
162
163                if ( $cluster->bounds['count'] > 200 )
164                {
165                        if ($lat_step>4*$maxLatPrecision) $lat_step = $lat_step/2;
166                        if ($lon_step>4*$maxLonPrecision) $lon_step = $lon_step/2;
167                }
168
169                $lat_step += 1e-7;
170                $lon_step += 1e-7;
171                //echo ( "$lat_step $latRange x $lon_step $lonRange tiles $lat_nb_tiles x $lon_nb_tiles\n" );
172
173                $lon_nb_tiles = ceil( $lonRange / $lon_step );
174
175                $clusters = array();
176                foreach ( $cluster->items as $id )
177                {
178                        $lon = $this->_image_map[$id]['lon'];
179                        $lat = $this->_image_map[$id]['lat'];
180
181                        $idx_lon = floor ( ( $lon - $cluster->bounds['w'] ) / $lon_step );
182                        $idx_lat = floor ( ( $lat - $cluster->bounds['s'] ) / $lat_step );
183
184                        $idx = $lon_nb_tiles * $idx_lat + $idx_lon;
185
186                        if ( !isset($clusters[$idx]) )
187                        {
188                                $clusters[$idx] = new Cluster();
189                        }
190                        $clusters[$idx]->items[] = $id;
191                        $clusters[$idx]->bounds = bounds_add( $clusters[$idx]->bounds , $lat, $lon );
192                }
193                return $clusters;
194        }
195}
196
197?>
Note: See TracBrowser for help on using the repository browser.