Add draw control example (#868)

This commit is contained in:
Xintong Xia 2019-08-18 17:15:30 -07:00 committed by GitHub
parent 515c575687
commit f3752f68d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 336 additions and 0 deletions

View File

@ -0,0 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Draw Polygon
Demonstrates how to use [`react-map-gl-draw`](https://github.com/uber/nebula.gl/tree/master/modules/react-map-gl-draw) to draw polygons with react-map-gl.
```
yarn
yarn start-local
```

View File

@ -0,0 +1,21 @@
body {
margin: 0;
background: #000;
}
#map {
width: 100vw;
height: 100vh;
}
.control-panel {
max-width: 320px;
background: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
padding: 12px 24px;
margin: 20px;
font-size: 13px;
line-height: 2;
color: #6b6b76;
text-transform: uppercase;
outline: none;
}

View File

@ -0,0 +1,17 @@
<!doctype html>
<html>
<head>
<meta charset='UTF-8' />
<title>react-map-gl Example</title>
<link rel="stylesheet" type="text/css" href="app.css" />
</head>
<body>
<div id="map"></div>
<!--use mapbox button icon css-->
<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.0.9/mapbox-gl-draw.css" type="text/css">
<script src='app.js'></script>
</body>
<script type="text/javascript">
App.renderToDom(document.getElementById('map'));
</script>
</html>

View File

@ -0,0 +1,27 @@
{
"scripts": {
"start": "webpack-dev-server --progress --hot --open",
"start-local": "webpack-dev-server --env.local --progress --hot --open"
},
"dependencies": {
"react": "^16.3.0",
"react-dom": "^16.3.0",
"react-map-gl": "^5.0.0",
"react-map-gl-draw": "^0.14.9",
"styled-components": "^4.3.2",
"@turf/area": "^6.0.1"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.0",
"webpack": "^4.20.0",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.0"
},
"resolutions": {
"@turf/difference": "6.0.1"
}
}

View File

@ -0,0 +1,123 @@
import React, {Component} from 'react';
import {render} from 'react-dom';
import MapGL from 'react-map-gl';
import {Editor, EditorModes} from 'react-map-gl-draw';
import ControlPanel from './control-panel';
import {getFeatureStyle, getEditHandleStyle} from './style';
const TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
viewport: {
longitude: -91.874,
latitude: 42.76,
zoom: 12
},
mode: EditorModes.READ_ONLY,
features: [],
selectedFeatureId: null
};
this._mapRef = null;
}
_updateViewport = viewport => {
this.setState({viewport});
};
_onSelect = ({selectedFeatureId}) => {
this.setState({selectedFeatureId});
};
_onDelete = () => {
const selectedIndex = this._getSelectedFeatureIndex();
if (selectedIndex >= 0) {
const newFeatures = [...this.state.features];
newFeatures.splice(selectedIndex, 1);
this.setState({features: newFeatures, selectedFeatureId: null});
}
};
_onUpdate = features => {
this.setState({
features,
mode: EditorModes.EDITING
});
};
_getSelectedFeatureIndex = () => {
const {selectedFeatureId} = this.state;
if (selectedFeatureId === null || selectedFeatureId === undefined) {
return -1;
}
return this.state.features.findIndex(f => f.properties.id === selectedFeatureId);
};
_renderDrawTools = () => {
// copy from mapbox
return (
<div className="mapboxgl-ctrl-top-right">
<div className="mapboxgl-ctrl-group mapboxgl-ctrl">
<button
className="mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_polygon"
title="Polygon tool (p)"
onClick={() => this.setState({mode: EditorModes.DRAW_POLYGON})}
/>
<button
className="mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_trash"
title="Delete"
onClick={this._onDelete}
/>
</div>
</div>
);
};
_renderControlPanel = () => {
const features = this.state.features;
let featureIndex = this._getSelectedFeatureIndex();
if (featureIndex < 0) {
featureIndex = features.length - 1;
}
const polygon = features && features.length ? features[featureIndex] : null;
return <ControlPanel polygon={polygon} />;
};
render() {
const {viewport, mode, selectedFeatureId, features} = this.state;
return (
<MapGL
{...viewport}
ref={_ => (this._mapRef = _)}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/satellite-v9"
mapboxApiAccessToken={TOKEN}
onViewportChange={this._updateViewport}
>
<Editor
style={{width: '100%', height: '100%'}}
clickRadius={12}
mode={mode}
features={features}
selectedFeatureId={selectedFeatureId}
onSelect={this._onSelect}
onUpdate={this._onUpdate}
getEditHandleShape={'circle'}
getFeatureStyle={getFeatureStyle}
getEditHandleStyle={getEditHandleStyle}
/>
{this._renderDrawTools()}
{this._renderControlPanel()}
</MapGL>
);
}
}
export function renderToDom(container) {
render(<App />, container);
}

View File

@ -0,0 +1,30 @@
import React, {PureComponent} from 'react';
import area from '@turf/area';
const defaultContainer = ({children}) => (
<div className="mapboxgl-ctrl-top-left control-panel">{children}</div>
);
export default class ControlPanel extends PureComponent {
render() {
const Container = this.props.containerComponent || defaultContainer;
const polygon = this.props.polygon;
const polygonArea = polygon && area(polygon);
return (
<Container>
<h3>Draw Polygon</h3>
{polygon && (
<p>
{polygonArea} <br />
square meters
</p>
)}
<a
href="https://github.com/uber/react-map-gl/tree/master/examples/draw-polygon"
target="_new"
>
View Code
</a>
</Container>
);
}
}

View File

@ -0,0 +1,49 @@
import {RenderStates} from 'react-map-gl-draw';
export function getEditHandleStyle({feature, state}) {
switch (state) {
case RenderStates.SELECTED:
case RenderStates.HOVERED:
case RenderStates.UNCOMMITTED:
return {
fill: 'rgb(251, 176, 59)',
fillOpacity: 1,
stroke: 'rgb(255, 255, 255)',
strokeWidth: 2,
r: 7
};
default:
return {
fill: 'rgb(251, 176, 59)',
fillOpacity: 1,
stroke: 'rgb(255, 255, 255)',
strokeWidth: 2,
r: 5
};
}
}
export function getFeatureStyle({feature, index, state}) {
switch (state) {
case RenderStates.SELECTED:
case RenderStates.HOVERED:
case RenderStates.UNCOMMITTED:
case RenderStates.CLOSING:
return {
stroke: 'rgb(251, 176, 59)',
strokeWidth: 2,
fill: 'rgb(251, 176, 59)',
fillOpacity: 0.3,
strokeDasharray: '4,2'
};
default:
return {
stroke: 'rgb(60, 178, 208)',
strokeWidth: 2,
fill: 'rgb(60, 178, 208)',
fillOpacity: 0.1
};
}
}

View File

@ -0,0 +1,51 @@
// NOTE: To use this example standalone (e.g. outside of repo)
// delete the local development overrides at the bottom of this file
// avoid destructuring for older Node version support
const resolve = require('path').resolve;
const webpack = require('webpack');
const BABEL_CONFIG = {
presets: ['@babel/env', '@babel/react'],
plugins: ['@babel/proposal-class-properties']
};
const config = {
mode: 'development',
entry: {
app: resolve('./src/app.js')
},
devServer: {
contentBase: [resolve(__dirname), resolve(__dirname, './static')]
},
output: {
library: 'App'
},
module: {
rules: [
{
// Compile ES2015 using babel
test: /\.js$/,
include: [resolve('.')],
exclude: [/node_modules/],
use: [
{
loader: 'babel-loader',
options: BABEL_CONFIG
}
]
}
]
},
// Optional: Enables reading mapbox token from environment variable
plugins: [new webpack.EnvironmentPlugin(['MapboxAccessToken'])]
};
// Enables bundling against src in this repo rather than the installed version
module.exports = env =>
env && env.local ? require('../webpack.config.local')(config)(env) : config;

View File

@ -18,6 +18,7 @@ import Layers from '../../layers/src/app';
import ViewportAnimation from '../../viewport-animation/src/app';
import ZoomToBounds from '../../zoom-to-bounds/src/app';
import Heatmap from '../../heatmap/src/app';
import DrawPolygon from '../../draw-polygon/src/app';
export const BASIC_EXAMPLES = 'basicExamples';
export const STANDALONE_EXAMPLES = 'standalonExamples';
@ -85,6 +86,11 @@ export const standaloneExamples = [
path: 'heatmap',
name: 'Heatmap',
component: Heatmap
},
{
path: 'drawPolygon',
name: 'DrawPolygon',
component: DrawPolygon
}
];