Installed leaflet in extlib
[mediagoblin.git] / extlib / leaflet / src / layer / tile / TileLayer.js
CommitLineData
c5ba5b04
JW
1/*\r
2 * L.TileLayer is used for standard xyz-numbered tile layers.\r
3 */\r
4\r
5L.TileLayer = L.Class.extend({\r
6 includes: L.Mixin.Events,\r
7 \r
8 options: {\r
9 minZoom: 0,\r
10 maxZoom: 18,\r
11 tileSize: 256,\r
12 subdomains: 'abc',\r
13 errorTileUrl: '',\r
14 attribution: '',\r
15 opacity: 1,\r
16 scheme: 'xyz',\r
17 noWrap: false,\r
18 \r
19 unloadInvisibleTiles: L.Browser.mobileWebkit,\r
20 updateWhenIdle: L.Browser.mobileWebkit\r
21 },\r
22 \r
23 initialize: function(url, options) {\r
24 L.Util.setOptions(this, options);\r
25 \r
26 this._url = url;\r
27 \r
28 if (typeof this.options.subdomains == 'string') {\r
29 this.options.subdomains = this.options.subdomains.split('');\r
30 }\r
31 },\r
32 \r
33 onAdd: function(map) {\r
34 this._map = map;\r
35 \r
36 // create a container div for tiles\r
37 this._initContainer();\r
38 \r
39 // create an image to clone for tiles\r
40 this._createTileProto();\r
41 \r
42 // set up events\r
43 map.on('viewreset', this._reset, this);\r
44 \r
45 if (this.options.updateWhenIdle) {\r
46 map.on('moveend', this._update, this);\r
47 } else {\r
48 this._limitedUpdate = L.Util.limitExecByInterval(this._update, 100, this);\r
49 map.on('move', this._limitedUpdate, this);\r
50 }\r
51 \r
52 this._reset();\r
53 this._update();\r
54 },\r
55 \r
56 onRemove: function(map) {\r
57 this._map.getPanes().tilePane.removeChild(this._container);\r
58 this._container = null;\r
59 \r
60 this._map.off('viewreset', this._reset, this);\r
61 \r
62 if (this.options.updateWhenIdle) {\r
63 this._map.off('moveend', this._update, this);\r
64 } else {\r
65 this._map.off('move', this._limitedUpdate, this);\r
66 }\r
67 },\r
68 \r
69 getAttribution: function() {\r
70 return this.options.attribution;\r
71 },\r
72 \r
73 setOpacity: function(opacity) {\r
74 this.options.opacity = opacity;\r
75 \r
76 this._setOpacity(opacity);\r
77 \r
78 // stupid webkit hack to force redrawing of tiles\r
79 if (L.Browser.webkit) {\r
80 for (i in this._tiles) {\r
81 this._tiles[i].style.webkitTransform += ' translate(0,0)';\r
82 }\r
83 }\r
84 },\r
85 \r
86 _setOpacity: function(opacity) {\r
87 if (opacity < 1) {\r
88 L.DomUtil.setOpacity(this._container, opacity);\r
89 }\r
90 },\r
91 \r
92 _initContainer: function() {\r
93 var tilePane = this._map.getPanes().tilePane;\r
94 \r
95 if (!this._container || tilePane.empty) {\r
96 this._container = L.DomUtil.create('div', 'leaflet-layer', tilePane);\r
97 \r
98 this._setOpacity(this.options.opacity);\r
99 }\r
100 },\r
101 \r
102 _reset: function() {\r
103 this._tiles = {};\r
104 this._initContainer();\r
105 this._container.innerHTML = '';\r
106 },\r
107 \r
108 _update: function() {\r
109 var bounds = this._map.getPixelBounds(),\r
110 tileSize = this.options.tileSize;\r
111 \r
112 var nwTilePoint = new L.Point(\r
113 Math.floor(bounds.min.x / tileSize),\r
114 Math.floor(bounds.min.y / tileSize)),\r
115 seTilePoint = new L.Point(\r
116 Math.floor(bounds.max.x / tileSize),\r
117 Math.floor(bounds.max.y / tileSize)),\r
118 tileBounds = new L.Bounds(nwTilePoint, seTilePoint);\r
119 \r
120 this._addTilesFromCenterOut(tileBounds);\r
121 \r
122 if (this.options.unloadInvisibleTiles) {\r
123 this._removeOtherTiles(tileBounds);\r
124 }\r
125 },\r
126 \r
127 _addTilesFromCenterOut: function(bounds) {\r
128 var queue = [],\r
129 center = bounds.getCenter();\r
130 \r
131 for (var j = bounds.min.y; j <= bounds.max.y; j++) {\r
132 for (var i = bounds.min.x; i <= bounds.max.x; i++) { \r
133 if ((i + ':' + j) in this._tiles) { continue; }\r
134 queue.push(new L.Point(i, j));\r
135 }\r
136 }\r
137 \r
138 // load tiles in order of their distance to center\r
139 queue.sort(function(a, b) {\r
140 return a.distanceTo(center) - b.distanceTo(center);\r
141 });\r
142 \r
143 this._tilesToLoad = queue.length;\r
144 for (var k = 0, len = this._tilesToLoad; k < len; k++) {\r
145 this._addTile(queue[k]);\r
146 }\r
147 },\r
148 \r
149 _removeOtherTiles: function(bounds) {\r
150 var kArr, x, y, key;\r
151 \r
152 for (key in this._tiles) {\r
153 if (this._tiles.hasOwnProperty(key)) {\r
154 kArr = key.split(':');\r
155 x = parseInt(kArr[0], 10);\r
156 y = parseInt(kArr[1], 10);\r
157 \r
158 // remove tile if it's out of bounds\r
159 if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) {\r
160 this._tiles[key].src = '';\r
161 if (this._tiles[key].parentNode == this._container) {\r
162 this._container.removeChild(this._tiles[key]);\r
163 }\r
164 delete this._tiles[key];\r
165 }\r
166 }\r
167 } \r
168 },\r
169 \r
170 _addTile: function(tilePoint) {\r
171 var tilePos = this._getTilePos(tilePoint),\r
172 zoom = this._map.getZoom(),\r
173 key = tilePoint.x + ':' + tilePoint.y;\r
174 \r
175 // wrap tile coordinates\r
176 var tileLimit = (1 << zoom);\r
177 if (!this.options.noWrap) {\r
178 tilePoint.x = ((tilePoint.x % tileLimit) + tileLimit) % tileLimit;\r
179 }\r
180 if (tilePoint.y < 0 || tilePoint.y >= tileLimit) { return; }\r
181 \r
182 // create tile\r
183 var tile = this._createTile();\r
184 L.DomUtil.setPosition(tile, tilePos);\r
185 \r
186 this._tiles[key] = tile;\r
187 \r
188 if (this.options.scheme == 'tms') {\r
189 tilePoint.y = tileLimit - tilePoint.y - 1;\r
190 }\r
191\r
192 this._loadTile(tile, tilePoint, zoom);\r
193 \r
194 this._container.appendChild(tile);\r
195 },\r
196 \r
197 _getTilePos: function(tilePoint) {\r
198 var origin = this._map.getPixelOrigin(),\r
199 tileSize = this.options.tileSize;\r
200 \r
201 return tilePoint.multiplyBy(tileSize).subtract(origin);\r
202 },\r
203 \r
204 // image-specific code (override to implement e.g. Canvas or SVG tile layer)\r
205 \r
206 getTileUrl: function(tilePoint, zoom) {\r
207 var subdomains = this.options.subdomains,\r
208 s = this.options.subdomains[(tilePoint.x + tilePoint.y) % subdomains.length];\r
209\r
210 return this._url\r
211 .replace('{s}', s)\r
212 .replace('{z}', zoom)\r
213 .replace('{x}', tilePoint.x)\r
214 .replace('{y}', tilePoint.y);\r
215 },\r
216 \r
217 _createTileProto: function() {\r
218 this._tileImg = L.DomUtil.create('img', 'leaflet-tile');\r
219 this._tileImg.galleryimg = 'no';\r
220 \r
221 var tileSize = this.options.tileSize;\r
222 this._tileImg.style.width = tileSize + 'px';\r
223 this._tileImg.style.height = tileSize + 'px';\r
224 },\r
225 \r
226 _createTile: function() {\r
227 var tile = this._tileImg.cloneNode(false);\r
228 tile.onselectstart = tile.onmousemove = L.Util.falseFn;\r
229 return tile;\r
230 },\r
231 \r
232 _loadTile: function(tile, tilePoint, zoom) {\r
233 tile._layer = this;\r
234 tile.onload = this._tileOnLoad;\r
235 tile.onerror = this._tileOnError;\r
236 tile.src = this.getTileUrl(tilePoint, zoom);\r
237 },\r
238 \r
239 _tileOnLoad: function(e) {\r
240 var layer = this._layer;\r
241 \r
242 this.className += ' leaflet-tile-loaded'; \r
243\r
244 layer.fire('tileload', {tile: this, url: this.src});\r
245 \r
246 layer._tilesToLoad--;\r
247 if (!layer._tilesToLoad) {\r
248 layer.fire('load');\r
249 }\r
250 },\r
251 \r
252 _tileOnError: function(e) {\r
253 var layer = this._layer;\r
254 \r
255 layer.fire('tileerror', {tile: this, url: this.src});\r
256 \r
257 var newUrl = layer.options.errorTileUrl;\r
258 if (newUrl) {\r
259 this.src = newUrl;\r
260 }\r
261 }\r
262});\r