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

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

rv_gmaps can show now different markers based on number of photos at the location

  • 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->_image_map = $images;
33                $this->_image_ranks = array();
34                $this->debug_str = '';
35
36                $start = get_moment();
37                $total_iterations = 0;
38                $total_generations = 0;
39
40                $this->bounds = array();
41                foreach($images as $img)
42                        $this->bounds = bounds_add($this->bounds, $img['lat'], $img['lon'] );
43
44                $pending_split = array( new Cluster($this->bounds, array_keys($this->_image_map) ) );
45                $result = array();
46
47                while ( count($pending_split) )
48                {
49                        $total_generations++;
50                        $next_level_to_split = array();
51                        while ( count($pending_split) )
52                        {
53                                $current = array_shift( $pending_split );
54                                $splitted = $this->_split_cluster($current, $maxLatPrecision, $maxLonPrecision );
55                                if ( count($splitted)>1 )
56                                {
57                                        /*echo('splitted: ' . var_export($current['bounds'],true). ' in '. count($splitted)."\n" );
58                                        foreach( $splitted as $k => $split )
59                                                echo("sub $k: " . var_export($split['bounds'],true)."\n" );*/
60                                        $total_iterations += count($current->items);
61                                        $next_level_to_split = array_merge($next_level_to_split, $splitted );
62                                        if ( count($result)+count($pending_split)+count($next_level_to_split) >= $maxNbMarkers )
63                                        {
64                                                $result = array_merge($result, $pending_split, $next_level_to_split );
65                                                $pending_split = $next_level_to_split = array();
66                                                break;
67                                        }
68                                }
69                                else
70                                        array_push( $result, $current );
71                        }
72                        $pending_split = $next_level_to_split;
73                        usort( $pending_split, 'cluster_area_compare' );
74                }
75
76                $merged = $this->_try_merge_clusters( $result, $maxLatPrecision, $maxLonPrecision );
77                $this->debug_str = get_elapsed_time($start, get_moment()) .' gen:'.$total_generations.' alg:'.count($this->_image_map).'+'.$total_iterations;
78                if ($merged)
79                        $this->debug_str .= " merged:$merged";
80                $this->_image_map = null;
81                return $result;
82        }
83
84        function _try_merge_clusters(&$clusters, $maxLatPrecision, $maxLonPrecision)
85        {
86                $ret = 0;
87                for ($i=0; $i<count($clusters)-1; $i++)
88                {
89                        $ci = bounds_center( $clusters[$i]->bounds );
90                        for ($j=$i+1; $j<count($clusters); $j++)
91                        {
92                                $cj = bounds_center( $clusters[$j]->bounds );
93                                $rlat = abs($ci['lat']-$cj['lat']) / $maxLatPrecision;
94                                $rlon = abs($ci['lon']-$cj['lon']) / $maxLonPrecision;
95                                if ( $rlat<1 && $rlon<1 
96                                                && $rlat+$rlon<1.1)
97                                {
98                                        //print_r( "i=$i; j=$j \n"); var_export( $clusters[$i]['bounds'] ); var_export( $clusters[$j]['bounds'] );
99                                        $clusters[$i]->items = array_merge( $clusters[$i]->items, $clusters[$j]->items );
100                                        if ( empty($this->_image_ranks) )
101                                                $this->_image_ranks = array_flip( array_keys($this->_image_map) );
102                                        usort( $clusters[$i]->items, array($this, '_image_rank_compare') );
103                                        $clusters[$i]->bounds = bounds_union($clusters[$i]->bounds, $clusters[$j]->bounds );
104                                        array_splice( $clusters, $j, 1);
105                                        $j--;
106                                        $ci = bounds_center( $clusters[$i]->bounds );
107                                        $ret++;
108                                }
109                        }
110                }
111                return $ret;
112        }
113
114        function _image_rank_compare($a, $b)
115        {
116                return $this->_image_ranks[$a] - $this->_image_ranks[$b];
117        }
118
119        function _split_cluster($cluster, $maxLatPrecision, $maxLonPrecision)
120        {
121                $latRange = bounds_lat_range($cluster->bounds);
122                $lonRange = bounds_lon_range($cluster->bounds);
123
124                $lat_nb_tiles = max( 1, $latRange/$maxLatPrecision );
125                $lon_nb_tiles = max( 1, $lonRange/$maxLonPrecision );
126                //echo('lat_nb '.$lat_nb_tiles.' lon_nb '.$lon_nb_tiles."\n");
127                if ($lat_nb_tiles<2 and $lon_nb_tiles<2)
128                        return array();
129
130                $ll_tile_factor = $lat_nb_tiles/$lon_nb_tiles;
131
132                if ($ll_tile_factor > 3)
133                { // 1x3
134                        $lat_step = $latRange/3;
135                        $lon_step = $lonRange;
136                }
137                elseif ($ll_tile_factor < 1/3)
138                { // 3x1
139                        $lat_step = $latRange;
140                        $lon_step = $lonRange/3;
141                }
142                elseif ($ll_tile_factor > 2)
143                { // 2x3
144                        $lat_step = max( $latRange/3, $maxLatPrecision );
145                        $lon_step = max( $lonRange/2, $maxLonPrecision );
146                }
147                elseif ($ll_tile_factor < 1/2)
148                { // 3x2
149                        $lat_step = max( $latRange/2, $maxLatPrecision );
150                        $lon_step = max( $lonRange/3, $maxLonPrecision );
151                }
152                else
153                { // 3x3
154                        $lat_step = max( $latRange/3, $maxLatPrecision );
155                        $lon_step = max( $lonRange/3, $maxLonPrecision );
156                }
157
158                if ( $cluster->bounds['count'] > 200 )
159                {
160                        if ($lat_step>4*$maxLatPrecision) $lat_step = $lat_step/2;
161                        if ($lon_step>4*$maxLonPrecision) $lon_step = $lon_step/2;
162                }
163
164                $lat_step += 1e-7;
165                $lon_step += 1e-7;
166                //echo ( "$lat_step $latRange x $lon_step $lonRange tiles $lat_nb_tiles x $lon_nb_tiles\n" );
167
168                $lon_nb_tiles = ceil( $lonRange / $lon_step );
169
170                $clusters = array();
171                foreach ( $cluster->items as $id )
172                {
173                        $lon = $this->_image_map[$id]['lon'];
174                        $lat = $this->_image_map[$id]['lat'];
175
176                        $idx_lon = floor ( ( $lon - $cluster->bounds['w'] ) / $lon_step );
177                        $idx_lat = floor ( ( $lat - $cluster->bounds['s'] ) / $lat_step );
178
179                        $idx = $lon_nb_tiles * $idx_lat + $idx_lon;
180
181                        if ( !isset($clusters[$idx]) )
182                        {
183                                $clusters[$idx] = new Cluster();
184                        }
185                        array_push( $clusters[$idx]->items, $id );
186                        $clusters[$idx]->bounds = bounds_add( $clusters[$idx]->bounds , $lat, $lon );
187                }
188                return $clusters;
189        }
190}
191
192?>
Note: See TracBrowser for help on using the repository browser.