foliage 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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