foliage 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|