From 9b60f00020cad9822797ac066b0e70553502284a Mon Sep 17 00:00:00 2001
From: Clay Anderson <469582+supersonicclay@users.noreply.github.com>
Date: Wed, 27 Nov 2019 20:39:51 -0700
Subject: [PATCH] Measure distance mode + tooltips (#299)
---
.../layers/editable-geojson-layer.md | 4 +-
docs/api-reference/modes/overview.md | 20 +++
examples/advanced/example.js | 70 ++++++--
modules/edit-modes/package.json | 1 +
modules/edit-modes/src/index.js | 1 +
modules/edit-modes/src/lib/edit-mode.js | 4 +
.../edit-modes/src/lib/geojson-edit-mode.js | 6 +
.../src/lib/measure-distance-mode.js | 134 ++++++++++++++
modules/edit-modes/src/types.js | 5 +
.../measure-distance-mode.test.js.snap | 169 ++++++++++++++++++
.../test/lib/measure-distance-mode.test.js | 122 +++++++++++++
.../src/layers/editable-geojson-layer.js | 18 +-
modules/main/src/index.js | 1 +
yarn.lock | 105 ++++++++++-
14 files changed, 639 insertions(+), 21 deletions(-)
create mode 100644 modules/edit-modes/src/lib/measure-distance-mode.js
create mode 100644 modules/edit-modes/test/lib/__snapshots__/measure-distance-mode.test.js.snap
create mode 100644 modules/edit-modes/test/lib/measure-distance-mode.test.js
diff --git a/docs/api-reference/layers/editable-geojson-layer.md b/docs/api-reference/layers/editable-geojson-layer.md
index 36e79b876..81cb0fff0 100644
--- a/docs/api-reference/layers/editable-geojson-layer.md
+++ b/docs/api-reference/layers/editable-geojson-layer.md
@@ -248,11 +248,11 @@ Edit handle objects can be represented by either points or icons. `editHandlePoi
#### `editHandlePointOutline` (Boolean, optional)
-* Default: `false`
+* Default: `true`
#### `editHandlePointStrokeWidth` (Number, optional)
-* Default: `1`
+* Default: `2`
#### `editHandlePointRadiusMinPixels` (Number, optional)
diff --git a/docs/api-reference/modes/overview.md b/docs/api-reference/modes/overview.md
index b27f4bc5d..17113e741 100644
--- a/docs/api-reference/modes/overview.md
+++ b/docs/api-reference/modes/overview.md
@@ -133,6 +133,26 @@ User can split a polygon by drawing a new `LineString` feature on top of the pol
* If the clicked position is inside the polygon, it will not split the polygon
+## [MeasureDistanceMode](https://github.com/uber/nebula.gl/blob/master/modules/edit-modes/src/lib/measure-distance-mode.js)
+
+User can measure a distance between two points.
+
+### ModeConfig
+
+The following options can be provided in the `modeConfig` object:
+
+* `turfOptions` (Object, optional)
+ * `options` object passed to turf's [distance](https://turfjs.org/docs/#distance) function
+ * Default: `undefined`
+
+* `formatTooltip` (Function, optional)
+ * Function to format tooltip text (argument is the numeric distance)
+ * Default: `(distance) => parseFloat(distance).toFixed(2) + units`
+
+* `measurementCallback` (Function, optional)
+ * Function to call as measurements are calculated
+ * Default: `undefined`
+
## [ElevationMode](https://github.com/uber/nebula.gl/blob/master/modules/edit-modes/src/lib/elevation-mode.js)
User can move a point up and down.
diff --git a/examples/advanced/example.js b/examples/advanced/example.js
index df20b7414..696b7e92c 100644
--- a/examples/advanced/example.js
+++ b/examples/advanced/example.js
@@ -31,6 +31,7 @@ import {
DrawEllipseUsingThreePointsMode,
DrawRectangleUsingThreePointsMode,
Draw90DegreePolygonMode,
+ MeasureDistanceMode,
ViewMode,
CompositeMode,
SnappableMode,
@@ -78,19 +79,9 @@ const initialViewport = {
const ALL_MODES = [
{
category: 'View',
- modes: [{ label: 'View', mode: ViewMode }]
- },
- {
- category: 'Alter',
modes: [
- { label: 'Modify', mode: ModifyMode },
- { label: 'Elevation', mode: ElevationMode },
- { label: 'Translate', mode: new SnappableMode(new TranslateMode()) },
- { label: 'Rotate', mode: RotateMode },
- { label: 'Scale', mode: ScaleMode },
- { label: 'Duplicate', mode: DuplicateMode },
- { label: 'Extrude', mode: ExtrudeMode },
- { label: 'Split', mode: SplitPolygonMode }
+ { label: 'View', mode: ViewMode },
+ { label: 'Measure Distance', mode: MeasureDistanceMode }
]
},
{
@@ -108,6 +99,19 @@ const ALL_MODES = [
{ label: 'Draw Ellipse Using 3 Points', mode: DrawEllipseUsingThreePointsMode }
]
},
+ {
+ category: 'Alter',
+ modes: [
+ { label: 'Modify', mode: ModifyMode },
+ { label: 'Elevation', mode: ElevationMode },
+ { label: 'Translate', mode: new SnappableMode(new TranslateMode()) },
+ { label: 'Rotate', mode: RotateMode },
+ { label: 'Scale', mode: ScaleMode },
+ { label: 'Duplicate', mode: DuplicateMode },
+ { label: 'Extrude', mode: ExtrudeMode },
+ { label: 'Split', mode: SplitPolygonMode }
+ ]
+ },
{
category: 'Composite',
modes: [{ label: 'Draw LineString + Modify', mode: COMPOSITE_MODE }]
@@ -525,6 +529,32 @@ export default class Example extends Component<
);
}
+ _renderMeasureDistanceControls() {
+ return (
+
+ Units
+
+
+
+
+ );
+ }
+
_renderModeConfigControls() {
const controls = [];
@@ -543,6 +573,9 @@ export default class Example extends Component<
if (this.state.mode instanceof SnappableMode) {
controls.push(this._renderSnappingControls());
}
+ if (this.state.mode === MeasureDistanceMode) {
+ controls.push(this._renderMeasureDistanceControls());
+ }
return controls;
}
@@ -807,9 +840,14 @@ export default class Example extends Component<
}
// Demonstrate how to override sub layer properties
- let _subLayerProps = null;
+ let _subLayerProps = {
+ tooltips: {
+ getColor: [255, 255, 255, 255]
+ }
+ };
+
if (this.state.editHandleType === 'elevated') {
- _subLayerProps = {
+ _subLayerProps = Object.assign(_subLayerProps, {
guides: {
_subLayerProps: {
points: {
@@ -818,11 +856,11 @@ export default class Example extends Component<
}
}
}
- };
+ });
}
if (this.state.pathMarkerLayer) {
- _subLayerProps = Object.assign(_subLayerProps || {}, {
+ _subLayerProps = Object.assign(_subLayerProps, {
geojson: {
_subLayerProps: {
'line-strings': {
diff --git a/modules/edit-modes/package.json b/modules/edit-modes/package.json
index 81b814dde..b68d9d8be 100644
--- a/modules/edit-modes/package.json
+++ b/modules/edit-modes/package.json
@@ -67,6 +67,7 @@
"@turf/transform-scale": ">=4.0.0",
"@turf/transform-translate": ">=4.0.0",
"@turf/union": ">=4.0.0",
+ "memoizee": "^0.4.14",
"viewport-mercator-project": ">=6.0.0"
}
}
diff --git a/modules/edit-modes/src/index.js b/modules/edit-modes/src/index.js
index aea10d315..05ff0fd8e 100644
--- a/modules/edit-modes/src/index.js
+++ b/modules/edit-modes/src/index.js
@@ -27,6 +27,7 @@ export { ImmutableFeatureCollection } from './lib/immutable-feature-collection.j
// Other modes
export { ViewMode } from './lib/view-mode.js';
+export { MeasureDistanceMode } from './lib/measure-distance-mode.js';
export { CompositeMode } from './lib/composite-mode.js';
export { SnappableMode } from './lib/snappable-mode.js';
diff --git a/modules/edit-modes/src/lib/edit-mode.js b/modules/edit-modes/src/lib/edit-mode.js
index 9b0003341..f295b8966 100644
--- a/modules/edit-modes/src/lib/edit-mode.js
+++ b/modules/edit-modes/src/lib/edit-mode.js
@@ -6,6 +6,7 @@ import type {
PointerMoveEvent,
StartDraggingEvent,
StopDraggingEvent,
+ Tooltip,
ModeProps
} from '../types.js';
@@ -24,4 +25,7 @@ export interface EditMode {
// Return features that can be used as a guide for editing the data
getGuides(props: ModeProps): TGuides;
+
+ // Return tooltips
+ getTooltips(props: ModeProps): Tooltip[];
}
diff --git a/modules/edit-modes/src/lib/geojson-edit-mode.js b/modules/edit-modes/src/lib/geojson-edit-mode.js
index 60459600b..9626e57e4 100644
--- a/modules/edit-modes/src/lib/geojson-edit-mode.js
+++ b/modules/edit-modes/src/lib/geojson-edit-mode.js
@@ -11,6 +11,7 @@ import type {
StartDraggingEvent,
StopDraggingEvent,
Pick,
+ Tooltip,
ModeProps
} from '../types.js';
import type {
@@ -39,6 +40,7 @@ export type EditHandle = {
export type GeoJsonEditAction = EditAction;
const DEFAULT_EDIT_HANDLES: EditHandle[] = [];
+const DEFAULT_TOOLTIPS: Tooltip[] = [];
// Main interface for `EditMode`s that edit GeoJSON
export type GeoJsonEditMode = EditMode;
@@ -75,6 +77,10 @@ export class BaseGeoJsonEditMode implements EditMode): Tooltip[] {
+ return DEFAULT_TOOLTIPS;
+ }
+
getSelectedFeature(props: ModeProps): ?Feature {
if (props.selectedIndexes.length === 1) {
return props.data.features[props.selectedIndexes[0]];
diff --git a/modules/edit-modes/src/lib/measure-distance-mode.js b/modules/edit-modes/src/lib/measure-distance-mode.js
new file mode 100644
index 000000000..928d03ff5
--- /dev/null
+++ b/modules/edit-modes/src/lib/measure-distance-mode.js
@@ -0,0 +1,134 @@
+// @flow
+
+import turfDistance from '@turf/distance';
+import memoize from 'memoizee';
+import type {
+ ClickEvent,
+ PointerMoveEvent,
+ StartDraggingEvent,
+ StopDraggingEvent,
+ Tooltip,
+ ModeProps
+} from '../types.js';
+import type { FeatureCollection, Position } from '../geojson-types.js';
+import { BaseGeoJsonEditMode } from './geojson-edit-mode.js';
+
+const DEFAULT_TOOLTIPS = [];
+
+export class MeasureDistanceMode extends BaseGeoJsonEditMode {
+ startingPoint = null;
+ endingPoint = null;
+ endingPointLocked = false;
+
+ _setEndingPoint(mapCoords: Position) {
+ this.endingPoint = {
+ type: 'Feature',
+ properties: {
+ guideType: 'editHandle',
+ editHandleType: 'existing'
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: mapCoords
+ }
+ };
+ }
+
+ _getTooltips = memoize((modeConfig, startingPoint, endingPoint) => {
+ let tooltips = DEFAULT_TOOLTIPS;
+
+ if (startingPoint && endingPoint) {
+ const { formatTooltip, turfOptions, measurementCallback } = modeConfig || {};
+ const units = (turfOptions && turfOptions.units) || 'kilometers';
+
+ const distance = turfDistance(startingPoint, endingPoint, turfOptions);
+
+ let text;
+ if (formatTooltip) {
+ text = formatTooltip(distance);
+ }
+ if (!formatTooltip) {
+ // By default, round to 2 decimal places and append units
+ text = `${parseFloat(distance).toFixed(2)} ${units}`;
+ }
+
+ if (measurementCallback) {
+ measurementCallback(distance);
+ }
+
+ tooltips = [
+ {
+ position: endingPoint.geometry.coordinates,
+ text
+ }
+ ];
+ }
+
+ return tooltips;
+ });
+
+ handleClick(event: ClickEvent, props: ModeProps): void {
+ if (!this.startingPoint || this.endingPointLocked) {
+ this.startingPoint = {
+ type: 'Feature',
+ properties: {
+ guideType: 'editHandle',
+ editHandleType: 'existing'
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: event.mapCoords
+ }
+ };
+ this.endingPoint = null;
+ this.endingPointLocked = false;
+ } else if (this.startingPoint) {
+ this._setEndingPoint(event.mapCoords);
+ this.endingPointLocked = true;
+ }
+ }
+
+ // Called when the pointer moved, regardless of whether the pointer is down, up, and whether something was picked
+ handlePointerMove(event: PointerMoveEvent, props: ModeProps): void {
+ if (this.startingPoint && !this.endingPointLocked) {
+ this._setEndingPoint(event.mapCoords);
+ }
+
+ props.onUpdateCursor('cell');
+ }
+
+ // Called when the pointer went down on something rendered by this layer and the pointer started to move
+ handleStartDragging(event: StartDraggingEvent, props: ModeProps): void {}
+
+ // Called when the pointer went down on something rendered by this layer, the pointer moved, and now the pointer is up
+ handleStopDragging(event: StopDraggingEvent, props: ModeProps): void {}
+
+ // Return features that can be used as a guide for editing the data
+ getGuides(props: ModeProps): FeatureCollection {
+ const features = [];
+ if (this.startingPoint) {
+ features.push(this.startingPoint);
+ }
+ if (this.endingPoint) {
+ features.push(this.endingPoint);
+ }
+ if (this.startingPoint && this.endingPoint) {
+ features.push({
+ type: 'Feature',
+ properties: { guideType: 'tentative' },
+ geometry: {
+ type: 'LineString',
+ coordinates: [
+ this.startingPoint.geometry.coordinates,
+ this.endingPoint.geometry.coordinates
+ ]
+ }
+ });
+ }
+ return { type: 'FeatureCollection', features };
+ }
+
+ getTooltips(props: ModeProps): Tooltip[] {
+ return this._getTooltips(props.modeConfig, this.startingPoint, this.endingPoint);
+ }
+}
diff --git a/modules/edit-modes/src/types.js b/modules/edit-modes/src/types.js
index 389e517e8..a3099f39d 100644
--- a/modules/edit-modes/src/types.js
+++ b/modules/edit-modes/src/types.js
@@ -74,6 +74,11 @@ export type PointerMoveEvent = {
sourceEvent: any
};
+export type Tooltip = {
+ position: Position,
+ text: string
+};
+
export type ModeProps = {
// The data being edited, this can be an array or an object
data: TData,
diff --git a/modules/edit-modes/test/lib/__snapshots__/measure-distance-mode.test.js.snap b/modules/edit-modes/test/lib/__snapshots__/measure-distance-mode.test.js.snap
new file mode 100644
index 000000000..61b90076c
--- /dev/null
+++ b/modules/edit-modes/test/lib/__snapshots__/measure-distance-mode.test.js.snap
@@ -0,0 +1,169 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`one click + pointer move guides are two points + line string 1`] = `
+Object {
+ "features": Array [
+ Object {
+ "geometry": Object {
+ "coordinates": Array [
+ 1,
+ 2,
+ ],
+ "type": "Point",
+ },
+ "properties": Object {
+ "editHandleType": "existing",
+ "guideType": "editHandle",
+ },
+ "type": "Feature",
+ },
+ Object {
+ "geometry": Object {
+ "coordinates": Array [
+ 3,
+ 4,
+ ],
+ "type": "Point",
+ },
+ "properties": Object {
+ "editHandleType": "existing",
+ "guideType": "editHandle",
+ },
+ "type": "Feature",
+ },
+ Object {
+ "geometry": Object {
+ "coordinates": Array [
+ Array [
+ 1,
+ 2,
+ ],
+ Array [
+ 3,
+ 4,
+ ],
+ ],
+ "type": "LineString",
+ },
+ "properties": Object {
+ "guideType": "tentative",
+ },
+ "type": "Feature",
+ },
+ ],
+ "type": "FeatureCollection",
+}
+`;
+
+exports[`one click + pointer move tooltip contains distance 1`] = `
+Array [
+ Object {
+ "position": Array [
+ 3,
+ 4,
+ ],
+ "text": "314.28 kilometers",
+ },
+]
+`;
+
+exports[`one click guides are a single point 1`] = `
+Object {
+ "features": Array [
+ Object {
+ "geometry": Object {
+ "coordinates": Array [
+ 1,
+ 2,
+ ],
+ "type": "Point",
+ },
+ "properties": Object {
+ "editHandleType": "existing",
+ "guideType": "editHandle",
+ },
+ "type": "Feature",
+ },
+ ],
+ "type": "FeatureCollection",
+}
+`;
+
+exports[`two clicks + pointer move tooltip contains distance 1`] = `
+Array [
+ Object {
+ "position": Array [
+ 3,
+ 4,
+ ],
+ "text": "314.28 kilometers",
+ },
+]
+`;
+
+exports[`two clicks guides are two points + line string 1`] = `
+Object {
+ "features": Array [
+ Object {
+ "geometry": Object {
+ "coordinates": Array [
+ 1,
+ 2,
+ ],
+ "type": "Point",
+ },
+ "properties": Object {
+ "editHandleType": "existing",
+ "guideType": "editHandle",
+ },
+ "type": "Feature",
+ },
+ Object {
+ "geometry": Object {
+ "coordinates": Array [
+ 3,
+ 4,
+ ],
+ "type": "Point",
+ },
+ "properties": Object {
+ "editHandleType": "existing",
+ "guideType": "editHandle",
+ },
+ "type": "Feature",
+ },
+ Object {
+ "geometry": Object {
+ "coordinates": Array [
+ Array [
+ 1,
+ 2,
+ ],
+ Array [
+ 3,
+ 4,
+ ],
+ ],
+ "type": "LineString",
+ },
+ "properties": Object {
+ "guideType": "tentative",
+ },
+ "type": "Feature",
+ },
+ ],
+ "type": "FeatureCollection",
+}
+`;
+
+exports[`two clicks tooltip contains distance 1`] = `
+Array [
+ Object {
+ "position": Array [
+ 3,
+ 4,
+ ],
+ "text": "314.28 kilometers",
+ },
+]
+`;
diff --git a/modules/edit-modes/test/lib/measure-distance-mode.test.js b/modules/edit-modes/test/lib/measure-distance-mode.test.js
new file mode 100644
index 000000000..dd2befda3
--- /dev/null
+++ b/modules/edit-modes/test/lib/measure-distance-mode.test.js
@@ -0,0 +1,122 @@
+// @flow
+/* eslint-env jest */
+
+import { MeasureDistanceMode } from '../../src/lib/measure-distance-mode.js';
+import {
+ createFeatureCollectionProps,
+ createClickEvent,
+ createPointerMoveEvent
+} from '../test-utils.js';
+
+describe('move without click', () => {
+ let mode;
+ beforeEach(() => {
+ mode = new MeasureDistanceMode();
+ mode.handlePointerMove(createPointerMoveEvent(), createFeatureCollectionProps());
+ });
+
+ it('guides are empty', () => {
+ const guides = mode.getGuides(createFeatureCollectionProps());
+ expect(guides).toEqual({ type: 'FeatureCollection', features: [] });
+ });
+
+ it('tooltips are empty', () => {
+ const tooltips = mode.getTooltips(createFeatureCollectionProps());
+ expect(tooltips).toEqual([]);
+ });
+});
+
+describe('one click', () => {
+ let mode;
+ beforeEach(() => {
+ mode = new MeasureDistanceMode();
+ mode.handleClick(createClickEvent([1, 2]), createFeatureCollectionProps());
+ });
+
+ it('guides are a single point', () => {
+ const guides = mode.getGuides(createFeatureCollectionProps());
+ expect(guides).toMatchSnapshot();
+ });
+
+ it('tooltips are empty', () => {
+ const tooltips = mode.getTooltips(createFeatureCollectionProps());
+ expect(tooltips).toEqual([]);
+ });
+});
+
+describe('one click + pointer move', () => {
+ let mode;
+ beforeEach(() => {
+ mode = new MeasureDistanceMode();
+ mode.handleClick(createClickEvent([1, 2]), createFeatureCollectionProps());
+ mode.handlePointerMove(createPointerMoveEvent([3, 4]), createFeatureCollectionProps());
+ });
+
+ it('guides are two points + line string', () => {
+ const guides = mode.getGuides(createFeatureCollectionProps());
+ expect(guides).toMatchSnapshot();
+ });
+
+ it('tooltip contains distance', () => {
+ const tooltips = mode.getTooltips(createFeatureCollectionProps());
+ expect(tooltips).toMatchSnapshot();
+ });
+});
+
+describe('two clicks', () => {
+ let mode;
+ beforeEach(() => {
+ mode = new MeasureDistanceMode();
+ mode.handleClick(createClickEvent([1, 2]), createFeatureCollectionProps());
+ mode.handleClick(createClickEvent([3, 4]), createFeatureCollectionProps());
+ });
+
+ it('guides are two points + line string', () => {
+ const guides = mode.getGuides(createFeatureCollectionProps());
+ expect(guides).toMatchSnapshot();
+ });
+
+ it('tooltip contains distance', () => {
+ const tooltips = mode.getTooltips(createFeatureCollectionProps());
+ expect(tooltips).toMatchSnapshot();
+ });
+
+ it('can measure kilometers', () => {
+ const tooltips = mode.getTooltips(createFeatureCollectionProps());
+ expect(tooltips[0].text).toContain('kilometers');
+ });
+
+ it('can measure miles', () => {
+ const tooltips = mode.getTooltips(
+ createFeatureCollectionProps({ modeConfig: { turfOptions: { units: 'miles' } } })
+ );
+ expect(tooltips[0].text).toContain('miles');
+ });
+
+ it('can format distance', () => {
+ const tooltips = mode.getTooltips(
+ createFeatureCollectionProps({ modeConfig: { formatTooltip: String } })
+ );
+ expect(tooltips[0].text).toEqual('314.28368918020476');
+ });
+});
+
+describe('two clicks + pointer move', () => {
+ let mode;
+ beforeEach(() => {
+ mode = new MeasureDistanceMode();
+ mode.handleClick(createClickEvent([1, 2]), createFeatureCollectionProps());
+ mode.handleClick(createClickEvent([3, 4]), createFeatureCollectionProps());
+ mode.handlePointerMove(createPointerMoveEvent([4, 5]), createFeatureCollectionProps());
+ });
+
+ it('ending point is clicked point not hovered point', () => {
+ const guides = mode.getGuides(createFeatureCollectionProps());
+ expect(guides.features[1].geometry.coordinates).toEqual([3, 4]);
+ });
+
+ it('tooltip contains distance', () => {
+ const tooltips = mode.getTooltips(createFeatureCollectionProps());
+ expect(tooltips).toMatchSnapshot();
+ });
+});
diff --git a/modules/layers/src/layers/editable-geojson-layer.js b/modules/layers/src/layers/editable-geojson-layer.js
index 43699e52a..06eb8610c 100644
--- a/modules/layers/src/layers/editable-geojson-layer.js
+++ b/modules/layers/src/layers/editable-geojson-layer.js
@@ -1,7 +1,7 @@
// @flow
/* eslint-env browser */
-import { GeoJsonLayer, ScatterplotLayer, IconLayer } from '@deck.gl/layers';
+import { GeoJsonLayer, ScatterplotLayer, IconLayer, TextLayer } from '@deck.gl/layers';
import {
ViewMode,
@@ -252,7 +252,7 @@ export default class EditableGeoJsonLayer extends EditableLayer {
let layers: any = [new GeoJsonLayer(subLayerProps)];
- layers = layers.concat(this.createGuidesLayers());
+ layers = layers.concat(this.createGuidesLayers(), this.createTooltipsLayers());
return layers;
}
@@ -428,6 +428,20 @@ export default class EditableGeoJsonLayer extends EditableLayer {
return [layer];
}
+ createTooltipsLayers() {
+ const mode = this.getActiveMode();
+ const tooltips = mode.getTooltips(this.getModeProps(this.props));
+
+ const layer = new TextLayer(
+ this.getSubLayerProps({
+ id: `tooltips`,
+ data: tooltips
+ })
+ );
+
+ return [layer];
+ }
+
onLayerClick(event: ClickEvent) {
this.getActiveMode().handleClick(event, this.getModeProps(this.props));
}
diff --git a/modules/main/src/index.js b/modules/main/src/index.js
index 7da127382..32c4b0899 100644
--- a/modules/main/src/index.js
+++ b/modules/main/src/index.js
@@ -53,5 +53,6 @@ export { ImmutableFeatureCollection } from '@nebula.gl/edit-modes';
// Other modes
export { ViewMode } from '@nebula.gl/edit-modes';
+export { MeasureDistanceMode } from '@nebula.gl/edit-modes';
export { CompositeMode } from '@nebula.gl/edit-modes';
export { SnappableMode } from '@nebula.gl/edit-modes';
diff --git a/yarn.lock b/yarn.lock
index 902183afa..eac04efa6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3858,6 +3858,14 @@ d3-geo@1.7.1:
dependencies:
d3-array "1"
+d@1, d@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
+ integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
+ dependencies:
+ es5-ext "^0.10.50"
+ type "^1.0.1"
+
dargs@^4.0.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17"
@@ -4307,6 +4315,24 @@ es-to-primitive@^1.2.0:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
+es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
+ version "0.10.52"
+ resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.52.tgz#bb21777e919a04263736ded120a9d665f10ea63f"
+ integrity sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==
+ dependencies:
+ es6-iterator "~2.0.3"
+ es6-symbol "~3.1.2"
+ next-tick "~1.0.0"
+
+es6-iterator@^2.0.3, es6-iterator@~2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
+ integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c=
+ dependencies:
+ d "1"
+ es5-ext "^0.10.35"
+ es6-symbol "^3.1.1"
+
es6-promise@^4.0.3:
version "4.2.5"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054"
@@ -4319,6 +4345,24 @@ es6-promisify@^5.0.0:
dependencies:
es6-promise "^4.0.3"
+es6-symbol@^3.1.1, es6-symbol@~3.1.2:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
+ integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
+ dependencies:
+ d "^1.0.1"
+ ext "^1.1.2"
+
+es6-weak-map@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53"
+ integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==
+ dependencies:
+ d "1"
+ es5-ext "^0.10.46"
+ es6-iterator "^2.0.3"
+ es6-symbol "^3.1.1"
+
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -4534,6 +4578,14 @@ etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+event-emitter@^0.3.5:
+ version "0.3.5"
+ resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
+ integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=
+ dependencies:
+ d "1"
+ es5-ext "~0.10.14"
+
eventemitter3@1.x.x:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
@@ -4676,6 +4728,13 @@ express@^4.16.2:
utils-merge "1.0.1"
vary "~1.1.2"
+ext@^1.1.2:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/ext/-/ext-1.2.0.tgz#8dd8d2dd21bcced3045be09621fa0cbf73908ba4"
+ integrity sha512-0ccUQK/9e3NreLFg6K6np8aPyRgwycx+oFGtfx1dSp7Wj00Ozw9r05FgBRlzjf2XBM7LAzwgLyDscRrtSU91hA==
+ dependencies:
+ type "^2.0.0"
+
extend-shallow@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
@@ -5940,7 +5999,7 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies:
isobject "^3.0.1"
-is-promise@^2.1.0:
+is-promise@^2.1, is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
@@ -7075,6 +7134,13 @@ lru-cache@^5.1.1:
dependencies:
yallist "^3.0.2"
+lru-queue@0.1:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3"
+ integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=
+ dependencies:
+ es5-ext "~0.10.2"
+
magic-string@~0.16.0:
version "0.16.0"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.16.0.tgz#970ebb0da7193301285fb1aa650f39bdd81eb45a"
@@ -7221,6 +7287,20 @@ mem@^4.0.0:
mimic-fn "^2.0.0"
p-is-promise "^2.0.0"
+memoizee@^0.4.14:
+ version "0.4.14"
+ resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57"
+ integrity sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==
+ dependencies:
+ d "1"
+ es5-ext "^0.10.45"
+ es6-weak-map "^2.0.2"
+ event-emitter "^0.3.5"
+ is-promise "^2.1"
+ lru-queue "0.1"
+ next-tick "1"
+ timers-ext "^0.1.5"
+
memory-fs@^0.4.0, memory-fs@~0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@@ -7552,6 +7632,11 @@ neo-async@^2.5.0, neo-async@^2.6.0:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835"
integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==
+next-tick@1, next-tick@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
+ integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
+
nice-try@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
@@ -10006,6 +10091,14 @@ timers-browserify@^2.0.4:
dependencies:
setimmediate "^1.0.4"
+timers-ext@^0.1.5:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6"
+ integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==
+ dependencies:
+ es5-ext "~0.10.46"
+ next-tick "1"
+
tinyqueue@^1.2.0:
version "1.2.3"
resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-1.2.3.tgz#b6a61de23060584da29f82362e45df1ec7353f3d"
@@ -10157,6 +10250,16 @@ type-is@~1.6.15, type-is@~1.6.16:
media-typer "0.3.0"
mime-types "~2.1.18"
+type@^1.0.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
+ integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
+
+type@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3"
+ integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==
+
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"