foliage 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +9 -0
  4. data/app/assets/images/.keep +0 -0
  5. data/app/assets/images/map/marker/icon-2x.png +0 -0
  6. data/app/assets/images/map/marker/icon.png +0 -0
  7. data/app/assets/images/map/marker/icon.svg +67 -0
  8. data/app/assets/images/map/marker/shadow.png +0 -0
  9. data/app/assets/javascripts/core_ext.js.coffee +61 -0
  10. data/app/assets/javascripts/foliage.js.coffee +23 -0
  11. data/app/assets/javascripts/foliage/band.js.coffee +99 -0
  12. data/app/assets/javascripts/foliage/bubbles.js.coffee +77 -0
  13. data/app/assets/javascripts/foliage/categories.js.coffee +70 -0
  14. data/app/assets/javascripts/foliage/choropleth.js.coffee +51 -0
  15. data/app/assets/javascripts/foliage/color.js.coffee +39 -0
  16. data/app/assets/javascripts/foliage/gradient.js.coffee +72 -0
  17. data/app/assets/javascripts/foliage/heatmap.js.coffee +49 -0
  18. data/app/assets/javascripts/foliage/leaf.js.coffee +422 -0
  19. data/app/assets/javascripts/foliage/path.js.coffee +76 -0
  20. data/app/assets/javascripts/foliage/paths.js.coffee +131 -0
  21. data/app/assets/javascripts/foliage/point_group.js.coffee +83 -0
  22. data/app/assets/javascripts/foliage/points.js.coffee +79 -0
  23. data/app/assets/javascripts/foliage/simple.js.coffee +35 -0
  24. data/app/assets/javascripts/leaflet/geographic_util.js.coffee +23 -0
  25. data/app/assets/javascripts/leaflet/ghost_label.js.coffee +100 -0
  26. data/app/assets/javascripts/leaflet/ghost_label_cluster.js.coffee +192 -0
  27. data/app/assets/javascripts/leaflet/layers_scheduler.js.coffee +57 -0
  28. data/app/assets/javascripts/leaflet/reactive_measure.js.coffee +414 -0
  29. data/app/assets/stylesheets/all.scss +16 -0
  30. data/app/assets/stylesheets/application.css +15 -0
  31. data/app/assets/stylesheets/compass/reset.scss +3 -0
  32. data/app/assets/stylesheets/compass/reset/utilities.scss +142 -0
  33. data/app/assets/stylesheets/leaflet.scss +1093 -0
  34. data/app/assets/stylesheets/leaflet/label.scss +40 -0
  35. data/app/assets/stylesheets/leaflet/tooltip.scss +42 -0
  36. data/app/assets/stylesheets/mixins.scss +131 -0
  37. data/app/assets/stylesheets/reset.scss +89 -0
  38. data/app/assets/stylesheets/variables.scss +47 -0
  39. data/app/helpers/foliage_helper.rb +23 -0
  40. data/lib/foliage.rb +9 -0
  41. data/lib/foliage/leaf.rb +235 -0
  42. data/lib/foliage/rails.rb +2 -0
  43. data/lib/foliage/rails/engine.rb +7 -0
  44. data/lib/foliage/rails/integration.rb +8 -0
  45. data/lib/foliage/version.rb +3 -0
  46. data/vendor/assets/javascripts/.keep +0 -0
  47. data/vendor/assets/javascripts/autosize.js +211 -0
  48. data/vendor/assets/javascripts/geographiclib.js +3074 -0
  49. data/vendor/assets/javascripts/leaflet.js.erb +9175 -0
  50. data/vendor/assets/javascripts/leaflet/draw.js +3573 -0
  51. data/vendor/assets/javascripts/leaflet/easy-button.js +366 -0
  52. data/vendor/assets/javascripts/leaflet/fullscreen.js +162 -0
  53. data/vendor/assets/javascripts/leaflet/heatmap.js +142 -0
  54. data/vendor/assets/javascripts/leaflet/label.js +545 -0
  55. data/vendor/assets/javascripts/leaflet/measure.js +6966 -0
  56. data/vendor/assets/javascripts/leaflet/modal.js +364 -0
  57. data/vendor/assets/javascripts/leaflet/providers.js +479 -0
  58. data/vendor/assets/javascripts/rbush.js +621 -0
  59. data/vendor/assets/stylesheets/.keep +0 -0
  60. data/vendor/assets/stylesheets/bootstrap/mixins.scss +55 -0
  61. data/vendor/assets/stylesheets/bootstrap/variables.scss +10 -0
  62. data/vendor/assets/stylesheets/leaflet.scss +479 -0
  63. data/vendor/assets/stylesheets/leaflet/draw.scss +282 -0
  64. data/vendor/assets/stylesheets/leaflet/easy-button.scss +56 -0
  65. data/vendor/assets/stylesheets/leaflet/fullscreen.scss +2 -0
  66. data/vendor/assets/stylesheets/leaflet/measure.scss +168 -0
  67. data/vendor/assets/stylesheets/leaflet/modal.scss +85 -0
  68. metadata +171 -0
@@ -0,0 +1,364 @@
1
+ /**
2
+ * Leaflet modal control
3
+ *
4
+ * @license MIT
5
+ * @author Alexander Milevski <info@w8r.name>
6
+ * @preserve
7
+ */
8
+
9
+ "use strict";
10
+
11
+ /**
12
+ * "foo bar baz" -> ".foo.bar.baz"
13
+ * @param {String} classString
14
+ * @return {String}
15
+ */
16
+ L.DomUtil.classNameToSelector = function(classString) {
17
+ return (' ' + classString).split(' ').join('.').replace(/^\s+|\s+$/g, '');
18
+ };
19
+
20
+ /**
21
+ * Modal handler
22
+ * @class {L.Map.Modal}
23
+ * @extends {L.Mixin.Events}
24
+ * @extends {L.Handler}
25
+ */
26
+ L.Map.Modal = L.Handler.extend( /** @lends {L.Map.Hadler.prototype} */ {
27
+
28
+ includes: L.Mixin.Events,
29
+
30
+ /**
31
+ * @static
32
+ * @type {Object}
33
+ */
34
+ statics: {
35
+ BASE_CLS: 'leaflet-modal',
36
+ HIDE: 'modal.hide',
37
+ SHOW_START: 'modal.showStart',
38
+ SHOW: 'modal.show',
39
+ CHANGED: 'modal.changed'
40
+ },
41
+
42
+ /**
43
+ * @type {Object}
44
+ */
45
+ options: {
46
+ OVERLAY_CLS: 'overlay',
47
+ MODAL_CLS: 'modal',
48
+ MODAL_CONTENT_CLS: 'modal-content',
49
+ INNER_CONTENT_CLS: 'modal-inner',
50
+ SHOW_CLS: 'show',
51
+ CLOSE_CLS: 'close',
52
+
53
+ closeTitle: 'close',
54
+ zIndex: 10000,
55
+ transitionDuration: 300,
56
+
57
+ wrapperTemplate: [
58
+ '<div class="{OVERLAY_CLS}"></div>',
59
+ '<div class="{MODAL_CLS}"><div class="{MODAL_CONTENT_CLS}">',
60
+ '<span class="{CLOSE_CLS}" title="{closeTitle}">&times;</span>',
61
+ '<div class="{INNER_CONTENT_CLS}">{_content}</div>',
62
+ '</div></div>'
63
+ ].join(''),
64
+
65
+ template: '{content}',
66
+ content: ''
67
+
68
+ },
69
+
70
+ /**
71
+ * @constructor
72
+ * @param {L.Map} map
73
+ * @param {Object=} options
74
+ */
75
+ initialize: function(map, options) {
76
+ L.Handler.prototype.initialize.call(this, map);
77
+ L.Util.setOptions(this, options);
78
+
79
+ this._visible = false;
80
+
81
+ var container = this._container =
82
+ L.DomUtil.create('div', L.Map.Modal.BASE_CLS, map._container);
83
+ container.style.zIndex = this.options.zIndex;
84
+ container.style.position = 'absolute';
85
+ this._map._controlContainer.appendChild(container);
86
+
87
+ L.DomEvent
88
+ .disableClickPropagation(container)
89
+ .disableScrollPropagation(container);
90
+ L.DomEvent.on(container, 'contextmenu', L.DomEvent.stopPropagation);
91
+
92
+ this.enable();
93
+ },
94
+
95
+ /**
96
+ * Add events and keyboard handlers
97
+ */
98
+ addHooks: function() {
99
+ L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
100
+ this._map.on({
101
+ modal: this._show
102
+ }, this);
103
+ },
104
+
105
+ /**
106
+ * Disable handlers
107
+ */
108
+ removeHooks: function() {
109
+ L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
110
+ this._map.off({
111
+ modal: this._show
112
+ }, this);
113
+ },
114
+
115
+ /**
116
+ * @return {L.Map.Modal}
117
+ */
118
+ hide: function() {
119
+ this._hide();
120
+ return this;
121
+ },
122
+
123
+ /**
124
+ * @return {Boolean}
125
+ */
126
+ isVisible: function() {
127
+ return this._visible;
128
+ },
129
+
130
+ /**
131
+ * Show again, or just resize and re-position
132
+ * @param {Object=} options
133
+ */
134
+ update: function(options) {
135
+ if (options) {
136
+ this._show(options);
137
+ } else {
138
+ this._updatePosition();
139
+ }
140
+ },
141
+
142
+ /**
143
+ * @param {String} content
144
+ */
145
+ setContent: function(content) {
146
+ this._getInnerContentContainer().innerHTML = content;
147
+ this.update();
148
+ },
149
+
150
+ /**
151
+ * Update container position
152
+ */
153
+ _updatePosition: function() {
154
+ var content = this._getContentContainer();
155
+ var mapSize = this._map.getSize();
156
+
157
+ if (content.offsetHeight < mapSize.y) {
158
+ content.style.marginTop = ((mapSize.y - content.offsetHeight) / 2) + 'px';
159
+ } else {
160
+ content.style.marginTop = '';
161
+ }
162
+ },
163
+
164
+ /**
165
+ * @param {Object} options
166
+ */
167
+ _show: function(options) {
168
+ if (this._visible) {
169
+ this._hide();
170
+ }
171
+ options = L.Util.extend({}, this.options, options);
172
+
173
+ this._render(options);
174
+ this._setContainerSize(options);
175
+
176
+ this._updatePosition();
177
+
178
+ L.Util.requestAnimFrame(function() {
179
+ var contentContainer = this._getContentContainer();
180
+ L.DomEvent.on(contentContainer, 'transitionend', this._onTransitionEnd, this);
181
+ L.DomEvent.disableClickPropagation(contentContainer);
182
+ L.DomUtil.addClass(this._container, this.options.SHOW_CLS);
183
+
184
+ if (!L.Browser.any3d) {
185
+ L.Util.requestAnimFrame(this._onTransitionEnd, this);
186
+ }
187
+ }, this);
188
+
189
+ var closeBtn = this._container.querySelector('.' + this.options.CLOSE_CLS);
190
+ if (closeBtn) {
191
+ L.DomEvent.on(closeBtn, 'click', this._onCloseClick, this);
192
+ }
193
+
194
+ var modal = this._container.querySelector('.' + this.options.MODAL_CLS);
195
+ if (modal) {
196
+ L.DomEvent.on(modal, 'mousedown', this._onMouseDown, this);
197
+ }
198
+
199
+ // callbacks
200
+ if (typeof options.onShow === 'function') {
201
+ this._map.once(L.Map.Modal.SHOW, options.onShow);
202
+ }
203
+
204
+ if (typeof options.onHide === 'function') {
205
+ this._map.once(L.Map.Modal.HIDE, options.onHide);
206
+ }
207
+
208
+ // fire event
209
+ this._map.fire(L.Map.Modal.SHOW_START, {
210
+ modal: this
211
+ });
212
+ },
213
+
214
+ /**
215
+ * Show transition ended
216
+ * @param {TransitionEvent=} e
217
+ */
218
+ _onTransitionEnd: function(e) {
219
+ var data = {
220
+ modal: this
221
+ };
222
+ var map = this._map;
223
+ if (!this._visible) {
224
+ if (L.DomUtil.hasClass(this._container, this.options.SHOW_CLS)) {
225
+ this._visible = true;
226
+ map.fire(L.Map.Modal.SHOW, data);
227
+ } else {
228
+ map.fire(L.Map.Modal.HIDE, data);
229
+ }
230
+ } else {
231
+ map.fire(L.Map.Modal.CHANGED, data);
232
+ }
233
+ },
234
+
235
+ /**
236
+ * @param {L.MouseEvent} evt
237
+ */
238
+ _onCloseClick: function(evt) {
239
+ L.DomEvent.stop(evt);
240
+ this._hide();
241
+ },
242
+
243
+ /**
244
+ * Render template
245
+ * @param {Object} options
246
+ */
247
+ _render: function(options) {
248
+ this._container.innerHTML = L.Util.template(
249
+ options.wrapperTemplate,
250
+ L.Util.extend({}, options, {
251
+ _content: L.Util.template(options.template, options)
252
+ })
253
+ );
254
+ if (options.element) {
255
+ var contentContainer = this._container.querySelector(
256
+ L.DomUtil.classNameToSelector(this.options.MODAL_CONTENT_CLS));
257
+ if (contentContainer) {
258
+ contentContainer.appendChild(options.element);
259
+ }
260
+ }
261
+ },
262
+
263
+ /**
264
+ * @return {Element}
265
+ */
266
+ _getContentContainer: function() {
267
+ return this._container.querySelector(
268
+ L.DomUtil.classNameToSelector(this.options.MODAL_CONTENT_CLS));
269
+ },
270
+
271
+ /**
272
+ * Inner content, don't touch destroy button
273
+ * @return {Element}
274
+ */
275
+ _getInnerContentContainer: function() {
276
+ return this._container.querySelector(
277
+ L.DomUtil.classNameToSelector(this.options.INNER_CONTENT_CLS))
278
+ },
279
+
280
+ /**
281
+ * @param {Object} options
282
+ * @param {Number} options.width
283
+ * @param {Number} options.height
284
+ */
285
+ _setContainerSize: function(options) {
286
+ var content = this._getContentContainer();
287
+
288
+ if (options.width) {
289
+ content.style.width = options.width + 'px';
290
+ }
291
+
292
+ if (options.height) {
293
+ content.style.height = options.height + 'px';
294
+ }
295
+ },
296
+
297
+ /**
298
+ * Hide blocks immediately
299
+ */
300
+ _hideInternal: function() {
301
+ this._visible = false;
302
+ L.DomUtil.removeClass(this._container, this.options.SHOW_CLS);
303
+ },
304
+
305
+ /**
306
+ * Hide modal
307
+ */
308
+ _hide: function() {
309
+ if (this._visible) {
310
+ this._hideInternal();
311
+
312
+ if (!L.Browser.any3d) {
313
+ L.Util.requestAnimFrame(this._onTransitionEnd, this);
314
+ }
315
+ }
316
+ },
317
+
318
+ /**
319
+ * Mouse down on overlay
320
+ * @param {L.MouseEvent} evt
321
+ */
322
+ _onMouseDown: function(evt) {
323
+ L.DomEvent.stop(evt);
324
+ var target = (evt.target || evt.srcElement);
325
+ if (L.DomUtil.hasClass(target, this.options.MODAL_CLS)) {
326
+ this._hide();
327
+ }
328
+ },
329
+
330
+ /**
331
+ * Key stroke(escape)
332
+ * @param {KeyboardEvent} evt
333
+ */
334
+ _onKeyDown: function(evt) {
335
+ var key = evt.keyCode || evt.which;
336
+ if (key === 27) {
337
+ this._hide();
338
+ }
339
+ }
340
+
341
+ });
342
+
343
+ // register hook
344
+ L.Map.addInitHook('addHandler', 'modal', L.Map.Modal);
345
+
346
+ L.Map.include( /** @lends {L.Map.prototype} */ {
347
+
348
+ /**
349
+ * @param {Object} options
350
+ * @return {L.Map}
351
+ */
352
+ openModal: function(options) {
353
+ return this.fire('modal', options);
354
+ },
355
+
356
+ /**
357
+ * @return {L.Map}
358
+ */
359
+ closeModal: function() {
360
+ this.modal.hide();
361
+ return this;
362
+ }
363
+
364
+ });
@@ -0,0 +1,479 @@
1
+ (function () {
2
+ 'use strict';
3
+
4
+ L.TileLayer.Provider = L.TileLayer.extend({
5
+ initialize: function (arg, options) {
6
+ var providers = L.TileLayer.Provider.providers;
7
+
8
+ var parts = arg.split('.');
9
+
10
+ var providerName = parts[0];
11
+ var variantName = parts[1];
12
+
13
+ if (!providers[providerName]) {
14
+ throw 'No such provider (' + providerName + ')';
15
+ }
16
+
17
+ var provider = {
18
+ url: providers[providerName].url,
19
+ options: providers[providerName].options
20
+ };
21
+
22
+ // overwrite values in provider from variant.
23
+ if (variantName && 'variants' in providers[providerName]) {
24
+ if (!(variantName in providers[providerName].variants)) {
25
+ throw 'No such name in provider (' + variantName + ')';
26
+ }
27
+ var variant = providers[providerName].variants[variantName];
28
+ provider = {
29
+ url: variant.url || provider.url,
30
+ options: L.Util.extend({}, provider.options, variant.options)
31
+ };
32
+ } else if (typeof provider.url === 'function') {
33
+ provider.url = provider.url(parts.splice(1).join('.'));
34
+ }
35
+
36
+ // replace attribution placeholders with their values from toplevel provider attribution,
37
+ // recursively
38
+ var attributionReplacer = function (attr) {
39
+ if (attr.indexOf('{attribution.') === -1) {
40
+ return attr;
41
+ }
42
+ return attr.replace(/\{attribution.(\w*)\}/,
43
+ function (match, attributionName) {
44
+ return attributionReplacer(providers[attributionName].options.attribution);
45
+ }
46
+ );
47
+ };
48
+ provider.options.attribution = attributionReplacer(provider.options.attribution);
49
+
50
+ // Compute final options combining provider options with any user overrides
51
+ var layerOpts = L.Util.extend({}, provider.options, options);
52
+ L.TileLayer.prototype.initialize.call(this, provider.url, layerOpts);
53
+ }
54
+ });
55
+
56
+ /**
57
+ * Definition of providers.
58
+ * see http://leafletjs.com/reference.html#tilelayer for options in the options map.
59
+ */
60
+
61
+ //jshint maxlen:220
62
+ L.TileLayer.Provider.providers = {
63
+ OpenStreetMap: {
64
+ url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
65
+ options: {
66
+ attribution:
67
+ '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' +
68
+ '<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'
69
+ },
70
+ variants: {
71
+ Mapnik: {},
72
+ BlackAndWhite: {
73
+ url: 'http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png'
74
+ },
75
+ DE: {
76
+ url: 'http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png'
77
+ },
78
+ HOT: {
79
+ url: 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
80
+ options: {
81
+ attribution: '{attribution.OpenStreetMap}, Tiles courtesy of <a href="http://hot.openstreetmap.org/" target="_blank">Humanitarian OpenStreetMap Team</a>'
82
+ }
83
+ }
84
+ }
85
+ },
86
+ OpenCycleMap: {
87
+ url: 'http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png',
88
+ options: {
89
+ attribution:
90
+ '&copy; <a href="http://www.opencyclemap.org">OpenCycleMap</a>, {attribution.OpenStreetMap}'
91
+ }
92
+ },
93
+ OpenSeaMap: {
94
+ url: 'http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png',
95
+ options: {
96
+ attribution: 'Map data: &copy; <a href="http://www.openseamap.org">OpenSeaMap</a> contributors'
97
+ }
98
+ },
99
+ Thunderforest: {
100
+ url: 'http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png',
101
+ options: {
102
+ attribution: '{attribution.OpenCycleMap}'
103
+ },
104
+ variants: {
105
+ OpenCycleMap: {},
106
+ Transport: {
107
+ url: 'http://{s}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png'
108
+ },
109
+ Landscape: {
110
+ url: 'http://{s}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png'
111
+ },
112
+ Outdoors: {
113
+ url: 'http://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png'
114
+ }
115
+ }
116
+ },
117
+ OpenMapSurfer: {
118
+ url: 'http://openmapsurfer.uni-hd.de/tiles/roads/x={x}&y={y}&z={z}',
119
+ options: {
120
+ attribution: 'Imagery from <a href="http://giscience.uni-hd.de/">GIScience Research Group @ University of Heidelberg</a> &mdash; Map data {attribution.OpenStreetMap}'
121
+ },
122
+ variants: {
123
+ Roads: {},
124
+ AdminBounds: {
125
+ url: 'http://openmapsurfer.uni-hd.de/tiles/adminb/x={x}&y={y}&z={z}'
126
+ },
127
+ Grayscale: {
128
+ url: 'http://openmapsurfer.uni-hd.de/tiles/roadsg/x={x}&y={y}&z={z}'
129
+ }
130
+ }
131
+ },
132
+ MapQuestOpen: {
133
+ url: 'http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpeg',
134
+ options: {
135
+ attribution:
136
+ 'Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a> &mdash; ' +
137
+ 'Map data {attribution.OpenStreetMap}',
138
+ subdomains: '1234'
139
+ },
140
+ variants: {
141
+ OSM: {},
142
+ Aerial: {
143
+ url: 'http://oatile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg',
144
+ options: {
145
+ attribution:
146
+ 'Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a> &mdash; ' +
147
+ 'Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency'
148
+ }
149
+ }
150
+ }
151
+ },
152
+ MapBox: {
153
+ url: function (id) {
154
+ return 'http://{s}.tiles.mapbox.com/v3/' + id + '/{z}/{x}/{y}.png';
155
+ },
156
+ options: {
157
+ attribution:
158
+ 'Imagery from <a href="http://mapbox.com/about/maps/">MapBox</a> &mdash; ' +
159
+ 'Map data {attribution.OpenStreetMap}',
160
+ subdomains: 'abcd'
161
+ }
162
+ },
163
+ Stamen: {
164
+ url: 'http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png',
165
+ options: {
166
+ attribution:
167
+ 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, ' +
168
+ '<a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a> &mdash; ' +
169
+ 'Map data {attribution.OpenStreetMap}',
170
+ subdomains: 'abcd',
171
+ minZoom: 0,
172
+ maxZoom: 20
173
+ },
174
+ variants: {
175
+ Toner: {},
176
+ TonerBackground: {
177
+ url: 'http://{s}.tile.stamen.com/toner-background/{z}/{x}/{y}.png'
178
+ },
179
+ TonerHybrid: {
180
+ url: 'http://{s}.tile.stamen.com/toner-hybrid/{z}/{x}/{y}.png'
181
+ },
182
+ TonerLines: {
183
+ url: 'http://{s}.tile.stamen.com/toner-lines/{z}/{x}/{y}.png'
184
+ },
185
+ TonerLabels: {
186
+ url: 'http://{s}.tile.stamen.com/toner-labels/{z}/{x}/{y}.png'
187
+ },
188
+ TonerLite: {
189
+ url: 'http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png'
190
+ },
191
+ Terrain: {
192
+ url: 'http://{s}.tile.stamen.com/terrain/{z}/{x}/{y}.jpg',
193
+ options: {
194
+ minZoom: 4,
195
+ maxZoom: 18
196
+ }
197
+ },
198
+ TerrainBackground: {
199
+ url: 'http://{s}.tile.stamen.com/terrain-background/{z}/{x}/{y}.jpg',
200
+ options: {
201
+ minZoom: 4,
202
+ maxZoom: 18
203
+ }
204
+ },
205
+ Watercolor: {
206
+ url: 'http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.jpg',
207
+ options: {
208
+ minZoom: 3,
209
+ maxZoom: 16
210
+ }
211
+ }
212
+ }
213
+ },
214
+ Esri: {
215
+ url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
216
+ options: {
217
+ attribution: 'Tiles &copy; Esri'
218
+ },
219
+ variants: {
220
+ WorldStreetMap: {
221
+ options: {
222
+ attribution:
223
+ '{attribution.Esri} &mdash; ' +
224
+ 'Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012'
225
+ }
226
+ },
227
+ DeLorme: {
228
+ url: 'http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/{z}/{y}/{x}',
229
+ options: {
230
+ minZoom: 1,
231
+ maxZoom: 11,
232
+ attribution: '{attribution.Esri} &mdash; Copyright: &copy;2012 DeLorme'
233
+ }
234
+ },
235
+ WorldTopoMap: {
236
+ url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
237
+ options: {
238
+ attribution:
239
+ '{attribution.Esri} &mdash; ' +
240
+ 'Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community'
241
+ }
242
+ },
243
+ WorldImagery: {
244
+ url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
245
+ options: {
246
+ attribution:
247
+ '{attribution.Esri} &mdash; ' +
248
+ 'Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
249
+ }
250
+ },
251
+ WorldTerrain: {
252
+ url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{z}/{y}/{x}',
253
+ options: {
254
+ maxZoom: 13,
255
+ attribution:
256
+ '{attribution.Esri} &mdash; ' +
257
+ 'Source: USGS, Esri, TANA, DeLorme, and NPS'
258
+ }
259
+ },
260
+ WorldShadedRelief: {
261
+ url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}',
262
+ options: {
263
+ maxZoom: 13,
264
+ attribution: '{attribution.Esri} &mdash; Source: Esri'
265
+ }
266
+ },
267
+ WorldPhysical: {
268
+ url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer/tile/{z}/{y}/{x}',
269
+ options: {
270
+ maxZoom: 8,
271
+ attribution: '{attribution.Esri} &mdash; Source: US National Park Service'
272
+ }
273
+ },
274
+ OceanBasemap: {
275
+ url: 'http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/{z}/{y}/{x}',
276
+ options: {
277
+ maxZoom: 13,
278
+ attribution: '{attribution.Esri} &mdash; Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri'
279
+ }
280
+ },
281
+ NatGeoWorldMap: {
282
+ url: 'http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}',
283
+ options: {
284
+ maxZoom: 16,
285
+ attribution: '{attribution.Esri} &mdash; National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC'
286
+ }
287
+ },
288
+ WorldGrayCanvas: {
289
+ url: 'http://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}',
290
+ options: {
291
+ maxZoom: 16,
292
+ attribution: '{attribution.Esri} &mdash; Esri, DeLorme, NAVTEQ'
293
+ }
294
+ }
295
+ }
296
+ },
297
+ OpenWeatherMap: {
298
+ options: {
299
+ attribution: 'Map data &copy; <a href="http://openweathermap.org">OpenWeatherMap</a>',
300
+ opacity: 0.5
301
+ },
302
+ variants: {
303
+ Clouds: {
304
+ url: 'http://{s}.tile.openweathermap.org/map/clouds/{z}/{x}/{y}.png'
305
+ },
306
+ CloudsClassic: {
307
+ url: 'http://{s}.tile.openweathermap.org/map/clouds_cls/{z}/{x}/{y}.png'
308
+ },
309
+ Precipitation: {
310
+ url: 'http://{s}.tile.openweathermap.org/map/precipitation/{z}/{x}/{y}.png'
311
+ },
312
+ PrecipitationClassic: {
313
+ url: 'http://{s}.tile.openweathermap.org/map/precipitation_cls/{z}/{x}/{y}.png'
314
+ },
315
+ Rain: {
316
+ url: 'http://{s}.tile.openweathermap.org/map/rain/{z}/{x}/{y}.png'
317
+ },
318
+ RainClassic: {
319
+ url: 'http://{s}.tile.openweathermap.org/map/rain_cls/{z}/{x}/{y}.png'
320
+ },
321
+ Pressure: {
322
+ url: 'http://{s}.tile.openweathermap.org/map/pressure/{z}/{x}/{y}.png'
323
+ },
324
+ PressureContour: {
325
+ url: 'http://{s}.tile.openweathermap.org/map/pressure_cntr/{z}/{x}/{y}.png'
326
+ },
327
+ Wind: {
328
+ url: 'http://{s}.tile.openweathermap.org/map/wind/{z}/{x}/{y}.png'
329
+ },
330
+ Temperature: {
331
+ url: 'http://{s}.tile.openweathermap.org/map/temp/{z}/{x}/{y}.png'
332
+ },
333
+ Snow: {
334
+ url: 'http://{s}.tile.openweathermap.org/map/snow/{z}/{x}/{y}.png'
335
+ }
336
+ }
337
+ },
338
+ Nokia: {
339
+ options: {
340
+ attribution:
341
+ 'Map &copy; <a href="http://developer.here.com">Nokia</a>, Data &copy; NAVTEQ 2012',
342
+ subdomains: '1234',
343
+ devID: 'xyz', //These basemaps are free and you can sign up here: http://developer.here.com/plans
344
+ appID: 'abc'
345
+ },
346
+ variants: {
347
+ normalDay: {
348
+ url: 'http://{s}.maptile.lbs.ovi.com/maptiler/v2/maptile/newest/normal.day/{z}/{x}/{y}/256/png8?token={devID}&app_id={appID}'
349
+ },
350
+ normalGreyDay: {
351
+ url: 'http://{s}.maptile.lbs.ovi.com/maptiler/v2/maptile/newest/normal.day.grey/{z}/{x}/{y}/256/png8?token={devID}&app_id={appID}'
352
+ },
353
+ satelliteNoLabelsDay: {
354
+ url: 'http://{s}.maptile.lbs.ovi.com/maptiler/v2/maptile/newest/satellite.day/{z}/{x}/{y}/256/png8?token={devID}&app_id={appID}'
355
+ },
356
+ satelliteYesLabelsDay: {
357
+ url: 'http://{s}.maptile.lbs.ovi.com/maptiler/v2/maptile/newest/hybrid.day/{z}/{x}/{y}/256/png8?token={devID}&app_id={appID}'
358
+ },
359
+ terrainDay: {
360
+ url: 'http://{s}.maptile.lbs.ovi.com/maptiler/v2/maptile/newest/terrain.day/{z}/{x}/{y}/256/png8?token={devID}&app_id={appID}'
361
+ }
362
+ }
363
+ },
364
+ Acetate: {
365
+ url: 'http://a{s}.acetate.geoiq.com/tiles/acetate-hillshading/{z}/{x}/{y}.png',
366
+ options: {
367
+ attribution:
368
+ '&copy;2012 Esri & Stamen, Data from OSM and Natural Earth',
369
+ subdomains: '0123',
370
+ minZoom: 2,
371
+ maxZoom: 18
372
+ },
373
+ variants: {
374
+ all: {},
375
+ basemap: {
376
+ url: 'http://a{s}.acetate.geoiq.com/tiles/acetate-base/{z}/{x}/{y}.png'
377
+ },
378
+ terrain: {
379
+ url: 'http://a{s}.acetate.geoiq.com/tiles/terrain/{z}/{x}/{y}.png'
380
+ },
381
+ foreground: {
382
+ url: 'http://a{s}.acetate.geoiq.com/tiles/acetate-fg/{z}/{x}/{y}.png'
383
+ },
384
+ roads: {
385
+ url: 'http://a{s}.acetate.geoiq.com/tiles/acetate-roads/{z}/{x}/{y}.png'
386
+ },
387
+ labels: {
388
+ url: 'http://a{s}.acetate.geoiq.com/tiles/acetate-labels/{z}/{x}/{y}.png'
389
+ },
390
+ hillshading: {
391
+ url: 'http://a{s}.acetate.geoiq.com/tiles/hillshading/{z}/{x}/{y}.png'
392
+ }
393
+ }
394
+ },
395
+ CloudMade: {
396
+ url: 'http://{s}.tile.cloudmade.com/{apiKey}/{styleID}/256/{z}/{x}/{y}.png',
397
+ options: {
398
+ attribution:
399
+ 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' +
400
+ '<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' +
401
+ 'Map tile imagery © <a href="http://cloudmade.com">CloudMade</a>',
402
+ minZoom: 0,
403
+ apiKey: 'abc', // Sign up for an API key at http://cloudmade.com/ - first 500,000 tile requests are free
404
+ styleID: '1'
405
+ },
406
+ variants: {
407
+ standardResolution: {
408
+ maxZoom: 18
409
+ },
410
+ highResolution: {
411
+ url: 'http://{s}.tile.cloudmade.com/{apiKey}/{styleID}@2x/256/{z}/{x}/{y}.png',
412
+ maxZoom: 19
413
+ }
414
+ }
415
+ }
416
+ };
417
+
418
+ L.tileLayer.provider = function (provider, options) {
419
+ return new L.TileLayer.Provider(provider, options);
420
+ };
421
+
422
+ L.Control.Layers.Provided = L.Control.Layers.extend({
423
+ initialize: function (base, overlay, options) {
424
+ var first;
425
+
426
+ var labelFormatter = function (label) {
427
+ return label.replace(/\./g, ': ').replace(/([a-z])([A-Z])/g, '$1 $2');
428
+ };
429
+
430
+ if (base.length) {
431
+ (function () {
432
+ var out = {},
433
+ len = base.length,
434
+ i = 0;
435
+
436
+ while (i < len) {
437
+ if (typeof base[i] === 'string') {
438
+ if (i === 0) {
439
+ first = L.tileLayer.provider(base[0]);
440
+ out[labelFormatter(base[i])] = first;
441
+ } else {
442
+ out[labelFormatter(base[i])] = L.tileLayer.provider(base[i]);
443
+ }
444
+ }
445
+ i++;
446
+ }
447
+ base = out;
448
+ }());
449
+ this._first = first;
450
+ }
451
+
452
+ if (overlay && overlay.length) {
453
+ (function () {
454
+ var out = {},
455
+ len = overlay.length,
456
+ i = 0;
457
+
458
+ while (i < len) {
459
+ if (typeof base[i] === 'string') {
460
+ out[labelFormatter(overlay[i])] = L.tileLayer.provider(overlay[i]);
461
+ }
462
+ i++;
463
+ }
464
+ overlay = out;
465
+ }());
466
+ }
467
+ L.Control.Layers.prototype.initialize.call(this, base, overlay, options);
468
+ },
469
+ onAdd: function (map) {
470
+ this._first.addTo(map);
471
+ return L.Control.Layers.prototype.onAdd.call(this, map);
472
+ }
473
+ });
474
+
475
+ L.control.layers.provided = function (baseLayers, overlays, options) {
476
+ return new L.Control.Layers.Provided(baseLayers, overlays, options);
477
+ };
478
+ }());
479
+