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,51 @@
1
+ # Add sprockets directives below:
2
+ #= require foliage/gradient
3
+
4
+ class Foliage.Choropleth extends Foliage.Gradient
5
+
6
+ constructor: (@layer, @data, options = {}) ->
7
+ super @data, @layer.reference, options
8
+
9
+ if this.valid()
10
+
11
+ # Compute colors
12
+ start = new Foliage.Color(options.startColor)
13
+ stop = new Foliage.Color(options.stopColor)
14
+ for grade in @grades
15
+ level = grade.index / (@levelNumber - 1.0)
16
+ grade.fillColor = Foliage.Color.toString
17
+ red: start.red + (Math.round(stop.red - start.red) * level)
18
+ green: start.green + (Math.round(stop.green - start.green) * level)
19
+ blue: start.blue + (Math.round(stop.blue - start.blue) * level)
20
+ console.log "Colors computed"
21
+ else
22
+ console.warn "Invalid choropleth for #{@layer.reference}"
23
+ console.warn @data
24
+
25
+ # Build layer as wanted
26
+ buildLayerGroup: (widget, globalStyle = {}) ->
27
+ group = []
28
+ for zone in @data
29
+ zoneStyle =
30
+ fillColor: this.gradeFor(zone[@layer.reference]).fillColor
31
+ zoneLayer = new L.GeoJSON(zone.shape, $.extend(true, {}, globalStyle, zoneStyle))
32
+ widget._bindPopup(zoneLayer, zone)
33
+ group.push(zoneLayer)
34
+ group
35
+
36
+ # Build HTML legend for given choropleth computed layer
37
+ buildLegend: () ->
38
+ html = "<div class='leaflet-legend-item' id='legend-#{@layer.name}'>"
39
+ html += "<h3>#{@layer.label}"+ if @layer.unit then " (#{@layer.unit})" else "" +"</h3>"
40
+ html += "<div class='leaflet-legend-body leaflet-choropleth-scale'>"
41
+ html += "<span class='min-value'>#{@grades[0].minLabel}</span>"
42
+ html += "<span class='max-value'>#{@grades[@levelNumber - 1].maxLabel}</span>"
43
+ html += "<span class='leaflet-choropleth-grades'>"
44
+ for grade in @grades
45
+ html += "<i class='leaflet-choropleth-grade' style='width: #{100 / @levelNumber}%; background-color: #{grade.fillColor}' title='#{grade.minLabel} ~ #{grade.maxLabel}'></i>"
46
+ html += "</span>"
47
+ html += "</div>"
48
+ html += "</div>"
49
+ return html
50
+
51
+ Foliage.registerLayerType "choropleth", Foliage.Choropleth
@@ -0,0 +1,39 @@
1
+ # Add sprockets directives below:
2
+
3
+ class Foliage.Color
4
+ constructor: (color) ->
5
+ @red = parseInt(color.slice(1, 3), 16)
6
+ @green = parseInt(color.slice(3, 5), 16)
7
+ @blue = parseInt(color.slice(5, 7), 16)
8
+
9
+ toString: () ->
10
+ Foliage.Color.toString(this)
11
+
12
+
13
+ Foliage.Color.toString = (color) ->
14
+ return "##{Foliage.Color.toHexCanal(color.red)}#{Foliage.Color.toHexCanal(color.green)}#{Foliage.Color.toHexCanal(color.blue)}"
15
+
16
+ Foliage.Color.toHexCanal = (integer) ->
17
+ hex = Math.round(integer).toString(16)
18
+ if integer <= 0
19
+ return "00"
20
+ else if integer < 16
21
+ return "0" + hex
22
+ else if integer > 255
23
+ return "FF"
24
+ else
25
+ return hex
26
+
27
+ Foliage.Color.parse = (color) ->
28
+ value =
29
+ red: parseInt(color.slice(1, 3), 16)
30
+ green: parseInt(color.slice(3, 5), 16)
31
+ blue: parseInt(color.slice(5, 7), 16)
32
+ return value
33
+
34
+ Foliage.Color.random = () ->
35
+ value =
36
+ red: 16 * Math.round(16*Math.random())
37
+ green: 16 * Math.round(16*Math.random())
38
+ blue: 16 * Math.round(16*Math.random())
39
+ return Foliage.Color.toString value
@@ -0,0 +1,72 @@
1
+ # Add sprockets directives below:
2
+ #
3
+
4
+ # Computes grades for a given
5
+ class Foliage.Gradient
6
+
7
+ constructor: (data, property, options = {}) ->
8
+ options.round ?= 5
9
+ options.levelNumber ?= 7
10
+
11
+ # Default values
12
+ @round ?= options.round
13
+ @levelNumber ?= options.levelNumber
14
+
15
+ # Find min and max values
16
+ @grades = []
17
+ if data[0]
18
+ @maxValue = data[0][property]
19
+ @minValue = data[0][property]
20
+ for zone in data
21
+ if zone[property] > @maxValue
22
+ @maxValue = zone[property]
23
+ if zone[property] < @minValue
24
+ @minValue = zone[property]
25
+ if @maxValue == @minValue
26
+ console.warn "Force max value to be different of min value"
27
+ @maxValue += 1
28
+ else
29
+ console.warn "Sets default min and max without data"
30
+ @maxValue = 10
31
+ @minValue = 0
32
+ console.log "Exact min (#{@minValue}) and max (#{@maxValue}) computed"
33
+
34
+ # Simplify values
35
+ maxMagnitude = Math.magnitude(@maxValue)
36
+ minMagnitude = Math.magnitude(@minValue)
37
+ ref = minMagnitude
38
+ if maxMagnitude.power > minMagnitude.power
39
+ ref = maxMagnitude
40
+ @power = ref.power
41
+ mag = ref.magnitude
42
+ mag = mag / 10 if mag >= 100
43
+ @maxValue = Math.ceil2(@maxValue, mag * @round)
44
+ @minValue = Math.floor2(@minValue, mag * @round)
45
+ @length = @maxValue - @minValue
46
+
47
+ if @length > 0
48
+ if @levelNumber > @length and @length > 2
49
+ @levelNumber = @length
50
+ console.log "Min (#{@minValue}) and max (#{@maxValue}) computed"
51
+
52
+ # Compute grades
53
+ for g in [1..@levelNumber]
54
+ grade =
55
+ index: (g-1)
56
+ min: @minValue + (g-1) * @length / @levelNumber
57
+ max: @minValue + g * @length / @levelNumber
58
+ grade.minLabel = Math.humanize(grade.min, @power)
59
+ grade.maxLabel = Math.humanize(grade.max, @power)
60
+ @grades.push grade
61
+ console.log "Grades computed"
62
+
63
+ # Returns the grade for the given value
64
+ gradeFor: (value) ->
65
+ level = Math.round(@levelNumber * (value - @minValue) / @length)
66
+ level = @levelNumber - 1 if level >= @levelNumber
67
+ level = 0 if level < 0 or isNaN(level)
68
+ return @grades[level]
69
+
70
+ # Returns if the gradient is valid
71
+ valid: () ->
72
+ @length > 0
@@ -0,0 +1,49 @@
1
+ class Foliage.Heatmap
2
+
3
+ constructor: (@layer, @data, options = {}) ->
4
+ @items = []
5
+ property = @layer.reference
6
+ console.log property
7
+ console.log @data
8
+ for zone in @data
9
+ coords = zone.shape.coordinates
10
+ @items.push [coords[1], coords[0], ]
11
+
12
+ if this.valid()
13
+ console.log "Heatmap computed"
14
+ else
15
+ console.warn "Invalid heatmap points"
16
+
17
+ # Build layer as wanted
18
+ buildLayerGroup: (widget, globalStyle = {}) ->
19
+ group = []
20
+ overlay = new L.TileLayer.WebGLHeatMap
21
+ size: 12
22
+ opacity: 0.8
23
+ autoresize: true
24
+ overlay.setData(@items)
25
+
26
+ group.push(overlay)
27
+ group
28
+
29
+ # Build HTML legend for given categories computed layer
30
+ buildLegend: () ->
31
+ html = "<div class='leaflet-legend-item' id='legend-#{@layer.name}'>"
32
+ html += "<h3>#{@layer.label}</h3>"
33
+ html += "<div class='leaflet-legend-body leaflet-categories-scale'>"
34
+ # html += "<span class='leaflet-categories-items'>"
35
+ # for name, item of @items
36
+ # html += "<span class='leaflet-categories-item'>"
37
+ # html += "<i class='leaflet-categories-sample' style='background-color: #{item.fillColor};'></i>"
38
+ # html += " #{item.name}"
39
+ # html += "</span>"
40
+ # html += "</span>"
41
+ html += "</div>"
42
+ html += "</div>"
43
+ return html
44
+
45
+ # Check if categories are valid
46
+ valid: () ->
47
+ @items.length > 0
48
+
49
+ Foliage.registerLayerType "heatmap", Foliage.Heatmap
@@ -0,0 +1,422 @@
1
+ # Add sprockets directives below:
2
+ #= require core_ext
3
+ #= require foliage/color
4
+ #= require_self
5
+ #= require foliage/band
6
+ #= require foliage/bubbles
7
+ #= require foliage/categories
8
+ #= require foliage/choropleth
9
+ #= require foliage/heatmap
10
+ #= require foliage/path
11
+ #= require foliage/paths
12
+ #= require foliage/points
13
+ #= require foliage/point_group
14
+ #= require foliage/simple
15
+
16
+ ((F, $) ->
17
+ "use strict"
18
+
19
+ F.layer = (layer, data, options) ->
20
+ @layerTypes ?= {}
21
+ if type = @layerTypes[layer.type]
22
+ return new type(layer, data, options)
23
+ else
24
+ console.warn "Invalid layer type: #{layer.type}"
25
+ return null
26
+
27
+ F.registerLayerType = (name, klass) ->
28
+ @layerTypes ?= {}
29
+ @layerTypes[name] = klass
30
+
31
+ # Generated colors in ruby
32
+ # $ p = Proc.new{[37 * rand(7), 255].min.to_i.to_s(16).rjust(2, "0")}
33
+ # $ (1..300).collect{|x| "##{p[]}#{p[]}#{p[]}"}.uniq
34
+ F.colors = ["#00de00", "#6f006f", "#4ade94", "#004ab9", "#de6f4a", "#b9b925", "#00b994", "#25946f", "#de00b9", "#94006f", "#de6f94", "#252594", "#dede94", "#4a2594", "#940000", "#deb9de", "#00b9b9", "#00de94", "#25254a", "#6fde6f", "#4a0094", "#256f4a", "#6f4a25", "#4a4a00", "#b9006f", "#4a6f25", "#6f946f", "#009425", "#6f4ade", "#2525de", "#b9946f", "#b9b994", "#b9de94", "#de256f", "#b900b9", "#4a4a6f", "#4a2525", "#006fde", "#940025", "#250094", "#b900de", "#4ab9b9", "#00004a", "#6f6fde", "#256fde", "#b92594", "#6f944a", "#6f6f25", "#4ab9de", "#de2525", "#2525b9", "#944a94", "#b94a94", "#946f94", "#b94a6f", "#000094", "#4a6f6f", "#006f00", "#946f4a", "#00256f", "#6f4a6f", "#de6fb9", "#6fdeb9", "#de6f00", "#94b94a", "#94b994", "#6f6fb9", "#b925de", "#de2594", "#dede25", "#6f4a94", "#946f6f", "#de25de", "#b92525", "#6fde94", "#254a25", "#4adeb9", "#00deb9", "#b9b9b9", "#6f4a4a", "#256f25", "#25deb9", "#6f25de", "#94b925", "#b9254a", "#4ade25", "#4a006f", "#25006f", "#94de00", "#6fb925", "#259425", "#6f9425", "#944a00", "#25b9b9", "#25de4a", "#00254a", "#94254a", "#4a6f94", "#002500", "#6fdede", "#deb925", "#b9b9de", "#4a4a94", "#004a4a", "#25b994", "#6f6f00", "#b92500", "#b925b9", "#940094", "#2594de", "#4ade4a", "#949400", "#256f6f", "#de00de", "#6fde25", "#4a6fde", "#4a4ab9", "#deb96f", "#6f0025", "#00b925", "#0000b9", "#254a94", "#4a25b9", "#b9004a", "#b9de00", "#6f254a", "#6f2500", "#94b96f", "#25de00", "#b99425", "#b90025", "#0094b9", "#4ab925", "#4ab96f", "#6fde00", "#b9b96f", "#94b9b9", "#de4a6f", "#4a2500", "#de0000", "#4a4a4a", "#259494", "#9400b9", "#b9deb9", "#254a00", "#0000de", "#dede4a", "#94dede", "#94de25", "#4a9494", "#4a94de", "#6fb9b9", "#dede00", "#b9256f", "#de9494", "#009494", "#006f4a", "#94944a", "#4ab900", "#6f6f4a", "#b99494", "#6f004a", "#4a256f", "#00b9de", "#b99400", "#00b96f", "#deb9b9", "#4a6f00", "#000025", "#00006f", "#00de4a", "#b96f94", "#6fb9de", "#946fde", "#deb900", "#004ade", "#254ab9", "#25de6f", "#94deb9", "#b994de", "#004a25", "#94256f", "#250025", "#6f6f6f", "#4a944a", "#4a25de", "#00b94a", "#4a4a25", "#9400de", "#94004a", "#4a94b9", "#94de94", "#6f256f", "#6fb900", "#b9944a", "#de94de", "#944a25", "#6f2594"]
35
+
36
+ $.widget "ui.leaf",
37
+ options:
38
+ box:
39
+ height: 400
40
+ width: null
41
+ backgrounds: {}
42
+ overlays: {}
43
+ controls: {}
44
+ series: {}
45
+ controlDefaults:
46
+ fullscreen:
47
+ position: 'topleft'
48
+ title: "Fullscreen"
49
+ geocoder:
50
+ collapsed: true,
51
+ position: 'topright',
52
+ text: 'Locate',
53
+ bounds: null,
54
+ email: null
55
+ layerSelector:
56
+ collapsed: true,
57
+ position: 'topright',
58
+ autoZIndex: true
59
+ scale:
60
+ position: 'bottomleft'
61
+ maxWidth: 200
62
+ metric: true
63
+ imperial: false
64
+ updateWhenIdle: false
65
+ zoom:
66
+ position: 'topleft'
67
+ zoomInText: ''
68
+ zoomInTitle: "Zoom in"
69
+ zoomOutText: ''
70
+ zoomOutTitle: "Zoom out"
71
+ layers: {}
72
+ layerDefaults:
73
+ band:
74
+ stroke: true
75
+ color: "#333333"
76
+ weight: 1
77
+ opacity: 1
78
+ fill: true
79
+ fillColor: "blue"
80
+ fillOpacity: 1
81
+ round: 5
82
+ startColor: '#FFFFFF'
83
+ stopColor: '#910000'
84
+ levelNumber: 7
85
+ bubbles:
86
+ stroke: true
87
+ color: "#333333"
88
+ weight: 1
89
+ opacity: 1
90
+ fill: true
91
+ fillColor: "orange"
92
+ fillOpacity: 1
93
+ categories:
94
+ stroke: true
95
+ color: "#333333"
96
+ weight: 1
97
+ opacity: 1
98
+ fill: true
99
+ fillOpacity: 1
100
+ choropleth:
101
+ stroke: true
102
+ color: "#333333"
103
+ weight: 1
104
+ opacity: 1
105
+ fill: true
106
+ fillColor: "blue"
107
+ fillOpacity: 1
108
+ round: 5
109
+ startColor: '#FFFFFF'
110
+ stopColor: '#910000'
111
+ levelNumber: 7
112
+ simple:
113
+ stroke: true
114
+ color: "#333333"
115
+ weight: 1
116
+ opacity: 1
117
+ fill: true
118
+ fillColor: "green"
119
+ fillOpacity: 1
120
+ paths:
121
+ stroke: true
122
+ color: "#333333"
123
+ weight: 2
124
+ opacity: 0.2
125
+ fill: true
126
+ fillOpacity: 1
127
+ path:
128
+ stroke: true
129
+ color: "#333333"
130
+ weight: 3
131
+ opacity: 1
132
+ fill: true
133
+ fillOpacity: 1
134
+ fillColor: "#333333"
135
+ radius: 3
136
+ points:
137
+ stroke: true
138
+ color: "#333333"
139
+ weight: 2
140
+ opacity: 1
141
+ fill: true
142
+ fillOpacity: 1
143
+ radius: 5
144
+ point_group:
145
+ stroke: true
146
+ color: "#333333"
147
+ weight: 2
148
+ opacity: 1
149
+ fill: true
150
+ fillOpacity: 1
151
+ radius: 5
152
+ map:
153
+ maxZoom: 25
154
+ minZoom:2
155
+ scrollWheelZoom: false
156
+ zoomControl: false
157
+ attributionControl: true
158
+ setDefaultBackground: false
159
+ dragging: true
160
+ touchZoom: true
161
+ doubleClickZoom: true
162
+ boxZoom: true
163
+ tap: true
164
+ view:
165
+ center:[]
166
+ zoom : 13
167
+ colors: F.colors
168
+
169
+ _create: ->
170
+ $.extend(true, @options, @element.data("leaf"))
171
+ @mapElement = $("<div>", class: "map").insertAfter(@element)
172
+ @map = L.map(@mapElement[0], @options.map)
173
+ @layers = []
174
+
175
+ if @options.map.setDefaultBackground
176
+ opts = {}
177
+ opts['attribution'] = @options.backgrounds.attribution if @options.backgrounds.attribution?
178
+ opts['minZoom'] = @options.backgrounds.minZoom if @options.backgrounds.minZoom?
179
+ opts['maxZoom'] = @options.backgrounds.maxZoom if @options.backgrounds.maxZoom?
180
+ opts['subdomains'] = @options.backgrounds.subdomains if @options.backgrounds.subdomains?
181
+ opts['tms'] = true if @options.backgrounds.tms
182
+
183
+ backgroundLayer = L.tileLayer(@options.backgrounds.url, opts)
184
+ backgroundLayer.addTo @map
185
+ @ghostLabelCluster = L.ghostLabelCluster(type: 'number', innerClassName: 'leaflet-ghost-label-collapsed')
186
+ @ghostLabelCluster.addTo @map
187
+
188
+ @layersScheduler = L.layersScheduler()
189
+ @layersScheduler.addTo @map
190
+
191
+ this._resize()
192
+ this._refreshView()
193
+ this._refreshControls()
194
+
195
+ _destroy: ->
196
+ @mapElement.remove()
197
+
198
+ zoom: (zoom) ->
199
+ return @map.getZoom() unless zoom?
200
+ @options.view.zoom = zoom
201
+ this._refreshZoom()
202
+
203
+ height: (height) ->
204
+ return @options.box.height unless height?
205
+ @options.view.box.height = height
206
+ this._resize()
207
+
208
+ rebuild: ->
209
+ this._destroy()
210
+ this._create()
211
+
212
+ layrs: ->
213
+ return @layers
214
+
215
+ mappo: ->
216
+ return @map
217
+
218
+ mappoElement: ->
219
+ return @mapElement
220
+
221
+ _resize: ->
222
+ if @options.box?
223
+ if @options.box.height?
224
+ @mapElement.height @options.box.height
225
+ if @options.box.width?
226
+ @mapElement.width @options.box.width
227
+ this._trigger "resize"
228
+
229
+ # Retuns data from a serie found with the given name
230
+ _getSerieData: (name) ->
231
+ if @options.series[name]?
232
+ return @options.series[name]
233
+ else
234
+ console.error "Cannot find serie #{name}"
235
+ alert "Cannot find serie #{name}"
236
+
237
+ # Displays all given controls
238
+ _refreshControls: ->
239
+ console.log "Refresh controls...", @options.controls
240
+ unless @options and @options.controls?
241
+ console.log "No controls..."
242
+ return false
243
+ widget = this
244
+ for name, options of @options.controls
245
+ console.log "Add control #{name}..."
246
+ if options isnt false
247
+ functionName = "_add#{name.camelize()}Control"
248
+ if $.isFunction widget[functionName]
249
+ options = {} if options is true
250
+ widget[functionName].call(widget, options)
251
+ else
252
+ console.log "Unknown control: #{name}"
253
+
254
+ _addFullscreenControl: (options) ->
255
+ options = $.extend true, {}, @options.controlDefaults.fullscreen, options
256
+ control = new L.Control.FullScreen options
257
+ @map.addControl control
258
+ @map.on "enterFullscreen", (e) =>
259
+ @map.scrollWheelZoom.enable();
260
+
261
+ @map.on "exitFullscreen", (e) =>
262
+ @map.scrollWheelZoom.disable();
263
+
264
+
265
+ _addGeocoderControl: (options) ->
266
+ options = $.extend true, {}, @options.controlDefaults.geocoder, options
267
+ control = new L.Control.OSMGeocoder options
268
+ @map.addControl control
269
+
270
+ _addLayerSelectorControl: (options) ->
271
+ baseLayers = {}
272
+ overlays = {}
273
+
274
+ if @options.backgrounds.length > 0
275
+ for layer, index in @options.backgrounds
276
+ opts = {}
277
+ opts['attribution'] = layer.attribution if layer.attribution?
278
+ opts['minZoom'] = layer.minZoom if layer.minZoom?
279
+ opts['maxZoom'] = layer.maxZoom if layer.maxZoom?
280
+ opts['subdomains'] = layer.subdomains if layer.subdomains?
281
+ opts['tms'] = true if layer.tms
282
+
283
+ backgroundLayer = L.tileLayer(layer.url, opts)
284
+ baseLayers[layer.name] = backgroundLayer
285
+ @map.addLayer(backgroundLayer) if layer.byDefault
286
+ else
287
+ # no backgrounds, set defaults
288
+ backgrounds = ['OpenStreetMap.HOT',"OpenStreetMap.Mapnik", "Thunderforest.Landscape", "Esri.WorldImagery"]
289
+
290
+ baseLayers = {}
291
+ for layer, index in backgrounds
292
+ backgroundLayer = L.tileLayer.provider(layer)
293
+ baseLayers[layer] = backgroundLayer
294
+ @map.addLayer(backgroundLayer) if index == 0
295
+
296
+
297
+ for layer in @options.overlays
298
+ overlayLayer = L.tileLayer.provider(layer.provider_name)
299
+ overlays[layer.name] = overlayLayer
300
+
301
+ legendControl = new L.control(position: "bottomright")
302
+ legendControl.onAdd = (map) ->
303
+ L.DomUtil.create('div', 'leaflet-legend-control')
304
+ @map.addControl legendControl
305
+
306
+ $('.leaflet-legend-control').on 'click', () ->
307
+ $(this).find('#legend-activity').toggleClass('minified')
308
+
309
+
310
+ for layer in @options.layers
311
+ if console.group isnt undefined
312
+ console.group "Add layer #{layer.name} (#{layer.type})..."
313
+ else
314
+ console.log "Add layer #{layer.name}..."
315
+ options = {} if options is true
316
+
317
+ data = this._getSerieData(layer.serie)
318
+ options = $.extend true, {}, @options.layerDefaults[layer.type], layer, parent: this
319
+ renderedLayer = F.layer(layer, data, options)
320
+ if renderedLayer and renderedLayer.valid()
321
+ # Build layer group
322
+ layerGroup = renderedLayer.buildLayerGroup(this, options)
323
+ console.log("#{layer.name} layer rendered", layerGroup)
324
+ # Add layer overlay
325
+ overlayLayer = L.layerGroup(layerGroup)
326
+ overlayLayer.name = layer.name
327
+ @layers.push layer
328
+ layer.overlay = overlays[layer.label] = overlayLayer
329
+ @map.addLayer(overlayLayer)
330
+ @layersScheduler.insert overlayLayer._leaflet_id
331
+ console.log("#{layer.name} layer added")
332
+ try
333
+ group = new L.featureGroup(layerGroup)
334
+ bounds = group.getBounds()
335
+ @map.fitBounds(bounds)
336
+ if bounds.getNorthEast().equals bounds.getSouthWest()
337
+ @map.setZoom 18
338
+ # Add legend
339
+ legend = legendControl.getContainer()
340
+ legend.innerHTML += renderedLayer.buildLegend()
341
+ else
342
+ console.warn "Cannot add layer #{layer.type}"
343
+
344
+ console.groupEnd() if console.groupEnd isnt undefined
345
+
346
+ @map.on "overlayadd", (event) =>
347
+ @layersScheduler.schedule event.layer
348
+ console.log "Add legend control..."
349
+ legend = $(legendControl.getContainer())
350
+ legend.children("#legend-#{event.layer.name}").show()
351
+ legend.children(".first").removeClass("first")
352
+ legend.children(":visible:first").addClass("first")
353
+ legend.removeClass("empty")
354
+ return
355
+
356
+ @map.on "overlayremove", (event) ->
357
+ console.log "Remove legend control..."
358
+ legend = $(legendControl.getContainer())
359
+ legend.children("#legend-#{event.layer.name}").hide()
360
+ legend.children(".first").removeClass("first")
361
+ legend.children(":visible:first").addClass("first")
362
+ legend.addClass("empty") if legend.children(":visible").length <= 0
363
+ return
364
+
365
+ control = new L.Control.Layers(baseLayers, overlays, @options.controlDefaults.layerSelector)
366
+ @map.addControl control
367
+
368
+ _addScaleControl: (options) ->
369
+ options = $.extend true, {}, @options.controlDefaults.scale, options
370
+ control = new L.Control.Scale options
371
+ @map.addControl control
372
+
373
+ _addZoomControl: (options) ->
374
+ options = $.extend true, {}, @options.controlDefaults.zoom, options
375
+ control = new L.Control.Zoom options
376
+ @map.addControl control
377
+
378
+
379
+ # Build a popup from given parameters. For now it only uses popup attribute of
380
+ # a zone. After it will use a global template for all zone by default which can be
381
+ # overriden with a local popup
382
+ _bindPopup: (layer, zone) ->
383
+ return unless zone.popup?
384
+ popup = ""
385
+ for block in zone.popup
386
+ popup += "<div class='popup-#{block.type}'>"
387
+ if block.label?
388
+ popup += "<span class='popup-block-label'>#{block.label}</span>"
389
+ if block.content?
390
+ popup += "<span class='popup-block-content'>#{block.content}</span>"
391
+ else if block.value?
392
+ popup += "<span class='popup-block-value'>#{block.value}</span>"
393
+ popup += "</div>"
394
+ layer.bindPopup(popup)
395
+ return layer
396
+
397
+
398
+ _refreshView: (view) ->
399
+ this._setDefaultView()
400
+ if @options.view.center.length > 0
401
+ @map.setView(@options.view.center, @options.view.zoom)
402
+
403
+ _setDefaultView: ->
404
+ @map.fitWorld()
405
+ @map.setZoom 6
406
+
407
+ _refreshZoom: ->
408
+ if @options.view.zoom?
409
+ @map.setZoom(@options.view.zoom)
410
+
411
+ $.loadLeaves = ->
412
+ $("*[data-leaf]").each ->
413
+ $(this).leaf()
414
+ return
415
+
416
+ # Needed to easily write setTimeout in CoffeeScript
417
+ delay = (time, method) -> setTimeout method, time
418
+
419
+ $(document).ready $.loadLeaves
420
+ $(document).on "page:load cocoon:after-insert cell:load", $.loadFoliages
421
+
422
+ ) Foliage, jQuery