Passed
Push — master ( 485b67...7b011d )
by Andreas
05:28
created

WebMapTileService   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 198
Duplicated Lines 0 %

Test Coverage

Coverage 65.59%

Importance

Changes 0
Metric Value
eloc 112
dl 0
loc 198
ccs 61
cts 93
cp 0.6559
rs 10
c 0
b 0
f 0
wmc 21

7 Methods

Rating   Name   Duplication   Size   Complexity  
B index() 0 24 7
A vector_renderer() 0 65 4
A readCache() 0 3 1
A cacheAge() 0 5 2
A raster_renderer() 0 31 3
A writeCache() 0 3 1
A cacheFile() 0 12 3
1
<?php
2
3
namespace Smindel\GIS\Control;
4
5
use SilverStripe\Control\Controller;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Security\Security;
8
use SilverStripe\Security\Permission;
9
use SilverStripe\ORM\DB;
10
use Smindel\GIS\GIS;
11
use Smindel\GIS\Service\Tile;
12
13
class WebMapTileService extends AbstractGISWebServiceController
14
{
15
    private static $url_handlers = array(
0 ignored issues
show
introduced by
The private property $url_handlers is not used, and could be removed.
Loading history...
16
        '$Model//$z/$x/$y' => 'handleAction',
17
    );
18
19
    /**
20
     * Buffer in pixel by wich the tile box is enlarged, which is used for
21
     * filtering the data. That's in orderer to include points from a bordering
22
     * tile that was cut and that wouldn't be rendered in this tile in order to
23
     * show a complete point. Use int as a uniform buffer around the current tile
24
     * or a two values for left/right and top/bottom or four values for left, top,
25
     * right and bottom.
26
     *
27
     * @var mixed
28
     */
29
    private static $tile_buffer = 5;
0 ignored issues
show
introduced by
The private property $tile_buffer is not used, and could be removed.
Loading history...
30
31
    private static $tile_size = 256;
0 ignored issues
show
introduced by
The private property $tile_size is not used, and could be removed.
Loading history...
32
33
    private static $wrap_date = true;
0 ignored issues
show
introduced by
The private property $wrap_date is not used, and could be removed.
Loading history...
34
35
    private static $cache_path = 'tile-cache';
0 ignored issues
show
introduced by
The private property $cache_path is not used, and could be removed.
Loading history...
36
37
    private static $cache_ttl = 0;
0 ignored issues
show
introduced by
The private property $cache_ttl is not used, and could be removed.
Loading history...
38
39
    private static $default_style = [
0 ignored issues
show
introduced by
The private property $default_style is not used, and could be removed.
Loading history...
40
        'gd' => [
41
            'backgroundcolor' => [0, 0, 0, 127],
42
            'strokecolor' => [60, 60, 210, 0],
43
            'fillcolor' => [60, 60, 210, 80],
44
            'setthickness' => [2],
45
            'pointradius' => 5,
46
        ],
47
        'imagick' => [
48
            'StrokeOpacity' => 1,
49
            'StrokeWidth' => 2,
50
            'StrokeColor' => 'rgb(60,60,210)',
51
            'FillColor' => 'rgba(60,60,210,.25)',
52
            'PointRadius' => 5,
53
        ],
54
    ];
55
56 2
    public function index($request)
57
    {
58 2
        $model = $this->model = $this->getModel($request);
59 2
        $config = $this->getConfig($model);
60
61
        if (
62 2
            ($cache = $config['cache_ttl'] ? sha1(json_encode($request->getVars())) : false)
63 1
            && ($age = $this->cacheAge($cache)) !== false
64
            && $config['cache_ttl'] > $age
65
        ) {
66
            $response = $this->getResponse();
67
            $response->addHeader('Content-Type', 'image/png');
68
            $response->setBody($this->readCache($cache));
69
            return $response;
70
        }
71
72 2
        $renderer = Config::inst()->get($this->getModel($request), 'tile_renderer');
73 2
        $response = $this->$renderer($request);
74
75 2
        if ($cache && $response->getStatusCode() == 200) {
76 1
            $this->writeCache($cache, $response->getBody());
77
        }
78
79 2
        return $response;
80
    }
81
82 2
    public function vector_renderer($request)
83
    {
84 2
        $model = $this->model = $this->getModel($request);
85 2
        $config = $this->getConfig($model);
86 2
        $list = $this->getRecords($request);
87
88 2
        $z = $request->param('z');
89 2
        $x = $request->param('x');
90 2
        $y = $request->param('y');
91
92 2
        $tileSize = $config['tile_size'];
93 2
        $tile = Tile::create($z, $x, $y, $config['default_style'], $config['wrap_date'], $tileSize);
94
95 2
        list($lon1, $lat1) = Tile::zxy2lonlat($z, $x, $y);
96 2
        list($lon2, $lat2) = Tile::zxy2lonlat($z, $x + 1, $y + 1);
97
98 2
        $geometryField = $config['geometry_field'];
99
100 2
        $bufferSize = $config['tile_buffer'];
101 2
        if (!is_array($bufferSize)) {
102 2
            $bufferSize = array_fill(0, 4, $bufferSize);
103
        } elseif (count($bufferSize) == 2) {
104
            $bufferSize += $bufferSize;
105
        }
106
107
        $buffer = [
108 2
            ($lon2 - $lon1) / $tileSize * $bufferSize[0],
109 2
            ($lat2 - $lat1) / $tileSize * $bufferSize[1],
110 2
            ($lon2 - $lon1) / $tileSize * $bufferSize[2],
111 2
            ($lat2 - $lat1) / $tileSize * $bufferSize[3],
112
        ];
113
114
        $boxes = [[
115 2
            [$lon1 - $buffer[0], $lat1 - $buffer[1]],
116 2
            [$lon2 + $buffer[2], $lat1 - $buffer[1]],
117 2
            [$lon2 + $buffer[2], $lat2 + $buffer[3]],
118 2
            [$lon1 - $buffer[0], $lat2 + $buffer[3]],
119 2
            [$lon1 - $buffer[0], $lat1 - $buffer[1]],
120
        ]];
121
122
        $bounds = [
123 2
            'type' => 'Polygon',
124 2
            'srid' => 4326,
125 2
            'coordinates' => $boxes,
126
        ];
127
128 2
        $list = $list->filter(
129 2
            $geometryField . ':ST_Intersects',
130 2
            GIS::to_ewkt(
131 2
                GIS::reproject(
132 2
                    $bounds,
133 2
                    GIS::config()->default_srid
134
                )
135
            )
136
        );
137
138 2
        if ($request->requestVar('debug')) {
139
            $tile->debug("$z, $x, $y, " . $list->count());
140
        }
141
142 2
        $response = $this->getResponse();
143 2
        $response->addHeader('Content-Type', $tile->getContentType());
144 2
        $response->setBody($tile->render($list));
145
146 2
        return $response;
147
    }
148
149
    public function raster_renderer($request)
150
    {
151
        $model = $this->model = $this->getModel($request);
152
        $raster = singleton($model);
153
154
        $z = $request->param('z');
155
        $x = $request->param('x');
156
        $y = $request->param('y');
157
158
        list($lon1, $lat1) = Tile::zxy2lonlat($z, $x, $y);
159
        list($lon2, $lat2) = Tile::zxy2lonlat($z, $x + 1, $y + 1);
160
161
        list($x1, $y1) = ($srid = $raster->getSrid()) == 4326 ? [$lon1, $lat1] : GIS::reproject(['srid' => 4326, 'type' => 'Point', 'coordinates' => [$lon1, $lat1]], $srid)['coordinates'];
162
        list($x2, $y2) = ($srid = $raster->getSrid()) == 4326 ? [$lon2, $lat2] : GIS::reproject(['srid' => 4326, 'type' => 'Point', 'coordinates' => [$lon2, $lat2]], $srid)['coordinates'];
163
164
        $tile_size_x = $tile_size_y = 256;
165
        $input_filename = $raster->getFullPath();
166
        $output_filename = '/dev/stdout';
167
        $response = $this->getResponse();
168
169
        $cmd = sprintf('
170
            gdal_translate -of PNG -q -projwin %1$f, %2$f, %3$f, %4$f -outsize %5$d %6$d %7$s %8$s',
171
            $x1, $y1, $x2, $y2,
172
            $tile_size_x, $tile_size_y,
173
            $input_filename,
174
            $output_filename
175
        );
176
177
        return $response
178
            ->addHeader('Content-Type', 'image/png')
179
            ->setBody(`$cmd`);
180
    }
181
182 1
    protected function cacheFile($cache)
183
    {
184 1
        $dir = $this->getConfig($this->model)['cache_path'] . DIRECTORY_SEPARATOR;
185 1
        $dir = $dir[0] != DIRECTORY_SEPARATOR
186 1
            ? TEMP_PATH . DIRECTORY_SEPARATOR . $dir
187 1
            : $dir;
188
189 1
        if (!file_exists($dir)) {
190 1
            mkdir($dir, fileperms(TEMP_PATH), true);
191
        }
192
193 1
        return $dir . $cache;
194
    }
195
196 1
    protected function cacheAge($cache)
197
    {
198 1
        return is_readable($file = $this->cacheFile($cache))
199
            ? time() - filemtime($file)
200 1
            : false;
201
    }
202
203
    protected function readCache($cache)
204
    {
205
        return file_get_contents($this->cacheFile($cache));
206
    }
207
208 1
    protected function writeCache($cache, $data)
209
    {
210 1
        file_put_contents($this->cacheFile($cache), $data);
211 1
    }
212
}
213