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,192 @@
1
+ ###
2
+ # Provides clustering for GhostLabels
3
+
4
+ # options:
5
+ # type {string} ('number' || 'hidden') if number, display count of collapsed items inside a group. If hide, collapsed items are hidden.
6
+ # className {string} Overrides container class name. By default, it inherits from layer
7
+ # innerClassName {string} Set a custom inner class name
8
+ # margin: {number (as px)} Wrap labels in a margin box, considering as the clustering limits. Default: 0
9
+ #
10
+ #
11
+ # Thanks to LayerGroup.collision for inspiration https://github.com/MazeMap/Leaflet.LayerGroup.Collision
12
+ # Thanks to RBush for awesome lib https://github.com/mourner/rbush
13
+ #
14
+ ###
15
+
16
+ L.GhostLabelCluster = L.LayerGroup.extend
17
+ __initialize: L.LayerGroup::initialize
18
+ __addLayer: L.LayerGroup::addLayer
19
+ __removeLayer: L.LayerGroup::removeLayer
20
+ __clearLayers: L.LayerGroup::clearLayers
21
+
22
+ initialize: (options) ->
23
+ L.setOptions @, options
24
+ @_originalLayers = []
25
+ @_clusterIndex = []
26
+ @_visibleLayers = {}
27
+ @_rbush = []
28
+ @_cachedRelativeBoxes = []
29
+ @_margin = 0
30
+ @__initialize.call @, options
31
+ @_margin = options.margin or 0
32
+ @_rbush = null
33
+ return
34
+
35
+ addLayer: (layer) ->
36
+ @_originalLayers.push layer unless @_originalLayers.indexOf(layer) != -1
37
+ if @_map
38
+ @__addClusteredLayer layer
39
+ return
40
+
41
+ bind: (layer, parent) ->
42
+ @addLayer layer
43
+
44
+ if @_originalLayers.indexOf(layer) != -1
45
+ # To be updated when feature name change
46
+ parent.bindGhostLabel layer
47
+ parent.on 'remove', @removeLayer, @
48
+
49
+
50
+ removeLayer: (e) ->
51
+ layer = e.target.label
52
+ @_rbush.remove @_cachedRelativeBoxes[layer._leaflet_id]
53
+ delete @_cachedRelativeBoxes[layer._leaflet_id]
54
+
55
+ i = @_originalLayers.indexOf(layer)
56
+ if i != -1
57
+ @_originalLayers.splice i, 1
58
+
59
+ delete @_visibleLayers[layer._leaflet_id]
60
+ @__removeLayer.call @, layer
61
+
62
+ return
63
+
64
+ clearLayers: ->
65
+ @_rbush = rbush()
66
+ _clusterIndex: []
67
+ @_originalLayers = []
68
+ @_visibleLayers = {}
69
+ @_cachedRelativeBoxes = []
70
+ @__clearLayers.call this
71
+ return
72
+
73
+ onAdd: (map) ->
74
+ unless @_map
75
+ @_map = map
76
+ @refresh()
77
+ map.on 'zoomend', @refresh, this
78
+ return
79
+
80
+ onRemove: (map) ->
81
+ map.off 'zoomend', @refresh, this
82
+ return
83
+
84
+ __addClusteredLayer: (layer) ->
85
+ className = if @options.className? then @options.className else layer.options.className
86
+ innerClass = if @options.innerClassName? then @options.innerClassName else ''
87
+
88
+ bush = @_rbush
89
+
90
+ box = @_cachedRelativeBoxes[layer._leaflet_id]
91
+ visible = false
92
+ if !box
93
+ # Add the layer to the map so it's instantiated on the DOM,
94
+ # in order to fetch its position and size.
95
+ @__addLayer.call @, layer
96
+ visible = true
97
+
98
+ box = @_getContainerBox(layer._container)
99
+
100
+ @_cachedRelativeBoxes[layer._leaflet_id] = box
101
+
102
+ box = @_positionBox(@_map.latLngToLayerPoint(layer.getLatLng()), box)
103
+
104
+ # Search collisions from absolute position
105
+ collidedItems = bush.search(box)
106
+
107
+ # Add reference to layer to track collided layers. Take advantage of rbush properties
108
+ box.push id: layer._leaflet_id
109
+
110
+ if collidedItems.length is 0
111
+ if !visible
112
+ @__addLayer.call @, layer
113
+ @_visibleLayers[layer._leaflet_id] = layer
114
+ bush.load [box]
115
+ else
116
+ @__removeLayer.call @, layer
117
+ # Layers which collided
118
+ latLngBounds = new L.LatLngBounds
119
+ latLngBounds.extend layer.getLatLng()
120
+
121
+ idsToCollapse = []
122
+ for item in collidedItems
123
+ otherLayer = @getLayer(item[4].id)
124
+
125
+ if otherLayer
126
+ bounds = otherLayer.getLatLng()
127
+ @__removeLayer.call @, otherLayer
128
+ idsToCollapse.push otherLayer._leaflet_id
129
+ else
130
+ collapsedLayer = @_visibleLayers[@_clusterIndex[item[4].id]]
131
+ bounds = collapsedLayer.getLatLng() unless collapsedLayer is undefined
132
+
133
+ latLngBounds.extend bounds unless bounds is undefined
134
+
135
+
136
+ collapsedLayer ||= new L.GhostLabel(className: className)
137
+
138
+ collapsedLayer.setLatLng latLngBounds.getCenter()
139
+
140
+
141
+ # add to this layer group
142
+ @__addLayer.call @, collapsedLayer
143
+
144
+ @_visibleLayers[collapsedLayer._leaflet_id] = collapsedLayer
145
+
146
+
147
+ # clustering track
148
+ @_clusterIndex[layer._leaflet_id] = collapsedLayer._leaflet_id
149
+ for id in idsToCollapse
150
+ @_clusterIndex[id] = collapsedLayer._leaflet_id
151
+
152
+ if @options.type is 'number'
153
+ count = @_clusterIndex.filter((i) -> i == collapsedLayer._leaflet_id).length
154
+ collapsedLayer.setContent("<span class='#{innerClass}'>#{count}</span>")
155
+
156
+ collapsedLayer.setLatLng latLngBounds.getCenter()
157
+
158
+
159
+ return
160
+
161
+ _getContainerBox: (el) ->
162
+ styles = window.getComputedStyle(el)
163
+ [
164
+ parseInt(styles.marginLeft)
165
+ parseInt(styles.marginTop)
166
+ parseInt(styles.marginLeft) + parseInt(styles.width)
167
+ parseInt(styles.marginTop) + parseInt(styles.height)
168
+ ]
169
+
170
+ _positionBox: (offset, box) ->
171
+ [
172
+ box[0] + offset.x - (@_margin)
173
+ box[1] + offset.y - (@_margin)
174
+ box[2] + offset.x + @_margin
175
+ box[3] + offset.y + @_margin
176
+ ]
177
+
178
+ refresh: ->
179
+ for id, layer of @_visibleLayers
180
+ @__removeLayer.call @, layer
181
+ delete @_visibleLayers[id]
182
+
183
+ @_rbush = rbush()
184
+
185
+ for layer in @_originalLayers
186
+ @__addClusteredLayer layer
187
+ return
188
+
189
+
190
+ L.ghostLabelCluster = (options) ->
191
+ new (L.GhostLabelCluster)(options or {})
192
+
@@ -0,0 +1,57 @@
1
+ # The scheduler allows to render a layerGroup at a specified level (compared to others).
2
+ # It "schedules" the layerGroup i.e. it replays layerGroup's rendering to the right time.
3
+ L.LayersScheduler = L.Class.extend
4
+
5
+ # flow is an array of layerGroup ids
6
+ initialize: (flow = [], options = {}) ->
7
+ L.Util.setOptions @, options
8
+ @flow = flow
9
+
10
+
11
+ addTo: (map) ->
12
+ @_map = map
13
+
14
+ insert: (id, options = {}) ->
15
+ if options.back then @flow.unshift(id) else @flow.push(id)
16
+ @flow
17
+
18
+ # Note: if layer not found in the flow, we consider to let it in top.
19
+ schedule: (layerGroup = undefined) ->
20
+
21
+ if layerGroup and layerGroup._leaflet_id
22
+ index = @flow.indexOf(layerGroup._leaflet_id)
23
+
24
+ # unless layerGroup to schedule is already the last element
25
+ unless index == -1 or index == @flow.length - 1
26
+ # we need to find the next element to place it before
27
+ nextSibling = @_map._layers[@flow[index + 1]]
28
+
29
+ # to do that, we need to iterate until finding geometry layer and its first svg path
30
+ if nextSibling and nextSibling.getLayers().length
31
+ # as a ILayer (including feature layer)
32
+ objectLayer = nextSibling
33
+
34
+ loop
35
+ break unless (objectLayer and Object.keys(objectLayer._layers || []).length) or not objectLayer._latlngs
36
+ objectLayer = objectLayer.getLayers()[0]
37
+
38
+ layerGroup.bringBefore objectLayer._container if objectLayer._container
39
+
40
+ else
41
+ #redraw all ?
42
+
43
+ # factory
44
+ L.layersScheduler = (flow = [], options = {}) ->
45
+ new L.LayersScheduler(flow, options)
46
+
47
+
48
+ L.LayerGroup.include
49
+ bringBefore: (node) ->
50
+ @invoke 'bringBefore', node
51
+
52
+ L.Path.include
53
+ bringBefore: (node) ->
54
+ root = @_map._pathRoot
55
+ path = @_container
56
+ root.insertBefore path, node
57
+
@@ -0,0 +1,414 @@
1
+ # Extends featureGroup to return measure. Works with inherited classes (L.MultiPolygon, L.MultiPolyline)
2
+ L.FeatureGroup.include
3
+ getMeasure: () ->
4
+
5
+ measure =
6
+ perimeter: 0
7
+ area: 0
8
+
9
+ this.eachLayer (layer) ->
10
+ m = layer.getMeasure()
11
+ measure.perimeter += m.perimeter
12
+ measure.area += m.area
13
+
14
+ measure
15
+
16
+
17
+ L.Polygon.include
18
+ ###
19
+ # Get centroid of the polygon in square meters
20
+ # Portage from leaflet1.0.0-rc1: https://github.com/Leaflet/Leaflet/blob/master/src/layer/vector/Polygon.js
21
+ # @return {number} polygon centroid
22
+ ###
23
+ __getCenter: ->
24
+ @__project()
25
+ points = @_rings[0]
26
+ len = points.length
27
+ if !len
28
+ return null
29
+ # polygon centroid algorithm; only uses the first ring if there are multiple
30
+ area = x = y = 0
31
+ i = 0
32
+ j = len - 1
33
+ while i < len
34
+ p1 = points[i]
35
+ p2 = points[j]
36
+ f = p1.y * p2.x - (p2.y * p1.x)
37
+ x += (p1.x + p2.x) * f
38
+ y += (p1.y + p2.y) * f
39
+ area += f * 3
40
+ j = i++
41
+ if area == 0
42
+ # Polygon is so small that all points are on same pixel.
43
+ center = points[0]
44
+ else
45
+ center = [
46
+ x / area
47
+ y / area
48
+ ]
49
+ @_map.layerPointToLatLng center
50
+
51
+ L.Polyline.include
52
+ ###
53
+ # Return LatLngs as array of [lat, lng] pair.
54
+ # @return {Array} [[lat,lng], [lat,lng]]
55
+ ###
56
+ getLatLngsAsArray: ->
57
+ arr = []
58
+ for latlng in @_latlngs
59
+ arr.push [latlng.lat, latlng.lng]
60
+ arr
61
+
62
+ ###
63
+ # Get center of the polyline in meters
64
+ # Portage from leaflet1.0.0-rc1: https://github.com/Leaflet/Leaflet/blob/master/src/layer/vector/Polyline.js
65
+ # @return {number} polyline center
66
+ ###
67
+ __getCenter: ->
68
+ @__project()
69
+ i = undefined
70
+ halfDist = undefined
71
+ segDist = undefined
72
+ dist = undefined
73
+ p1 = undefined
74
+ p2 = undefined
75
+ ratio = undefined
76
+ points = @_rings[0]
77
+ len = points.length
78
+ if !len
79
+ return null
80
+ # polyline centroid algorithm; only uses the first ring if there are multiple
81
+ i = 0
82
+ halfDist = 0
83
+ while i < len - 1
84
+ halfDist += points[i].distanceTo(points[i + 1]) / 2
85
+ i++
86
+ # The line is so small in the current view that all points are on the same pixel.
87
+ if halfDist == 0
88
+ return @_map.layerPointToLatLng(points[0])
89
+ i = 0
90
+ dist = 0
91
+ while i < len - 1
92
+ p1 = points[i]
93
+ p2 = points[i + 1]
94
+ segDist = p1.distanceTo(p2)
95
+ dist += segDist
96
+ if dist > halfDist
97
+ ratio = (dist - halfDist) / segDist
98
+ return @_map.layerPointToLatLng([
99
+ p2.x - (ratio * (p2.x - (p1.x)))
100
+ p2.y - (ratio * (p2.y - (p1.y)))
101
+ ])
102
+ i++
103
+ return
104
+
105
+ __project: ->
106
+ pxBounds = new (L.Bounds)
107
+ @_rings = []
108
+ @__projectLatlngs @_latlngs, @_rings, pxBounds
109
+ return
110
+
111
+ # recursively turns latlngs into a set of rings with projected coordinates
112
+ __projectLatlngs: (latlngs, result, projectedBounds) ->
113
+ flat = latlngs[0] instanceof L.LatLng
114
+ len = latlngs.length
115
+ i = undefined
116
+ ring = undefined
117
+ if flat
118
+ ring = []
119
+ i = 0
120
+ while i < len
121
+ ring[i] = @_map.latLngToLayerPoint(latlngs[i])
122
+ projectedBounds.extend ring[i]
123
+ i++
124
+ result.push ring
125
+ else
126
+ i = 0
127
+ while i < len
128
+ @__projectLatlngs latlngs[i], result, projectedBounds
129
+ i++
130
+ return
131
+
132
+ getMeasure: () ->
133
+ g = new L.GeographicUtil.Polygon @getLatLngsAsArray()
134
+
135
+ measure =
136
+ perimeter: g.perimeter()
137
+ area: g.area()
138
+
139
+ measure
140
+
141
+
142
+ L.Draw.Polyline.include
143
+ __addHooks: L.Draw.Polyline.prototype.addHooks
144
+ __removeHooks: L.Draw.Polyline.prototype.removeHooks
145
+ __vertexChanged: L.Draw.Polyline.prototype._vertexChanged
146
+
147
+ _vertexChanged: () ->
148
+ @__vertexChanged.apply this, arguments
149
+ @_tooltip.hide()
150
+
151
+
152
+ __onMouseMove: (e) ->
153
+ @_tooltip.hide() if @_tooltip?
154
+ return unless @_markers.length > 0
155
+ newPos = @_map.mouseEventToLayerPoint(e.originalEvent)
156
+ mouseLatLng = @_map.layerPointToLatLng(newPos)
157
+
158
+ latLngArray = []
159
+ for latLng in @_poly.getLatLngs()
160
+ latLngArray.push latLng
161
+ latLngArray.push mouseLatLng
162
+
163
+ # draw a polyline
164
+ if @_markers.length == 1
165
+ clone = L.polyline latLngArray
166
+
167
+ # draw a polygon
168
+ if @_markers.length >= 2
169
+ clone = L.polygon latLngArray
170
+
171
+ clone._map = @_map
172
+ center = clone.__getCenter()
173
+
174
+ g = new L.GeographicUtil.Polygon clone.getLatLngsAsArray()
175
+
176
+ measure =
177
+ perimeter: g.perimeter()
178
+ area: g.area()
179
+
180
+ e.target.reactiveMeasureControl.updateContent measure, {selection: true}
181
+
182
+
183
+
184
+ addHooks: () ->
185
+ @__addHooks.apply this, arguments
186
+ @_map.on 'mousemove', @__onMouseMove, this
187
+ return
188
+
189
+ removeHooks: () ->
190
+ if @_map.reactiveMeasureControl
191
+ @_map.off 'mousemove'
192
+ @__removeHooks.apply this, arguments
193
+ return
194
+
195
+ L.Edit.Poly.include
196
+ __addHooks: L.Edit.Poly.prototype.addHooks
197
+ __removeHooks: L.Edit.Poly.prototype.removeHooks
198
+
199
+ __onHandlerDrag: (e) ->
200
+ center = @_poly.__getCenter()
201
+
202
+ g = new L.GeographicUtil.Polygon @_poly.getLatLngsAsArray()
203
+
204
+ measure =
205
+ perimeter: g.perimeter()
206
+ area: g.area()
207
+
208
+ L.extend(L.Draw.Polyline.prototype.options, target: e.marker.getLatLng())
209
+
210
+ e.marker._map.reactiveMeasureControl.updateContent measure, {selection: true}
211
+
212
+
213
+ addHooks: () ->
214
+ @__addHooks.apply this, arguments
215
+ this._poly.on 'editdrag', @__onHandlerDrag, this
216
+
217
+ removeHooks: () ->
218
+
219
+ g = new L.GeographicUtil.Polygon @_poly.getLatLngsAsArray()
220
+
221
+ measure =
222
+ perimeter: g.perimeter()
223
+ area: g.area()
224
+
225
+ @._poly._map.reactiveMeasureControl.updateContent measure, {selection: false} if @._poly._map?
226
+
227
+ if L.EditToolbar.reactiveMeasure
228
+ this._poly.off 'editdrag'
229
+
230
+ @__removeHooks.apply this, arguments
231
+
232
+ L.Edit.PolyVerticesEdit.include
233
+ __onTouchMove: L.Edit.PolyVerticesEdit::_onTouchMove
234
+ __removeMarker: L.Edit.PolyVerticesEdit::_removeMarker
235
+
236
+ _onMarkerDrag: (e) ->
237
+ marker = e.target
238
+ L.extend marker._origLatLng, marker._latlng
239
+ if marker._middleLeft
240
+ marker._middleLeft.setLatLng @_getMiddleLatLng(marker._prev, marker)
241
+ if marker._middleRight
242
+ marker._middleRight.setLatLng @_getMiddleLatLng(marker, marker._next)
243
+ @_poly.redraw()
244
+ # Overrides to track mouse position
245
+ @_poly.fire 'editdrag', marker: e.target
246
+ return
247
+
248
+ _onTouchMove: (e) ->
249
+ @__onTouchMove.apply @, arguments
250
+ @_poly.fire 'editdrag'
251
+
252
+ _removeMarker: (marker) ->
253
+ @__removeMarker.apply @, arguments
254
+ @_poly.fire 'editdrag', marker: marker
255
+
256
+
257
+ L.LatLng.prototype.toArray = ->
258
+ [@lat, @lng]
259
+
260
+ L.Tooltip.include
261
+ __initialize: L.Tooltip.prototype.initialize
262
+ __dispose: L.Tooltip.prototype.dispose
263
+
264
+ initialize: (map,options = {}) ->
265
+ @__initialize.apply this, arguments
266
+
267
+ dispose: ->
268
+ @_map.off 'mouseover'
269
+ @__dispose.apply this, arguments
270
+
271
+ __updateTooltipMeasure: (latLng, measure = {}, options = {}) ->
272
+ labelText =
273
+ text: ''
274
+ #TODO: use L.drawLocal to i18n tooltip
275
+ if measure['perimeter']
276
+ labelText['text'] += "<span class='leaflet-draw-tooltip-measure perimeter'>#{L.GeometryUtil.readableDistance(measure.perimeter, !!options.metric, !!options.feet)}</span>"
277
+
278
+ if measure['area']
279
+ labelText['text'] += "<span class='leaflet-draw-tooltip-measure area'>#{L.GeometryUtil.readableArea(measure.area, !!options.metric)}</span>"
280
+
281
+ if latLng
282
+ @updateContent labelText
283
+ @__updatePosition latLng, options
284
+
285
+ return
286
+
287
+ __updatePosition: (latlng, options = {}) ->
288
+ pos = @_map.latLngToLayerPoint(latlng)
289
+ labelWidth = @_container.offsetWidth
290
+
291
+ map_width = @_map.getContainer().offsetWidth
292
+ L.DomUtil.removeClass(@_container, 'leaflet-draw-tooltip-left')
293
+
294
+ if @_container
295
+ @_container.style.visibility = 'inherit'
296
+ container = @_map.layerPointToContainerPoint pos
297
+ styles = window.getComputedStyle(@_container)
298
+
299
+ container_width = @_container.offsetWidth + parseInt(styles.paddingLeft) + parseInt(styles.paddingRight) + parseInt(styles.marginLeft) + parseInt(styles.marginRight)
300
+
301
+
302
+ if (container.x < 0 || container.x > (map_width - container_width) || container.y < @_container.offsetHeight)
303
+ pos = pos.add(L.point(-container_width, 0))
304
+ L.DomUtil.addClass(@_container, 'leaflet-draw-tooltip-left')
305
+
306
+ L.DomUtil.setPosition(@_container, pos)
307
+
308
+ hide: ->
309
+ @_container.style.visibility = 'hidden'
310
+
311
+ L.EditToolbar.Edit.include
312
+ _onMouseMove: (e) ->
313
+ return
314
+
315
+ L.EditToolbar.Delete.include
316
+ _onMouseMove: (e) ->
317
+ return
318
+
319
+ ###
320
+ #Add Configuration options
321
+ ###
322
+
323
+ L.DrawToolbar.include
324
+ __initialize: L.DrawToolbar.prototype.initialize
325
+
326
+ initialize: (options) ->
327
+ @__initialize.apply this, arguments
328
+ return
329
+
330
+ L.EditToolbar.include
331
+ __initialize: L.EditToolbar.prototype.initialize
332
+
333
+ initialize: () ->
334
+ @__initialize.apply this, arguments
335
+ return
336
+
337
+
338
+ ###
339
+ # Leaflet.Draw Patches
340
+ ###
341
+ L.EditToolbar.Edit.include
342
+ __removeHooks: L.EditToolbar.Edit::removeHooks
343
+ __revertLayer: L.EditToolbar.Edit::_revertLayer
344
+
345
+ # Patch missing event
346
+ removeHooks: ->
347
+ @__removeHooks.apply @, arguments
348
+ if @_map
349
+ @_map.off 'draw:editvertex', @_updateTooltip, @
350
+
351
+ # Patch handlers not reverted on cancel edit. See https://github.com/Leaflet/Leaflet.draw/issues/532
352
+ _revertLayer: (layer) ->
353
+ id = L.Util.stamp layer
354
+ @__revertLayer.apply @, arguments
355
+ layer.editing.latlngs = this._uneditedLayerProps[id].latlngs
356
+ layer.editing._poly._latlngs = this._uneditedLayerProps[id].latlngs
357
+ layer.editing._verticesHandlers[0]._latlngs = this._uneditedLayerProps[id].latlngs
358
+
359
+ _editStyle: ->
360
+ # missing method declaration in Leaflet.Draw
361
+ return
362
+
363
+ L.EditToolbar.include
364
+ # Patch _activeMode is null
365
+ _save: ->
366
+ handler = this._activeMode.handler
367
+ handler.save()
368
+ handler.disable()
369
+
370
+ L.ReactiveMeasureControl = L.Control.extend
371
+ options:
372
+ position: 'bottomright'
373
+ metric: true
374
+ feet: false
375
+ measure:
376
+ perimeter: 0
377
+ area: 0
378
+
379
+ initialize: (layers, options = {}) ->
380
+ L.Util.setOptions @, options
381
+ # Be sure to reset
382
+ @options.measure.perimeter = 0
383
+ @options.measure.area = 0
384
+
385
+ if layers.getLayers().length > 0
386
+ layers.eachLayer (layer) =>
387
+ if typeof layer.getMeasure is 'function'
388
+ m = layer.getMeasure()
389
+ @options.measure.perimeter += m.perimeter
390
+ @options.measure.area += m.area
391
+
392
+ onAdd: (map) ->
393
+ @_container = L.DomUtil.create('div', "reactive-measure-control #{map._leaflet_id}")
394
+ map.reactiveMeasureControl = @
395
+
396
+ if map and @_container
397
+ @updateContent(@options.measure)
398
+ @_container
399
+
400
+ updateContent: (measure = {}, options = {}) ->
401
+ text = ''
402
+ if measure['perimeter']
403
+ text += "<span class='leaflet-draw-tooltip-measure perimeter'>#{L.GeometryUtil.readableDistance(measure.perimeter, !!@options.metric, !!options.feet)}</span>"
404
+ if measure['area']
405
+ text += "<span class='leaflet-draw-tooltip-measure area'>#{L.GeometryUtil.readableArea(measure.area, !!@options.metric)}</span>"
406
+
407
+ if options.selection? && options.selection is true
408
+ L.DomUtil.addClass @_container, 'selection'
409
+ else
410
+ L.DomUtil.removeClass @_container, 'selection'
411
+
412
+ @_container.innerHTML = text
413
+
414
+ return