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.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +9 -0
- data/app/assets/images/.keep +0 -0
- data/app/assets/images/map/marker/icon-2x.png +0 -0
- data/app/assets/images/map/marker/icon.png +0 -0
- data/app/assets/images/map/marker/icon.svg +67 -0
- data/app/assets/images/map/marker/shadow.png +0 -0
- data/app/assets/javascripts/core_ext.js.coffee +61 -0
- data/app/assets/javascripts/foliage.js.coffee +23 -0
- data/app/assets/javascripts/foliage/band.js.coffee +99 -0
- data/app/assets/javascripts/foliage/bubbles.js.coffee +77 -0
- data/app/assets/javascripts/foliage/categories.js.coffee +70 -0
- data/app/assets/javascripts/foliage/choropleth.js.coffee +51 -0
- data/app/assets/javascripts/foliage/color.js.coffee +39 -0
- data/app/assets/javascripts/foliage/gradient.js.coffee +72 -0
- data/app/assets/javascripts/foliage/heatmap.js.coffee +49 -0
- data/app/assets/javascripts/foliage/leaf.js.coffee +422 -0
- data/app/assets/javascripts/foliage/path.js.coffee +76 -0
- data/app/assets/javascripts/foliage/paths.js.coffee +131 -0
- data/app/assets/javascripts/foliage/point_group.js.coffee +83 -0
- data/app/assets/javascripts/foliage/points.js.coffee +79 -0
- data/app/assets/javascripts/foliage/simple.js.coffee +35 -0
- data/app/assets/javascripts/leaflet/geographic_util.js.coffee +23 -0
- data/app/assets/javascripts/leaflet/ghost_label.js.coffee +100 -0
- data/app/assets/javascripts/leaflet/ghost_label_cluster.js.coffee +192 -0
- data/app/assets/javascripts/leaflet/layers_scheduler.js.coffee +57 -0
- data/app/assets/javascripts/leaflet/reactive_measure.js.coffee +414 -0
- data/app/assets/stylesheets/all.scss +16 -0
- data/app/assets/stylesheets/application.css +15 -0
- data/app/assets/stylesheets/compass/reset.scss +3 -0
- data/app/assets/stylesheets/compass/reset/utilities.scss +142 -0
- data/app/assets/stylesheets/leaflet.scss +1093 -0
- data/app/assets/stylesheets/leaflet/label.scss +40 -0
- data/app/assets/stylesheets/leaflet/tooltip.scss +42 -0
- data/app/assets/stylesheets/mixins.scss +131 -0
- data/app/assets/stylesheets/reset.scss +89 -0
- data/app/assets/stylesheets/variables.scss +47 -0
- data/app/helpers/foliage_helper.rb +23 -0
- data/lib/foliage.rb +9 -0
- data/lib/foliage/leaf.rb +235 -0
- data/lib/foliage/rails.rb +2 -0
- data/lib/foliage/rails/engine.rb +7 -0
- data/lib/foliage/rails/integration.rb +8 -0
- data/lib/foliage/version.rb +3 -0
- data/vendor/assets/javascripts/.keep +0 -0
- data/vendor/assets/javascripts/autosize.js +211 -0
- data/vendor/assets/javascripts/geographiclib.js +3074 -0
- data/vendor/assets/javascripts/leaflet.js.erb +9175 -0
- data/vendor/assets/javascripts/leaflet/draw.js +3573 -0
- data/vendor/assets/javascripts/leaflet/easy-button.js +366 -0
- data/vendor/assets/javascripts/leaflet/fullscreen.js +162 -0
- data/vendor/assets/javascripts/leaflet/heatmap.js +142 -0
- data/vendor/assets/javascripts/leaflet/label.js +545 -0
- data/vendor/assets/javascripts/leaflet/measure.js +6966 -0
- data/vendor/assets/javascripts/leaflet/modal.js +364 -0
- data/vendor/assets/javascripts/leaflet/providers.js +479 -0
- data/vendor/assets/javascripts/rbush.js +621 -0
- data/vendor/assets/stylesheets/.keep +0 -0
- data/vendor/assets/stylesheets/bootstrap/mixins.scss +55 -0
- data/vendor/assets/stylesheets/bootstrap/variables.scss +10 -0
- data/vendor/assets/stylesheets/leaflet.scss +479 -0
- data/vendor/assets/stylesheets/leaflet/draw.scss +282 -0
- data/vendor/assets/stylesheets/leaflet/easy-button.scss +56 -0
- data/vendor/assets/stylesheets/leaflet/fullscreen.scss +2 -0
- data/vendor/assets/stylesheets/leaflet/measure.scss +168 -0
- data/vendor/assets/stylesheets/leaflet/modal.scss +85 -0
- 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
|