diff --git a/mapshaper-gui.js b/mapshaper-gui.js index 11fb0946..99e113ed 100644 --- a/mapshaper-gui.js +++ b/mapshaper-gui.js @@ -7820,6 +7820,7 @@ function onMouseUp(e) { var evt = procMouseEvent(e), elapsed, dx, dy; + _self.dispatchEvent('mouseup', evt); if (_dragging) { stopDragging(evt); } @@ -7847,6 +7848,7 @@ function onMouseMove(e) { var evt = procMouseEvent(e); + _self.dispatchEvent('mousemove', evt); if (!_dragging && _downEvt && _downEvt.hover) { _dragging = true; _self.dispatchEvent('dragstart', evt); @@ -7969,37 +7971,21 @@ }); }); - document.addEventListener('mousemove', function(e) { + gui.map.getMouse().on('mousemove', function(e) { if (!_on || !activeHandle || !prevXY || !boxCoords || !_visible) return; var xy = {x: e.pageX, y: e.pageY}; - var scale = gui.map.getExtent().getPixelSize(); - var dx = (xy.x - prevXY.x) * scale; - var dy = -(xy.y - prevXY.y) * scale; - var shifting = activeHandle.col == 'center' && activeHandle.row == 'center'; - var centered = gui.keyboard.shiftIsPressed() && !shifting; - if (activeHandle.col == 'left' || shifting) { - boxCoords[0] += dx; - if (centered) boxCoords[2] -= dx; - } - if (activeHandle.col == 'right' || shifting) { - boxCoords[2] += dx; - if (centered) boxCoords[0] -= dx; - } - if (activeHandle.row == 'top' || shifting) { - boxCoords[3] += dy; - if (centered) boxCoords[1] -= dy; - } - if (activeHandle.row == 'bottom' || shifting) { - boxCoords[1] += dy; - if (centered) boxCoords[3] -= dy; + var scaling = gui.keyboard.shiftIsPressed() && activeHandle.type == 'corner'; + if (scaling) { + rescaleBox(e.x, e.y); + } else { + resizeBox(xy.x - prevXY.x, xy.y - prevXY.y, activeHandle); } - prevXY = xy; redraw(); box.dispatchEvent('handle_drag'); }); - document.addEventListener('mouseup', function() { + gui.map.getMouse().on('mouseup', function(e) { if (activeHandle && _on) { activeHandle.el.css('background', null); activeHandle = null; @@ -8012,6 +7998,43 @@ }); } + function resizeBox(dx, dy, activeHandle) { + var shifting = activeHandle.type == 'center'; + var centered = gui.keyboard.shiftIsPressed() && activeHandle.type == 'edge'; + var scale = gui.map.getExtent().getPixelSize(); + dx *= scale; + dy *= -scale; + + if (activeHandle.col == 'left' || shifting) { + boxCoords[0] += dx; + if (centered) boxCoords[2] -= dx; + } + if (activeHandle.col == 'right' || shifting) { + boxCoords[2] += dx; + if (centered) boxCoords[0] -= dx; + } + if (activeHandle.row == 'top' || shifting) { + boxCoords[3] += dy; + if (centered) boxCoords[1] -= dy; + } + if (activeHandle.row == 'bottom' || shifting) { + boxCoords[1] += dy; + if (centered) boxCoords[3] -= dy; + } + } + + function rescaleBox(x, y) { + var p = gui.map.getExtent().translatePixelCoords(x, y); + var cx = (boxCoords[0] + boxCoords[2])/2; + var cy = (boxCoords[1] + boxCoords[3])/2; + var dist2 = geom.distance2D(cx, cy, p[0], p[1]); + var dist = geom.distance2D(cx, cy, boxCoords[0], boxCoords[1]); + var k = dist2 / dist; + var dx = (boxCoords[2] - cx) * k; + var dy = (boxCoords[3] - cy) * k; + boxCoords = [cx - dx, cy - dy, cx + dx, cy + dy]; + } + box.setDataCoords = function(bbox) { boxCoords = bbox; redraw(); @@ -8107,8 +8130,10 @@ // if (i == 4) continue; // skip middle handle var c = Math.floor(i / 3); var r = i % 3; + var type = i == 4 && 'center' || c != 1 && r != 1 && 'corner' || 'edge'; handles.push({ el: El('div').addClass('handle').appendTo(el), + type: type, col: c == 0 && 'left' || c == 1 && 'center' || 'right', row: r == 0 && 'top' || r == 1 && 'center' || 'bottom' }); @@ -9347,50 +9372,22 @@ dotColor: violet, dotSize: 3 }, polyline: { - strokeColor: black, // violet, + strokeColor: violet, // black, // violet, strokeWidth: 3 } }; - function getIntersectionStyle(lyr) { + function getIntersectionStyle(lyr, opts) { return getDefaultStyle(lyr, intersectionStyle); } - function getDefaultStyle(lyr, baseStyle) { - var style = utils$1.extend({}, baseStyle); - // reduce the dot size of large point layers - if (lyr.geometry_type == 'point' && style.dotSize > 0) { - style.dotSize *= getDotScale$1(lyr); - } - return style; - } - - function getDotScale$1(lyr) { - var topTier = 50000; - var n = countPoints(lyr.shapes, topTier + 2); // short-circuit point counting above top threshold - var k = n < 200 && 4 || n < 2500 && 3 || n < 10000 && 2 || 1; - // var k = n >= topTier && 0.25 || n > 10000 && 0.45 || n > 2500 && 0.65 || n > 200 && 0.85 || 1; - return k; - } - - function countPoints(shapes, max) { - var count = 0; - var i, n, shp; - max = max || Infinity; - for (i=0, n=shapes.length; i -1) { // pinned or hover style - topStyle = getSelectedFeatureStyle(lyr, o); + topStyle = getSelectedFeatureStyle(baseLyr, o, opts); topIdx = ids.length; ids.push(o.id); // put the pinned/hover feature last in the render order } var style = { - styler: styler, - ids: ids, + baseStyle: baseStyle, + styler, + ids, overlay: true }; - if (layerHasCanvasDisplayStyle(lyr)) { + if (layerHasCanvasDisplayStyle(baseLyr) && !opts.outlineMode) { if (geomType == 'point') { - style.styler = getOverlayPointStyler(getCanvasDisplayStyle(lyr).styler, styler); + style.styler = getOverlayPointStyler(getCanvasDisplayStyle(baseLyr).styler, styler); } style.type = 'styled'; } return ids.length > 0 ? style : null; } - function getSelectedFeatureStyle(lyr, o) { + + function getDefaultStyle(lyr, baseStyle) { + var style = Object.assign({}, baseStyle); + // reduce the dot size of large point layers + if (lyr.geometry_type == 'point' && style.dotSize > 0) { + style.dotSize *= getDotScale$1(lyr); + } + return style; + } + + function getDotScale$1(lyr) { + var topTier = 10000; + var n = countPoints(lyr.shapes, topTier); // short-circuit point counting above top threshold + var k = n < 200 && 4 || n < 2500 && 3 || n < topTier && 2 || 1; + return k; + } + + function countPoints(shapes, max) { + var count = 0; + var i, n, shp; + max = max || Infinity; + for (i=0, n=shapes.length; i -1; var geomType = lyr.geometry_type; var style; - if (isPinned && o.mode == 'rectangles') { + if (isPinned && opts.interactionMode == 'rectangles') { // kludge for rectangle editing mode style = selectionStyles[geomType]; } else if (isPinned) { @@ -9903,19 +9941,13 @@ } function calcBounds(cx, cy, scale) { - var full, bounds, w, h; - if (_frame) { - full = fillOutFrameBounds(_frame); - } else { - full = fillOut(_fullBounds); - } + var full = fillOut(_fullBounds); if (_strictBounds) { full = fitIn(full, _strictBounds); } - w = full.width() / scale; - h = full.height() / scale; - bounds = new Bounds(cx - w/2, cy - h/2, cx + w/2, cy + h/2); - return bounds; + var w = full.width() / scale; + var h = full.height() / scale; + return new Bounds(cx - w/2, cy - h/2, cx + w/2, cy + h/2); } // Calculate viewport bounds from frame data @@ -10235,7 +10267,7 @@ var iter = new internal.ShapeIter(arcs); var t = getScaledTransform(_ext); var bounds = _ext.getBounds(); - var radius = (style.strokeWidth > 2 ? style.strokeWidth * 0.9 : 2) * GUI.getPixelRatio() * getScaledLineScale(_ext); + var radius = (style.strokeWidth > 2 ? style.strokeWidth * 0.9 : 2) * GUI.getPixelRatio() * getScaledLineScale(_ext, style); var color = style.strokeColor || 'black'; var i, j, p; @@ -10269,7 +10301,7 @@ _self.drawStyledPaths = function(shapes, arcs, style, filter) { var styleIndex = {}; var batchSize = 1500; - var startPath = getPathStart(_ext, getScaledLineScale(_ext)); + var startPath = getPathStart(_ext, getScaledLineScale(_ext, style)); var draw = getShapePencil(arcs, _ext); var key, item, shp; var styler = style.styler || null; @@ -10445,7 +10477,7 @@ return (style.strokeWidth > 0 ? style.strokeColor + '~' + style.strokeWidth + '~' + (style.lineDash ? style.lineDash + '~' : '') : '') + (style.fillColor || '') + - // styles with <1 opacity are no longer batch-rendered + // styles with <1 opacity are no longer batch-rendered, not relevent to key // (style.strokeOpacity >= 0 ? style.strokeOpacity + '~' : '') : '') + // (style.fillOpacity ? '~' + style.fillOpacity : '') + // (style.opacity < 1 ? '~' + style.opacity : '') + @@ -10454,9 +10486,17 @@ return _self; } - function getScaledLineScale(ext) { + function getScaledLineScale(ext, style) { var previewScale = ext.getSymbolScale(); - return previewScale == 1 ? getLineScale(ext) : previewScale; + var k = 1; + if (previewScale == 1 || style.type != 'styled' || style.baseStyle && style.baseStyle.type != 'styled') { + return getLineScale(ext); + } + if (style.baseStyle?.type == 'styled') { + // bump up overlay line width in preview mode + k = previewScale < 2 && 2 || previewScale < 5 && 1.5 || previewScale < 10 && 1.25 || 1.1; + } + return previewScale * k; } // Vary line width according to zoom ratio. @@ -10626,9 +10666,6 @@ return function(ctx, style) { var strokeWidth; ctx.beginPath(); - // if (style.opacity >= 0) { - // ctx.globalAlpha = style.opacity; - // } if (style.strokeWidth > 0) { strokeWidth = style.strokeWidth; if (pixRatio > 1) { @@ -11587,7 +11624,7 @@ if (lyr == _intersectionLyr) return; // no change if (lyr) { _intersectionLyr = getDisplayLayer(lyr, dataset, getDisplayOptions()); - _intersectionLyr.style = getIntersectionStyle(_intersectionLyr.layer); + _intersectionLyr.style = getIntersectionStyle(_intersectionLyr.layer, getGlobalStyleOptions()); } else { _intersectionLyr = null; } @@ -11623,6 +11660,7 @@ }; this.getExtent = function() {return _ext;}; + this.getMouse = function() {return _mouse;}; this.isActiveLayer = isActiveLayer; this.isVisibleLayer = isVisibleLayer; this.getActiveLayer = function() { return _activeLyr; }; @@ -11659,6 +11697,15 @@ updateFullBounds(); }; + function getGlobalStyleOptions() { + var mode = gui.state.interaction_mode; + return { + darkMode: !!gui.state.dark_basemap, + outlineMode: mode == 'vertices', + interactionMode: mode + }; + } + // Refresh map display in response to data changes, layer selection, etc. function onUpdate(e) { var prevLyr = _activeLyr || null; @@ -11691,7 +11738,6 @@ } _activeLyr = getDisplayLayer(e.layer, e.dataset, getDisplayOptions()); - _activeLyr.style = getActiveStyle(_activeLyr.layer, gui.state.dark_basemap); _activeLyr.active = true; if (popupCanStayOpen(e.flags)) { @@ -11749,7 +11795,7 @@ } function updateOverlayLayer(e) { - var style = getOverlayStyle(_activeLyr.layer, e); + var style = getOverlayStyle(_activeLyr.layer, e, getGlobalStyleOptions()); if (style) { _overlayLyr = utils$1.defaults({ layer: filterLayerByIds(_activeLyr.layer, style.ids), @@ -11896,9 +11942,10 @@ function updateLayerStyles(layers) { layers.forEach(function(mapLayer, i) { if (mapLayer.active) { - // assume: style is already assigned + // regenerating active style everytime, to support style change when + // switching between outline and preview modes. + mapLayer.style = getActiveLayerStyle(mapLayer.layer, getGlobalStyleOptions()); if (mapLayer.style.type != 'styled' && layers.length > 1 && mapLayer.style.strokeColors) { - // if (false) { // always show ghosted arcs // kludge to hide ghosted layers when reference layers are present // TODO: consider never showing ghosted layers (which appear after // commands like dissolve and filter). @@ -11910,7 +11957,7 @@ if (mapLayer.layer == _activeLyr.layer) { console.error("Error: shared map layer"); } - mapLayer.style = getReferenceStyle(mapLayer.layer); + mapLayer.style = getReferenceLayerStyle(mapLayer.layer, getGlobalStyleOptions()); } }); } diff --git a/mapshaper.js b/mapshaper.js index 2d74d77f..dddf7edb 100644 --- a/mapshaper.js +++ b/mapshaper.js @@ -14726,6 +14726,9 @@ bounds: function() { return shapeBounds().toArray(); }, + bbox: function() { + return shapeBounds().toArray(); + }, height: function() { return shapeBounds().height(); }, @@ -17323,9 +17326,10 @@ } function getGapRemovalMessage(removed, retained, areaLabel) { + var tot = removed + retained; if (removed > 0 === false) return ''; - return utils.format('Removed %,d / %,d sliver%s using %s', - removed, removed + retained, utils.pluralSuffix(removed), areaLabel); + return utils.format('Removed %,d of %,d sliver%s using %s', + removed, tot, utils.pluralSuffix(tot), areaLabel); } function dissolvePolygonGroups2(groups, lyr, dataset, opts) { @@ -19274,12 +19278,12 @@ // Kludge for applying fill and other styles to a element // (for rendering labels in the GUI with the dot in Canvas, not SVG) function renderStyledLabel(rec) { - var o = renderLabel(rec); + var o = renderLabel$1(rec); applyStyleAttributes(o, 'label', rec); return o; } - function renderLabel(rec) { + function renderLabel$1(rec) { var line = toLabelString(rec['label-text']); var morelines, obj; // Accepting \n (two chars) as an alternative to the newline character @@ -19324,7 +19328,7 @@ var SvgLabels = /*#__PURE__*/Object.freeze({ __proto__: null, renderStyledLabel: renderStyledLabel, - renderLabel: renderLabel + renderLabel: renderLabel$1 }); // convert data records (properties like svg-symbol, label-text, fill, r) to svg symbols @@ -19536,13 +19540,64 @@ }; } - // TODO: generalize to other kinds of furniture as they are developed - function getScalebarPosition(opts) { - var pos = opts.position || 'top-left'; - return { - valign: pos.includes('top') ? 'top' : 'bottom', - halign: pos.includes('left') ? 'left' : 'right' + function renderScalebar(d, frame) { + if (!frame.crs) { + message('Unable to render scalebar: unknown CRS.'); + return []; + } + if (frame.width > 0 === false) { + return []; + } + + var opts = getScalebarOpts(d); + var metersPerPx = getMapFrameMetersPerPixel(frame); + var frameWidthPx = frame.width; + var labels = d.label ? d.label.split(',') : null; + var label1 = labels && labels[0] || null; + var props1 = parseScalebarLabel(label1 || getAutoScalebarLabel(frameWidthPx, metersPerPx, 'mile')); + var label2 = opts.style == 'b' && labels && labels[1] || null; + var props2, length2; + + if (props1.km > 0 === false) { + message('Unusable scalebar label:', label1); + return []; + } + + var length1 = Math.round(props1.km / metersPerPx * 1000); + if (length1 > 0 === false) { + stop("Null scalebar length"); + } + + if (label2) { + props2 = parseScalebarLabel(label2); + length2 = Math.round(props2.km / metersPerPx * 1000); + if (length2 > length1) { + stop("First part of a dual-unit scalebar must be longer than the second part."); + } + } + + var barPos = getScalebarPosition(opts); + var labelPos = getLabelPosition(opts); + var dx = barPos.xpos == 'right' ? frameWidthPx - length1 - opts.margin : opts.margin; + var dy = barPos.ypos == 'bottom' ? frame.height - opts.margin : opts.margin; + + // vshift to adjust for height above or below the baseline + var labelHeight = Math.round(opts.label_offset + opts.tic_length + opts.font_size * 0.8 + opts.bar_width / 2); + var bareHeight = Math.round(opts.bar_width / 2); + var topHeight = labelPos.ypos == 'top' || label2 ? labelHeight : bareHeight; + var bottomHeight = labelPos.ypos == 'bottom' || label2 ? labelHeight : bareHeight; + if (barPos.ypos == 'top') { + dy += topHeight; + } else { + dy -= bottomHeight; + } + + var g = renderAsSvg(length1, label1, length2, label2, opts); + g.properties = { + transform: 'translate(' + dx + ' ' + dy + ')' }; + + return [g]; } var styleOpts = { @@ -19569,105 +19624,112 @@ return Object.assign({}, defaultOpts, styleOpts[style], d, {style: style}); } - // approximate pixel height of the scalebar - function getScalebarHeight(opts) { - return Math.round(opts.bar_width + opts.label_offset + - opts.tic_length + opts.font_size * 0.8); + function renderAsSvg(length, text, length2, text2, opts) { + var labelPart = renderLabel(text, length, opts); + var zeroLabelPart = renderLabel('0', length, Object.assign({flipx: true}, opts)); + var barPart = renderBar(length, length2, opts); + var parts = opts.style == 'b' ? [zeroLabelPart, labelPart, barPart] : [labelPart, barPart]; + if (text2) { + parts.push(renderLabel(text2, length2, Object.assign({flipy: true}, opts))); + parts.push(renderLabel('0', length2, Object.assign({flipx: true, flipy: true}, opts))); + } + return { + tag: 'g', + children: parts + }; } - function renderAsSvg(length, text, opts) { - // label part - var xOff = opts.style == 'b' ? Math.round(opts.font_size / 4) : 0; - var alignLeft = opts.style == 'a' && opts.position.includes('left'); - var anchorX = alignLeft ? -xOff : length + xOff; - var anchorY = opts.bar_width + + opts.tic_length + opts.label_offset; - if (opts.label_position == 'top') { - anchorY = -opts.label_offset - opts.tic_length; - } + // TODO: generalize to other kinds of furniture as they are developed + function getScalebarPosition(opts) { + var pos = opts.position || 'top-left'; + return { + ypos: pos.includes('top') ? 'top' : 'bottom', + xpos: pos.includes('left') ? 'left' : 'right' + }; + } + + function renderLabel(text, length, opts) { + var labelPos = getLabelPosition(opts); + var anchorX = length * labelPos.kx + labelPos.dx; + var bottomLabelY = opts.bar_width + opts.tic_length + opts.label_offset; + var topLabelY = -opts.label_offset - opts.tic_length; + var anchorY = labelPos.ypos == 'top' ? topLabelY : bottomLabelY; var labelOpts = { 'label-text': text, 'font-size': opts.font_size, - 'text-anchor': alignLeft ? 'start': 'end', - 'dominant-baseline': opts.label_position == 'top' ? 'auto' : 'hanging' + 'text-anchor': labelPos.anchor, + 'dominant-baseline': labelPos.ypos == 'top' ? 'auto' : 'hanging' //// 'dominant-baseline': labelPos == 'top' ? 'text-after-edge' : 'text-before-edge' // 'text-after-edge' is buggy in Safari and unsupported by Illustrator, // so I'm using 'hanging' and 'auto', which seem to be well supported. // downside: requires a kludgy multiplier to calculate scalebar height (see above) }; - var labelPart = symbolRenderers.label(labelOpts, anchorX, anchorY); - var zeroOpts = Object.assign({}, labelOpts, {'label-text': '0', 'text-anchor': 'start'}); - var zeroLabel = symbolRenderers.label(zeroOpts, -xOff, anchorY); + return symbolRenderers.label(labelOpts, anchorX, anchorY); + } - // bar part - var y = 0; - var y2 = opts.tic_length + opts.bar_width / 2; - var coords; - if (opts.label_position == "top") { - y2 = -y2; - } - if (opts.tic_length > 0) { - coords = [[0, y2], [0, y], [length, y], [length, y2]]; + function getLabelPosition(opts) { + var pos = opts.label_position; + var ypos = pos.includes('bottom') && 'bottom' || 'top'; + var dx = 0; + var xpos; + if (opts.style == 'a') { + xpos = pos.includes('center') && 'center' || pos.includes('right') && 'right' || 'left'; } else { - coords = [[0, y], [length, y]]; + xpos = 'right'; // style b + } + if (opts.flipx) { + xpos = xpos == 'left' && 'right' || xpos == 'right' && 'left' || xpos; + } + if (opts.flipy) { + ypos = ypos == 'top' && 'bottom' || ypos == 'bottom' && 'top' || ypos; + } + if (opts.style == 'b') { + dx = xpos == 'left' && -opts.font_size / 4 || xpos == 'right' && opts.font_size / 4 || 0; } - var barPart = importLineString(coords); - Object.assign(barPart.properties, { - stroke: 'black', - fill: 'none', - 'stroke-width': opts.bar_width, - 'stroke-linecap': 'butt', - 'stroke-linejoin': 'miter' - }); - var parts = opts.style == 'b' ? [zeroLabel, labelPart, barPart] : [labelPart, barPart]; return { - tag: 'g', - children: parts + xpos, + ypos, + dx, + kx: xpos == 'right' && 1 || xpos == 'center' && 0.5 || 0, + anchor: xpos == 'center' && 'middle' || xpos == 'left' && 'start' || 'end' }; } - function renderScalebar(d, frame) { - if (!frame.crs) { - message('Unable to render scalebar: unknown CRS.'); - return []; - } - if (frame.width > 0 === false) { - return []; - } - - var opts = getScalebarOpts(d); - var metersPerPx = getMapFrameMetersPerPixel(frame); - var frameWidthPx = frame.width; - var unit = d.label ? parseScalebarUnits(d.label) : 'mile'; - var number = d.label ? parseScalebarNumber(d.label) : null; - var label = number && unit ? d.label : getAutoScalebarLabel(frameWidthPx, metersPerPx, unit); - - var scalebarKm = parseScalebarLabelToKm(label); - if (scalebarKm > 0 === false) { - message('Unusable scalebar label:', label); - return []; + // length1: length of main bar + // length2: length of optional second distance (assumes that length2 <= length1) + function getStyleBCoords(length1, length2, opts) { + var coords = []; + var labelPos = getLabelPosition(opts); + var y = opts.tic_length + opts.bar_width / 2; + if (labelPos.ypos == "top") { + y = -y; } - - var width = Math.round(scalebarKm / metersPerPx * 1000); - if (width > 0 === false) { - stop("Null scalebar length"); + coords.push([[0, y], [0, 0], [length1, 0], [length1, y]]); + if (length2 > 0) { + coords.push([[0, 0], [0, -y]]); + coords.push([[length2, 0], [length2, -y]]); } + return coords; + } - var pos = getScalebarPosition(opts); - var height = getScalebarHeight(opts); - var dx = pos.halign == 'right' ? frameWidthPx - width - opts.margin : opts.margin; - var dy = pos.valign == 'bottom' ? frame.height - height - opts.margin : opts.margin; - if (opts.label_position == 'top') { - dy += Math.round(opts.label_offset + opts.tic_length + opts.font_size * 0.8 + opts.bar_width / 2); + // length: length of scale bar in px + // length2: length of optional dual-units portion of the scalebar + function renderBar(length, length2, opts) { + var coords; + if (opts.style == 'b') { + coords = getStyleBCoords(length, length2, opts); } else { - dy += Math.round(opts.bar_width / 2); + coords = [[[0, 0], [length, 0]]]; } - - var g = renderAsSvg(width, label, opts); - g.properties = { - transform: 'translate(' + dx + ' ' + dy + ')' - }; - - return [g]; + var bar = importMultiLineString(coords); + Object.assign(bar.properties, { + stroke: 'black', + fill: 'none', + 'stroke-width': opts.bar_width, + 'stroke-linecap': 'butt', + 'stroke-linejoin': 'miter' + }); + return bar; } // unit: 'km' || 'mile' @@ -19701,6 +19763,20 @@ return units == 'mile' ? value * 1.60934 : value; } + function parseScalebarLabel(label) { + var num = label ? parseScalebarNumber(label) : null; + var units = label ? parseScalebarUnits(label) : 'mile'; + var km = NaN; + if (units && num) { + km = units == 'mile' ? num * 1.60934 : num; + } + return { + number: num, + units: units, + km: km + }; + } + function parseScalebarUnits(str) { var isMiles = /miles?$/.test(str.toLowerCase()); var isKm = /(k\.m\.|km|kilometers?|kilometres?)$/.test(str.toLowerCase()); @@ -25821,7 +25897,11 @@ ${svg} describe: 'e.g. bottom-right (default is top-left)' }) .option('label-position', { - describe: 'top or bottom' + describe: 'top, bottom, top-center (style a), etc' + }) + .option('dual-units', { + // describe: 'display both metric and imperial units', + type: 'flag' }) .option('margin', { describe: 'offset in pixels from edge of map', @@ -45224,7 +45304,7 @@ ${svg} }); } - var version = "0.6.67"; + var version = "0.6.68"; // Parse command line args into commands and run them // Function takes an optional Node-style callback. A Promise is returned if no callback is given.