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

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