Completed
Branch master (b03c4d)
by Andreas
06:02
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 20
CRAP Score 7.8542

Importance

Changes 0
Metric Value
cc 7
eloc 28
nc 11
nop 1
dl 0
loc 38
ccs 20
cts 27
cp 0.7407
crap 7.8542
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
            'PointRadius' => 5,
57
        ],
58
    ];
59
60 3
    public function index($request)
61
    {
62 3
        $model = $this->model = $this->getModel($request);
63 3
        $config = $this->getConfig($model);
64
65
        if (
66 3
            ($cache = $config['cache_ttl'] ? sha1(json_encode($request->getVars())) : false)
67 1
            && ($age = $this->cacheAge($cache)) !== false
68
            && $config['cache_ttl'] > $age
69
        ) {
70
            $response = $this->getResponse();
71
            $response->addHeader('Content-Type', 'image/png');
72
            $response->setBody($this->readCache($cache));
73
            return $response;
74
        }
75
76 3
        $renderer = Config::inst()->get($this->getModel($request), 'tile_renderer');
77 3
        $response = $this->$renderer($request);
78
79 3
        if ($cache && $response->getStatusCode() == 200) {
80 1
            $this->writeCache($cache, $response->getBody());
81
        }
82
83 3
        return $response;
84
    }
85
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 2
        $y = $request->param('y');
95
96 2
        $tileSize = $config['tile_size'];
97 2
        $tile = Tile::create($z, $x, $y, $config['default_style'], $config['wrap_date'], $tileSize);
98
99 2
        list($lon1, $lat1) = Tile::zxy2lonlat($z, $x, $y);
100 2
        list($lon2, $lat2) = Tile::zxy2lonlat($z, $x + 1, $y + 1);
101
102 2
        $geometryField = $config['geometry_field'];
103
104 2
        $bufferSize = $config['tile_buffer'];
105 2
        if (!is_array($bufferSize)) {
106 2
            $bufferSize = array_fill(0, 4, $bufferSize);
107
        } elseif (count($bufferSize) == 2) {
108
            $bufferSize += $bufferSize;
109
        }
110
111
        $buffer = [
112 2
            ($lon2 - $lon1) / $tileSize * $bufferSize[0],
113 2
            ($lat2 - $lat1) / $tileSize * $bufferSize[1],
114 2
            ($lon2 - $lon1) / $tileSize * $bufferSize[2],
115 2
            ($lat2 - $lat1) / $tileSize * $bufferSize[3],
116
        ];
117
118
        $boxes = [[
119 2
            [$lon1 - $buffer[0], $lat1 - $buffer[1]],
120 2
            [$lon2 + $buffer[2], $lat1 - $buffer[1]],
121 2
            [$lon2 + $buffer[2], $lat2 + $buffer[3]],
122 2
            [$lon1 - $buffer[0], $lat2 + $buffer[3]],
123 2
            [$lon1 - $buffer[0], $lat1 - $buffer[1]],
124
        ]];
125
126
        $bounds = [
127 2
            'type' => 'Polygon',
128 2
            'srid' => 4326,
129 2
            'coordinates' => $boxes,
130
        ];
131
132 2
        $list = $list->filter(
133 2
            $geometryField . ':ST_Intersects',
134 2
            GIS::to_ewkt(
135 2
                GIS::reproject(
136 2
                    $bounds,
137 2
                    GIS::config()->default_srid
138
                )
139
            )
140
        );
141
142 2
        if ($request->requestVar('debug')) {
143
            $tile->debug("$z, $x, $y, " . $list->count());
144
        }
145
146 2
        $response = $this->getResponse();
147 2
        $response->addHeader('Content-Type', $tile->getContentType());
148 2
        $response->setBody($tile->render($list));
149
150 2
        return $response;
151
    }
152
153 1
    public function raster_renderer($request)
154
    {
155 1
        $model = $this->model = $this->getModel($request);
156 1
        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 1
        } else if (is_a($model, Raster::class, true)) {
166 1
            $raster = singleton($model);
167
        } else {
168
            throw new Exception('Cannot render tile from ' . $model);
169
        }
170
171 1
        $z = $request->param('z');
172 1
        $x = $request->param('x');
173 1
        $y = $request->param('y');
174
175 1
        list($lon1, $lat1) = Tile::zxy2lonlat($z, $x, $y);
176 1
        list($lon2, $lat2) = Tile::zxy2lonlat($z, $x + 1, $y + 1);
177
178 1
        list($x1, $y1) = ($srid = $raster->getSrid()) == 4326 ? [$lon1, $lat1] : GIS::reproject(['srid' => 4326, 'type' => 'Point', 'coordinates' => [$lon1, $lat1]], $srid)['coordinates'];
179 1
        list($x2, $y2) = ($srid = $raster->getSrid()) == 4326 ? [$lon2, $lat2] : GIS::reproject(['srid' => 4326, 'type' => 'Point', 'coordinates' => [$lon2, $lat2]], $srid)['coordinates'];
180
181 1
        $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 1
        $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
186
        return $response
187 1
            ->addHeader('Content-Type', 'image/png')
188 1
            ->setBody($raster->translateRaster(
189 1
                [$x1, $y1], [$x2, $y2],
190 1
                256, 256
191
            ));
192
    }
193
194 1
    protected function cacheFile($cache)
195
    {
196 1
        $dir = $this->getConfig($this->model)['cache_path'] . DIRECTORY_SEPARATOR;
197 1
        $dir = $dir[0] != DIRECTORY_SEPARATOR
198 1
            ? TEMP_PATH . DIRECTORY_SEPARATOR . $dir
199 1
            : $dir;
200
201 1
        if (!file_exists($dir)) {
202 1
            mkdir($dir, fileperms(TEMP_PATH), true);
203
        }
204
205 1
        return $dir . $cache;
206
    }
207
208 1
    protected function cacheAge($cache)
209
    {
210 1
        return is_readable($file = $this->cacheFile($cache))
211
            ? time() - filemtime($file)
212 1
            : false;
213
    }
214
215
    protected function readCache($cache)
216
    {
217
        return file_get_contents($this->cacheFile($cache));
218
    }
219
220 1
    protected function writeCache($cache, $data)
221
    {
222 1
        file_put_contents($this->cacheFile($cache), $data);
223 1
    }
224
}
225