diff --git a/docs/api-reference/react-map-gl-draw/react-map-gl-draw.md b/docs/api-reference/react-map-gl-draw/react-map-gl-draw.md
index c5734b8fb..8ee1edcb0 100644
--- a/docs/api-reference/react-map-gl-draw/react-map-gl-draw.md
+++ b/docs/api-reference/react-map-gl-draw/react-map-gl-draw.md
@@ -1,23 +1,34 @@
# React Map GL Draw
+`react-map-gl-draw` is a react based drawing library tailored for [`react-map-gl`](https://github.com/uber/react-map-gl).
+
## Options
- `mode` (String, Optional)
- `EditorModes.READ_ONLY` - Not interactive. This is the default mode.
- - `EditorModes.SELECT_FEATURE` - Lets you select, delete, and drag features.
- - `EditorModes.EDIT_VERTEX` - Lets you select, delete, and drag vertices; and drag features.
+ - `EditorModes.SELECT` - Lets you select, delete, and drag features.
+ - `EditorModes.EDITTING` - Lets you select, delete, and drag vertices; and drag features.
- `EditorModes.DRAW_PATH` - Lets you draw a GeoJson `LineString` feature.
- `EditorModes.DRAW_POLYGON` - Lets you draw a GeoJson `Polygon` feature.
- `EditorModes.DRAW_POINT` - Lets you draw a GeoJson `Point` feature.
- `EditorModes.DRAW_RECTANGLE` - Lets you draw a `Rectangle` (represented as GeoJson `Polygon` feature).
-- `selectedFeatureId` (String, Optional) - id of the selected feature. `EditorModes` assigns a unique id to each feature which is stored in `feature.properties.id`.
-- `clickRadius` (Number, optional) - Radius to detect features around a hovered or clicked point. Default value is `0`
-
-- `onSelect` (Function, Required) - callback when a feature is selected. Receives an object containing `selectedFeatureId`.
-- `onUpdate` (Function, Required) - callback when anything is updated. Receives one argument `features` that is the updated list of GeoJSON features.
+- `features` (Feature[], Optional) - List of features in GeoJson format. If `features` are provided from users, then `react-map-gl-draw` respect the users' input, and therefore ignore any internal `features`. But if `features` are not provided, then `react-map-gl-draw` manages `features` internally, and users can access and manipulate the features by calling `getFeatures`, `addFeatures`, and `deleteFeatures`.
+- `selectedFeatureIndex` (String, Optional) - Index of the selected feature.
+- `clickRadius` (Number, Optional) - Radius to detect features around a hovered or clicked point. Default value is `0`
-Feature object structure:
-`react-map-gl-draw` is stateful component.
+- `onSelect` (Function, Optional) - callback when clicking a position under `SELECT` and `EDITTING` mode. Receives an object containing the following parameters
+ - `selectedFeature`: selected feature. `null` if clicked an empty space.
+ - `selectedFeatureIndex`: selected feature index.`null` if clicked an empty space.
+ - `editHandleIndex`: selected editHandle index. `null` if clicked an empty space.
+ - `screenCoords`: screen coordinates of the clicked position.
+ - `mapCoords`: map coordinates of the clicked position.
+
+- `onUpdate` (Function, Optional) - callback when anything is updated. Receives an object containing the following parameters
+ - `features` (Feature[]) - the updated list of GeoJSON features.
+ - `editType` (String) - `addFeature`, `addPosition`, `finishMovePosition`
+ - `editContext` (Array) - list of edit objects, depend on `editType`, each object may contain `featureIndexes`, `editHandleIndexes`, `screenCoords`, `mapCoords`.
+
+**Feature object structure:**
```js
{
id, // an unique identified generated inside react-map-gl-draw library
@@ -32,24 +43,31 @@ Feature object structure:
}
```
-### Styling related Options
-- `getFeatureStyle` (Function, Optional) : Object - A function to style a feature, function parameters are
- - `feature`: feature to style .
- - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`.
+### Styling related options
+- `featureStyle` (Object|Function, Optional) : Object - Either a [style objects](https://reactjs.org/docs/dom-elements.html#style) or a function to style a feature, function parameters are
+ - `feature`: feature to style.
+ - `index`: index of the feature.
+ - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`, `CLOSING`.
Returns is a map of [style objects](https://reactjs.org/docs/dom-elements.html#style) passed to SVG `path` elements.
-- `getEditHandleStyle` (Function, Optional) : Object - A function to style an `editHandle, function parameters are
+- `featureShape` (String|Function, Optional): if is a string, should be one of `rect` or `circle`. If is a function, will receive the following parameters
+ - `feature`: feature to style.
+ - `index`: index of the feature.
+ - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`, `CLOSING`.
+
+- `editHandleStyle` (Object|Function, Optional) : Object - Either a [style objects](https://reactjs.org/docs/dom-elements.html#style) or a function to style an `editHandle, function parameters are
- `feature`: feature to style.
- `index`: index of the editHandle vertex in the feature.
- - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`.
+ - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`, `CLOSING`.
+ - `shape`: shape resolved from `editHandleShape`.
Returns is a map of [style objects](https://reactjs.org/docs/dom-elements.html#style) passed to SVG `circle` or `rect` elements.
-- `getEditHandleShape` (String|Function, Optional): if is a string, should be one of `rect` or `circle`. If is a function, will receive the following parameters
+- `editHandleShape` (String|Function, Optional): if is a string, should be one of `rect` or `circle`. If is a function, will receive the following parameters
- `feature`: feature to style.
- `index`: index of the editHandle vertex in the feature.
- - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`.
+ - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`, `CLOSING`.
## Explanations
- `Feature`: any drawn shape, one of point, line, polygon or rectangle.
@@ -60,78 +78,70 @@ Returns is a map of [style objects](https://reactjs.org/docs/dom-elements.html#s
- `SELECTED`: being clicked or dragged.
- `HOVERED`: hovered over by the mouse pointer.
- `UNCOMMITTED`: in the middle of drawing, not yet added to the feature being edited.
+- `CLOSING`: closing a polygon.
### Styling based on `state`:
-![img](https://raw.githubusercontent.com/uber-common/deck.gl-data/master/nebula.gl/edit-handle.png)
+![img](https://raw.githubusercontent.com/uber-common/deck.gl-data/master/nebula.gl/react-map-gl-draw.png)
As shown in the above image, for the feature currently being edited,
-- `getFeatureStyle({feature, state: SELECTED})` will be applied to the committed parts of the feature. (Green strokes)
-- `getEditHandleStyle({state: SELECTED})` will be applied to the committed editHandle vertices. (Vertices with black stroke)
-- `getFeatureStyle({feature, state: UNCOMMITTED})` will be applied to the uncommitted parts of the feature. (Gray stroke)
-- `getEditHandleStyle({state: UNCOMMITTED})` will be applied to the uncommitted editHandle vertex. (Gray vertex)
+- `featureStyle({feature, state: SELECTED})` will be applied to the committed parts of the feature. (Green strokes)
+- `editHandleStyle({state: SELECTED})` will be applied to the committed editHandle vertices. (Vertices with black stroke)
+- `featureStyle({feature, state: UNCOMMITTED})` will be applied to the uncommitted parts of the feature. (Gray stroke)
+- `editHandleStyle({state: UNCOMMITTED})` will be applied to the uncommitted editHandle vertex. (Gray vertex)
+
+## Methods
+
+##### `getFeatures`
+- Return a list of finished GeoJson features.
+
+##### `addFeatures` (Feature | Feature[])
+
+- Add a single or multiple GeoJson features to editor.
+
+##### `deleteDeatures` (Feature | Feature[])
+
+- Delete a single or multiple GeoJson features to editor.
## Code Example
```js
import React, { Component } from 'react';
-import MapGL, {_MapContext as MapContext} from 'react-map-gl';
-import MapGLDraw, { EditorModes } from 'react-map-gl-draw';
+import MapGL from 'react-map-gl';
+import { Editor, EditorModes } from 'react-map-gl-draw';
const MODES = [
- { id: EditorModes.EDIT_VERTEX, text: 'Select and Edit Feature'},
+ { id: EditorModes.EDITING, text: 'Select and Edit Feature'},
{ id: EditorModes.DRAW_POINT, text: 'Draw Point'},
{ id: EditorModes.DRAW_PATH, text: 'Draw Polyline'},
{ id: EditorModes.DRAW_POLYGON, text: 'Draw Polygon'},
{ id: EditorModes.DRAW_RECTANGLE, text: 'Draw Rectangle'}
];
+const DEFAULT_VIEWPORT = {
+ width: 800,
+ height: 600,
+ longitude: -122.45,
+ latitude: 37.78,
+ zoom: 14
+};
+
class App extends Component {
- constructor(props) {
- super(props);
- this.state = {
- viewport: {
- width: 800,
- height: 600,
- longitude: -122.45,
- latitude: 37.78,
- zoom: 14
- },
- selectedMode: EditorModes.READ_ONLY,
- features: [],
- selectedFeatureId: null
- };
- }
-
- componentDidMount() {
- // add features
- const initialFeatures = [{...}];
- this._mapRef.add(initialFeatures);
- }
-
- _updateViewport = (viewport) => {
- this.setState({viewport});
- }
-
- _onSelect = ({ selectedFeatureId }) => {
- this.setState({ selectedFeatureId });
- };
-
- _onUpdate = features => {
- this.setState({
- features
- });
+ state = {
+ // map
+ viewport: DEFAULT_VIEWPORT,
+ // editor
+ selectedMode: EditorModes.READ_ONLY
};
_switchMode = evt => {
- const selectedMode = evt.target.id === this.state.selectedMode ? EditorModes.READ_ONLY : evt.target.id;
+ const selectedMode = evt.target.id;
this.setState({
- selectedMode,
- selectedFeatureId: null
+ selectedMode: selectedMode === this.state.selectedMode ? null : selectedMode
});
};
-
- _renderControlPanel = () => {
+
+ _renderToolbar = () => {
return (
);
- }
-
- _getEditHandleStyle = ({feature, featureState, vertexIndex, vertexState}) => {
- return {
- fill: vertexState === `SELECTED` ? '#000' : '#aaa',
- stroke: vertexState === `SELECTED` ? '#000' : 'none'
- }
- }
-
- _getFeatureStyle = ({feature, featureState}) => {
- return {
- stroke: featureState === `SELECTED` ? '#000' : 'none',
- fill: featureState === `SELECTED` ? '#080' : 'none',
- fillOpacity: 0.8
- }
- }
+ };
render() {
- const { viewport, selectedMode, selectedFeatureId, features } = this.state;
+ const { viewport, selectedMode } = this.state;
return (
- this._drawRef = _}
+
- {this._renderControlPanel()}
+ {this._renderToolbar()}
);
}
diff --git a/examples/react-map-gl-draw/app.js b/examples/react-map-gl-draw/app.js
index 4579e9926..e01b88225 100644
--- a/examples/react-map-gl-draw/app.js
+++ b/examples/react-map-gl-draw/app.js
@@ -1,4 +1,3 @@
-/* global window */
import React, { Component } from 'react';
import { render } from 'react-dom';
import MapGL from 'react-map-gl';
@@ -9,65 +8,36 @@ import Toolbar from './toolbar';
// eslint-disable-next-line no-process-env, no-undef
const MAP_STYLE = process.env.MapStyle || 'mapbox://styles/mapbox/light-v9';
+const DEFAULT_VIEWPORT = {
+ width: 800,
+ height: 600,
+ longitude: -122.45,
+ latitude: 37.78,
+ zoom: 14
+};
+
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
- viewport: {
- width: 800,
- height: 600,
- longitude: -122.45,
- latitude: 37.78,
- zoom: 14
- },
+ // map
+ viewport: DEFAULT_VIEWPORT,
+ // editor
selectedMode: EditorModes.READ_ONLY,
features: [],
- selectedFeatureId: null
+ selectedFeatureIndex: null
};
this._mapRef = null;
+ this._editorRef = null;
}
- componentDidMount() {
- window.addEventListener('keydown', this._onKeydown);
- }
-
- componentWillUnmount() {
- window.removeEventListener('keydown', this._onKeydown);
- }
-
- _onKeydown = evt => {
- if (evt.keyCode === 27) {
- // esc key
- this.setState({ selectedFeatureId: null });
- }
- };
-
- _updateViewport = viewport => {
- this.setState({ viewport });
- };
-
- _onSelect = ({ selectedFeatureId }) => {
- this.setState({ selectedFeatureId });
- };
-
_onDelete = () => {
- const { selectedFeatureId } = this.state;
- if (selectedFeatureId === null || selectedFeatureId === undefined) {
+ const { selectedFeatureIndex } = this.state;
+ if (selectedFeatureIndex === null || selectedFeatureIndex === undefined) {
return;
}
- const selectedIndex = this.state.features.findIndex(f => f.properties.id === selectedFeatureId);
- if (selectedIndex >= 0) {
- const newFeatures = [...this.state.features];
- newFeatures.splice(selectedIndex, 1);
- this.setState({ features: newFeatures, selectedFeatureId: null });
- }
- };
-
- _onUpdate = features => {
- this.setState({
- features
- });
+ this._editorRef.deleteFeatures(selectedFeatureIndex);
};
_switchMode = evt => {
@@ -76,10 +46,11 @@ export default class App extends Component {
selectedMode = null;
}
- this.setState({
- selectedMode,
- selectedFeatureId: null
- });
+ this.setState({ selectedMode });
+ };
+
+ _updateViewport = viewport => {
+ this.setState({ viewport });
};
_renderToolbar = () => {
@@ -92,12 +63,8 @@ export default class App extends Component {
);
};
- _getEditHandleShape = ({ feature }) => {
- return feature.properties.renderType === 'Point' ? 'circle' : 'rect';
- };
-
render() {
- const { viewport, selectedMode, selectedFeatureId, features } = this.state;
+ const { viewport, selectedMode } = this.state;
return (
(this._editorRef = _)}
clickRadius={12}
+ onSelect={selected => {
+ this.setState({ selectedFeatureIndex: selected && selected.selectedFeatureIndex });
+ }}
mode={selectedMode}
- features={features}
- selectedFeatureId={selectedFeatureId}
- onSelect={this._onSelect}
- onUpdate={this._onUpdate}
- getEditHandleShape={this._getEditHandleShape}
/>
{this._renderToolbar()}
diff --git a/examples/react-map-gl-draw/toolbar.js b/examples/react-map-gl-draw/toolbar.js
index fc168a2c7..a56da789f 100644
--- a/examples/react-map-gl-draw/toolbar.js
+++ b/examples/react-map-gl-draw/toolbar.js
@@ -4,7 +4,7 @@ import styled from 'styled-components';
import { EditorModes } from 'react-map-gl-draw';
const MODES = [
- { id: EditorModes.EDITING, text: 'Edit Feature', icon: 'icon-select.svg' },
+ { id: EditorModes.SELECT, text: 'Edit Feature', icon: 'icon-select.svg' },
{ id: EditorModes.DRAW_POINT, text: 'Draw Point', icon: 'icon-point.svg' },
{ id: EditorModes.DRAW_PATH, text: 'Draw Polyline', icon: 'icon-path.svg' },
{ id: EditorModes.DRAW_POLYGON, text: 'Draw Polygon', icon: 'icon-polygon.svg' },
diff --git a/modules/edit-modes/src/lib/immutable-feature-collection.js b/modules/edit-modes/src/lib/immutable-feature-collection.js
index b80724d30..4a897c69b 100644
--- a/modules/edit-modes/src/lib/immutable-feature-collection.js
+++ b/modules/edit-modes/src/lib/immutable-feature-collection.js
@@ -133,7 +133,7 @@ export class ImmutableFeatureCollection {
* Works with MultiPoint, LineString, MultiLineString, Polygon, and MultiPolygon.
*
* @param featureIndex The index of the feature to update
- * @param positionIndexes An array containing the indexes of the postion that will preceed the new position
+ * @param positionIndexes An array containing the indexes of the position that will proceed the new position
* @param positionToAdd The new position to place in the result (i.e. [lng, lat])
*
* @returns A new `ImmutableFeatureCollection` with the given coordinate removed. Does not modify this `ImmutableFeatureCollection`.
@@ -182,9 +182,35 @@ export class ImmutableFeatureCollection {
}
addFeature(feature: Feature): ImmutableFeatureCollection {
+ return this.addFeatures([feature]);
+ }
+
+ addFeatures(features: Feature[]): ImmutableFeatureCollection {
+ const updatedFeatureCollection = {
+ ...this.featureCollection,
+ features: [...this.featureCollection.features, ...features]
+ };
+
+ return new ImmutableFeatureCollection(updatedFeatureCollection);
+ }
+
+ deleteFeature(featureIndex: number) {
+ return this.deleteFeatures([featureIndex]);
+ }
+
+ deleteFeatures(featureIndexes: number[]) {
+ const features = [...this.featureCollection.features];
+ featureIndexes.sort();
+ for (let i = featureIndexes.length - 1; i >= 0; i--) {
+ const featureIndex = featureIndexes[i];
+ if (featureIndex >= 0 && featureIndex < features.length) {
+ features.splice(featureIndex, 1);
+ }
+ }
+
const updatedFeatureCollection = {
...this.featureCollection,
- features: [...this.featureCollection.features, feature]
+ features
};
return new ImmutableFeatureCollection(updatedFeatureCollection);
diff --git a/modules/edit-modes/test/immutable-feature-collection.test.js b/modules/edit-modes/test/immutable-feature-collection.test.js
index 10fd1c344..78a12ff8d 100644
--- a/modules/edit-modes/test/immutable-feature-collection.test.js
+++ b/modules/edit-modes/test/immutable-feature-collection.test.js
@@ -519,6 +519,137 @@ describe('addFeature()', () => {
});
});
+describe('addFeatures()', () => {
+ it(`doesn't mutate original`, () => {
+ const features = new ImmutableFeatureCollection({
+ type: 'FeatureCollection',
+ features: []
+ });
+ features.addFeatures([multiPointFeature, pointFeature]);
+
+ expect(features.getObject().features.length).toEqual(0);
+ });
+
+ it('adds features to empty array', () => {
+ const features = new ImmutableFeatureCollection({
+ type: 'FeatureCollection',
+ features: []
+ });
+ const actualFeatures = features.addFeatures([multiPointFeature, pointFeature]).getObject();
+
+ const expectedFeatures = {
+ type: 'FeatureCollection',
+ features: [multiPointFeature, pointFeature]
+ };
+
+ expect(actualFeatures).toEqual(expectedFeatures);
+ });
+
+ it('adds features to end of array', () => {
+ const features = new ImmutableFeatureCollection({
+ type: 'FeatureCollection',
+ features: [multiPointFeature]
+ });
+ const actualFeatures = features.addFeatures([multiLineStringFeature]).getObject();
+
+ const expectedFeatures = {
+ type: 'FeatureCollection',
+ features: [multiPointFeature, multiLineStringFeature]
+ };
+
+ expect(actualFeatures).toEqual(expectedFeatures);
+ });
+});
+
+describe('deleteFeature()', () => {
+ it(`Do nothing when empty array`, () => {
+ const features = new ImmutableFeatureCollection({
+ type: 'FeatureCollection',
+ features: []
+ });
+ features.deleteFeature(0);
+
+ expect(features.getObject().features.length).toEqual(0);
+ });
+
+ it(`doesn't mutate original`, () => {
+ const features = new ImmutableFeatureCollection({
+ type: 'FeatureCollection',
+ features: [multiPointFeature]
+ });
+ features.deleteFeature(0);
+
+ expect(features.getObject().features.length).toEqual(1);
+ });
+
+ it('delete feature', () => {
+ const features = new ImmutableFeatureCollection({
+ type: 'FeatureCollection',
+ features: [multiPointFeature, multiLineStringFeature]
+ });
+ const actualFeatures = features.deleteFeature(1).getObject();
+
+ const expectedFeatures = {
+ type: 'FeatureCollection',
+ features: [multiPointFeature]
+ };
+
+ expect(actualFeatures).toEqual(expectedFeatures);
+ });
+});
+
+describe('deleteFeatures()', () => {
+ it(`Do nothing when empty array`, () => {
+ const features = new ImmutableFeatureCollection({
+ type: 'FeatureCollection',
+ features: []
+ });
+ features.deleteFeatures([0, 1]);
+
+ expect(features.getObject().features.length).toEqual(0);
+ });
+
+ it(`doesn't mutate original`, () => {
+ const features = new ImmutableFeatureCollection({
+ type: 'FeatureCollection',
+ features: [multiPointFeature]
+ });
+ features.deleteFeatures([0]);
+
+ expect(features.getObject().features.length).toEqual(1);
+ });
+
+ it('delete single feature', () => {
+ const features = new ImmutableFeatureCollection({
+ type: 'FeatureCollection',
+ features: [multiPointFeature, multiLineStringFeature]
+ });
+ const actualFeatures = features.deleteFeatures([1]).getObject();
+
+ const expectedFeatures = {
+ type: 'FeatureCollection',
+ features: [multiPointFeature]
+ };
+
+ expect(actualFeatures).toEqual(expectedFeatures);
+ });
+
+ it('delete multiple features', () => {
+ const features = new ImmutableFeatureCollection({
+ type: 'FeatureCollection',
+ features: [multiPointFeature, multiLineStringFeature]
+ });
+ const actualFeatures = features.deleteFeatures([0, 1]).getObject();
+
+ const expectedFeatures = {
+ type: 'FeatureCollection',
+ features: []
+ };
+
+ expect(actualFeatures).toEqual(expectedFeatures);
+ });
+});
+
describe('replacePosition() with elevation', () => {
it('replaces position in Point', () => {
const elevatedPointFeature = {
diff --git a/modules/react-map-gl-draw/README.md b/modules/react-map-gl-draw/README.md
index ce7ad203a..8ee1edcb0 100644
--- a/modules/react-map-gl-draw/README.md
+++ b/modules/react-map-gl-draw/README.md
@@ -1,47 +1,37 @@
-[![docs](https://i.imgur.com/bRDL1oh.gif)](https://nebula.gl)
+# React Map GL Draw
-`react-map-gl-draw` is a drawing library tailored for [`react-map-gl`](https://github.com/uber/react-map-gl).
+`react-map-gl-draw` is a react based drawing library tailored for [`react-map-gl`](https://github.com/uber/react-map-gl).
-# Getting started
-
-## Running the example
-
-1. `git clone git@github.com:uber/nebula.gl.git`
-2. `cd nebula.gl`
-3. `yarn`
-4. `cd examples/react-map-gl-draw`
-5. `yarn`
-6. `export MapboxAccessToken=''`
-7. `yarn start-local`
-8. You can view/edit geometry.
-
-## Installation
-
-```
-yarn add react-map-gl-draw
-```
-
-## Options
+## Options
- `mode` (String, Optional)
- `EditorModes.READ_ONLY` - Not interactive. This is the default mode.
- - `EditorModes.SELECT_FEATURE` - Lets you select, delete, and drag features.
- - `EditorModes.EDIT_VERTEX` - Lets you select, delete, and drag vertices; and drag features.
+ - `EditorModes.SELECT` - Lets you select, delete, and drag features.
+ - `EditorModes.EDITTING` - Lets you select, delete, and drag vertices; and drag features.
- `EditorModes.DRAW_PATH` - Lets you draw a GeoJson `LineString` feature.
- `EditorModes.DRAW_POLYGON` - Lets you draw a GeoJson `Polygon` feature.
- `EditorModes.DRAW_POINT` - Lets you draw a GeoJson `Point` feature.
- `EditorModes.DRAW_RECTANGLE` - Lets you draw a `Rectangle` (represented as GeoJson `Polygon` feature).
-- `selectedFeatureId` (String, Optional) - id of the selected feature. `EditorModes` assigns a unique id to each feature which is stored in `feature.properties.id`.
-- `clickRadius` (Number, optional) - Radius to detect features around a hovered or clicked point. Default value is `0`
-
-- `onSelect` (Function, Required) - callback when a feature is selected. Receives an object containing `selectedFeatureId`.
-- `onUpdate` (Function, Required) - callback when anything is updated. Receives one argument `features` that is the updated list of GeoJSON features.
-
-Feature object structure:
-`react-map-gl-draw` is stateful component.
+- `features` (Feature[], Optional) - List of features in GeoJson format. If `features` are provided from users, then `react-map-gl-draw` respect the users' input, and therefore ignore any internal `features`. But if `features` are not provided, then `react-map-gl-draw` manages `features` internally, and users can access and manipulate the features by calling `getFeatures`, `addFeatures`, and `deleteFeatures`.
+- `selectedFeatureIndex` (String, Optional) - Index of the selected feature.
+- `clickRadius` (Number, Optional) - Radius to detect features around a hovered or clicked point. Default value is `0`
+
+- `onSelect` (Function, Optional) - callback when clicking a position under `SELECT` and `EDITTING` mode. Receives an object containing the following parameters
+ - `selectedFeature`: selected feature. `null` if clicked an empty space.
+ - `selectedFeatureIndex`: selected feature index.`null` if clicked an empty space.
+ - `editHandleIndex`: selected editHandle index. `null` if clicked an empty space.
+ - `screenCoords`: screen coordinates of the clicked position.
+ - `mapCoords`: map coordinates of the clicked position.
+
+- `onUpdate` (Function, Optional) - callback when anything is updated. Receives an object containing the following parameters
+ - `features` (Feature[]) - the updated list of GeoJSON features.
+ - `editType` (String) - `addFeature`, `addPosition`, `finishMovePosition`
+ - `editContext` (Array) - list of edit objects, depend on `editType`, each object may contain `featureIndexes`, `editHandleIndexes`, `screenCoords`, `mapCoords`.
+
+**Feature object structure:**
```js
{
- id, // an unique identified generated inside react-map-gl-draw library
+ id, // an unique identified generated inside react-map-gl-draw library
geometry: {
coordinates, // latitude longitude pairs of the geometry points
type // geojson type, one of `Point`, `LineString`, or `Polygon`
@@ -53,26 +43,31 @@ Feature object structure:
}
```
-### Styling related Options
-- `style` (Object, optional) - Customized css [style objects](https://reactjs.org/docs/dom-elements.html#style) apply to the editor. Default style includes width and height from current viewport.
-
-- `getFeatureStyle` (Function, Optional) : Object - A function to style a feature, function parameters are
- - `feature`: feature to style .
- - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`.
-
+### Styling related options
+- `featureStyle` (Object|Function, Optional) : Object - Either a [style objects](https://reactjs.org/docs/dom-elements.html#style) or a function to style a feature, function parameters are
+ - `feature`: feature to style.
+ - `index`: index of the feature.
+ - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`, `CLOSING`.
+
Returns is a map of [style objects](https://reactjs.org/docs/dom-elements.html#style) passed to SVG `path` elements.
-- `getEditHandleStyle` (Function, Optional) : Object - A function to style an `editHandle, function parameters are
+- `featureShape` (String|Function, Optional): if is a string, should be one of `rect` or `circle`. If is a function, will receive the following parameters
- `feature`: feature to style.
- - `index`: index of the editHandle vertex in the feature.
- - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`.
+ - `index`: index of the feature.
+ - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`, `CLOSING`.
+- `editHandleStyle` (Object|Function, Optional) : Object - Either a [style objects](https://reactjs.org/docs/dom-elements.html#style) or a function to style an `editHandle, function parameters are
+ - `feature`: feature to style.
+ - `index`: index of the editHandle vertex in the feature.
+ - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`, `CLOSING`.
+ - `shape`: shape resolved from `editHandleShape`.
+
Returns is a map of [style objects](https://reactjs.org/docs/dom-elements.html#style) passed to SVG `circle` or `rect` elements.
-- `getEditHandleShape` (String|Function, Optional): if is a string, should be one of `rect` or `circle`. If is a function, will receive the following parameters
+- `editHandleShape` (String|Function, Optional): if is a string, should be one of `rect` or `circle`. If is a function, will receive the following parameters
- `feature`: feature to style.
- `index`: index of the editHandle vertex in the feature.
- - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`.
+ - `state`: one of `SELECTED`, `HOVERED`, `INACTIVE`, `UNCOMMITTED`, `CLOSING`.
## Explanations
- `Feature`: any drawn shape, one of point, line, polygon or rectangle.
@@ -80,81 +75,73 @@ Returns is a map of [style objects](https://reactjs.org/docs/dom-elements.html#s
### State related concepts:
- `INACTIVE`: neither selected nor hovered, default state of a complete `feature` or `editHandle`.
-- `SELECTED`: being clicked or dragged.
+- `SELECTED`: being clicked or dragged.
- `HOVERED`: hovered over by the mouse pointer.
- `UNCOMMITTED`: in the middle of drawing, not yet added to the feature being edited.
+- `CLOSING`: closing a polygon.
### Styling based on `state`:
-![img](https://raw.githubusercontent.com/uber-common/deck.gl-data/master/nebula.gl/edit-handle.png)
+![img](https://raw.githubusercontent.com/uber-common/deck.gl-data/master/nebula.gl/react-map-gl-draw.png)
+
+As shown in the above image, for the feature currently being edited,
+- `featureStyle({feature, state: SELECTED})` will be applied to the committed parts of the feature. (Green strokes)
+- `editHandleStyle({state: SELECTED})` will be applied to the committed editHandle vertices. (Vertices with black stroke)
+- `featureStyle({feature, state: UNCOMMITTED})` will be applied to the uncommitted parts of the feature. (Gray stroke)
+- `editHandleStyle({state: UNCOMMITTED})` will be applied to the uncommitted editHandle vertex. (Gray vertex)
+
+## Methods
+
+##### `getFeatures`
+
+- Return a list of finished GeoJson features.
-As shown in the above image, for the feature currently being edited,
-- `getFeatureStyle({feature, state: SELECTED})` will be applied to the committed parts of the feature. (Green strokes)
-- `getEditHandleStyle({state: SELECTED})` will be applied to the committed editHandle vertices. (Vertices with black stroke)
-- `getFeatureStyle({feature, state: UNCOMMITTED})` will be applied to the uncommitted parts of the feature. (Gray stroke)
-- `getEditHandleStyle({state: UNCOMMITTED})` will be applied to the uncommitted editHandle vertex. (Gray vertex)
+##### `addFeatures` (Feature | Feature[])
+- Add a single or multiple GeoJson features to editor.
+
+##### `deleteDeatures` (Feature | Feature[])
+
+- Delete a single or multiple GeoJson features to editor.
## Code Example
```js
import React, { Component } from 'react';
-import MapGL, {_MapContext as MapContext} from 'react-map-gl';
-import MapGLDraw, { EditorModes } from 'react-map-gl-draw';
+import MapGL from 'react-map-gl';
+import { Editor, EditorModes } from 'react-map-gl-draw';
const MODES = [
- { id: EditorModes.EDIT_VERTEX, text: 'Select and Edit Feature'},
+ { id: EditorModes.EDITING, text: 'Select and Edit Feature'},
{ id: EditorModes.DRAW_POINT, text: 'Draw Point'},
{ id: EditorModes.DRAW_PATH, text: 'Draw Polyline'},
{ id: EditorModes.DRAW_POLYGON, text: 'Draw Polygon'},
{ id: EditorModes.DRAW_RECTANGLE, text: 'Draw Rectangle'}
];
-class App extends Component {
- constructor(props) {
- super(props);
- this.state = {
- viewport: {
- width: 800,
- height: 600,
- longitude: -122.45,
- latitude: 37.78,
- zoom: 14
- },
- selectedMode: EditorModes.READ_ONLY,
- features: [],
- selectedFeatureId: null
- };
- }
-
- componentDidMount() {
- // add features
- const initialFeatures = [{...}];
- this._mapRef.add(initialFeatures);
- }
-
- _updateViewport = (viewport) => {
- this.setState({viewport});
- }
-
- _onSelect = ({ selectedFeatureId }) => {
- this.setState({ selectedFeatureId });
- };
+const DEFAULT_VIEWPORT = {
+ width: 800,
+ height: 600,
+ longitude: -122.45,
+ latitude: 37.78,
+ zoom: 14
+};
- _onUpdate = features => {
- this.setState({
- features
- });
+class App extends Component {
+ state = {
+ // map
+ viewport: DEFAULT_VIEWPORT,
+ // editor
+ selectedMode: EditorModes.READ_ONLY
};
_switchMode = evt => {
- const selectedMode = evt.target.id === this.state.selectedMode ? EditorModes.READ_ONLY : evt.target.id;
+ const selectedMode = evt.target.id;
this.setState({
- selectedMode,
- selectedFeatureId: null
+ selectedMode: selectedMode === this.state.selectedMode ? null : selectedMode
});
};
- _renderControlPanel = () => {
+ _renderToolbar = () => {
return (