Skip to content

Commit

Permalink
Improve heuristic for deciding how much distance the bar represents b…
Browse files Browse the repository at this point in the history
…y allowing it to scale to 1x, 2x, 4x and 5x of an exponent.
  • Loading branch information
HarryStevens committed Sep 27, 2020
1 parent ce2123a commit ce9f49d
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 29 deletions.
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ d3-geo-scale-bar is a JavaScript library and [D3.js](https://d3js.org/) plugin t
If you use NPM, `npm install d3-geo-scale-bar`. Otherwise, download the [latest release](https://github.com/HarryStevens/d3-geo-scale-bar/raw/master/build/d3-geo-scale-bar.zip). AMD, CommonJS, and vanilla environments are supported. In vanilla, a d3 global is exported:

```html
<script src="https://unpkg.com/[email protected].0/build/d3-geo-scale-bar.min.js"></script>
<script src="https://unpkg.com/[email protected].1/build/d3-geo-scale-bar.min.js"></script>
<script>
const projection = d3.geoMercator()
Expand Down Expand Up @@ -93,15 +93,15 @@ If <i>top</i> is specified, sets the top position to the specified value which m

### Sizing

<a name="scaleBar_distance" href="#scaleBar_distance">#</a> <i>scaleBar</i>.<b>distance</b>([<i>distance</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L157 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo)
<a name="scaleBar_distance" href="#scaleBar_distance">#</a> <i>scaleBar</i>.<b>distance</b>([<i>distance</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L162 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo)

If <i>distance</i> is specifed, sets the maximum distance of the scale bar and returns the scale bar. [Defaults](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L40) to the smallest exponent of 10 or 10x4 that will render the bar at least 60 pixels wide. If <i>distance</i> is not specified, returns the current maximum distance of the scale bar.
If <i>distance</i> is specifed, sets the maximum distance of the scale bar and returns the scale bar. [Defaults](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L40) to the smallest exponent of 10, 10x2, 10x4 or 10x5 that will render the bar at least 60 pixels wide. If <i>distance</i> 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>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L185 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarMoon)
<a name="scaleBar_radius" href="#scaleBar_radius">#</a> <i>scaleBar</i>.<b>radius</b>([<i>radius</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L190 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarMoon)

If <i>radius</i> is specifed, sets the radius of the sphere on which the scale bar is placed and returns the scale bar. Defaults to 6371.0088, [the mean radius of Earth in kilometers](https://en.wikipedia.org/wiki/Earth_radius#Mean_radius). If you set [<i>units</i>](#scaleBar_units) to d3.[geoScaleMiles](#geoScaleMiles), the <i>radius</i> will also update to 3958.7613, [the mean radius of Earth in miles](https://en.wikipedia.org/wiki/Earth_radius#Mean_radius). You can set the *radius* to any number you like, useful for [mapping planets other than Earth](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#spaceBars). If <i>radius</i> is not specified, returns the current radius.

<a name="scaleBar_units" href="#scaleBar_units">#</a> <i>scaleBar</i>.<b>units</b>([<i>units</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L213 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo)
<a name="scaleBar_units" href="#scaleBar_units">#</a> <i>scaleBar</i>.<b>units</b>([<i>units</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L218 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo)

If <i>units</i> is specifed, sets the [radius](#scaleBar_radius) of the scale bar to the corresponding units and returns the scale bar. Defaults to [d3.geoScaleKilometers](https://github.com/HarryStevens/d3-geo-scale-bar#geoScaleKilometers), which sets the label to "Kilometers" and the radius to 6371.0088, [the mean radius of Earth in kilometers](https://en.wikipedia.org/wiki/Earth_radius#Mean_radius). Note that the Earth's radius varies depending upon latitude, so if extremely high precision matters, you can [perform your own calculation of the radius](https://web.archive.org/web/20200118181437/https://rechneronline.de/earth-radius/) and pass the output to <i>scaleBar</i>.[radius](#scaleBar_radius).

Expand All @@ -125,15 +125,15 @@ When passed to <i>scaleBar</i>.[units](#scaleBar_units), sets the [radius](scale

### Styling

<a name="scaleBar_label" href="#scaleBar_label">#</a> <i>scaleBar</i>.<b>label</b>([<i>label</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L165 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo)
<a name="scaleBar_label" href="#scaleBar_label">#</a> <i>scaleBar</i>.<b>label</b>([<i>label</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L170 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo)

If a <i>label</i> string is specified, sets the text in the scale bar's label to the specified string and returns the scale bar. Defaults to the capitalized unit, e.g. "Kilometers". If label is specified as null, 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>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L169 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo)
<a name="scaleBar_labelAnchor" href="#scaleBar_labelAnchor">#</a> <i>scaleBar</i>.<b>labelAnchor</b>([<i>anchor</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L174 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo)

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, and returns the scale bar. Defaults to "start". If an <i>anchor</i> string is not specified, returns the current anchor.

<a name="scaleBar_orient" href="#scaleBar_orient">#</a> <i>scaleBar</i>.<b>orient</b>([<i>orientation</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L177 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarTop)
<a name="scaleBar_orient" href="#scaleBar_orient">#</a> <i>scaleBar</i>.<b>orient</b>([<i>orientation</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L182 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarTop)

If an <i>orientation</i> is specified, styles the bar according to the specified orientation and returns the scale bar. If an <i>orientation</i> is not specified, returns the current orientation as a string, either "top" or "bottom". Defaults to [d3.geoScaleBottom](#geoScaleBottom).

Expand All @@ -153,29 +153,29 @@ When passed to <i>scaleBar</i>.[orient](#scaleBar_orient), orients the scale bar
scaleBar.orient(d3.geoScaleTop);
```

<a name="scaleBar_tickFormat" href="#scaleBar_tickFormat">#</a> <i>scaleBar</i>.<b>tickFormat</b>([<i>formatter</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L197 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarPositioned)
<a name="scaleBar_tickFormat" href="#scaleBar_tickFormat">#</a> <i>scaleBar</i>.<b>tickFormat</b>([<i>formatter</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L202 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarPositioned)

If a <i>formatter</i> function is specified, each tick value is passed through the formatter before being displayed. Defaults to (d, i, e) => Math.round(d), where d is the tick number, i is the tick index, and e is an array of all tick data. If a <i>formatter</i> is not specified, returns the current formatter.

<a name="scaleBar_tickPadding" href="#scaleBar_tickPadding">#</a> <i>scaleBar</i>.<b>tickPadding</b>([<i>padding</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L201 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarTop)
<a name="scaleBar_tickPadding" href="#scaleBar_tickPadding">#</a> <i>scaleBar</i>.<b>tickPadding</b>([<i>padding</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L206 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarTop)

If <i>padding</i> is specified, sets the padding to the specified value in pixels and returns the scale bar. If <i>padding</i> is not specified, returns the current padding which defaults to 2 pixels.

<a name="scaleBar_tickSize" href="#scaleBar_tickSize">#</a> <i>scaleBar</i>.<b>tickSize</b>([<i>size</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L205 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo)
<a name="scaleBar_tickSize" href="#scaleBar_tickSize">#</a> <i>scaleBar</i>.<b>tickSize</b>([<i>size</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L210 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo)

If a <i>size</i> number is specified, sets the vertical tick size of the scale bar in pixels and returns the scale bar. Defaults to 4. If <i>size</i> is not specified, returns the current tick size of the scale bar.

<a name="scaleBar_tickValues" href="#scaleBar_tickValues">#</a> <i>scaleBar</i>.<b>tickValues</b>([<i>values</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L209 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarBottom)
<a name="scaleBar_tickValues" href="#scaleBar_tickValues">#</a> <i>scaleBar</i>.<b>tickValues</b>([<i>values</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L214 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarBottom)

If a <i>values</i> array is specified, sets the tick values to the specified values in the array rather than using the scale bar’s automatic tick generator, and returns the scale bar. Defaults to [0, kilometers / 4, kilometers / 2, kilometers]. Passing <i>null</i> removes the values and their associated ticks from the scale bar. If <i>values</i> is not specified, returns the current tick values.

### Zooming

<a name="scaleBar_zoomClamp" href="#scaleBar_zoomClamp">#</a> <i>scaleBar</i>.<b>zoomClamp</b>([<i>clamp</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L217 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#zooming)
<a name="scaleBar_zoomClamp" href="#scaleBar_zoomClamp">#</a> <i>scaleBar</i>.<b>zoomClamp</b>([<i>clamp</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L222 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#zooming)

If a boolean <i>clamp</i> is specified, sets the scale bar's zooming behavior and returns the scale bar. If clamp is true, the scale bar's width will remain constant as the [zoom factor](#scaleBar_zoomFactor) changes. If clamp is false, the scale bar's width will change with the zoom factor, but the distance represented by the scale bar will remain constant unless the bar becomes too small or too large. If <i>clamp</i> is not specified, returns the current clamp behavior, which defaults to true.

<a name="scaleBar_zoomFactor" href="#scaleBar_zoomFactor">#</a> <i>scaleBar</i>.<b>zoomFactor</b>([<i>k</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L221 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#zooming)
<a name="scaleBar_zoomFactor" href="#scaleBar_zoomFactor">#</a> <i>scaleBar</i>.<b>zoomFactor</b>([<i>k</i>]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js#L226 "Source"), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#zooming)

If <i>k</i> is specified, zooms the scale bar by the <i>k</i> [scale factor](https://github.com/d3/d3-zoom#zoomTransform) and returns the scale bar. This will commonly [be used](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#zooming) in conjunction with [d3-zoom](https://github.com/d3/d3-zoom):

Expand Down
16 changes: 9 additions & 7 deletions build/d3-geo-scale-bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,19 +261,21 @@
if (distance) {
barDistance = distance;
barWidth = barDistance / (geoDistance(start, projection.invert([x + 1, y])) * radius);
} // Otherwise, make it an exponent of 10 or 10x4 with a minimum width of 60px
} // Otherwise, make it an exponent of 10, 10x2, 10x4 or 10x5 with a minimum width of 60px
else {
var dist = .01,
minWidth = 60 / (zoomClamp ? 1 : zoomFactor),
iters = 0,
maxiters = 100;
maxiters = 100,
multiples = [1, 2, 4, 5];

while (barWidth < minWidth && iters < maxiters) {
barDistance = dist;
barWidth = barDistance / (geoDistance(start, projection.invert([x + 1, y])) * radius);
if (barWidth >= minWidth) break;
barDistance = dist * 4;
barWidth = barDistance / (geoDistance(start, projection.invert([x + 1, y])) * radius);
for (var i = 0, l = multiples.length; i < l; i++) {
barDistance = dist * multiples[i];
barWidth = barDistance / (geoDistance(start, projection.invert([x + 1, y])) * radius);
if (barWidth >= minWidth) break;
}

dist *= 10;
iters++;
}
Expand Down
Loading

0 comments on commit ce9f49d

Please sign in to comment.