WebMapTileService::cacheAge()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 5
ccs 3
cts 4
cp 0.75
crap 2.0625
rs 10
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::create($bounds)->reproject(GIS::config()->default_srid)
135
        );
136
137 2
        if ($request->requestVar('debug')) {
138
            $tile->debug("$z, $x, $y, " . $list->count());
139
        }
140
141 2
        $response = $this->getResponse();
142 2
        $response->addHeader('Content-Type', $tile->getContentType());
143 2
        $response->setBody($tile->render($list));
144
145 2
        return $response;
146
    }
147
148 1
    public function raster_renderer($request)
149
    {
150 1
        $model = $this->model = $this->getModel($request);
151 1
        if (is_a($model, File::class, true)) {
152
            $file = $model::get()->byID($request->param('ID'));
153
            if (!$file) {
154
                return $this->getResponse()->setStatusCode(404);
155
            }
156
            if (!$file->canView()) {
157
                return $this->getResponse()->setStatusCode(403);
158
            }
159
            $raster = new Raster(PUBLIC_PATH . $file->getURL());
160 1
        } else if (is_a($model, Raster::class, true)) {
161 1
            $raster = singleton($model);
162
        } else {
163
            throw new Exception('Cannot render tile from ' . $model);
164
        }
165
166 1
        $z = $request->param('z');
167 1
        $x = $request->param('x');
168 1
        $y = $request->param('y');
169
170 1
        list($lon1, $lat1) = Tile::zxy2lonlat($z, $x, $y);
171 1
        list($lon2, $lat2) = Tile::zxy2lonlat($z, $x + 1, $y + 1);
172
173 1
        list($x1, $y1) = ($srid = $raster->getSrid()) == 4326
174 1
            ? [$lon1, $lat1]
175
            : GIS::create(['srid' => 4326, 'type' => 'Point', 'coordinates' => [$lon1, $lat1]])
176 1
            ->reproject($srid)->coordinates;
177 1
        list($x2, $y2) = ($srid = $raster->getSrid()) == 4326
178 1
            ? [$lon2, $lat2]
179
            : GIS::create(['srid' => 4326, 'type' => 'Point', 'coordinates' => [$lon2, $lat2]])
180 1
            ->reproject($srid)->coordinates;
181
182 1
        $tile_size_x = $tile_size_y = 256;
183 1
        $response = $this->getResponse();
184
185
        return $response
186 1
            ->addHeader('Content-Type', 'image/png')
187 1
            ->setBody($raster->translateRaster(
188 1
                [$x1, $y1], [$x2, $y2],
189 1
                $tile_size_x, $tile_size_y
190
            ));
191
    }
192
193 1
    protected function cacheFile($cache)
194
    {
195 1
        $dir = $this->getConfig($this->model)['cache_path'] . DIRECTORY_SEPARATOR;
196 1
        $dir = $dir[0] != DIRECTORY_SEPARATOR
197 1
            ? TEMP_PATH . DIRECTORY_SEPARATOR . $dir
198 1
            : $dir;
199
200 1
        if (!file_exists($dir)) {
201 1
            mkdir($dir, fileperms(TEMP_PATH), true);
202
        }
203
204 1
        return $dir . $cache;
205
    }
206
207 1
    protected function cacheAge($cache)
208
    {
209 1
        return is_readable($file = $this->cacheFile($cache))
210
            ? time() - filemtime($file)
211 1
            : false;
212
    }
213
214
    protected function readCache($cache)
215
    {
216
        return file_get_contents($this->cacheFile($cache));
217
    }
218
219 1
    protected function writeCache($cache, $data)
220
    {
221 1
        file_put_contents($this->cacheFile($cache), $data);
222 1
    }
223
}
224