├── .babelrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── build ├── d3-geo-scale-bar.js ├── d3-geo-scale-bar.min.js └── d3-geo-scale-bar.zip ├── img └── default.png ├── index.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── geo │ ├── adder.js │ ├── distance.js │ ├── length.js │ ├── noop.js │ └── stream.js ├── geoScaleBar.js ├── orient │ ├── bottom.js │ └── top.js └── units │ ├── feet.js │ ├── kilometers.js │ ├── meters.js │ └── miles.js └── test ├── data ├── india.json └── monaco.json ├── geo-scale-bar-test.js └── geo-scale-bar.html /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/env", {"modules": false}] 4 | ] 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-workspace 2 | .DS_Store 3 | node_modules 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.sublime-* 2 | build/*.zip 3 | test/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Harry Stevens 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of contributors may be used to 15 | endorse or promote products derived from this software without specific prior 16 | written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # d3-geo-scale-bar 2 | 3 | d3-geo-scale-bar is a JavaScript library and [D3.js](https://d3js.org/) plugin that makes it easy to add scale bars to maps created with [d3-geo](https://github.com/d3/d3-geo). 4 | 5 | ## Installing 6 | 7 | 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: 8 | 9 | ```html 10 | 11 | 27 | ``` 28 | 29 | [Try d3-geo-scale-bar in your browser](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar). 30 | 31 | ## API Reference 32 | 33 | * [Introduction](#introduction) 34 | * [Basic configuration](#basic-configuration) 35 | * [Positioning](#positioning) 36 | * [Sizing](#sizing) 37 | * [Styling](#styling) 38 | * [Zooming](#zooming) 39 | 40 | ### Introduction 41 | 42 | Scale bars help readers understand the geographic extent of maps. A scale bar's [default design](https://bl.ocks.org/HarryStevens/8c8d3a489aa1372e14b8084f94b32464) references the classic checkered design: 43 | 44 | [Scale Bar Design](https://bl.ocks.org/HarryStevens/8c8d3a489aa1372e14b8084f94b32464) 45 | 46 | By tweaking the scale bar's configuration and CSS, you can produce [several different scale bar designs](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#styling). 47 | 48 | A scale bar consists of a [path element](https://www.w3.org/TR/SVG/paths.html#PathElement) of class "domain", followed by four [g elements](https://www.w3.org/TR/SVG/struct.html#Groups) of class "tick" representing each of the scale bar's ticks. Each tick has a [line element](https://www.w3.org/TR/SVG/shapes.html#LineElement) to draw the tick line, a [text element](https://www.w3.org/TR/SVG/text.html#TextElement) for the tick label, and a [rect element](https://www.w3.org/TR/SVG/shapes.html#RectElement) of alternating black and white fill. There is also another text element of class "label" sitting above the bar that denotes the units. 49 | 50 | # d3.geoScaleBar() · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar) 51 | 52 | Constructs a new scale bar generator with the default settings. 53 | 54 | # scaleBar(context) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#basicUsage) 55 | 56 | Renders the scale bar to the given context, which may be either a [selection](https://github.com/d3/d3-selection) of an SVG [g element](https://www.w3.org/TR/SVG/struct.html#Groups) or a corresponding [transition](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#transitions). Configure the scale bar with [scaleBar.projection](#scaleBar_projection) and [scaleBar.extent](#scaleBar_fitSize) before rendering. Generally, you will use this with selection.[call](https://github.com/d3/d3-selection#selection_call): 57 | 58 | ```js 59 | const scaleBar = d3.geoScaleBar() 60 | .projection(projection) 61 | .size([width, height]); 62 | 63 | svg.append("g").call(scaleBar); 64 | ``` 65 | 66 | ### Basic configuration 67 | 68 | # scaleBar.extent([extent]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBar) 69 | 70 | If extent is specified, sets the extent of the scale bar generator to the specified bounds and returns the scale bar. The extent is specified as an array [[x0, y0], [x1, y1]], where x0 is the left side of the extent, y0 is the top, x1 is the right, and y1 is the bottom. If extent is not specified, returns the current extent which defaults to null. An extent is required to render a scale bar. 71 | 72 | # scaleBar.projection([projection]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBar) 73 | 74 | If projection is specified, sets the [projection](https://github.com/d3/d3-geo#projections) and returns the scale bar. If projection is not specified, returns the current projection. A projection is required to render a scale bar. 75 | 76 | # scaleBar.size([size]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBar) 77 | 78 | An alias for [scaleBar.extent](#scaleBar_extent) where the minimum x and y of the extent are ⟨0,0⟩. Equivalent to: 79 | 80 | ```js 81 | scaleBar.extent([[0, 0], size]); 82 | ``` 83 | 84 | ### Positioning 85 | 86 | # scaleBar.left([left]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarPositioned) 87 | 88 | If left is specified, sets the left position to the specified value which must be in the range [0, 1], where 0 is the leftmost side of the scale bar's extent and 1 is the rightmost, and returns the scale bar. If left is not specified, returns the current left position which defaults to 0. 89 | 90 | # scaleBar.top([top]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarPositioned) 91 | 92 | 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, and returns the scale bar. If top is not specified, returns the current top position which defaults to 0. 93 | 94 | ### Sizing 95 | 96 | # scaleBar.distance([distance]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo) 97 | 98 | If distance 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) to the smallest exponent of 10, 10x2, 10x4 or 10x5 that will render the bar at least 80 pixels wide. If distance is not specified, returns the current maximum distance of the scale bar. 99 | 100 | # scaleBar.radius([radius]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarMoon) 101 | 102 | If radius 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 [units](#scaleBar_units) to d3.[geoScaleMiles](#geoScaleMiles), the radius 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 radius is not specified, returns the current radius. 103 | 104 | # scaleBar.units([units]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo) 105 | 106 | If units 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 scaleBar.[radius](#scaleBar_radius). 107 | 108 | If units is not specified, returns a string representing the current unit, e.g. "kilometers". The capitalized version of this string will be used for the [label](#scaleBar_label) if no label is specified. 109 | 110 | # d3.geoScaleFeet · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/units/feet.js) 111 | 112 | When passed to scaleBar.[units](#scaleBar_units), sets the [radius](scaleBar_radius) to 20902259.664, the mean radius of Earth in feet. The [label](#scaleBar_label) will be set to "Feet" if no label is specified. 113 | 114 | # d3.geoScaleKilometers · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/units/kilometers.js) 115 | 116 | When passed to scaleBar.[units](#scaleBar_units), sets the [radius](scaleBar_radius) to 6371.0088, the mean radius of Earth in kilometers. The [label](#scaleBar_label) will be set to "Kilometers" if no label is specified. This is the default setting. 117 | 118 | # d3.geoScaleMeters · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/units/meters.js) 119 | 120 | When passed to scaleBar.[units](#scaleBar_units), sets the [radius](scaleBar_radius) to 6371008.8, the mean radius of Earth in meters. The [label](#scaleBar_label) will be set to "Meters" if no label is specified. 121 | 122 | # d3.geoScaleMiles · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/units/miles.js) 123 | 124 | When passed to scaleBar.[units](#scaleBar_units), sets the [radius](scaleBar_radius) to 3958.7613, the mean radius of Earth in miles. The [label](#scaleBar_label) will be set to "Miles" if no label is specified. 125 | 126 | ### Styling 127 | 128 | # scaleBar.label([label]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo) 129 | 130 | If a label 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 label is not specified, returns the current label. 131 | 132 | # scaleBar.labelAnchor([anchor]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo) 133 | 134 | If an anchor 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 anchor string is not specified, returns the current anchor. 135 | 136 | # scaleBar.orient([orientation]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarTop) 137 | 138 | If an orientation is specified, styles the bar according to the specified orientation and returns the scale bar. If an orientation is not specified, returns the current orientation as a string, either "top" or "bottom". Defaults to [d3.geoScaleBottom](#geoScaleBottom). 139 | 140 | # d3.geoScaleBottom · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/orient/bottom.js) 141 | 142 | When passed to scaleBar.[orient](#scaleBar_orient), orients the scale bar so that the label is on the top and the ticks are on bottom. This is the default orientation. 143 | 144 | ```js 145 | scaleBar.orient(d3.geoScaleBottom); 146 | ``` 147 | 148 | # d3.geoScaleTop · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/orient/top.js) 149 | 150 | When passed to scaleBar.[orient](#scaleBar_orient), orients the scale bar so that the label is on the bottom and the ticks are on top. 151 | 152 | ```js 153 | scaleBar.orient(d3.geoScaleTop); 154 | ``` 155 | 156 | # scaleBar.tickFormat([formatter]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarPositioned) 157 | 158 | If a formatter 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 formatter is not specified, returns the current formatter. 159 | 160 | # scaleBar.tickPadding([padding]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarTop) 161 | 162 | If padding is specified, sets the padding to the specified value in pixels and returns the scale bar. If padding is not specified, returns the current padding which defaults to 2 pixels. 163 | 164 | # scaleBar.tickSize([size]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarWapo) 165 | 166 | If a size number is specified, sets the vertical tick size of the scale bar in pixels and returns the scale bar. Defaults to 4. If size is not specified, returns the current tick size of the scale bar. 167 | 168 | # scaleBar.tickValues([values]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#scaleBarBottom) 169 | 170 | If a values 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 null removes the values and their associated ticks from the scale bar. If values is not specified, returns the current tick values. 171 | 172 | ### Zooming 173 | 174 | # scaleBar.zoomClamp([clamp]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#zooming) 175 | 176 | If a boolean clamp 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 clamp is not specified, returns the current clamp behavior, which defaults to true. 177 | 178 | # scaleBar.zoomFactor([k]) · [Source](https://github.com/HarryStevens/d3-geo-scale-bar/blob/master/src/geoScaleBar.js), [Example](https://observablehq.com/@harrystevens/introducing-d3-geo-scale-bar#zooming) 179 | 180 | If k is specified, zooms the scale bar by the k [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): 181 | 182 | ```js 183 | const zoom = d3.zoom() 184 | .on("zoom", _ => { 185 | const t = d3.event.transform; 186 | 187 | g.attr("transform", t); 188 | 189 | scaleBar.zoomFactor(t.k); // Zoom the scale bar by the k scale factor. 190 | scaleBarSelection.call(scaleBar); 191 | }); 192 | 193 | svg.call(zoom); 194 | ``` 195 | 196 | If k is not specified, returns the current zoom factor. -------------------------------------------------------------------------------- /build/d3-geo-scale-bar.js: -------------------------------------------------------------------------------- 1 | // https://github.com/HarryStevens/d3-geo-scale-bar Version 1.2.5. Copyright 2022 Harry Stevens. 2 | (function (global, factory) { 3 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 4 | typeof define === 'function' && define.amd ? define(['exports'], factory) : 5 | (factory((global.d3 = global.d3 || {}))); 6 | }(this, (function (exports) { 'use strict'; 7 | 8 | // Adds floating point numbers with twice the normal precision. 9 | // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and 10 | // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3) 11 | // 305–363 (1997). 12 | // Code adapted from GeographicLib by Charles F. F. Karney, 13 | // http://geographiclib.sourceforge.net/ 14 | // ...which was taken from d3-geo by Mike Bostock 15 | // Source: https://github.com/d3/d3-geo/blob/master/src/adder.js 16 | // License: https://github.com/d3/d3-geo/blob/master/LICENSE 17 | function adder () { 18 | return new Adder(); 19 | } 20 | 21 | function Adder() { 22 | this.reset(); 23 | } 24 | 25 | Adder.prototype = { 26 | constructor: Adder, 27 | reset: function reset() { 28 | this.s = // rounded value 29 | this.t = 0; // exact error 30 | }, 31 | add: function add(y) { 32 | _add(temp, y, this.t); 33 | 34 | _add(this, temp.s, this.s); 35 | 36 | if (this.s) this.t += temp.t;else this.s = temp.t; 37 | }, 38 | valueOf: function valueOf() { 39 | return this.s; 40 | } 41 | }; 42 | var temp = new Adder(); 43 | 44 | function _add(adder, a, b) { 45 | var x = adder.s = a + b, 46 | bv = x - a, 47 | av = x - bv; 48 | adder.t = a - av + (b - bv); 49 | } 50 | 51 | // From d3-geo by Mike Bostock 52 | // Source: https://github.com/d3/d3-geo/blob/master/src/stream.js 53 | // License: https://github.com/d3/d3-geo/blob/master/LICENSE 54 | function streamGeometry(geometry, stream) { 55 | if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) { 56 | streamGeometryType[geometry.type](geometry, stream); 57 | } 58 | } 59 | 60 | var streamObjectType = { 61 | Feature: function Feature(object, stream) { 62 | streamGeometry(object.geometry, stream); 63 | }, 64 | FeatureCollection: function FeatureCollection(object, stream) { 65 | var features = object.features, 66 | i = -1, 67 | n = features.length; 68 | 69 | while (++i < n) { 70 | streamGeometry(features[i].geometry, stream); 71 | } 72 | } 73 | }; 74 | var streamGeometryType = { 75 | Sphere: function Sphere(object, stream) { 76 | stream.sphere(); 77 | }, 78 | Point: function Point(object, stream) { 79 | object = object.coordinates; 80 | stream.point(object[0], object[1], object[2]); 81 | }, 82 | MultiPoint: function MultiPoint(object, stream) { 83 | var coordinates = object.coordinates, 84 | i = -1, 85 | n = coordinates.length; 86 | 87 | while (++i < n) { 88 | object = coordinates[i], stream.point(object[0], object[1], object[2]); 89 | } 90 | }, 91 | LineString: function LineString(object, stream) { 92 | streamLine(object.coordinates, stream, 0); 93 | }, 94 | MultiLineString: function MultiLineString(object, stream) { 95 | var coordinates = object.coordinates, 96 | i = -1, 97 | n = coordinates.length; 98 | 99 | while (++i < n) { 100 | streamLine(coordinates[i], stream, 0); 101 | } 102 | }, 103 | Polygon: function Polygon(object, stream) { 104 | streamPolygon(object.coordinates, stream); 105 | }, 106 | MultiPolygon: function MultiPolygon(object, stream) { 107 | var coordinates = object.coordinates, 108 | i = -1, 109 | n = coordinates.length; 110 | 111 | while (++i < n) { 112 | streamPolygon(coordinates[i], stream); 113 | } 114 | }, 115 | GeometryCollection: function GeometryCollection(object, stream) { 116 | var geometries = object.geometries, 117 | i = -1, 118 | n = geometries.length; 119 | 120 | while (++i < n) { 121 | streamGeometry(geometries[i], stream); 122 | } 123 | } 124 | }; 125 | 126 | function streamLine(coordinates, stream, closed) { 127 | var i = -1, 128 | n = coordinates.length - closed, 129 | coordinate; 130 | stream.lineStart(); 131 | 132 | while (++i < n) { 133 | coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]); 134 | } 135 | 136 | stream.lineEnd(); 137 | } 138 | 139 | function streamPolygon(coordinates, stream) { 140 | var i = -1, 141 | n = coordinates.length; 142 | stream.polygonStart(); 143 | 144 | while (++i < n) { 145 | streamLine(coordinates[i], stream, 1); 146 | } 147 | 148 | stream.polygonEnd(); 149 | } 150 | 151 | function stream (object, stream) { 152 | if (object && streamObjectType.hasOwnProperty(object.type)) { 153 | streamObjectType[object.type](object, stream); 154 | } else { 155 | streamGeometry(object, stream); 156 | } 157 | } 158 | 159 | // Adapted from d3-geo by Mike Bostock 160 | var lengthSum = adder(), 161 | lambda0, 162 | sinPhi0, 163 | cosPhi0; 164 | var lengthStream = { 165 | sphere: function sphere(_) {}, 166 | point: function point(_) {}, 167 | lineStart: lengthLineStart, 168 | lineEnd: function lineEnd(_) {}, 169 | polygonStart: function polygonStart(_) {}, 170 | polygonEnd: function polygonEnd(_) {} 171 | }; 172 | 173 | function lengthLineStart() { 174 | lengthStream.point = lengthPointFirst; 175 | lengthStream.lineEnd = lengthLineEnd; 176 | } 177 | 178 | function lengthLineEnd() { 179 | lengthStream.point = lengthStream.lineEnd = function (_) {}; 180 | } 181 | 182 | function lengthPointFirst(lambda, phi) { 183 | lambda *= Math.PI / 180, phi *= Math.PI / 180; 184 | lambda0 = lambda, sinPhi0 = Math.sin(phi), cosPhi0 = Math.cos(phi); 185 | lengthStream.point = lengthPoint; 186 | } 187 | 188 | function lengthPoint(lambda, phi) { 189 | lambda *= Math.PI / 180, phi *= Math.PI / 180; 190 | var sinPhi = Math.sin(phi), 191 | cosPhi = Math.cos(phi), 192 | delta = Math.abs(lambda - lambda0), 193 | cosDelta = Math.cos(delta), 194 | sinDelta = Math.sin(delta), 195 | x = cosPhi * sinDelta, 196 | y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosDelta, 197 | z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosDelta; 198 | lengthSum.add(Math.atan2(Math.sqrt(x * x + y * y), z)); 199 | lambda0 = lambda, sinPhi0 = sinPhi, cosPhi0 = cosPhi; 200 | } 201 | 202 | function length (object) { 203 | lengthSum.reset(); 204 | stream(object, lengthStream); 205 | return +lengthSum; 206 | } 207 | 208 | // From d3-geo by Mike Bostock 209 | var coordinates = [null, null], 210 | object = { 211 | type: "LineString", 212 | coordinates: coordinates 213 | }; 214 | function geoDistance (a, b) { 215 | coordinates[0] = a; 216 | coordinates[1] = b; 217 | return length(object); 218 | } 219 | 220 | function geoScaleBottom () { 221 | return 1; 222 | } 223 | 224 | var geoScaleKilometers = { 225 | units: "kilometers", 226 | radius: 6371.0088 227 | }; 228 | 229 | function geoScaleBar () { 230 | var extent = null, 231 | projection, 232 | left = 0, 233 | top = 0, 234 | orient = geoScaleBottom(), 235 | radius = geoScaleKilometers.radius, 236 | units = geoScaleKilometers.units, 237 | distance, 238 | distanceLog, 239 | tickFormat = function tickFormat(d) { 240 | return +d.toFixed(2); 241 | }, 242 | tickPadding = 2, 243 | tickSize = 4, 244 | tickValues, 245 | labelText, 246 | labelAnchor = "start", 247 | zoomFactor = 1, 248 | zoomClamp = true; 249 | 250 | function scaleBar(context) { 251 | // If a label has not been explicitly set, set it 252 | labelText = labelText === null ? null : labelText || units.charAt(0).toUpperCase() + units.slice(1); // The position and width of the scale bar 253 | 254 | var width = extent[1][0] - extent[0][0], 255 | height = extent[1][1] - extent[0][1], 256 | x = extent[0][0] + width * left, 257 | y = extent[0][1] + height * top, 258 | start = projection.invert([x, y]); 259 | var barDistance = 0, 260 | barWidth = 0; // If the distance has been set explicitly, calculate the bar's width 261 | 262 | if (distance) { 263 | barDistance = distance; 264 | barWidth = barDistance / (geoDistance(start, projection.invert([x + 1, y])) * radius); 265 | } // Otherwise, make it an exponent of 10, 10x2, 10x4 or 10x5 with a minimum width of 80px 266 | else { 267 | var dist = .01, 268 | minWidth = 80 / (zoomClamp ? 1 : zoomFactor), 269 | iters = 0, 270 | maxiters = 100, 271 | multiples = [1, 2, 4, 5]; 272 | 273 | while (barWidth < minWidth && iters < maxiters) { 274 | for (var i = 0, l = multiples.length; i < l; i++) { 275 | barDistance = dist * multiples[i]; 276 | barWidth = barDistance / (geoDistance(start, projection.invert([x + 1, y])) * radius); 277 | if (barWidth >= minWidth) break; 278 | } 279 | 280 | dist *= 10; 281 | iters++; 282 | } 283 | 284 | distanceLog = barDistance; 285 | } // The ticks and elements of the bar 286 | 287 | 288 | var max = barDistance / (zoomClamp ? zoomFactor : 1), 289 | values = tickValues === null ? [] : tickValues ? tickValues : [0, max / 4, max / 2, max], 290 | scale = function scale(dist) { 291 | return dist * barWidth / (barDistance / zoomFactor); 292 | }, 293 | selection = context.selection ? context.selection() : context, 294 | label = selection.selectAll(".label").data([labelText]), 295 | path = selection.selectAll(".domain").data([null]), 296 | tick = selection.selectAll(".tick").data(values, scale).order(), 297 | tickExit = tick.exit(), 298 | tickEnter = tick.enter().append("g").attr("class", "tick"), 299 | line = tick.select("line"), 300 | text = tick.select("text"), 301 | rect = tick.select("rect"); 302 | 303 | selection.attr("font-family", "sans-serif").attr("transform", "translate(".concat([x, y], ")")); 304 | path = path.merge(path.enter().insert("path", ".tick").attr("class", "domain").attr("fill", "none").attr("stroke", "currentColor")); 305 | tick = tick.merge(tickEnter); 306 | line = line.merge(tickEnter.append("line").attr("stroke", "currentColor").attr("y2", tickSize * orient)); 307 | text = text.merge(tickEnter.append("text").attr("fill", "currentColor").attr("y", tickSize * orient + tickPadding * orient).attr("font-size", 10).attr("text-anchor", "middle").attr("dy", "".concat(orient === 1 ? 0.71 : 0, "em"))); 308 | rect = rect.merge(tickEnter.append("rect").attr("fill", function (d, i) { 309 | return i % 2 === 0 ? "currentColor" : "#fff"; 310 | }).attr("stroke", "currentColor").attr("stroke-width", 0.5).attr("width", function (d, i, e) { 311 | return i === e.length - 1 ? 0 : scale(values[i + 1] - d); 312 | }).attr("y", orient === 1 ? 0 : -tickSize).attr("height", tickSize)); 313 | 314 | if (context !== selection) { 315 | tick = tick.transition(context); 316 | path = path.transition(context); 317 | rect = rect.transition(context); 318 | tickExit = tickExit.transition(context).attr("opacity", 1e-6).attr("transform", function (d) { 319 | return "translate(".concat(scale(d), ")"); 320 | }); 321 | tickEnter.attr("opacity", 1e-6).attr("transform", function (d) { 322 | return "translate(".concat(scale(d), ")"); 323 | }); 324 | } 325 | 326 | tickExit.remove(); 327 | path.attr("d", "M".concat(scale(0), ",").concat(tickSize * orient, " L").concat(scale(0), ",0 L").concat(scale(max), ",0 L").concat(scale(max), ",").concat(tickSize * orient)); 328 | tick.attr("transform", function (d) { 329 | return "translate(".concat(scale(d), ")"); 330 | }).attr("opacity", 1); 331 | line.attr("y2", tickSize * orient); 332 | text.attr("y", tickSize * orient + tickPadding * orient).text(tickFormat); 333 | rect.attr("fill", function (d, i) { 334 | return i % 2 === 0 ? "currentColor" : "#fff"; 335 | }).attr("width", function (d, i, e) { 336 | return i === e.length - 1 ? 0 : scale(values[i + 1] - d); 337 | }).attr("y", orient === 1 ? 0 : -tickSize).attr("height", tickSize); // The label 338 | 339 | if (label === null) { 340 | label.remove(); 341 | } else { 342 | if (context !== selection) { 343 | label.transition(context).attr("x", labelAnchor === "start" ? 0 : labelAnchor === "middle" ? scale(max / 2) : scale(max)).attr("y", orient === 1 ? 0 : "1.3em").attr("text-anchor", labelAnchor).text(function (d) { 344 | return d; 345 | }); 346 | } else { 347 | label.attr("x", labelAnchor === "start" ? 0 : labelAnchor === "middle" ? scale(max / 2) : scale(max)).attr("y", orient === 1 ? 0 : "1.3em").attr("text-anchor", labelAnchor).text(function (d) { 348 | return d; 349 | }); 350 | } 351 | 352 | label.enter().append("text").attr("class", "label").attr("fill", "currentColor").attr("font-size", 12).attr("dy", "-0.32em").attr("x", labelAnchor === "start" ? 0 : labelAnchor === "middle" ? scale(max / 2) : scale(max)).attr("y", orient === 1 ? 0 : "1.3em").attr("text-anchor", labelAnchor).text(function (d) { 353 | return d; 354 | }); 355 | } 356 | } 357 | 358 | scaleBar.distance = function (_) { 359 | return arguments.length ? (distance = +_, scaleBar) : distance || distanceLog; 360 | }; 361 | 362 | scaleBar.extent = function (_) { 363 | return arguments.length ? (extent = _, scaleBar) : extent; 364 | }; 365 | 366 | scaleBar.label = function (_) { 367 | return arguments.length ? (labelText = _, scaleBar) : labelText; 368 | }; 369 | 370 | scaleBar.labelAnchor = function (_) { 371 | return arguments.length ? (labelAnchor = _, scaleBar) : labelAnchor; 372 | }; 373 | 374 | scaleBar.left = function (_) { 375 | return arguments.length ? (left = +_ > 1 ? 1 : +_ < 0 ? 0 : +_, scaleBar) : left; 376 | }; 377 | 378 | scaleBar.orient = function (_) { 379 | return arguments.length ? (orient = _(), scaleBar) : orient === 1 ? "bottom" : "top"; 380 | }; 381 | 382 | scaleBar.projection = function (_) { 383 | return arguments.length ? (projection = _, scaleBar) : projection; 384 | }; 385 | 386 | scaleBar.radius = function (_) { 387 | return arguments.length ? (radius = +_, scaleBar) : radius; 388 | }; 389 | 390 | scaleBar.size = function (_) { 391 | return arguments.length ? (extent = [[0, 0], _], scaleBar) : extent[1]; 392 | }; 393 | 394 | scaleBar.top = function (_) { 395 | return arguments.length ? (top = +_ > 1 ? 1 : +_ < 0 ? 0 : +_, scaleBar) : top; 396 | }; 397 | 398 | scaleBar.tickFormat = function (_) { 399 | return arguments.length ? (tickFormat = _, scaleBar) : tickFormat; 400 | }; 401 | 402 | scaleBar.tickPadding = function (_) { 403 | return arguments.length ? (tickPadding = +_, scaleBar) : tickPadding; 404 | }; 405 | 406 | scaleBar.tickSize = function (_) { 407 | return arguments.length ? (tickSize = +_, scaleBar) : tickSize; 408 | }; 409 | 410 | scaleBar.tickValues = function (_) { 411 | return arguments.length ? (tickValues = _, scaleBar) : tickValues; 412 | }; 413 | 414 | scaleBar.units = function (_) { 415 | var _ref; 416 | 417 | return arguments.length ? ((_ref = _, units = _ref.units, radius = _ref.radius, _ref), scaleBar) : units; 418 | }; 419 | 420 | scaleBar.zoomClamp = function (_) { 421 | return arguments.length ? (zoomClamp = !!_, scaleBar) : zoomClamp; 422 | }; 423 | 424 | scaleBar.zoomFactor = function (_) { 425 | return arguments.length ? (zoomFactor = +_, scaleBar) : zoomFactor; 426 | }; 427 | 428 | return scaleBar; 429 | } 430 | 431 | function top () { 432 | return -1; 433 | } 434 | 435 | var feet = { 436 | units: "feet", 437 | radius: 20902259.664 438 | }; 439 | 440 | var meters = { 441 | units: "meters", 442 | radius: 6371008.8 443 | }; 444 | 445 | var miles = { 446 | units: "miles", 447 | radius: 3958.7613 448 | }; 449 | 450 | exports.geoScaleBar = geoScaleBar; 451 | exports.geoScaleBottom = geoScaleBottom; 452 | exports.geoScaleTop = top; 453 | exports.geoScaleFeet = feet; 454 | exports.geoScaleKilometers = geoScaleKilometers; 455 | exports.geoScaleMeters = meters; 456 | exports.geoScaleMiles = miles; 457 | 458 | Object.defineProperty(exports, '__esModule', { value: true }); 459 | 460 | }))); 461 | -------------------------------------------------------------------------------- /build/d3-geo-scale-bar.min.js: -------------------------------------------------------------------------------- 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&&S.hasOwnProperty(t.type)&&S[t.type](t,n)}function o(t,n,e){var r,o=-1,i=t.length-e;for(n.lineStart();++o=z));L++);w*=10,E++}r=C}var I=C/(x?v:1),O=null===o?[]:o||[0,I/4,I/2,I],j=function(t){return t*b/(C/v)},B=t.selection?t.selection():t,_=B.selectAll(".label").data([i]),q=B.selectAll(".domain").data([null]),G=B.selectAll(".tick").data(O,j).order(),K=G.exit(),T=G.enter().append("g").attr("class","tick"),U=G.select("line"),V=G.select("text"),D=G.select("rect");B.attr("font-family","sans-serif").attr("transform","translate(".concat([S,k],")")),q=q.merge(q.enter().insert("path",".tick").attr("class","domain").attr("fill","none").attr("stroke","currentColor")),G=G.merge(T),U=U.merge(T.append("line").attr("stroke","currentColor").attr("y2",m*l)),V=V.merge(T.append("text").attr("fill","currentColor").attr("y",m*l+p*l).attr("font-size",10).attr("text-anchor","middle").attr("dy","".concat(1===l?.71:0,"em"))),D=D.merge(T.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:j(O[n+1]-t)}).attr("y",1===l?0:-m).attr("height",m)),t!==B&&(G=G.transition(t),q=q.transition(t),D=D.transition(t),K=K.transition(t).attr("opacity",1e-6).attr("transform",function(t){return"translate(".concat(j(t),")")}),T.attr("opacity",1e-6).attr("transform",function(t){return"translate(".concat(j(t),")")})),K.remove(),q.attr("d","M".concat(j(0),",").concat(m*l," L").concat(j(0),",0 L").concat(j(I),",0 L").concat(j(I),",").concat(m*l)),G.attr("transform",function(t){return"translate(".concat(j(t),")")}).attr("opacity",1),U.attr("y2",m*l),V.attr("y",m*l+p*l).text(g),D.attr("fill",function(t,n){return n%2==0?"currentColor":"#fff"}).attr("width",function(t,n,e){return n===e.length-1?0:j(O[n+1]-t)}).attr("y",1===l?0:-m).attr("height",m),null===_?_.remove():(t!==B?_.transition(t).attr("x","start"===y?0:j("middle"===y?I/2:I)).attr("y",1===l?0:"1.3em").attr("text-anchor",y).text(function(t){return t}):_.attr("x","start"===y?0:j("middle"===y?I/2:I)).attr("y",1===l?0:"1.3em").attr("text-anchor",y).text(function(t){return t}),_.enter().append("text").attr("class","label").attr("fill","currentColor").attr("font-size",12).attr("dy","-0.32em").attr("x","start"===y?0:j("middle"===y?I/2:I)).attr("y",1===l?0:"1.3em").attr("text-anchor",y).text(function(t){return t}))}var n,e,r,o,i,a=null,c=0,u=0,l=d(),s=w.radius,f=w.units,g=function(t){return+t.toFixed(2)},p=2,m=4,y="start",v=1,x=!0;return t.distance=function(n){return arguments.length?(e=+n,t):e||r},t.extent=function(n){return arguments.length?(a=n,t):a},t.label=function(n){return arguments.length?(i=n,t):i},t.labelAnchor=function(n){return arguments.length?(y=n,t):y},t.left=function(n){return arguments.length?(c=+n>1?1:+n<0?0:+n,t):c},t.orient=function(n){return arguments.length?(l=n(),t):1===l?"bottom":"top"},t.projection=function(e){return arguments.length?(n=e,t):n},t.radius=function(n){return arguments.length?(s=+n,t):s},t.size=function(n){return arguments.length?(a=[[0,0],n],t):a[1]},t.top=function(n){return arguments.length?(u=+n>1?1:+n<0?0:+n,t):u},t.tickFormat=function(n){return arguments.length?(g=n,t):g},t.tickPadding=function(n){return arguments.length?(p=+n,t):p},t.tickSize=function(n){return arguments.length?(m=+n,t):m},t.tickValues=function(n){return arguments.length?(o=n,t):o},t.units=function(n){var e;return arguments.length?(e=n,f=e.units,s=e.radius,t):f},t.zoomClamp=function(n){return arguments.length?(x=!!n,t):x},t.zoomFactor=function(n){return arguments.length?(v=+n,t):v},t}function p(){return-1}n.prototype={constructor:n,reset:function(){this.s=this.t=0},add:function(t){e(x,t,this.t),e(this,x.s,this.s),this.s?this.t+=x.t:this.s=x.t},valueOf:function(){return this.s}};var m,y,v,x=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= 0.3.2 < 0.4.0", 1608 | "cssstyle": ">= 0.3.1 < 0.4.0", 1609 | "data-urls": "^1.0.0", 1610 | "domexception": "^1.0.0", 1611 | "escodegen": "^1.9.0", 1612 | "html-encoding-sniffer": "^1.0.2", 1613 | "left-pad": "^1.2.0", 1614 | "nwsapi": "^2.0.0", 1615 | "parse5": "4.0.0", 1616 | "pn": "^1.1.0", 1617 | "request": "^2.83.0", 1618 | "request-promise-native": "^1.0.5", 1619 | "sax": "^1.2.4", 1620 | "symbol-tree": "^3.2.2", 1621 | "tough-cookie": "^2.3.3", 1622 | "w3c-hr-time": "^1.0.1", 1623 | "webidl-conversions": "^4.0.2", 1624 | "whatwg-encoding": "^1.0.3", 1625 | "whatwg-mimetype": "^2.1.0", 1626 | "whatwg-url": "^6.4.1", 1627 | "ws": "^4.0.0", 1628 | "xml-name-validator": "^3.0.0" 1629 | } 1630 | }, 1631 | "jsesc": { 1632 | "version": "2.5.2", 1633 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 1634 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", 1635 | "dev": true 1636 | }, 1637 | "json-schema": { 1638 | "version": "0.2.3", 1639 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 1640 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", 1641 | "dev": true 1642 | }, 1643 | "json-schema-traverse": { 1644 | "version": "0.3.1", 1645 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 1646 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", 1647 | "dev": true 1648 | }, 1649 | "json-stringify-safe": { 1650 | "version": "5.0.1", 1651 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 1652 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", 1653 | "dev": true 1654 | }, 1655 | "json5": { 1656 | "version": "2.1.1", 1657 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", 1658 | "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", 1659 | "dev": true, 1660 | "requires": { 1661 | "minimist": "^1.2.0" 1662 | } 1663 | }, 1664 | "jsprim": { 1665 | "version": "1.4.1", 1666 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 1667 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 1668 | "dev": true, 1669 | "requires": { 1670 | "assert-plus": "1.0.0", 1671 | "extsprintf": "1.3.0", 1672 | "json-schema": "0.2.3", 1673 | "verror": "1.10.0" 1674 | } 1675 | }, 1676 | "kind-of": { 1677 | "version": "3.2.2", 1678 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 1679 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1680 | "dev": true, 1681 | "requires": { 1682 | "is-buffer": "^1.1.5" 1683 | } 1684 | }, 1685 | "lazy-cache": { 1686 | "version": "1.0.4", 1687 | "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", 1688 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", 1689 | "dev": true 1690 | }, 1691 | "left-pad": { 1692 | "version": "1.3.0", 1693 | "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", 1694 | "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", 1695 | "dev": true 1696 | }, 1697 | "levn": { 1698 | "version": "0.3.0", 1699 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 1700 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 1701 | "dev": true, 1702 | "requires": { 1703 | "prelude-ls": "~1.1.2", 1704 | "type-check": "~0.3.2" 1705 | } 1706 | }, 1707 | "lodash": { 1708 | "version": "4.17.21", 1709 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 1710 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 1711 | "dev": true 1712 | }, 1713 | "lodash.sortby": { 1714 | "version": "4.7.0", 1715 | "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", 1716 | "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", 1717 | "dev": true 1718 | }, 1719 | "longest": { 1720 | "version": "1.0.1", 1721 | "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", 1722 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", 1723 | "dev": true 1724 | }, 1725 | "loose-envify": { 1726 | "version": "1.4.0", 1727 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 1728 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 1729 | "dev": true, 1730 | "requires": { 1731 | "js-tokens": "^3.0.0 || ^4.0.0" 1732 | } 1733 | }, 1734 | "mime-db": { 1735 | "version": "1.33.0", 1736 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 1737 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", 1738 | "dev": true 1739 | }, 1740 | "mime-types": { 1741 | "version": "2.1.18", 1742 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 1743 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 1744 | "dev": true, 1745 | "requires": { 1746 | "mime-db": "~1.33.0" 1747 | } 1748 | }, 1749 | "minimatch": { 1750 | "version": "3.0.4", 1751 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1752 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1753 | "dev": true, 1754 | "requires": { 1755 | "brace-expansion": "^1.1.7" 1756 | } 1757 | }, 1758 | "minimist": { 1759 | "version": "1.2.5", 1760 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 1761 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 1762 | "dev": true 1763 | }, 1764 | "ms": { 1765 | "version": "2.1.2", 1766 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1767 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1768 | "dev": true 1769 | }, 1770 | "nwsapi": { 1771 | "version": "2.0.5", 1772 | "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.5.tgz", 1773 | "integrity": "sha512-cqfA/wLUW6YbFQLkd5ZKq2SCaZkCoxehU9qt6ccMwH3fHbzUkcien9BzOgfBXfIkxeWnRFKb1ZKmjwaa9MYOMw==", 1774 | "dev": true 1775 | }, 1776 | "oauth-sign": { 1777 | "version": "0.8.2", 1778 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", 1779 | "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", 1780 | "dev": true 1781 | }, 1782 | "object-inspect": { 1783 | "version": "1.6.0", 1784 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", 1785 | "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", 1786 | "dev": true 1787 | }, 1788 | "object-keys": { 1789 | "version": "1.0.12", 1790 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", 1791 | "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", 1792 | "dev": true 1793 | }, 1794 | "object.assign": { 1795 | "version": "4.1.0", 1796 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 1797 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 1798 | "dev": true, 1799 | "requires": { 1800 | "define-properties": "^1.1.2", 1801 | "function-bind": "^1.1.1", 1802 | "has-symbols": "^1.0.0", 1803 | "object-keys": "^1.0.11" 1804 | } 1805 | }, 1806 | "once": { 1807 | "version": "1.4.0", 1808 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1809 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1810 | "dev": true, 1811 | "requires": { 1812 | "wrappy": "1" 1813 | } 1814 | }, 1815 | "optionator": { 1816 | "version": "0.8.2", 1817 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 1818 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 1819 | "dev": true, 1820 | "requires": { 1821 | "deep-is": "~0.1.3", 1822 | "fast-levenshtein": "~2.0.4", 1823 | "levn": "~0.3.0", 1824 | "prelude-ls": "~1.1.2", 1825 | "type-check": "~0.3.2", 1826 | "wordwrap": "~1.0.0" 1827 | } 1828 | }, 1829 | "package-preamble": { 1830 | "version": "0.1.0", 1831 | "resolved": "https://registry.npmjs.org/package-preamble/-/package-preamble-0.1.0.tgz", 1832 | "integrity": "sha1-xDlzgbcDU0SOQlXqmkml5bQYymg=", 1833 | "dev": true 1834 | }, 1835 | "parse5": { 1836 | "version": "4.0.0", 1837 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", 1838 | "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", 1839 | "dev": true 1840 | }, 1841 | "path-is-absolute": { 1842 | "version": "1.0.1", 1843 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1844 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1845 | "dev": true 1846 | }, 1847 | "path-parse": { 1848 | "version": "1.0.7", 1849 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1850 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1851 | "dev": true 1852 | }, 1853 | "performance-now": { 1854 | "version": "2.1.0", 1855 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 1856 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", 1857 | "dev": true 1858 | }, 1859 | "pn": { 1860 | "version": "1.1.0", 1861 | "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", 1862 | "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", 1863 | "dev": true 1864 | }, 1865 | "prelude-ls": { 1866 | "version": "1.1.2", 1867 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 1868 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 1869 | "dev": true 1870 | }, 1871 | "private": { 1872 | "version": "0.1.8", 1873 | "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", 1874 | "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", 1875 | "dev": true 1876 | }, 1877 | "psl": { 1878 | "version": "1.1.28", 1879 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.28.tgz", 1880 | "integrity": "sha512-+AqO1Ae+N/4r7Rvchrdm432afjT9hqJRyBN3DQv9At0tPz4hIFSGKbq64fN9dVoCow4oggIIax5/iONx0r9hZw==", 1881 | "dev": true 1882 | }, 1883 | "punycode": { 1884 | "version": "2.1.1", 1885 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1886 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 1887 | "dev": true 1888 | }, 1889 | "qs": { 1890 | "version": "6.5.2", 1891 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1892 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", 1893 | "dev": true 1894 | }, 1895 | "regenerate": { 1896 | "version": "1.4.0", 1897 | "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", 1898 | "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", 1899 | "dev": true 1900 | }, 1901 | "regenerate-unicode-properties": { 1902 | "version": "8.1.0", 1903 | "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", 1904 | "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", 1905 | "dev": true, 1906 | "requires": { 1907 | "regenerate": "^1.4.0" 1908 | } 1909 | }, 1910 | "regenerator-transform": { 1911 | "version": "0.14.1", 1912 | "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", 1913 | "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==", 1914 | "dev": true, 1915 | "requires": { 1916 | "private": "^0.1.6" 1917 | } 1918 | }, 1919 | "regexpu-core": { 1920 | "version": "4.6.0", 1921 | "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", 1922 | "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", 1923 | "dev": true, 1924 | "requires": { 1925 | "regenerate": "^1.4.0", 1926 | "regenerate-unicode-properties": "^8.1.0", 1927 | "regjsgen": "^0.5.0", 1928 | "regjsparser": "^0.6.0", 1929 | "unicode-match-property-ecmascript": "^1.0.4", 1930 | "unicode-match-property-value-ecmascript": "^1.1.0" 1931 | } 1932 | }, 1933 | "regjsgen": { 1934 | "version": "0.5.1", 1935 | "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", 1936 | "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", 1937 | "dev": true 1938 | }, 1939 | "regjsparser": { 1940 | "version": "0.6.2", 1941 | "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.2.tgz", 1942 | "integrity": "sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q==", 1943 | "dev": true, 1944 | "requires": { 1945 | "jsesc": "~0.5.0" 1946 | }, 1947 | "dependencies": { 1948 | "jsesc": { 1949 | "version": "0.5.0", 1950 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", 1951 | "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", 1952 | "dev": true 1953 | } 1954 | } 1955 | }, 1956 | "repeat-string": { 1957 | "version": "1.6.1", 1958 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 1959 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", 1960 | "dev": true 1961 | }, 1962 | "request": { 1963 | "version": "2.87.0", 1964 | "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", 1965 | "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", 1966 | "dev": true, 1967 | "requires": { 1968 | "aws-sign2": "~0.7.0", 1969 | "aws4": "^1.6.0", 1970 | "caseless": "~0.12.0", 1971 | "combined-stream": "~1.0.5", 1972 | "extend": "~3.0.1", 1973 | "forever-agent": "~0.6.1", 1974 | "form-data": "~2.3.1", 1975 | "har-validator": "~5.0.3", 1976 | "http-signature": "~1.2.0", 1977 | "is-typedarray": "~1.0.0", 1978 | "isstream": "~0.1.2", 1979 | "json-stringify-safe": "~5.0.1", 1980 | "mime-types": "~2.1.17", 1981 | "oauth-sign": "~0.8.2", 1982 | "performance-now": "^2.1.0", 1983 | "qs": "~6.5.1", 1984 | "safe-buffer": "^5.1.1", 1985 | "tough-cookie": "~2.3.3", 1986 | "tunnel-agent": "^0.6.0", 1987 | "uuid": "^3.1.0" 1988 | }, 1989 | "dependencies": { 1990 | "punycode": { 1991 | "version": "1.4.1", 1992 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1993 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", 1994 | "dev": true 1995 | }, 1996 | "tough-cookie": { 1997 | "version": "2.3.4", 1998 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", 1999 | "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", 2000 | "dev": true, 2001 | "requires": { 2002 | "punycode": "^1.4.1" 2003 | } 2004 | } 2005 | } 2006 | }, 2007 | "request-promise-core": { 2008 | "version": "1.1.1", 2009 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", 2010 | "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", 2011 | "dev": true, 2012 | "requires": { 2013 | "lodash": "^4.13.1" 2014 | } 2015 | }, 2016 | "request-promise-native": { 2017 | "version": "1.0.5", 2018 | "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", 2019 | "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", 2020 | "dev": true, 2021 | "requires": { 2022 | "request-promise-core": "1.1.1", 2023 | "stealthy-require": "^1.1.0", 2024 | "tough-cookie": ">=2.3.3" 2025 | } 2026 | }, 2027 | "resolve": { 2028 | "version": "1.8.1", 2029 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", 2030 | "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", 2031 | "dev": true, 2032 | "requires": { 2033 | "path-parse": "^1.0.5" 2034 | } 2035 | }, 2036 | "resumer": { 2037 | "version": "0.0.0", 2038 | "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", 2039 | "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", 2040 | "dev": true, 2041 | "requires": { 2042 | "through": "~2.3.4" 2043 | } 2044 | }, 2045 | "right-align": { 2046 | "version": "0.1.3", 2047 | "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", 2048 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", 2049 | "dev": true, 2050 | "requires": { 2051 | "align-text": "^0.1.1" 2052 | } 2053 | }, 2054 | "rollup": { 2055 | "version": "0.62.0", 2056 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.62.0.tgz", 2057 | "integrity": "sha512-mZS0aIGfYzuJySJD78znu9/hCJsNfBzg4lDuZGMj0hFVcYHt2evNRHv8aqiu9/w6z6Qn8AQoVl4iyEjDmisGeA==", 2058 | "dev": true, 2059 | "requires": { 2060 | "@types/estree": "0.0.39", 2061 | "@types/node": "*" 2062 | } 2063 | }, 2064 | "rollup-plugin-babel": { 2065 | "version": "4.3.3", 2066 | "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-4.3.3.tgz", 2067 | "integrity": "sha512-tKzWOCmIJD/6aKNz0H1GMM+lW1q9KyFubbWzGiOG540zxPPifnEAHTZwjo0g991Y+DyOZcLqBgqOdqazYE5fkw==", 2068 | "dev": true, 2069 | "requires": { 2070 | "@babel/helper-module-imports": "^7.0.0", 2071 | "rollup-pluginutils": "^2.8.1" 2072 | } 2073 | }, 2074 | "rollup-plugin-node-resolve": { 2075 | "version": "3.3.0", 2076 | "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.3.0.tgz", 2077 | "integrity": "sha512-9zHGr3oUJq6G+X0oRMYlzid9fXicBdiydhwGChdyeNRGPcN/majtegApRKHLR5drboUvEWU+QeUmGTyEZQs3WA==", 2078 | "dev": true, 2079 | "requires": { 2080 | "builtin-modules": "^2.0.0", 2081 | "is-module": "^1.0.0", 2082 | "resolve": "^1.1.6" 2083 | } 2084 | }, 2085 | "rollup-pluginutils": { 2086 | "version": "2.8.2", 2087 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", 2088 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", 2089 | "dev": true, 2090 | "requires": { 2091 | "estree-walker": "^0.6.1" 2092 | } 2093 | }, 2094 | "safe-buffer": { 2095 | "version": "5.1.2", 2096 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 2097 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 2098 | "dev": true 2099 | }, 2100 | "safer-buffer": { 2101 | "version": "2.1.2", 2102 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 2103 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 2104 | "dev": true 2105 | }, 2106 | "sax": { 2107 | "version": "1.2.4", 2108 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 2109 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", 2110 | "dev": true 2111 | }, 2112 | "semver": { 2113 | "version": "5.7.1", 2114 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 2115 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 2116 | "dev": true 2117 | }, 2118 | "source-map": { 2119 | "version": "0.6.1", 2120 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2121 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 2122 | "dev": true, 2123 | "optional": true 2124 | }, 2125 | "sshpk": { 2126 | "version": "1.14.2", 2127 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", 2128 | "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", 2129 | "dev": true, 2130 | "requires": { 2131 | "asn1": "~0.2.3", 2132 | "assert-plus": "^1.0.0", 2133 | "bcrypt-pbkdf": "^1.0.0", 2134 | "dashdash": "^1.12.0", 2135 | "ecc-jsbn": "~0.1.1", 2136 | "getpass": "^0.1.1", 2137 | "jsbn": "~0.1.0", 2138 | "safer-buffer": "^2.0.2", 2139 | "tweetnacl": "~0.14.0" 2140 | } 2141 | }, 2142 | "stealthy-require": { 2143 | "version": "1.1.1", 2144 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", 2145 | "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", 2146 | "dev": true 2147 | }, 2148 | "string.prototype.trim": { 2149 | "version": "1.1.2", 2150 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", 2151 | "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", 2152 | "dev": true, 2153 | "requires": { 2154 | "define-properties": "^1.1.2", 2155 | "es-abstract": "^1.5.0", 2156 | "function-bind": "^1.0.2" 2157 | } 2158 | }, 2159 | "supports-color": { 2160 | "version": "5.5.0", 2161 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 2162 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 2163 | "dev": true, 2164 | "requires": { 2165 | "has-flag": "^3.0.0" 2166 | } 2167 | }, 2168 | "symbol-tree": { 2169 | "version": "3.2.2", 2170 | "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", 2171 | "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", 2172 | "dev": true 2173 | }, 2174 | "tape": { 2175 | "version": "4.9.1", 2176 | "resolved": "https://registry.npmjs.org/tape/-/tape-4.9.1.tgz", 2177 | "integrity": "sha512-6fKIXknLpoe/Jp4rzHKFPpJUHDHDqn8jus99IfPnHIjyz78HYlefTGD3b5EkbQzuLfaEvmfPK3IolLgq2xT3kw==", 2178 | "dev": true, 2179 | "requires": { 2180 | "deep-equal": "~1.0.1", 2181 | "defined": "~1.0.0", 2182 | "for-each": "~0.3.3", 2183 | "function-bind": "~1.1.1", 2184 | "glob": "~7.1.2", 2185 | "has": "~1.0.3", 2186 | "inherits": "~2.0.3", 2187 | "minimist": "~1.2.0", 2188 | "object-inspect": "~1.6.0", 2189 | "resolve": "~1.7.1", 2190 | "resumer": "~0.0.0", 2191 | "string.prototype.trim": "~1.1.2", 2192 | "through": "~2.3.8" 2193 | }, 2194 | "dependencies": { 2195 | "resolve": { 2196 | "version": "1.7.1", 2197 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", 2198 | "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", 2199 | "dev": true, 2200 | "requires": { 2201 | "path-parse": "^1.0.5" 2202 | } 2203 | } 2204 | } 2205 | }, 2206 | "through": { 2207 | "version": "2.3.8", 2208 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 2209 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 2210 | "dev": true 2211 | }, 2212 | "to-fast-properties": { 2213 | "version": "2.0.0", 2214 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 2215 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", 2216 | "dev": true 2217 | }, 2218 | "tough-cookie": { 2219 | "version": "2.4.3", 2220 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 2221 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 2222 | "dev": true, 2223 | "requires": { 2224 | "psl": "^1.1.24", 2225 | "punycode": "^1.4.1" 2226 | }, 2227 | "dependencies": { 2228 | "punycode": { 2229 | "version": "1.4.1", 2230 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 2231 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", 2232 | "dev": true 2233 | } 2234 | } 2235 | }, 2236 | "tr46": { 2237 | "version": "1.0.1", 2238 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", 2239 | "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", 2240 | "dev": true, 2241 | "requires": { 2242 | "punycode": "^2.1.0" 2243 | } 2244 | }, 2245 | "tunnel-agent": { 2246 | "version": "0.6.0", 2247 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 2248 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 2249 | "dev": true, 2250 | "requires": { 2251 | "safe-buffer": "^5.0.1" 2252 | } 2253 | }, 2254 | "tweetnacl": { 2255 | "version": "0.14.5", 2256 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 2257 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 2258 | "dev": true, 2259 | "optional": true 2260 | }, 2261 | "type-check": { 2262 | "version": "0.3.2", 2263 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 2264 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 2265 | "dev": true, 2266 | "requires": { 2267 | "prelude-ls": "~1.1.2" 2268 | } 2269 | }, 2270 | "uglify-js": { 2271 | "version": "2.8.29", 2272 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", 2273 | "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", 2274 | "dev": true, 2275 | "requires": { 2276 | "source-map": "~0.5.1", 2277 | "uglify-to-browserify": "~1.0.0", 2278 | "yargs": "~3.10.0" 2279 | }, 2280 | "dependencies": { 2281 | "source-map": { 2282 | "version": "0.5.7", 2283 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 2284 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 2285 | "dev": true 2286 | } 2287 | } 2288 | }, 2289 | "uglify-to-browserify": { 2290 | "version": "1.0.2", 2291 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", 2292 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 2293 | "dev": true, 2294 | "optional": true 2295 | }, 2296 | "unicode-canonical-property-names-ecmascript": { 2297 | "version": "1.0.4", 2298 | "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", 2299 | "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", 2300 | "dev": true 2301 | }, 2302 | "unicode-match-property-ecmascript": { 2303 | "version": "1.0.4", 2304 | "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", 2305 | "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", 2306 | "dev": true, 2307 | "requires": { 2308 | "unicode-canonical-property-names-ecmascript": "^1.0.4", 2309 | "unicode-property-aliases-ecmascript": "^1.0.4" 2310 | } 2311 | }, 2312 | "unicode-match-property-value-ecmascript": { 2313 | "version": "1.1.0", 2314 | "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", 2315 | "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", 2316 | "dev": true 2317 | }, 2318 | "unicode-property-aliases-ecmascript": { 2319 | "version": "1.0.5", 2320 | "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", 2321 | "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", 2322 | "dev": true 2323 | }, 2324 | "uuid": { 2325 | "version": "3.3.2", 2326 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 2327 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", 2328 | "dev": true 2329 | }, 2330 | "verror": { 2331 | "version": "1.10.0", 2332 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 2333 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 2334 | "dev": true, 2335 | "requires": { 2336 | "assert-plus": "^1.0.0", 2337 | "core-util-is": "1.0.2", 2338 | "extsprintf": "^1.2.0" 2339 | } 2340 | }, 2341 | "w3c-hr-time": { 2342 | "version": "1.0.1", 2343 | "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", 2344 | "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", 2345 | "dev": true, 2346 | "requires": { 2347 | "browser-process-hrtime": "^0.1.2" 2348 | } 2349 | }, 2350 | "webidl-conversions": { 2351 | "version": "4.0.2", 2352 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", 2353 | "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", 2354 | "dev": true 2355 | }, 2356 | "whatwg-encoding": { 2357 | "version": "1.0.3", 2358 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz", 2359 | "integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==", 2360 | "dev": true, 2361 | "requires": { 2362 | "iconv-lite": "0.4.19" 2363 | } 2364 | }, 2365 | "whatwg-mimetype": { 2366 | "version": "2.1.0", 2367 | "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz", 2368 | "integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==", 2369 | "dev": true 2370 | }, 2371 | "whatwg-url": { 2372 | "version": "6.5.0", 2373 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", 2374 | "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", 2375 | "dev": true, 2376 | "requires": { 2377 | "lodash.sortby": "^4.7.0", 2378 | "tr46": "^1.0.1", 2379 | "webidl-conversions": "^4.0.2" 2380 | } 2381 | }, 2382 | "window-size": { 2383 | "version": "0.1.0", 2384 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 2385 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", 2386 | "dev": true 2387 | }, 2388 | "wordwrap": { 2389 | "version": "1.0.0", 2390 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 2391 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 2392 | "dev": true 2393 | }, 2394 | "wrappy": { 2395 | "version": "1.0.2", 2396 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2397 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 2398 | "dev": true 2399 | }, 2400 | "ws": { 2401 | "version": "4.1.0", 2402 | "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", 2403 | "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==", 2404 | "dev": true, 2405 | "requires": { 2406 | "async-limiter": "~1.0.0", 2407 | "safe-buffer": "~5.1.0" 2408 | } 2409 | }, 2410 | "xml-name-validator": { 2411 | "version": "3.0.0", 2412 | "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", 2413 | "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", 2414 | "dev": true 2415 | }, 2416 | "yargs": { 2417 | "version": "3.10.0", 2418 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", 2419 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", 2420 | "dev": true, 2421 | "requires": { 2422 | "camelcase": "^1.0.2", 2423 | "cliui": "^2.1.0", 2424 | "decamelize": "^1.0.0", 2425 | "window-size": "0.1.0" 2426 | } 2427 | } 2428 | } 2429 | } 2430 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d3-geo-scale-bar", 3 | "version": "1.2.5", 4 | "description": "Displays automatic scale bars for projected geospatial data.", 5 | "keywords": [ 6 | "d3", 7 | "d3-module", 8 | "d3-geo", 9 | "scale", 10 | "visualization" 11 | ], 12 | "homepage": "https://github.com/HarryStevens/d3-geo-scale-bar", 13 | "license": "BSD-3-Clause", 14 | "author": { 15 | "name": "Harry Stevens", 16 | "url": "http://harryjstevens.com/" 17 | }, 18 | "main": "build/d3-geo-scale-bar.js", 19 | "module": "index", 20 | "jsnext:main": "index", 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/HarryStevens/d3-geo-scale-bar" 24 | }, 25 | "scripts": { 26 | "pretest": "rm -rf build && mkdir build && rollup -c --banner \"$(preamble)\"", 27 | "test": "tape 'test/**/*-test.js'", 28 | "prepublishOnly": "npm run test && uglifyjs build/d3-geo-scale-bar.js -c -m -o build/d3-geo-scale-bar.min.js", 29 | "postpublish": "zip -j build/d3-geo-scale-bar.zip -- LICENSE README.md build/d3-geo-scale-bar.js build/d3-geo-scale-bar.min.js" 30 | }, 31 | "devDependencies": { 32 | "@babel/core": "^7.7.7", 33 | "@babel/preset-env": "^7.7.7", 34 | "babel": "^6.23.0", 35 | "d3-geo": "^1.11.9", 36 | "d3-selection": "^1.3.0", 37 | "jsdom": "11", 38 | "package-preamble": "0.1", 39 | "rollup": "^0.62.0", 40 | "rollup-plugin-babel": "^4.3.3", 41 | "rollup-plugin-node-resolve": "^3.3.0", 42 | "tape": "4", 43 | "uglify-js": "^2.8.11" 44 | }, 45 | "dependencies": {} 46 | } 47 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from "rollup-plugin-babel"; 2 | import resolve from "rollup-plugin-node-resolve"; 3 | 4 | export default { 5 | input: "index.js", 6 | output: { 7 | extend: true, 8 | file: "build/d3-geo-scale-bar.js", 9 | format: "umd", 10 | name: "d3" 11 | }, 12 | plugins: [ 13 | resolve({ 14 | only: ["d3-format"] 15 | }), 16 | babel({ 17 | exclude: "node_modules/**" 18 | }) 19 | ] 20 | }; -------------------------------------------------------------------------------- /src/geo/adder.js: -------------------------------------------------------------------------------- 1 | // Adds floating point numbers with twice the normal precision. 2 | // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and 3 | // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3) 4 | // 305–363 (1997). 5 | // Code adapted from GeographicLib by Charles F. F. Karney, 6 | // http://geographiclib.sourceforge.net/ 7 | // ...which was taken from d3-geo by Mike Bostock 8 | // Source: https://github.com/d3/d3-geo/blob/master/src/adder.js 9 | // License: https://github.com/d3/d3-geo/blob/master/LICENSE 10 | 11 | export default function() { 12 | return new Adder; 13 | } 14 | 15 | function Adder() { 16 | this.reset(); 17 | } 18 | 19 | Adder.prototype = { 20 | constructor: Adder, 21 | reset: function() { 22 | this.s = // rounded value 23 | this.t = 0; // exact error 24 | }, 25 | add: function(y) { 26 | add(temp, y, this.t); 27 | add(this, temp.s, this.s); 28 | if (this.s) this.t += temp.t; 29 | else this.s = temp.t; 30 | }, 31 | valueOf: function() { 32 | return this.s; 33 | } 34 | }; 35 | 36 | const temp = new Adder; 37 | 38 | function add(adder, a, b) { 39 | const x = adder.s = a + b, 40 | bv = x - a, 41 | av = x - bv; 42 | adder.t = (a - av) + (b - bv); 43 | } -------------------------------------------------------------------------------- /src/geo/distance.js: -------------------------------------------------------------------------------- 1 | // From d3-geo by Mike Bostock 2 | // Source: https://github.com/d3/d3-geo/blob/master/src/distance.js 3 | // License: https://github.com/d3/d3-geo/blob/master/LICENSE 4 | 5 | import length from "./length.js"; 6 | 7 | const coordinates = [null, null], 8 | object = {type: "LineString", coordinates: coordinates}; 9 | 10 | export default function(a, b) { 11 | coordinates[0] = a; 12 | coordinates[1] = b; 13 | return length(object); 14 | } -------------------------------------------------------------------------------- /src/geo/length.js: -------------------------------------------------------------------------------- 1 | // Adapted from d3-geo by Mike Bostock 2 | // Source: https://github.com/d3/d3-geo/blob/master/src/length.js 3 | // License: https://github.com/d3/d3-geo/blob/master/LICENSE 4 | 5 | import adder from "./adder.js"; 6 | import stream from "./stream.js"; 7 | 8 | let lengthSum = adder(), 9 | lambda0, 10 | sinPhi0, 11 | cosPhi0; 12 | 13 | const lengthStream = { 14 | sphere: _ => {}, 15 | point: _ => {}, 16 | lineStart: lengthLineStart, 17 | lineEnd: _ => {}, 18 | polygonStart: _ => {}, 19 | polygonEnd: _ => {} 20 | }; 21 | 22 | function lengthLineStart() { 23 | lengthStream.point = lengthPointFirst; 24 | lengthStream.lineEnd = lengthLineEnd; 25 | } 26 | 27 | function lengthLineEnd() { 28 | lengthStream.point = lengthStream.lineEnd = _ => {}; 29 | } 30 | 31 | function lengthPointFirst(lambda, phi) { 32 | lambda *= Math.PI / 180, phi *= Math.PI / 180; 33 | lambda0 = lambda, sinPhi0 = Math.sin(phi), cosPhi0 = Math.cos(phi); 34 | lengthStream.point = lengthPoint; 35 | } 36 | 37 | function lengthPoint(lambda, phi) { 38 | lambda *= Math.PI / 180, phi *= Math.PI / 180; 39 | const sinPhi = Math.sin(phi), 40 | cosPhi = Math.cos(phi), 41 | delta = Math.abs(lambda - lambda0), 42 | cosDelta = Math.cos(delta), 43 | sinDelta = Math.sin(delta), 44 | x = cosPhi * sinDelta, 45 | y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosDelta, 46 | z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosDelta; 47 | lengthSum.add(Math.atan2(Math.sqrt(x * x + y * y), z)); 48 | lambda0 = lambda, sinPhi0 = sinPhi, cosPhi0 = cosPhi; 49 | } 50 | 51 | export default function(object) { 52 | lengthSum.reset(); 53 | stream(object, lengthStream); 54 | return +lengthSum; 55 | } -------------------------------------------------------------------------------- /src/geo/noop.js: -------------------------------------------------------------------------------- 1 | // From d3-geo by Mike Bostock 2 | // Source: https://github.com/d3/d3-geo/blob/master/src/noop.js 3 | // License: https://github.com/d3/d3-geo/blob/master/LICENSE 4 | 5 | export default function noop() {} -------------------------------------------------------------------------------- /src/geo/stream.js: -------------------------------------------------------------------------------- 1 | // From d3-geo by Mike Bostock 2 | // Source: https://github.com/d3/d3-geo/blob/master/src/stream.js 3 | // License: https://github.com/d3/d3-geo/blob/master/LICENSE 4 | 5 | function streamGeometry(geometry, stream) { 6 | if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) { 7 | streamGeometryType[geometry.type](geometry, stream); 8 | } 9 | } 10 | 11 | const streamObjectType = { 12 | Feature: function(object, stream) { 13 | streamGeometry(object.geometry, stream); 14 | }, 15 | FeatureCollection: function(object, stream) { 16 | let features = object.features, i = -1, n = features.length; 17 | while (++i < n) streamGeometry(features[i].geometry, stream); 18 | } 19 | }; 20 | 21 | const streamGeometryType = { 22 | Sphere: function(object, stream) { 23 | stream.sphere(); 24 | }, 25 | Point: function(object, stream) { 26 | object = object.coordinates; 27 | stream.point(object[0], object[1], object[2]); 28 | }, 29 | MultiPoint: function(object, stream) { 30 | let coordinates = object.coordinates, i = -1, n = coordinates.length; 31 | while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]); 32 | }, 33 | LineString: function(object, stream) { 34 | streamLine(object.coordinates, stream, 0); 35 | }, 36 | MultiLineString: function(object, stream) { 37 | let coordinates = object.coordinates, i = -1, n = coordinates.length; 38 | while (++i < n) streamLine(coordinates[i], stream, 0); 39 | }, 40 | Polygon: function(object, stream) { 41 | streamPolygon(object.coordinates, stream); 42 | }, 43 | MultiPolygon: function(object, stream) { 44 | let coordinates = object.coordinates, i = -1, n = coordinates.length; 45 | while (++i < n) streamPolygon(coordinates[i], stream); 46 | }, 47 | GeometryCollection: function(object, stream) { 48 | let geometries = object.geometries, i = -1, n = geometries.length; 49 | while (++i < n) streamGeometry(geometries[i], stream); 50 | } 51 | }; 52 | 53 | function streamLine(coordinates, stream, closed) { 54 | let i = -1, n = coordinates.length - closed, coordinate; 55 | stream.lineStart(); 56 | while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]); 57 | stream.lineEnd(); 58 | } 59 | 60 | function streamPolygon(coordinates, stream) { 61 | let i = -1, n = coordinates.length; 62 | stream.polygonStart(); 63 | while (++i < n) streamLine(coordinates[i], stream, 1); 64 | stream.polygonEnd(); 65 | } 66 | 67 | export default function(object, stream) { 68 | if (object && streamObjectType.hasOwnProperty(object.type)) { 69 | streamObjectType[object.type](object, stream); 70 | } else { 71 | streamGeometry(object, stream); 72 | } 73 | } -------------------------------------------------------------------------------- /src/geoScaleBar.js: -------------------------------------------------------------------------------- 1 | import { default as geoDistance } from "./geo/distance"; 2 | import { default as geoScaleBottom } from "./orient/bottom"; 3 | import { default as geoScaleKilometers } from "./units/kilometers"; 4 | 5 | export default function(){ 6 | let extent = null, 7 | projection, 8 | left = 0, 9 | top = 0, 10 | orient = geoScaleBottom(), 11 | radius = geoScaleKilometers.radius, 12 | units = geoScaleKilometers.units, 13 | distance, 14 | distanceLog, 15 | tickFormat = d => +d.toFixed(2), 16 | tickPadding = 2, 17 | tickSize = 4, 18 | tickValues, 19 | labelText, 20 | labelAnchor = "start", 21 | zoomFactor = 1, 22 | zoomClamp = true; 23 | 24 | function scaleBar(context){ 25 | // If a label has not been explicitly set, set it 26 | labelText = labelText === null ? null : labelText || units.charAt(0).toUpperCase() + units.slice(1); 27 | 28 | // The position and width of the scale bar 29 | let width = extent[1][0] - extent[0][0], 30 | height = extent[1][1] - extent[0][1], 31 | x = extent[0][0] + width * left, 32 | y = extent[0][1] + height * top, 33 | start = projection.invert([x, y]); 34 | 35 | let barDistance = 0, barWidth = 0; 36 | 37 | // If the distance has been set explicitly, calculate the bar's width 38 | if (distance){ 39 | barDistance = distance; 40 | barWidth = barDistance / (geoDistance(start, projection.invert([x + 1, y])) * radius); 41 | } 42 | 43 | // Otherwise, make it an exponent of 10, 10x2, 10x4 or 10x5 with a minimum width of 80px 44 | else { 45 | let dist = .01, 46 | minWidth = 80 / (zoomClamp ? 1 : zoomFactor), 47 | iters = 0, 48 | maxiters = 100, 49 | multiples = [1, 2, 4, 5]; 50 | 51 | while (barWidth < minWidth && iters < maxiters){ 52 | 53 | for (let i = 0, l = multiples.length; i < l; i++){ 54 | barDistance = dist * multiples[i]; 55 | barWidth = barDistance / (geoDistance(start, projection.invert([x + 1, y])) * radius); 56 | if (barWidth >= minWidth) break; 57 | } 58 | 59 | dist *= 10; 60 | iters++; 61 | } 62 | 63 | distanceLog = barDistance; 64 | } 65 | 66 | // The ticks and elements of the bar 67 | let max = barDistance / (zoomClamp ? zoomFactor : 1), 68 | values = tickValues === null ? [] : tickValues ? tickValues : [0, max / 4, max / 2, max], 69 | scale = dist => dist * barWidth / (barDistance / zoomFactor), 70 | selection = context.selection ? context.selection() : context, 71 | label = selection.selectAll(".label").data([labelText]), 72 | path = selection.selectAll(".domain").data([null]), 73 | tick = selection.selectAll(".tick").data(values, scale).order(), 74 | tickExit = tick.exit(), 75 | tickEnter = tick.enter().append("g").attr("class", "tick"), 76 | line = tick.select("line"), 77 | text = tick.select("text"), 78 | rect = tick.select("rect"); 79 | 80 | selection 81 | .attr("font-family", "sans-serif") 82 | .attr("transform", `translate(${[x, y]})`); 83 | 84 | path = path.merge(path.enter().insert("path", ".tick") 85 | .attr("class", "domain") 86 | .attr("fill", "none") 87 | .attr("stroke", "currentColor")); 88 | 89 | tick = tick.merge(tickEnter); 90 | 91 | line = line.merge(tickEnter.append("line") 92 | .attr("stroke", "currentColor") 93 | .attr("y2", tickSize * orient)); 94 | 95 | text = text.merge(tickEnter.append("text") 96 | .attr("fill", "currentColor") 97 | .attr("y", tickSize * orient + tickPadding * orient) 98 | .attr("font-size", 10) 99 | .attr("text-anchor", "middle") 100 | .attr("dy", `${orient === 1 ? 0.71 : 0}em`)); 101 | 102 | rect = rect.merge(tickEnter.append("rect") 103 | .attr("fill", (d, i) => i % 2 === 0 ? "currentColor" : "#fff") 104 | .attr("stroke", "currentColor") 105 | .attr("stroke-width", 0.5) 106 | .attr("width", (d, i, e) => i === e.length - 1 ? 0 : scale(values[i + 1] - d)) 107 | .attr("y", orient === 1 ? 0 : -tickSize) 108 | .attr("height", tickSize)); 109 | 110 | if (context !== selection){ 111 | tick = tick.transition(context); 112 | path = path.transition(context); 113 | rect = rect.transition(context); 114 | 115 | tickExit = tickExit.transition(context) 116 | .attr("opacity", 1e-6) 117 | .attr("transform", d => `translate(${scale(d)})`); 118 | 119 | tickEnter 120 | .attr("opacity", 1e-6) 121 | .attr("transform", d => `translate(${scale(d)})`); 122 | } 123 | 124 | tickExit.remove(); 125 | 126 | path 127 | .attr("d", `M${scale(0)},${tickSize * orient} L${scale(0)},0 L${scale(max)},0 L${scale(max)},${tickSize * orient}`); 128 | 129 | tick 130 | .attr("transform", d => `translate(${scale(d)})`) 131 | .attr("opacity", 1); 132 | 133 | line 134 | .attr("y2", tickSize * orient); 135 | 136 | text 137 | .attr("y", tickSize * orient + tickPadding * orient) 138 | .text(tickFormat); 139 | 140 | rect 141 | .attr("fill", (d, i) => i % 2 === 0 ? "currentColor" : "#fff") 142 | .attr("width", (d, i, e) => i === e.length - 1 ? 0 : scale(values[i + 1] - d)) 143 | .attr("y", orient === 1 ? 0 : -tickSize) 144 | .attr("height", tickSize); 145 | 146 | // The label 147 | if (label === null) { 148 | label.remove(); 149 | } 150 | else { 151 | if (context !== selection){ 152 | label.transition(context) 153 | .attr("x", labelAnchor === "start" ? 0 : labelAnchor === "middle" ? scale(max / 2) : scale(max)) 154 | .attr("y", orient === 1 ? 0 : "1.3em") 155 | .attr("text-anchor", labelAnchor) 156 | .text(d => d); 157 | } 158 | else { 159 | label 160 | .attr("x", labelAnchor === "start" ? 0 : labelAnchor === "middle" ? scale(max / 2) : scale(max)) 161 | .attr("y", orient === 1 ? 0 : "1.3em") 162 | .attr("text-anchor", labelAnchor) 163 | .text(d => d); 164 | } 165 | 166 | label.enter().append("text") 167 | .attr("class", "label") 168 | .attr("fill", "currentColor") 169 | .attr("font-size", 12) 170 | .attr("dy", "-0.32em") 171 | .attr("x", labelAnchor === "start" ? 0 : labelAnchor === "middle" ? scale(max / 2) : scale(max)) 172 | .attr("y", orient === 1 ? 0 : "1.3em") 173 | .attr("text-anchor", labelAnchor) 174 | .text(d => d); 175 | } 176 | 177 | } 178 | 179 | scaleBar.distance = function(_) { 180 | return arguments.length ? (distance = +_, scaleBar) : distance || distanceLog; 181 | } 182 | 183 | scaleBar.extent = function(_) { 184 | return arguments.length ? (extent = _, scaleBar) : extent; 185 | } 186 | 187 | scaleBar.label = function(_) { 188 | return arguments.length ? (labelText = _, scaleBar) : labelText; 189 | } 190 | 191 | scaleBar.labelAnchor = function(_) { 192 | return arguments.length ? (labelAnchor = _, scaleBar) : labelAnchor; 193 | } 194 | 195 | scaleBar.left = function(_) { 196 | return arguments.length ? (left = +_ > 1 ? 1 : +_ < 0 ? 0 : +_, scaleBar) : left; 197 | } 198 | 199 | scaleBar.orient = function(_) { 200 | return arguments.length ? (orient = _(), scaleBar) : (orient === 1 ? "bottom" : "top"); 201 | } 202 | 203 | scaleBar.projection = function(_) { 204 | return arguments.length ? (projection = _, scaleBar) : projection; 205 | } 206 | 207 | scaleBar.radius = function(_) { 208 | return arguments.length ? (radius = +_, scaleBar) : radius; 209 | } 210 | 211 | scaleBar.size = function(_) { 212 | return arguments.length ? (extent = [[0, 0], _], scaleBar) : extent[1]; 213 | } 214 | 215 | scaleBar.top = function(_) { 216 | return arguments.length ? (top = +_ > 1 ? 1 : +_ < 0 ? 0 : +_, scaleBar) : top; 217 | } 218 | 219 | scaleBar.tickFormat = function(_) { 220 | return arguments.length ? (tickFormat = _, scaleBar) : tickFormat; 221 | } 222 | 223 | scaleBar.tickPadding = function(_) { 224 | return arguments.length ? (tickPadding = +_, scaleBar) : tickPadding; 225 | } 226 | 227 | scaleBar.tickSize = function(_) { 228 | return arguments.length ? (tickSize = +_, scaleBar) : tickSize; 229 | } 230 | 231 | scaleBar.tickValues = function(_) { 232 | return arguments.length ? (tickValues = _, scaleBar) : tickValues; 233 | } 234 | 235 | scaleBar.units = function(_) { 236 | return arguments.length ? ({units, radius} = _, scaleBar) : units; 237 | } 238 | 239 | scaleBar.zoomClamp = function(_) { 240 | return arguments.length ? (zoomClamp = !!_, scaleBar) : zoomClamp; 241 | } 242 | 243 | scaleBar.zoomFactor = function(_) { 244 | return arguments.length ? (zoomFactor = +_, scaleBar) : zoomFactor; 245 | } 246 | 247 | return scaleBar; 248 | } -------------------------------------------------------------------------------- /src/orient/bottom.js: -------------------------------------------------------------------------------- 1 | export default function(){ return 1; } -------------------------------------------------------------------------------- /src/orient/top.js: -------------------------------------------------------------------------------- 1 | export default function(){ return -1; } -------------------------------------------------------------------------------- /src/units/feet.js: -------------------------------------------------------------------------------- 1 | export default { 2 | units: "feet", 3 | radius: 20902259.664 4 | }; -------------------------------------------------------------------------------- /src/units/kilometers.js: -------------------------------------------------------------------------------- 1 | export default { 2 | units: "kilometers", 3 | radius: 6371.0088 4 | }; -------------------------------------------------------------------------------- /src/units/meters.js: -------------------------------------------------------------------------------- 1 | export default { 2 | units: "meters", 3 | radius: 6371008.8 4 | }; -------------------------------------------------------------------------------- /src/units/miles.js: -------------------------------------------------------------------------------- 1 | export default { 2 | units: "miles", 3 | radius: 3958.7613 4 | }; -------------------------------------------------------------------------------- /test/data/monaco.json: -------------------------------------------------------------------------------- 1 | {"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[7.438671875000011,43.750439453125],[7.377734375000017,43.731738281249996],[7.380078125000011,43.753222656249996],[7.39501953125,43.76533203125],[7.414453125000023,43.770898437499994],[7.436914062500023,43.761474609375],[7.438671875000011,43.750439453125]]]},"properties":{"ADMIN":"Monaco"}} -------------------------------------------------------------------------------- /test/geo-scale-bar-test.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"), 2 | tape = require("tape"), 3 | jsdom = require("jsdom"), 4 | d3 = Object.assign({}, require("d3-geo"), require("d3-selection"), require("../")); 5 | 6 | tape("geoScaleBar() has the expected defaults", test => { 7 | const s = d3.geoScaleBar(); 8 | test.equal(s.units(), "kilometers"); 9 | test.equal(s.radius(), 6371.0088); 10 | test.equal(s.left(), 0); 11 | test.equal(s.top(), 0); 12 | test.equal(s.tickPadding(), 2); 13 | test.equal(s.tickSize(), 4); 14 | test.equal(s.zoomClamp(), true); 15 | test.equal(s.zoomFactor(), 1); 16 | test.end(); 17 | }); 18 | 19 | tape("Orientation methods are exported", test => { 20 | test.equal(d3.geoScaleBottom(), 1); 21 | test.equal(d3.geoScaleTop(), -1); 22 | const s = d3.geoScaleBar().orient(d3.geoScaleBottom); 23 | test.equal(s.orient(), "bottom"); 24 | s.orient(d3.geoScaleTop); 25 | test.equal(s.orient(), "top"); 26 | test.end(); 27 | }); 28 | 29 | tape("Units objects are exported", test => { 30 | const feet = d3.geoScaleFeet; 31 | test.equal(feet.units, "feet"); 32 | test.equal(feet.radius, 20902259.664); 33 | 34 | const kilometers = d3.geoScaleKilometers; 35 | test.equal(kilometers.units, "kilometers"); 36 | test.equal(kilometers.radius, 6371.0088); 37 | 38 | const meters = d3.geoScaleMeters; 39 | test.equal(meters.units, "meters"); 40 | test.equal(meters.radius, 6371008.8); 41 | 42 | const miles = d3.geoScaleMiles; 43 | test.equal(miles.units, "miles"); 44 | test.equal(miles.radius, 3958.7613); 45 | 46 | const s = d3.geoScaleBar().units(feet); 47 | test.equal(s.units(), "feet"); 48 | test.equal(s.radius(), 20902259.664); 49 | 50 | s.units(kilometers); 51 | test.equal(s.units(), "kilometers"); 52 | test.equal(s.radius(), 6371.0088); 53 | 54 | s.units(meters); 55 | test.equal(s.units(), "meters"); 56 | test.equal(s.radius(), 6371008.8); 57 | 58 | s.units(miles); 59 | test.equal(s.units(), "miles"); 60 | test.equal(s.radius(), 3958.7613); 61 | 62 | test.end(); 63 | }); 64 | 65 | tape("scaleBar.extent(extent) sets the extent explicitly", test => { 66 | const s = d3.geoScaleBar().extent([[0, 0], [100, 100]]); 67 | test.deepEqual(s.extent(), [[0, 0], [100, 100]]); 68 | test.end(); 69 | }); 70 | 71 | tape("scaleBar.size(size) sets the size explicitly and the extent implicitly", test => { 72 | const s = d3.geoScaleBar().size([100, 100]); 73 | test.deepEqual(s.extent(), [[0, 0], [100, 100]]); 74 | test.deepEqual(s.size(), [100, 100]); 75 | test.end(); 76 | }); 77 | 78 | tape("scaleBar(selection) produces the expected result", test => { 79 | const bodyActual = (new jsdom.JSDOM("")).window.document.body; 80 | const bodyExpected = (new jsdom.JSDOM(file("geo-scale-bar.html"))).window.document.body; 81 | 82 | const india = JSON.parse(file("data/india.json")); 83 | const size = [900, 600]; 84 | const projection = d3.geoMercator().fitSize(size, india); 85 | const s = d3.geoScaleBar() 86 | .size(size) 87 | .projection(projection) 88 | .distance(1000) 89 | .left(.1) 90 | .top(.1); 91 | 92 | const svg = d3.select(bodyActual).select("svg").attr("width", size[0]).attr("height", size[1]); 93 | svg.append("g").call(s); 94 | test.equal(bodyActual.outerHTML, bodyExpected.outerHTML); 95 | test.end(); 96 | }); 97 | 98 | tape("Properly rounds decimal ticks", test => { 99 | const body = (new jsdom.JSDOM("")).window.document.body; 100 | 101 | const monaco = JSON.parse(file("data/monaco.json")); 102 | const size = 500; 103 | const projection = d3.geoMercator().fitSize([size, size], monaco); 104 | const path = d3.geoPath(projection); 105 | 106 | const s = d3.geoScaleBar() 107 | .top(.1) 108 | .left(.01) 109 | .projection(projection) 110 | .size([size, size]); 111 | 112 | const svg = d3.select(body).select("svg") 113 | .attr("width", size) 114 | .attr("height", size); 115 | 116 | const bar = svg.append("g") 117 | .call(s); 118 | 119 | const array = []; 120 | bar.selectAll(".tick").each(function(){ 121 | array.push(+d3.select(this).text()) 122 | }); 123 | 124 | test.deepEqual(array, [0, 0.25, 0.5, 1]); 125 | test.end(); 126 | }); 127 | 128 | tape("scaleBar.distance() gets distance after scale bar is appended", test => { 129 | const body = (new jsdom.JSDOM("")).window.document.body; 130 | 131 | const india = JSON.parse(file("data/india.json")); 132 | const size = [900, 600]; 133 | const projection = d3.geoMercator().fitSize(size, india); 134 | const s = d3.geoScaleBar() 135 | .size(size) 136 | .projection(projection) 137 | .distance(1000) 138 | .left(.1) 139 | .top(.1); 140 | 141 | const svg = d3.select(body).select("svg").attr("width", size[0]).attr("height", size[1]); 142 | svg.append("g").call(s); 143 | 144 | test.equal(s.distance(), 1000); 145 | 146 | test.end(); 147 | }); 148 | 149 | function file(file) { 150 | return fs.readFileSync(`${__dirname}/${file}`, "utf8").replace(/\n\s*/mg, ""); 151 | } -------------------------------------------------------------------------------- /test/geo-scale-bar.html: -------------------------------------------------------------------------------- 1 | 02505001000Kilometers --------------------------------------------------------------------------------