d3-geo-scale-bar
Advanced tools
Comparing version 0.4.1 to 0.5.0
@@ -1,2 +0,2 @@ | ||
// https://github.com/HarryStevens/d3-geo-scale-bar Version 0.4.1. Copyright 2020 Harry Stevens. | ||
// https://github.com/HarryStevens/d3-geo-scale-bar Version 0.5.0. Copyright 2020 Harry Stevens. | ||
(function (global, factory) { | ||
@@ -223,3 +223,2 @@ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
projection, | ||
height = 4, | ||
left = 0, | ||
@@ -234,5 +233,6 @@ top = 0, | ||
}, | ||
label, | ||
tickSize = 4, | ||
labelText, | ||
labelAnchor = "start", | ||
scaleFactor = 1; | ||
zoomFactor = 1; | ||
@@ -248,59 +248,83 @@ var unitPresets = { | ||
function inferDistance(extent, radius) { | ||
return Math.pow(10, countDigits(geoDistance(projection.invert(extent[0]), projection.invert([extent[1][0], extent[0][1]])) * radius) - 1); | ||
} | ||
function countDigits(num) { | ||
return Math.floor(num).toString().length; | ||
} | ||
function capitalizeFirstLetter(str) { | ||
return str.charAt(0).toUpperCase() + str.slice(1); | ||
} | ||
function scaleBar(context) { | ||
var barWidth = extent[1][0] - extent[0][0], | ||
barHeight = extent[1][1] - extent[0][1]; | ||
context.attr("width", barWidth).attr("height", barHeight); | ||
var x = extent[0][0] + barWidth * left, | ||
y = extent[0][1] + barHeight * top, | ||
start = projection.invert([x, y]); | ||
distance = distance || Math.pow(10, countDigits(geoDistance(projection.invert(extent[0]), projection.invert([extent[1][0], extent[0][1]])) * radius) - 1); | ||
// If a distance has not been explicitly set, set it | ||
distance = distance || inferDistance(extent, radius); // If a label has not been explicitly set, set it | ||
var w = distance / (geoDistance(start, projection.invert([x + 1, y])) * radius), | ||
labelText = labelText === null ? null : labelText || capitalizeFirstLetter(units); // The position, width, and ticks of the scale bar | ||
var width = extent[1][0] - extent[0][0], | ||
height = extent[1][1] - extent[0][1], | ||
x = extent[0][0] + width * left, | ||
y = extent[0][1] + height * top, | ||
start = projection.invert([x, y]), | ||
barWidth = distance / (geoDistance(start, projection.invert([x + 1, y])) * radius), | ||
max = distance / zoomFactor, | ||
values = tickValues === null ? [] : tickValues ? tickValues : [0, max / 4, max / 2, max], | ||
scale = function scale(dist) { | ||
return dist * w / (distance / scaleFactor); | ||
return dist * barWidth / (distance / zoomFactor); | ||
}, | ||
tickMax = distance / scaleFactor, | ||
ticks = tickValues === null ? [] : tickValues ? tickValues : [0, tickMax / 4, tickMax / 2, tickMax]; | ||
selection = context.selection ? context.selection() : context, | ||
label = selection.selectAll(".label").data([labelText]), | ||
path = selection.selectAll(".domain").data([null]), | ||
tick = selection.selectAll(".tick").data(values, scale).order(), | ||
tickExit = tick.exit(), | ||
tickEnter = tick.enter().append("g").attr("class", "tick"), | ||
line = tick.select("line"), | ||
text = tick.select("text"), | ||
rect = tick.select("rect"); | ||
var g = context.select("g"); | ||
selection.attr("font-family", "sans-serif").attr("transform", "translate(".concat([x, y], ")")); | ||
path = path.merge(path.enter().insert("path", ".tick").attr("class", "domain").attr("fill", "none").attr("stroke", "currentColor")); | ||
tick = tick.merge(tickEnter); | ||
line = line.merge(tickEnter.append("line").attr("stroke", "currentColor").attr("y2", tickSize)); | ||
text = text.merge(tickEnter.append("text").attr("fill", "currentColor").attr("y", tickSize + 2).attr("font-size", 10).attr("text-anchor", "middle").attr("dy", "0.71em")); | ||
rect = rect.merge(tickEnter.append("rect").attr("fill", function (d, i) { | ||
return i % 2 === 0 ? "currentColor" : "#fff"; | ||
}).attr("stroke", "currentColor").attr("stroke-width", 0.5).attr("width", function (d, i, e) { | ||
return i === e.length - 1 ? 0 : scale(values[i + 1] - d); | ||
}).attr("height", tickSize)); | ||
if (!g._groups[0][0]) { | ||
g = context.append("g"); | ||
if (context !== selection) { | ||
tick = tick.transition(context); | ||
path = path.transition(context); | ||
rect = rect.transition(context); | ||
tickExit = tickExit.transition(context).attr("opacity", 1e-6).attr("transform", function (d) { | ||
return "translate(".concat(scale(d), ")"); | ||
}); | ||
tickEnter.attr("opacity", 1e-6).attr("transform", function (d) { | ||
return "translate(".concat(scale(d), ")"); | ||
}); | ||
} | ||
g.attr("transform", "translate(".concat([x, y], ")")); | ||
var baseline = g.select(".baseline"); | ||
tickExit.remove(); | ||
path.attr("d", "M".concat(scale(0), ",").concat(tickSize, " L").concat(scale(0), ",0 L").concat(scale(max), ",0 L").concat(scale(max), ",").concat(tickSize)); | ||
tick.attr("transform", function (d) { | ||
return "translate(".concat(scale(d), ")"); | ||
}).attr("opacity", 1); | ||
line.attr("y2", tickSize); | ||
text.attr("y", tickSize + 2).text(tickFormat); | ||
rect.attr("fill", function (d, i) { | ||
return i % 2 === 0 ? "currentColor" : "#fff"; | ||
}).attr("width", function (d, i, e) { | ||
return i === e.length - 1 ? 0 : scale(values[i + 1] - d); | ||
}).attr("height", tickSize); // The label | ||
if (!baseline._groups[0][0]) { | ||
baseline = g.append("rect").attr("class", "baseline"); | ||
} | ||
baseline.attr("fill", "black").attr("height", height).attr("width", scale(tickMax)); | ||
var rects = g.selectAll(".rectangle").data(ticks.map(function (d, i, data) { | ||
return [d, data[i + 1]]; | ||
}).filter(function (d, i, data) { | ||
return i !== data.length - 1; | ||
})); | ||
rects.exit().remove(); | ||
rects.enter().append("rect").attr("class", "rectangle").attr("height", height).attr("stroke", "#000").attr("fill", function (d, i) { | ||
return i % 2 === 0 ? "#000" : "#fff"; | ||
}).merge(rects).attr("x", function (d) { | ||
return scale(d[0]); | ||
}).attr("width", function (d) { | ||
return scale(d[1] - d[0]); | ||
}); | ||
var valueText = g.selectAll(".value").data(ticks); | ||
valueText.exit().remove(); | ||
valueText.enter().append("text").attr("class", "value").attr("text-anchor", "middle").attr("font-family", "sans-serif").attr("font-size", 12).merge(valueText).attr("x", scale).attr("y", height + 11).text(tickFormat); | ||
var labelText = g.select(".label"); | ||
if (!labelText._groups[0][0]) { | ||
labelText = g.append("text").attr("class", "label"); | ||
} | ||
if (label === null) { | ||
labelText.remove(); | ||
label.remove(); | ||
} else { | ||
label = label || capitalizeFirstLetter(units); | ||
labelText.attr("x", labelAnchor === "start" ? 0 : labelAnchor === "middle" ? scale(tickMax / 2) : scale(tickMax)).attr("class", "label").attr("fill", "#000").attr("text-anchor", labelAnchor).attr("font-size", 14).attr("font-family", "sans-serif").attr("y", -4).text(label); | ||
label.enter().append("text").attr("class", "label").attr("fill", "currentColor").attr("font-size", 12).attr("dy", "-0.32em").merge(label).attr("x", labelAnchor === "start" ? 0 : labelAnchor === "middle" ? scale(max / 2) : scale(max)).attr("text-anchor", labelAnchor).text(function (d) { | ||
return d; | ||
}); | ||
} | ||
@@ -335,2 +359,10 @@ } | ||
scaleBar.left = function (_) { | ||
return arguments.length ? (left = +_ > 1 ? 1 : +_ < 0 ? 0 : +_, scaleBar) : left; | ||
}; | ||
scaleBar.top = function (_) { | ||
return arguments.length ? (top = +_ > 1 ? 1 : +_ < 0 ? 0 : +_, scaleBar) : top; | ||
}; | ||
scaleBar.distance = function (_) { | ||
@@ -352,4 +384,8 @@ return arguments.length ? (distance = +_, scaleBar) : distance; | ||
scaleBar.tickSize = function (_) { | ||
return arguments.length ? (tickSize = +_, scaleBar) : tickSize; | ||
}; | ||
scaleBar.label = function (_) { | ||
return arguments.length ? (label = _, scaleBar) : label; | ||
return arguments.length ? (labelText = _, scaleBar) : labelText; | ||
}; | ||
@@ -361,26 +397,6 @@ | ||
scaleBar.height = function (_) { | ||
return arguments.length ? (height = +_, scaleBar) : height; | ||
scaleBar.zoomFactor = function (_) { | ||
return arguments.length ? (zoomFactor = +_, scaleBar) : zoomFactor; | ||
}; | ||
scaleBar.left = function (_) { | ||
return arguments.length ? (left = _ > 1 ? 1 : _ < 0 ? 0 : +_, scaleBar) : left; | ||
}; | ||
scaleBar.top = function (_) { | ||
return arguments.length ? (top = _ > 1 ? 1 : _ < 0 ? 0 : +_, scaleBar) : top; | ||
}; | ||
scaleBar.scaleFactor = function (_) { | ||
return arguments.length ? (scaleFactor = _, scaleBar) : scaleFactor; | ||
}; | ||
function countDigits(_) { | ||
return Math.floor(_).toString().length; | ||
} | ||
function capitalizeFirstLetter(_) { | ||
return _.charAt(0).toUpperCase() + _.slice(1); | ||
} | ||
return scaleBar; | ||
@@ -387,0 +403,0 @@ } |
@@ -1,1 +0,1 @@ | ||
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})}(this,function(t){"use strict";function n(){this.reset()}function e(t,n,e){var r=t.s=n+e,o=r-n,i=r-o;t.t=n-i+(e-o)}function r(t,n){t&&M.hasOwnProperty(t.type)&&M[t.type](t,n)}function o(t,n,e){var r,o=-1,i=t.length-e;for(n.lineStart();++o<i;)r=t[o],n.point(r[0],r[1],r[2]);n.lineEnd()}function i(t,n){var e=-1,r=t.length;for(n.polygonStart();++e<r;)o(t[e],n,1);n.polygonEnd()}function a(t,n){t&&m.hasOwnProperty(t.type)?m[t.type](t,n):r(t,n)}function u(){b.point=c,b.lineEnd=l}function l(){b.point=b.lineEnd=function(t){}}function c(t,n){t*=Math.PI/180,n*=Math.PI/180,d=t,p=Math.sin(n),v=Math.cos(n),b.point=s}function s(t,n){t*=Math.PI/180,n*=Math.PI/180;var e=Math.sin(n),r=Math.cos(n),o=Math.abs(t-d),i=Math.cos(o),a=Math.sin(o),u=r*a,l=v*e-p*r*i,c=p*e+v*r*i;x.add(Math.atan2(Math.sqrt(u*u+l*l),c)),d=t,p=e,v=r}function f(t){return x.reset(),a(t,b),+x}function h(t,n){return P[0]=t,P[1]=n,f(S)}function g(){function t(t){var y=u[1][0]-u[0][0],m=u[1][1]-u[0][1];t.attr("width",y).attr("height",m);var M=u[0][0]+y*c,x=u[0][1]+m*s,b=r.invert([M,x]);o=o||Math.pow(10,n(h(r.invert(u[0]),r.invert([u[1][0],u[0][1]]))*g)-1);var P=o/(h(b,r.invert([M+1,x]))*g),S=function(t){return t*P/(o/v)},w=o/v,k=null===i?[]:i||[0,w/4,w/2,w],E=t.select("g");E._groups[0][0]||(E=t.append("g")),E.attr("transform","translate(".concat([M,x],")"));var O=E.select(".baseline");O._groups[0][0]||(O=E.append("rect").attr("class","baseline")),O.attr("fill","black").attr("height",l).attr("width",S(w));var _=E.selectAll(".rectangle").data(k.map(function(t,n,e){return[t,e[n+1]]}).filter(function(t,n,e){return n!==e.length-1}));_.exit().remove(),_.enter().append("rect").attr("class","rectangle").attr("height",l).attr("stroke","#000").attr("fill",function(t,n){return n%2==0?"#000":"#fff"}).merge(_).attr("x",function(t){return S(t[0])}).attr("width",function(t){return S(t[1]-t[0])});var j=E.selectAll(".value").data(k);j.exit().remove(),j.enter().append("text").attr("class","value").attr("text-anchor","middle").attr("font-family","sans-serif").attr("font-size",12).merge(j).attr("x",S).attr("y",l+11).text(d);var A=E.select(".label");A._groups[0][0]||(A=E.append("text").attr("class","label")),null===a?A.remove():(a=a||e(f),A.attr("x","start"===p?0:S("middle"===p?w/2:w)).attr("class","label").attr("fill","#000").attr("text-anchor",p).attr("font-size",14).attr("font-family","sans-serif").attr("y",-4).text(a))}function n(t){return Math.floor(t).toString().length}function e(t){return t.charAt(0).toUpperCase()+t.slice(1)}var r,o,i,a,u=null,l=4,c=0,s=0,f="kilometers",g=6371,d=function(t){return Math.round(t)},p="start",v=1,y={miles:{radius:3959},kilometers:{radius:6371}};return t.extent=function(n){return arguments.length?(u=n,t):u},t.size=function(n){return arguments.length?(u=[[0,0],n],t):u[1]},t.projection=function(n){return arguments.length?(r=n,t):r},t.units=function(n){return arguments.length?(f=n,Object.keys(y).includes(n)&&(g=y[n].radius),t):f},t.distance=function(n){return arguments.length?(o=+n,t):o},t.radius=function(n){return arguments.length?(g=+n,t):g},t.tickValues=function(n){return arguments.length?(i=n,t):i},t.tickFormat=function(n){return arguments.length?(d=n,t):d},t.label=function(n){return arguments.length?(a=n,t):a},t.labelAnchor=function(n){return arguments.length?(p=n,t):p},t.height=function(n){return arguments.length?(l=+n,t):l},t.left=function(n){return arguments.length?(c=n>1?1:n<0?0:+n,t):c},t.top=function(n){return arguments.length?(s=n>1?1:n<0?0:+n,t):s},t.scaleFactor=function(n){return arguments.length?(v=n,t):v},t}n.prototype={constructor:n,reset:function(){this.s=this.t=0},add:function(t){e(y,t,this.t),e(this,y.s,this.s),this.s?this.t+=y.t:this.s=y.t},valueOf:function(){return this.s}};var d,p,v,y=new n,m={Feature:function(t,n){r(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,o=-1,i=e.length;++o<i;)r(e[o].geometry,n)}},M={Sphere:function(t,n){n.sphere()},Point:function(t,n){t=t.coordinates,n.point(t[0],t[1],t[2])},MultiPoint:function(t,n){for(var e=t.coordinates,r=-1,o=e.length;++r<o;)t=e[r],n.point(t[0],t[1],t[2])},LineString:function(t,n){o(t.coordinates,n,0)},MultiLineString:function(t,n){for(var e=t.coordinates,r=-1,i=e.length;++r<i;)o(e[r],n,0)},Polygon:function(t,n){i(t.coordinates,n)},MultiPolygon:function(t,n){for(var e=t.coordinates,r=-1,o=e.length;++r<o;)i(e[r],n)},GeometryCollection:function(t,n){for(var e=t.geometries,o=-1,i=e.length;++o<i;)r(e[o],n)}},x=function(){return new n}(),b={sphere:function(t){},point:function(t){},lineStart:u,lineEnd:function(t){},polygonStart:function(t){},polygonEnd:function(t){}},P=[null,null],S={type:"LineString",coordinates:P};t.geoScaleBar=g,Object.defineProperty(t,"__esModule",{value:!0})}); | ||
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})}(this,function(t){"use strict";function n(){this.reset()}function e(t,n,e){var r=t.s=n+e,o=r-n,i=r-o;t.t=n-i+(e-o)}function r(t,n){t&&v.hasOwnProperty(t.type)&&v[t.type](t,n)}function o(t,n,e){var r,o=-1,i=t.length-e;for(n.lineStart();++o<i;)r=t[o],n.point(r[0],r[1],r[2]);n.lineEnd()}function i(t,n){var e=-1,r=t.length;for(n.polygonStart();++e<r;)o(t[e],n,1);n.polygonEnd()}function a(t,n){t&&M.hasOwnProperty(t.type)?M[t.type](t,n):r(t,n)}function c(){x.point=l,x.lineEnd=u}function u(){x.point=x.lineEnd=function(t){}}function l(t,n){t*=Math.PI/180,n*=Math.PI/180,p=t,g=Math.sin(n),y=Math.cos(n),x.point=f}function f(t,n){t*=Math.PI/180,n*=Math.PI/180;var e=Math.sin(n),r=Math.cos(n),o=Math.abs(t-p),i=Math.cos(o),a=Math.sin(o),c=r*a,u=y*e-g*r*i,l=g*e+y*r*i;k.add(Math.atan2(Math.sqrt(c*c+u*u),l)),p=t,g=e,y=r}function s(t){return k.reset(),a(t,x),+k}function h(t,n){return P[0]=t,P[1]=n,s(S)}function d(){function t(t,e){return Math.pow(10,n(h(o.invert(t[0]),o.invert([t[1][0],t[0][1]]))*e)-1)}function n(t){return Math.floor(t).toString().length}function e(t){return t.charAt(0).toUpperCase()+t.slice(1)}function r(n){i=i||t(u,d),c=null===c?null:c||e(s);var r=u[1][0]-u[0][0],M=u[1][1]-u[0][1],v=u[0][0]+r*l,k=u[0][1]+M*f,x=o.invert([v,k]),P=i/(h(x,o.invert([v+1,k]))*d),S=i/m,C=null===a?[]:a||[0,S/4,S/2,S],b=function(t){return t*P/(i/m)},w=n.selection?n.selection():n,E=w.selectAll(".label").data([c]),L=w.selectAll(".domain").data([null]),z=w.selectAll(".tick").data(C,b).order(),A=z.exit(),O=z.enter().append("g").attr("class","tick"),j=z.select("line"),F=z.select("text"),I=z.select("rect");w.attr("font-family","sans-serif").attr("transform","translate(".concat([v,k],")")),L=L.merge(L.enter().insert("path",".tick").attr("class","domain").attr("fill","none").attr("stroke","currentColor")),z=z.merge(O),j=j.merge(O.append("line").attr("stroke","currentColor").attr("y2",g)),F=F.merge(O.append("text").attr("fill","currentColor").attr("y",g+2).attr("font-size",10).attr("text-anchor","middle").attr("dy","0.71em")),I=I.merge(O.append("rect").attr("fill",function(t,n){return n%2==0?"currentColor":"#fff"}).attr("stroke","currentColor").attr("stroke-width",.5).attr("width",function(t,n,e){return n===e.length-1?0:b(C[n+1]-t)}).attr("height",g)),n!==w&&(z=z.transition(n),L=L.transition(n),I=I.transition(n),A=A.transition(n).attr("opacity",1e-6).attr("transform",function(t){return"translate(".concat(b(t),")")}),O.attr("opacity",1e-6).attr("transform",function(t){return"translate(".concat(b(t),")")})),A.remove(),L.attr("d","M".concat(b(0),",").concat(g," L").concat(b(0),",0 L").concat(b(S),",0 L").concat(b(S),",").concat(g)),z.attr("transform",function(t){return"translate(".concat(b(t),")")}).attr("opacity",1),j.attr("y2",g),F.attr("y",g+2).text(p),I.attr("fill",function(t,n){return n%2==0?"currentColor":"#fff"}).attr("width",function(t,n,e){return n===e.length-1?0:b(C[n+1]-t)}).attr("height",g),null===E?E.remove():E.enter().append("text").attr("class","label").attr("fill","currentColor").attr("font-size",12).attr("dy","-0.32em").merge(E).attr("x","start"===y?0:b("middle"===y?S/2:S)).attr("text-anchor",y).text(function(t){return t})}var o,i,a,c,u=null,l=0,f=0,s="kilometers",d=6371,p=function(t){return Math.round(t)},g=4,y="start",m=1,M={miles:{radius:3959},kilometers:{radius:6371}};return r.extent=function(t){return arguments.length?(u=t,r):u},r.size=function(t){return arguments.length?(u=[[0,0],t],r):u[1]},r.projection=function(t){return arguments.length?(o=t,r):o},r.units=function(t){return arguments.length?(s=t,Object.keys(M).includes(t)&&(d=M[t].radius),r):s},r.left=function(t){return arguments.length?(l=+t>1?1:+t<0?0:+t,r):l},r.top=function(t){return arguments.length?(f=+t>1?1:+t<0?0:+t,r):f},r.distance=function(t){return arguments.length?(i=+t,r):i},r.radius=function(t){return arguments.length?(d=+t,r):d},r.tickValues=function(t){return arguments.length?(a=t,r):a},r.tickFormat=function(t){return arguments.length?(p=t,r):p},r.tickSize=function(t){return arguments.length?(g=+t,r):g},r.label=function(t){return arguments.length?(c=t,r):c},r.labelAnchor=function(t){return arguments.length?(y=t,r):y},r.zoomFactor=function(t){return arguments.length?(m=+t,r):m},r}n.prototype={constructor:n,reset:function(){this.s=this.t=0},add:function(t){e(m,t,this.t),e(this,m.s,this.s),this.s?this.t+=m.t:this.s=m.t},valueOf:function(){return this.s}};var p,g,y,m=new n,M={Feature:function(t,n){r(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,o=-1,i=e.length;++o<i;)r(e[o].geometry,n)}},v={Sphere:function(t,n){n.sphere()},Point:function(t,n){t=t.coordinates,n.point(t[0],t[1],t[2])},MultiPoint:function(t,n){for(var e=t.coordinates,r=-1,o=e.length;++r<o;)t=e[r],n.point(t[0],t[1],t[2])},LineString:function(t,n){o(t.coordinates,n,0)},MultiLineString:function(t,n){for(var e=t.coordinates,r=-1,i=e.length;++r<i;)o(e[r],n,0)},Polygon:function(t,n){i(t.coordinates,n)},MultiPolygon:function(t,n){for(var e=t.coordinates,r=-1,o=e.length;++r<o;)i(e[r],n)},GeometryCollection:function(t,n){for(var e=t.geometries,o=-1,i=e.length;++o<i;)r(e[o],n)}},k=function(){return new n}(),x={sphere:function(t){},point:function(t){},lineStart:c,lineEnd:function(t){},polygonStart:function(t){},polygonEnd:function(t){}},P=[null,null],S={type:"LineString",coordinates:P};t.geoScaleBar=d,Object.defineProperty(t,"__esModule",{value:!0})}); |
{ | ||
"name": "d3-geo-scale-bar", | ||
"version": "0.4.1", | ||
"version": "0.5.0", | ||
"description": "Displays automatic scale bars for projected geospatial data.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -14,3 +14,3 @@ # d3-geo-scale-bar | ||
```html | ||
<script src="https://unpkg.com/d3-geo-scale-bar@0.4.1/build/d3-geo-scale-bar.min.js"></script> | ||
<script src="https://unpkg.com/d3-geo-scale-bar@0.5.0/build/d3-geo-scale-bar.min.js"></script> | ||
<script> | ||
@@ -22,4 +22,4 @@ | ||
const scaleBar = d3.geoScaleBar() | ||
.projection() | ||
.size([width, height]); | ||
.projection(projection) | ||
.size([width, height]); | ||
@@ -70,2 +70,6 @@ d3.select("svg") | ||
<a name="scaleBar_distance" href="#scaleBar_distance">#</a> <i>scaleBar</i>.<b>distance</b>([<i>distance</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L114 "Source") | ||
If *distance* is specifed, sets the maxiumum distance of the scale bar in the scale bar's units. Defaults to the largest exponent of 10 that will fit on the map. If *distance* is not specified, returns the current maximum distance of the scale bar. | ||
<a name="scaleBar_extent" href="#scaleBar_extent">#</a> <i>scaleBar</i>.<b>extent</b>([<i>extent</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L91 "Source") | ||
@@ -83,2 +87,10 @@ | ||
<a name="scaleBar_left" href="#scaleBar_left">#</a> <i>scaleBar</i>.<b>left</b>([<i>left</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L134 "Source") | ||
If *left* is specified, sets the left position to the specified value which must be in the range [0, 1], where 0 is the left-most side of the scale bar's extent and 1 is the right-most. If *left* is not specified, returns the current left position which defaults to 0. | ||
<a name="scaleBar_top" href="#scaleBar_top">#</a> <i>scaleBar</i>.<b>top</b>([<i>top</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L138 "Source") | ||
If *top* is specified, sets the top position to the specified value which must be in the range [0, 1], where 0 is the top-most side of the scale bar's extent and 1 is the bottom-most. If *top* is not specified, returns the current top position which defaults to 0. | ||
<a name="scaleBar_units" href="#scaleBar_units">#</a> <i>scaleBar</i>.<b>units</b>([<i>units</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L99 "Source") | ||
@@ -88,6 +100,3 @@ | ||
<a name="scaleBar_distance" href="#scaleBar_distance">#</a> <i>scaleBar</i>.<b>distance</b>([<i>distance</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L114 "Source") | ||
If *distance* is specifed, sets the maxiumum distance of the scale bar in the scale bar's units. Defaults to the largest exponent of 10 that will fit on the map. If *distance* is not specified, returns the current maximum distance of the scale bar. | ||
<a name="scaleBar_radius" href="#scaleBar_radius">#</a> <i>scaleBar</i>.<b>radius</b>([<i>radius</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L118 "Source") | ||
@@ -97,6 +106,10 @@ | ||
<a name="scaleBar_tickValues" href="#scaleBar_tickValues">#</a> <i>scaleBar</i>.<b>tickValues</b>([<i>values</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L122 "Source") | ||
<a name="scaleBar_label" href="#scaleBar_label">#</a> <i>scaleBar</i>.<b>label</b>([<i>label</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L126 "Source") | ||
If a <i>values</i> array is specified, the specified values are used for ticks rather than using the scale bar’s automatic tick generator. Defaults to [0, kilometers / 4, kilometers / 2, kilometers]. Passing <i>null</i> removes the values from the scale bar. If <i>values</i> is not specified, returns the current tick values. | ||
If a <i>label</i> string is specified, updates the text in the scale bar's label to the specified string. Defaults to the capitalized unit, e.g. "Kilometers". If label is specified as <i>null</i>, removes the label. If <i>label</i> is not specified, returns the current label. | ||
<a name="scaleBar_labelAnchor" href="#scaleBar_labelAnchor">#</a> <i>scaleBar</i>.<b>labelAnchor</b>([<i>anchor</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L126 "Source") | ||
If an <i>anchor</i> string is specified, aligns the scale bar's label such that it is either at the "start" of the scale bar, the "middle" of the scale bar, or the "end" of the scale bar. Defaults to "start". If an <i>anchor</i> string is not specified, returns the current anchor. | ||
<a name="scaleBar_tickFormat" href="#scaleBar_tickFormat">#</a> <i>scaleBar</i>.<b>tickFormat</b>([<i>formatter</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L122 "Source") | ||
@@ -106,13 +119,13 @@ | ||
<a name="scaleBar_label" href="#scaleBar_label">#</a> <i>scaleBar</i>.<b>label</b>([<i>label</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L126 "Source") | ||
<a name="scaleBar_tickSize" href="#scaleBar_tickSize">#</a> <i>scaleBar</i>.<b>tickSize</b>([<i>size</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L130 "Source") | ||
If a <i>label</i> string is specified, updates the text in the scale bar's label to the specified string. Defaults to the capitalized unit, e.g. "Kilometers". If label is specified as <i>null</i>, removes the label. If <i>label</i> is not specified, returns the current label. | ||
If *size* is specified, sets the vertical tick size of the scale bar in pixels. Defaults to 4. If *size* is not specified, returns the current tick size of the scale bar. | ||
<a name="scaleBar_labelAnchor" href="#scaleBar_labelAnchor">#</a> <i>scaleBar</i>.<b>labelAnchor</b>([<i>anchor</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L126 "Source") | ||
<a name="scaleBar_tickValues" href="#scaleBar_tickValues">#</a> <i>scaleBar</i>.<b>tickValues</b>([<i>values</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L122 "Source") | ||
If an <i>anchor</i> string is specified, aligns the scale bar's label such that it is either at the "start" of the scale bar, the "middle" of the scale bar, or the "end" of the scale bar. Defaults to "start". If an <i>anchor</i> string is not specified, returns the current anchor. | ||
If a <i>values</i> array is specified, the specified values are used for ticks rather than using the scale bar’s automatic tick generator. Defaults to [0, kilometers / 4, kilometers / 2, kilometers]. Passing <i>null</i> removes the values from the scale bar. If <i>values</i> is not specified, returns the current tick values. | ||
<a name="scaleBar_scaleFactor" href="#scaleBar_scaleFactor">#</a> <i>scaleBar</i>.<b>scaleFactor</b>([<i>k</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L142 "Source") | ||
<a name="scaleBar_zoomFactor" href="#scaleBar_zoomFactor">#</a> <i>scaleBar</i>.<b>zoomFactor</b>([<i>k</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L142 "Source") | ||
If *k* is specified, zooms the scale bar by the *k* scale factor. This will commonly [be used](https://bl.ocks.org/HarryStevens/64fc5f1a4489abe78433b7d19510f864) in conjunction with [d3-zoom](https://github.com/d3/d3-zoom): | ||
If *k* is specified, zooms the scale bar by the *k* zoom factor. This will commonly [be used](https://bl.ocks.org/HarryStevens/64fc5f1a4489abe78433b7d19510f864) in conjunction with [d3-zoom](https://github.com/d3/d3-zoom): | ||
@@ -126,3 +139,3 @@ ```js | ||
scaleBar.scaleFactor(t.k); // Zoom the scale bar by the k scale factor. | ||
scaleBar.zoomFactor(t.k); // Zoom the scale bar by the k scale factor. | ||
scaleBarSelection.call(scaleBar); | ||
@@ -134,14 +147,2 @@ }); | ||
If *k* is not specified, returns the current scale factor. | ||
<a name="scaleBar_height" href="#scaleBar_height">#</a> <i>scaleBar</i>.<b>height</b>([<i>height</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L130 "Source") | ||
If *height* is specified, sets the height of the scale bar in pixels. Defaults to 4. If *height* is not specified, returns the current height of the scale bar. | ||
<a name="scaleBar_left" href="#scaleBar_left">#</a> <i>scaleBar</i>.<b>left</b>([<i>left</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L134 "Source") | ||
If *left* is specified, sets the left position to the specified value which must be in the range [0, 1], where 0 is the left-most side of the scale bar's extent and 1 is the right-most. If *left* is not specified, returns the current left position which defaults to 0. | ||
<a name="scaleBar_top" href="#scaleBar_top">#</a> <i>scaleBar</i>.<b>top</b>([<i>top</i>]) [<>](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L138 "Source") | ||
If *top* is specified, sets the top position to the specified value which must be in the range [0, 1], where 0 is the top-most side of the scale bar's extent and 1 is the bottom-most. If *top* is not specified, returns the current top position which defaults to 0. | ||
If *k* is not specified, returns the current scale factor. |
@@ -6,3 +6,2 @@ import { default as geoDistance } from "./geo/distance"; | ||
projection, | ||
height = 4, | ||
left = 0, | ||
@@ -15,5 +14,6 @@ top = 0, | ||
tickFormat = d => Math.round(d), | ||
label, | ||
tickSize = 4, | ||
labelText, | ||
labelAnchor = "start", | ||
scaleFactor = 1; | ||
zoomFactor = 1; | ||
@@ -28,88 +28,122 @@ const unitPresets = { | ||
}; | ||
function inferDistance(extent, radius){ | ||
return Math.pow(10, countDigits(geoDistance(projection.invert(extent[0]), projection.invert([extent[1][0], extent[0][1]])) * radius) - 1); | ||
} | ||
function countDigits(num){ | ||
return Math.floor(num).toString().length; | ||
} | ||
function capitalizeFirstLetter(str) { | ||
return str.charAt(0).toUpperCase() + str.slice(1); | ||
} | ||
function scaleBar(context){ | ||
const barWidth = extent[1][0] - extent[0][0], | ||
barHeight = extent[1][1] - extent[0][1]; | ||
function scaleBar(context){ | ||
// If a distance has not been explicitly set, set it | ||
distance = distance || inferDistance(extent, radius); | ||
context.attr("width", barWidth).attr("height", barHeight); | ||
// If a label has not been explicitly set, set it | ||
labelText = labelText === null ? null : labelText || capitalizeFirstLetter(units); | ||
const x = extent[0][0] + barWidth * left, | ||
y = extent[0][1] + barHeight * top, | ||
start = projection.invert([x, y]); | ||
// The position, width, and ticks of the scale bar | ||
let width = extent[1][0] - extent[0][0], | ||
height = extent[1][1] - extent[0][1], | ||
x = extent[0][0] + width * left, | ||
y = extent[0][1] + height * top, | ||
start = projection.invert([x, y]), | ||
barWidth = distance / (geoDistance(start, projection.invert([x + 1, y])) * radius), | ||
max = distance / zoomFactor, | ||
values = tickValues === null ? [] : tickValues ? tickValues : [0, max / 4, max / 2, max], | ||
scale = dist => dist * barWidth / (distance / zoomFactor), | ||
selection = context.selection ? context.selection() : context, | ||
label = selection.selectAll(".label").data([labelText]), | ||
path = selection.selectAll(".domain").data([null]), | ||
tick = selection.selectAll(".tick").data(values, scale).order(), | ||
tickExit = tick.exit(), | ||
tickEnter = tick.enter().append("g").attr("class", "tick"), | ||
line = tick.select("line"), | ||
text = tick.select("text"), | ||
rect = tick.select("rect"); | ||
distance = distance || Math.pow(10, countDigits(geoDistance(projection.invert(extent[0]), projection.invert([extent[1][0], extent[0][1]])) * radius) - 1); | ||
selection | ||
.attr("font-family", "sans-serif") | ||
.attr("transform", `translate(${[x, y]})`); | ||
const w = distance / (geoDistance(start, projection.invert([x + 1, y])) * radius), | ||
scale = dist => dist * w / (distance / scaleFactor), | ||
tickMax = distance / scaleFactor, | ||
ticks = tickValues === null ? [] : tickValues ? tickValues : [0, tickMax / 4, tickMax / 2, tickMax]; | ||
path = path.merge(path.enter().insert("path", ".tick") | ||
.attr("class", "domain") | ||
.attr("fill", "none") | ||
.attr("stroke", "currentColor")); | ||
let g = context.select("g") | ||
if (!g._groups[0][0]) { | ||
g = context.append("g"); | ||
tick = tick.merge(tickEnter); | ||
line = line.merge(tickEnter.append("line") | ||
.attr("stroke", "currentColor") | ||
.attr("y2", tickSize)); | ||
text = text.merge(tickEnter.append("text") | ||
.attr("fill", "currentColor") | ||
.attr("y", tickSize + 2) | ||
.attr("font-size", 10) | ||
.attr("text-anchor", "middle") | ||
.attr("dy", "0.71em")); | ||
rect = rect.merge(tickEnter.append("rect") | ||
.attr("fill", (d, i) => i % 2 === 0 ? "currentColor" : "#fff") | ||
.attr("stroke", "currentColor") | ||
.attr("stroke-width", 0.5) | ||
.attr("width", (d, i, e) => i === e.length - 1 ? 0 : scale(values[i + 1] - d)) | ||
.attr("height", tickSize)); | ||
if (context !== selection){ | ||
tick = tick.transition(context); | ||
path = path.transition(context); | ||
rect = rect.transition(context); | ||
tickExit = tickExit.transition(context) | ||
.attr("opacity", 1e-6) | ||
.attr("transform", d => `translate(${scale(d)})`); | ||
tickEnter | ||
.attr("opacity", 1e-6) | ||
.attr("transform", d => `translate(${scale(d)})`); | ||
} | ||
g.attr("transform", `translate(${[x, y]})`); | ||
let baseline = g.select(".baseline") | ||
if (!baseline._groups[0][0]) { | ||
baseline = g.append("rect") | ||
.attr("class", "baseline"); | ||
} | ||
baseline | ||
.attr("fill", "black") | ||
.attr("height", height) | ||
.attr("width", scale(tickMax)); | ||
tickExit.remove(); | ||
path | ||
.attr("d", `M${scale(0)},${tickSize} L${scale(0)},0 L${scale(max)},0 L${scale(max)},${tickSize}`); | ||
tick | ||
.attr("transform", d => `translate(${scale(d)})`) | ||
.attr("opacity", 1); | ||
line | ||
.attr("y2", tickSize); | ||
const rects = g.selectAll(".rectangle") | ||
.data(ticks.map((d, i, data) => [d, data[i + 1]]).filter((d, i, data) => i !== data.length - 1)); | ||
rects.exit().remove(); | ||
rects.enter().append("rect") | ||
.attr("class", "rectangle") | ||
.attr("height", height) | ||
.attr("stroke", "#000") | ||
.attr("fill", (d, i) => i % 2 === 0 ? "#000" : "#fff") | ||
.merge(rects) | ||
.attr("x", d => scale(d[0])) | ||
.attr("width", d => scale(d[1] - d[0])); | ||
const valueText = g.selectAll(".value") | ||
.data(ticks); | ||
text | ||
.attr("y", tickSize + 2) | ||
.text(tickFormat); | ||
valueText.exit().remove(); | ||
rect | ||
.attr("fill", (d, i) => i % 2 === 0 ? "currentColor" : "#fff") | ||
.attr("width", (d, i, e) => i === e.length - 1 ? 0 : scale(values[i + 1] - d)) | ||
.attr("height", tickSize); | ||
valueText.enter().append("text") | ||
.attr("class", "value") | ||
.attr("text-anchor", "middle") | ||
.attr("font-family", "sans-serif") | ||
.attr("font-size", 12) | ||
.merge(valueText) | ||
.attr("x", scale) | ||
.attr("y", height + 11) | ||
.text(tickFormat); | ||
let labelText = g.select(".label"); | ||
if (!labelText._groups[0][0]){ | ||
labelText = g.append("text") | ||
.attr("class", "label") | ||
// The label | ||
if (label === null) { | ||
label.remove(); | ||
} | ||
if (label === null){ | ||
labelText.remove(); | ||
} | ||
else { | ||
label = label || capitalizeFirstLetter(units); | ||
labelText | ||
.attr("x", labelAnchor === "start" ? 0 : labelAnchor === "middle" ? scale(tickMax / 2) : scale(tickMax)) | ||
label.enter().append("text") | ||
.attr("class", "label") | ||
.attr("fill", "#000") | ||
.attr("fill", "currentColor") | ||
.attr("font-size", 12) | ||
.attr("dy", "-0.32em") | ||
.merge(label) | ||
.attr("x", labelAnchor === "start" ? 0 : labelAnchor === "middle" ? scale(max / 2) : scale(max)) | ||
.attr("text-anchor", labelAnchor) | ||
.attr("font-size", 14) | ||
.attr("font-family", "sans-serif") | ||
.attr("y", -4) | ||
.text(label); | ||
.text(d => d); | ||
} | ||
} | ||
@@ -144,2 +178,10 @@ | ||
scaleBar.left = function(_) { | ||
return arguments.length ? (left = +_ > 1 ? 1 : +_ < 0 ? 0 : +_, scaleBar) : left; | ||
} | ||
scaleBar.top = function(_) { | ||
return arguments.length ? (top = +_ > 1 ? 1 : +_ < 0 ? 0 : +_, scaleBar) : top; | ||
} | ||
scaleBar.distance = function(_) { | ||
@@ -160,5 +202,9 @@ return arguments.length ? (distance = +_, scaleBar) : distance; | ||
} | ||
scaleBar.tickSize = function(_) { | ||
return arguments.length ? (tickSize = +_, scaleBar) : tickSize; | ||
} | ||
scaleBar.label = function(_) { | ||
return arguments.length ? (label = _, scaleBar) : label; | ||
return arguments.length ? (labelText = _, scaleBar) : labelText; | ||
} | ||
@@ -170,27 +216,7 @@ | ||
scaleBar.height = function(_) { | ||
return arguments.length ? (height = +_, scaleBar) : height; | ||
scaleBar.zoomFactor = function(_) { | ||
return arguments.length ? (zoomFactor = +_, scaleBar) : zoomFactor; | ||
} | ||
scaleBar.left = function(_) { | ||
return arguments.length ? (left = _ > 1 ? 1 : _ < 0 ? 0 : +_, scaleBar) : left; | ||
} | ||
scaleBar.top = function(_) { | ||
return arguments.length ? (top = _ > 1 ? 1 : _ < 0 ? 0 : +_, scaleBar) : top; | ||
} | ||
scaleBar.scaleFactor = function(_) { | ||
return arguments.length ? (scaleFactor = _, scaleBar) : scaleFactor; | ||
} | ||
function countDigits(_){ | ||
return Math.floor(_).toString().length; | ||
} | ||
function capitalizeFirstLetter(_) { | ||
return _.charAt(0).toUpperCase() + _.slice(1); | ||
} | ||
return scaleBar; | ||
} |
698
139
49899
15