Created
November 17, 2016 22:58
-
-
Save seb-thomas/d678302063457105edf174acdcccc703 to your computer and use it in GitHub Desktop.
Walking circle for Living Map, uses location API and D3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| L.WalkingCircleLayer = (L.Layer ? L.Layer : L.Class).extend({ | |
| initialize: function() { | |
| this._prefix = this._prefixMatch(["webkit", "ms", "Moz", "O"]), | |
| this._canvasPadding = 4, | |
| this._isActive = !1 | |
| }, | |
| onAdd: function(t) { | |
| var a = L.DomUtil.create("div", "leaflet-control-walkingcircle leaflet-bar leaflet-control"); | |
| this._link = L.DomUtil.create("a", "leaflet-bar-part leaflet-bar-part-single", a), | |
| this._icon = L.DomUtil.create("span", "fa fa-location-arrow", this._link), | |
| this._link.href = "#", | |
| document.querySelector(".leaflet-right").appendChild(a); | |
| var i = this; | |
| L.DomEvent.on(this._link, "click", L.DomEvent.stopPropagation).on(this._link, "click", L.DomEvent.preventDefault).on(this._link, "click", function() { | |
| this._isActive ? i.onRemove(t) : this._locate() | |
| }, this), | |
| this._map = t | |
| }, | |
| onRemove: function(t) { | |
| this._isActive = !1, | |
| t.getPanes().overlayPane.removeChild(this._canvas), | |
| t.stopLocate(), | |
| t.off("viewreset moveend resize", this._reset, this), | |
| t.off("zoomanim", this._animateZoom, this), | |
| t.off("locationfound", this._onLocationFound, this), | |
| t.off("locationerror", this._onLocationError, this), | |
| L.DomUtil.removeClass(this._link, "active") | |
| }, | |
| addTo: function(t) { | |
| return t.addLayer(this), | |
| this | |
| }, | |
| _initSVG: function() { | |
| var t = (this._map.getSize(), | |
| d3.select(map.getPanes().overlayPane).append("svg").attr("id", "walking-circle-canvas").attr("class", "svg-zoom-animated")); | |
| this.D3_wcContainer = t.append("g").attr("id", "walking-circle-container").attr("visibility", "hidden"), | |
| this._canvas = L.DomUtil.get("walking-circle-canvas"), | |
| this._drawWalkingCircle(), | |
| this._drawPositionCircle(), | |
| this._reset(), | |
| map.on("viewreset moveend resize", this._reset, this) | |
| }, | |
| _reset: function(t) { | |
| var a = this._map.getSize() | |
| , i = a.x * this._canvasPadding | |
| , e = a.y * this._canvasPadding | |
| , r = -(i - a.x) / 2 | |
| , n = -(e - a.y) / 2 | |
| , o = this._map.containerPointToLayerPoint([r, n]).round(); | |
| this._canvas.setAttribute("width", i), | |
| this._canvas.setAttribute("height", e), | |
| this._canvas.setAttribute("viewBox", [o.x, o.y, i, e].join(" ")), | |
| L.DomUtil.setPosition(this._canvas, o); | |
| var s = this; | |
| void 0 !== t && "viewreset" == t.type && t.hard && this.D3_wcContainer.attr({ | |
| style: function() { | |
| var t = map.latLngToLayerPoint(s._latlng); | |
| return s._prefix + "transform: translate(" + t.x + "px," + t.y + "px)" | |
| } | |
| }) | |
| }, | |
| _drawWalkingCircle: function() { | |
| this._count = 1; | |
| this.D3_wcContainer.append("g").attr("id", "walking-circle").attr("transform", "rotate(0)"); | |
| this.D3_wc = d3.select("#walking-circle"), | |
| this._minsPerZoom = { | |
| 15: [1, 2, 5], | |
| 14: [2, 5, 10], | |
| 13: [3, 8, 15], | |
| 12: [5, 12, 30], | |
| 11: [10, 25, 45], | |
| 10: [15, 30, 60], | |
| 9: [25, 40, 60], | |
| 8: [25, 50, 75], | |
| 7: [30, 75, 120] | |
| }, | |
| this._zoomToRadius = {}; | |
| for (var t = 15, a = 7, i = 1, e = t; e >= a; e--) | |
| this._zoomToRadius[e] = 672 / i, | |
| i += i; | |
| this._arc = d3.svg.arc().startAngle(0); | |
| var r = this; | |
| this._minsPerZoom[this._map.getZoom()].forEach(function(t) { | |
| r._makeCircle(t) | |
| }) | |
| }, | |
| _makeCircle: function(t) { | |
| var a = this.D3_wc | |
| , i = 2 * Math.PI | |
| , e = this._map.getZoom() | |
| , r = this._zoomToRadius[e] | |
| , n = this._arc | |
| , o = this._calcArc(r * t, 3) | |
| , s = a.append("g").attr("class", "circle-container") | |
| , c = (s.append("path").datum({ | |
| endAngle: i, | |
| innerRadius: o.innerRadius, | |
| outerRadius: o.outerRadius | |
| }).style("fill", "white").attr("class", "circle circle" + this._count).attr("d", n), | |
| s.append("path").datum({ | |
| endAngle: i / 16, | |
| innerRadius: o.tagInnerRadius, | |
| outerRadius: o.tagOuterRadius | |
| }).style("fill", "white").attr("d", n).attr("class", "tag").attr("id", "tag" + this._count), | |
| s.append("text").attr("x", 6).attr("dy", 12)); | |
| c.append("textPath").attr("class", "walking-circle-text").attr("xlink:href", "#tag" + this._count).text(t + " mins walk"), | |
| this._count += 1 | |
| }, | |
| _drawPositionCircle: function() { | |
| { | |
| var t = this.D3_wcContainer.append("g").attr("id", "position-circle").attr("transform", "rotate(0)") | |
| , a = d3.svg.line().x(function(t) { | |
| return t.x | |
| }).y(function(t) { | |
| return t.y | |
| }).interpolate("linear") | |
| , i = { | |
| arrow: { | |
| width: 14, | |
| height: 26, | |
| tip: 14 | |
| }, | |
| circle: { | |
| radius: 40 | |
| } | |
| } | |
| , e = [{ | |
| x: i.arrow.width, | |
| y: i.arrow.height | |
| }, { | |
| x: i.arrow.width / 2, | |
| y: 0 | |
| }, { | |
| x: 0, | |
| y: i.arrow.height | |
| }, { | |
| x: i.arrow.width / 2, | |
| y: i.arrow.height - 5 | |
| }]; | |
| t.append("circle").attr("id", "pc-circle").attr("r", i.circle.radius).attr("fill", "none").attr("stroke", "white").attr("stroke-width", 4).attr("stroke-linecap", "round").attr("stroke-dasharray", "0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 7, 0.5, 33"), | |
| t.append("path").attr("id", "pc-letter-n").attr("d", "M3.8,5.4L1.3,2.2v3.3H0V0h1.2l2.5,3.3V0H5v5.4H3.8z").attr("fill", "white").attr("transform", "translate(1, -60) scale(0)"), | |
| t.append("path").attr("id", "pc-arrow").attr("d", a(e) + "Z").attr("stroke", "white").attr("stroke-width", 3).attr("stroke-linejoin", "round").attr("fill", "white").style("fill-opacity", 0).attr("transform", "translate(-6, " + -(i.circle.radius + i.arrow.tip) + ") scale(1)"), | |
| t.append("path").attr("id", "pc-bars").attr("d", "M40,0 L31,0 M0,40 L0,31 M-40,0 L-31,0").attr("stroke", "white").attr("stroke-width", 4).attr("stroke-linecap", "round") | |
| } | |
| }, | |
| _locate: function() { | |
| L.DomUtil.addClass(this._link, "finding"), | |
| map.on("locationfound", this._onLocationFound, this), | |
| map.once("locationfound", function(t) { | |
| map.panTo(params.demo ? [CONFIG.startCenter.lat, CONFIG.startCenter.lng] : t.latlng) | |
| }, this), | |
| map.on("locationerror", this._onLocationError, this), | |
| this._isActive = !0, | |
| this._initSVG(), | |
| map.locate({ | |
| watch: !0 | |
| }), | |
| map.on("zoomanim", this._animateZoom, this), | |
| window.setTimeout(function() { | |
| var t = 0 | |
| , a = 0; | |
| if ("ondeviceorientation"in window) { | |
| window.addEventListener("deviceorientation", function(i) { | |
| var e, r, n; | |
| for ("undefined" != typeof i.webkitCompassHeading ? (e = i.webkitCompassHeading, | |
| "undefined" != typeof window.orientation && (e += window.orientation)) : e = 360 - i.alpha, | |
| r = Math.round(e) - a, | |
| a = Math.round(e), | |
| -180 > r && (r += 360), | |
| r > 180 && (r -= 360), | |
| t += r, | |
| n = e; n >= 360; ) | |
| n -= 360; | |
| for (; 0 > n; ) | |
| n += 360; | |
| n = Math.round(n) | |
| }); | |
| var i, e = d3.select("#position-circle"), r = d3.select("#pc-arrow"), n = d3.select("#pc-letter-n"), o = r.attr("transform"), s = n.attr("transform"); | |
| window.setInterval(function() { | |
| var a = -t | |
| , c = Math.abs(a - i); | |
| c >= 1 && (a > -8 && 8 > a ? (e.transition().duration(300).ease("bounce").attr("transform", "rotate(0)"), | |
| r.transition().duration(200).ease("elastic").attr("transform", "translate(-8, -58) scale(1.3)").style("fill-opacity", 1).transition().duration(200).ease("out").attr("transform", o), | |
| n.transition().duration(200).ease("elastic").attr("transform", "translate(-5, -78) scale(2.5)").transition().duration(200).ease("out").attr("transform", s)) : (e.transition().duration(500).attr("transform", "rotate(" + a + ")"), | |
| r.transition().attr("transform", o).style("fill-opacity", 0))), | |
| i = a | |
| }, 200) | |
| } | |
| }, 400) | |
| }, | |
| _onLocationFound: function(t) { | |
| this._latlng = params.demo ? [CONFIG.startCenter.lat, CONFIG.startCenter.lng] : [t.latitude, t.longitude]; | |
| var a = this; | |
| L.DomUtil.addClass(this._link, "active"), | |
| L.DomUtil.removeClass(this._link, "finding"), | |
| this.D3_wcContainer.attr("visibility", "visible").attr({ | |
| style: function() { | |
| var t = map.latLngToLayerPoint(a._latlng); | |
| return a._prefix + "transform: translate(" + t.x + "px," + t.y + "px)" | |
| } | |
| }) | |
| }, | |
| _onLocationError: function(t) { | |
| alert(t.message) | |
| }, | |
| _animateZoom: function(t) { | |
| var a = t.zoom | |
| , i = this; | |
| this.D3_wcContainer.attr({ | |
| style: function() { | |
| var e = map._latLngToNewLayerPoint(i._latlng, a, t.center); | |
| return i._prefix + "transform: translate(" + e.x + "px," + e.y + "px)" | |
| } | |
| }); | |
| var e = this._minsPerZoom[a] | |
| , r = this._zoomToRadius[a]; | |
| d3.selectAll(".circle-container").each(function(t, a) { | |
| var n = e[a] | |
| , o = i._calcArc(r * n, 3) | |
| , s = n + (1 == n ? " min walk" : " mins walk"); | |
| d3.select(this).select(".circle").transition().duration(750).ease("elastic").call(i._arcTween, i._arc, o.innerRadius, o.outerRadius), | |
| d3.select(this).select(".tag").transition().duration(750).ease("elastic").call(i._arcTween, i._arc, o.tagInnerRadius, o.tagOuterRadius), | |
| d3.select(this).select(".walking-circle-text").text(s) | |
| }) | |
| }, | |
| _arcTween: function(t, a, i, e) { | |
| t.attrTween("d", function(t) { | |
| var r = d3.interpolate(t.innerRadius, i) | |
| , n = d3.interpolate(t.outerRadius, e); | |
| return function(i) { | |
| return t.outerRadius = n(i), | |
| t.innerRadius = r(i), | |
| a(t) | |
| } | |
| }) | |
| }, | |
| _calcArc: function(t, a) { | |
| var i = { | |
| innerRadius: t, | |
| outerRadius: t + a, | |
| tagInnerRadius: t - a - 12, | |
| tagOuterRadius: t + 1 | |
| }; | |
| return i | |
| }, | |
| _prefixMatch: function(t) { | |
| for (var a = -1, i = t.length, e = document.body.style; ++a < i; ) | |
| if (t[a] + "Transform"in e) | |
| return "-" + t[a].toLowerCase() + "-"; | |
| return "" | |
| } | |
| }), | |
| L.walkingCircle = function() { | |
| return new L.WalkingCircleLayer | |
| } | |
| ; | |
| var wc = L.walkingCircle().addTo(map); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment