Test Failed
Push — master ( 7b011d...e1b672 )
by Andreas
05:38
created

WebMapTileService::raster_renderer()   B

Complexity

Conditions 7
Paths 11

Size

Total Lines 38
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 25.2891

Importance

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