Migrate examples to functional components (#1273)

This commit is contained in:
Xiaoji Chen 2020-12-28 11:57:50 -08:00 committed by GitHub
parent da2158c952
commit e7f1cc61fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 1417 additions and 1859 deletions

View File

@ -19,11 +19,10 @@
// THE SOFTWARE.
/* global document */
import * as ReactDOM from 'react-dom';
import * as React from 'react';
import {Component} from 'react';
import {useState} from 'react';
import {render} from 'react-dom';
import MapGL from 'react-map-gl';
import Immutable from 'immutable';
import ScatterplotOverlay from './scatterplot-overlay';
import ChoroplethOverlay from './choropleth-overlay';
@ -33,59 +32,51 @@ import CITIES from '../../.data/cities.json';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
const ZIPCODES = Immutable.fromJS(ZIPCODES_SF.features).map(f =>
f.setIn(['properties', 'value'], Math.random() * 1000)
);
const ZIPCODES = ZIPCODES_SF.features.map(f => {
f.properties.value = Math.random() * 1000;
return f;
});
const CITY_LOCATIONS = Immutable.fromJS(CITIES.map(c => [c.longitude, c.latitude]));
const CITY_LOCATIONS = CITIES.map(c => [c.longitude, c.latitude]);
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
viewport: {
latitude: 37.785164,
longitude: -122.41669,
zoom: 8,
bearing: 0,
pitch: 0
}
};
}
export default function App() {
const [viewport, setViewport] = useState({
latitude: 37.785164,
longitude: -122.41669,
zoom: 8,
bearing: 0,
pitch: 0
});
render() {
const {viewport} = this.state;
return (
<MapGL
{...viewport}
width="100vw"
height="100vh"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={setViewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
<ChoroplethOverlay
key="choropleth"
globalOpacity={0.8}
colorDomain={[0, 500, 1000]}
colorRange={['#31a354', '#addd8e', '#f7fcb9']}
renderWhileDragging={false}
features={ZIPCODES}
/>
return (
<MapGL
{...viewport}
width="100vw"
height="100vh"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={v => this.setState({viewport: v})}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
<ChoroplethOverlay
key="choropleth"
globalOpacity={0.8}
colorDomain={[0, 500, 1000]}
colorRange={['#31a354', '#addd8e', '#f7fcb9']}
renderWhileDragging={false}
features={ZIPCODES}
/>
<ScatterplotOverlay
key="scatterplot"
locations={CITY_LOCATIONS}
dotRadius={10}
globalOpacity={0.8}
compositeOperation="lighter"
dotFill="#00a8fe"
renderWhileDragging={true}
/>
</MapGL>
);
}
<ScatterplotOverlay
key="scatterplot"
locations={CITY_LOCATIONS}
dotRadius={10}
globalOpacity={0.8}
compositeOperation="lighter"
dotFill="#00a8fe"
renderWhileDragging={true}
/>
</MapGL>
);
}
ReactDOM.render(<App />, document.body.appendChild(document.createElement('div')));
render(<App />, document.body.appendChild(document.createElement('div')));

View File

@ -18,12 +18,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import * as React from 'react';
import {PureComponent} from 'react';
import {useCallback} from 'react';
import PropTypes from 'prop-types';
import {extent} from 'd3-array';
import {scaleLinear} from 'd3-scale';
import {geoPath, geoTransform} from 'd3-geo';
import Immutable from 'immutable';
import {CanvasOverlay} from 'react-map-gl';
@ -33,7 +32,7 @@ const propTypes = {
/**
* An Immutable List of feature objects.
*/
features: PropTypes.instanceOf(Immutable.List),
features: PropTypes.array.isRequired,
/* eslint-disable react/forbid-prop-types */
colorDomain: PropTypes.array,
colorRange: PropTypes.array.isRequired,
@ -45,11 +44,29 @@ const defaultProps = {
globalOpacity: 1,
colorDomain: null,
colorRange: ['#FFFFFF', '#1FBAD6'],
valueAccessor: feature => feature.get('properties').get('value')
valueAccessor: feature => feature.properties.value
};
export default class ChoroplethOverlay extends PureComponent {
_redraw = ({width, height, ctx, isDragging, project, unproject}) => {
function drawFeatures(ctx, path, props) {
const {features} = props;
const colorDomain = props.colorDomain || extent(features, props.valueAccessor);
const colorScale = scaleLinear().domain(colorDomain).range(props.colorRange).clamp(true);
for (const feature of features) {
ctx.beginPath();
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
ctx.lineWidth = '1';
ctx.fillStyle = colorScale(props.valueAccessor(feature));
const geometry = feature.geometry;
path(geometry);
ctx.fill();
ctx.stroke();
}
}
export default function ChoroplethOverlay(props) {
const redraw = useCallback(({width, height, ctx, isDragging, project, unproject}) => {
ctx.clearRect(0, 0, width, height);
function projectPoint(lon, lat) {
@ -59,41 +76,14 @@ export default class ChoroplethOverlay extends PureComponent {
/* eslint-enable no-invalid-this */
}
if (this.props.renderWhileDragging || !isDragging) {
if (props.renderWhileDragging || !isDragging) {
const transform = geoTransform({point: projectPoint});
const path = geoPath().projection(transform).context(ctx);
this._drawFeatures(ctx, path);
drawFeatures(ctx, path, props);
}
};
}, []);
_drawFeatures(ctx, path) {
const {features} = this.props;
if (!features) {
return;
}
const colorDomain =
this.props.colorDomain || extent(features.toArray(), this.props.valueAccessor);
const colorScale = scaleLinear().domain(colorDomain).range(this.props.colorRange).clamp(true);
for (const feature of features) {
ctx.beginPath();
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
ctx.lineWidth = '1';
ctx.fillStyle = colorScale(this.props.valueAccessor(feature));
const geometry = feature.get('geometry');
path({
type: geometry.get('type'),
coordinates: geometry.get('coordinates').toJS()
});
ctx.fill();
ctx.stroke();
}
}
render() {
return <CanvasOverlay redraw={this._redraw} />;
}
return <CanvasOverlay redraw={redraw} />;
}
ChoroplethOverlay.propTypes = propTypes;

View File

@ -21,10 +21,10 @@ const ChoroplethOverlay = require('./choropleth-overlay');
## Props
#### features
An [ImmutableJS](https://facebook.github.io/immutable-js/) [List](https://facebook.github.io/immutable-js/docs/#/List) of GeoJSON features.
An array of GeoJSON features.
```js
const features = Immutable.fromJS([
const features = [
{
type: 'Feature',
geometry: {
@ -33,7 +33,7 @@ const features = Immutable.fromJS([
}
},
...
]);
];
```
#### valueAccessor

View File

@ -18,10 +18,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import * as React from 'react';
import {PureComponent} from 'react';
import {useCallback} from 'react';
import PropTypes from 'prop-types';
import Immutable from 'immutable';
import {CanvasOverlay} from 'react-map-gl';
function round(x, n) {
@ -30,7 +29,7 @@ function round(x, n) {
}
const propTypes = {
locations: PropTypes.instanceOf(Immutable.List).isRequired,
locations: PropTypes.array.isRequired,
lngLatAccessor: PropTypes.func,
renderWhileDragging: PropTypes.bool,
globalOpacity: PropTypes.number,
@ -40,7 +39,7 @@ const propTypes = {
};
const defaultProps = {
lngLatAccessor: location => [location.get(0), location.get(1)],
lngLatAccessor: location => location,
renderWhileDragging: true,
dotRadius: 4,
dotFill: '#1FBAD6',
@ -49,9 +48,9 @@ const defaultProps = {
compositeOperation: 'source-over'
};
export default class ScatterplotOverlay extends PureComponent {
export default function ScatterplotOverlay(props) {
/* eslint-disable max-statements */
_redraw = ({width, height, ctx, isDragging, project, unproject}) => {
const redraw = useCallback(({width, height, ctx, isDragging, project, unproject}) => {
const {
dotRadius,
dotFill,
@ -59,12 +58,12 @@ export default class ScatterplotOverlay extends PureComponent {
renderWhileDragging,
locations,
lngLatAccessor
} = this.props;
} = props;
ctx.clearRect(0, 0, width, height);
ctx.globalCompositeOperation = compositeOperation;
if ((renderWhileDragging || !isDragging) && locations) {
if (renderWhileDragging || !isDragging) {
for (const location of locations) {
const pixel = project(lngLatAccessor(location));
const pixelRounded = [round(pixel[0], 1), round(pixel[1], 1)];
@ -81,14 +80,11 @@ export default class ScatterplotOverlay extends PureComponent {
}
}
}
};
}, []);
/* eslint-enable max-statements */
render() {
return <CanvasOverlay redraw={this._redraw} />;
}
return <CanvasOverlay redraw={redraw} />;
}
ScatterplotOverlay.displayName = 'ScatterplotOverlay';
ScatterplotOverlay.propTypes = propTypes;
ScatterplotOverlay.defaultProps = defaultProps;

View File

@ -21,15 +21,15 @@ const ScatterplotOverlay = require('./scatterplot-overlay');
## Props
#### locations
An [ImmutableJS](https://facebook.github.io/immutable-js/) [List](https://facebook.github.io/immutable-js/docs/#/List) of points.
An array of points.
```js
const locations = Immutable.fromJS([
const locations = [
[-122.39851786165565, 37.78736425435588],
[-122.40015469418074, 37.78531678199267],
[-122.4124101516789, 37.80051001607987],
// ...
]);
];
```
#### lngLatAccessor

View File

@ -1,9 +1,14 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Supercluster
# Example: Supercluster
This app reproduces Mapbox's [Create and style clusters](https://docs.mapbox.com/mapbox-gl-js/example/cluster/) example.
This example showcases how to visualize points as clusters.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import {Component} from 'react';
import {useState, useRef} from 'react';
import {render} from 'react-dom';
import MapGL, {Source, Layer} from 'react-map-gl';
@ -8,34 +8,29 @@ import {clusterLayer, clusterCountLayer, unclusteredPointLayer} from './layers';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
state = {
viewport: {
latitude: 40.67,
longitude: -103.59,
zoom: 3,
bearing: 0,
pitch: 0
}
};
export default function App() {
const [viewport, setViewport] = useState({
latitude: 40.67,
longitude: -103.59,
zoom: 3,
bearing: 0,
pitch: 0
});
const mapRef = useRef(null);
_sourceRef = React.createRef();
_onViewportChange = viewport => this.setState({viewport});
_onClick = event => {
const onClick = event => {
const feature = event.features[0];
const clusterId = feature.properties.cluster_id;
const mapboxSource = this._sourceRef.current.getSource();
const mapboxSource = mapRef.current.getMap().getSource('earthquakes');
mapboxSource.getClusterExpansionZoom(clusterId, (err, zoom) => {
if (err) {
return;
}
this._onViewportChange({
...this.state.viewport,
setViewport({
...viewport,
longitude: feature.geometry.coordinates[0],
latitude: feature.geometry.coordinates[1],
zoom,
@ -44,36 +39,35 @@ export default class App extends Component {
});
};
render() {
const {viewport} = this.state;
return (
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={this._onViewportChange}
onViewportChange={setViewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
interactiveLayerIds={[clusterLayer.id]}
onClick={this._onClick}
onClick={onClick}
ref={mapRef}
>
<Source
id="earthquakes"
type="geojson"
data="https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"
cluster={true}
clusterMaxZoom={14}
clusterRadius={50}
ref={this._sourceRef}
>
<Layer {...clusterLayer} />
<Layer {...clusterCountLayer} />
<Layer {...unclusteredPointLayer} />
</Source>
<ControlPanel containerComponent={this.props.containerComponent} />
</MapGL>
);
}
<ControlPanel />
</>
);
}
export function renderToDom(container) {

View File

@ -1,21 +1,20 @@
import * as React from 'react';
import {PureComponent} from 'react';
export default class ControlPanel extends PureComponent {
render() {
return (
<div className="control-panel">
<h3>Create and Style Clusters</h3>
<p>Use Mapbox GL JS' built-in functions to visualize points as clusters.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/clusters"
target="_new"
>
View Code
</a>
</div>
function ControlPanel() {
return (
<div className="control-panel">
<h3>Create and Style Clusters</h3>
<p>Use Mapbox GL JS' built-in functions to visualize points as clusters.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/clusters"
target="_new"
>
View Code
</a>
</div>
);
}
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,12 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Controls
# Example: Controls
Demonstrates how various control components can be used with react-map-gl.
```
npm install
npm start
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import {Component} from 'react';
import {useState} from 'react';
import {render} from 'react-dom';
import MapGL, {
Popup,
@ -45,63 +45,40 @@ const scaleControlStyle = {
padding: '10px'
};
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
viewport: {
latitude: 37.785164,
longitude: -100,
zoom: 3.5,
bearing: 0,
pitch: 0
},
popupInfo: null
};
}
export default function App() {
const [viewport, setViewport] = useState({
latitude: 40,
longitude: -100,
zoom: 3.5,
bearing: 0,
pitch: 0
});
const [popupInfo, setPopupInfo] = useState(null);
_updateViewport = viewport => {
this.setState({viewport});
};
_onClickMarker = city => {
this.setState({popupInfo: city});
};
_renderPopup() {
const {popupInfo} = this.state;
return (
popupInfo && (
<Popup
tipSize={5}
anchor="top"
longitude={popupInfo.longitude}
latitude={popupInfo.latitude}
closeOnClick={false}
onClose={() => this.setState({popupInfo: null})}
>
<CityInfo info={popupInfo} />
</Popup>
)
);
}
render() {
const {viewport} = this.state;
return (
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={this._updateViewport}
onViewportChange={setViewport}
mapboxApiAccessToken={TOKEN}
>
<Pins data={CITIES} onClick={this._onClickMarker} />
<Pins data={CITIES} onClick={setPopupInfo} />
{this._renderPopup()}
{popupInfo && (
<Popup
tipSize={5}
anchor="top"
longitude={popupInfo.longitude}
latitude={popupInfo.latitude}
closeOnClick={false}
onClose={setPopupInfo}
>
<CityInfo info={popupInfo} />
</Popup>
)}
<div style={geolocateStyle}>
<GeolocateControl />
@ -115,11 +92,11 @@ export default class App extends Component {
<div style={scaleControlStyle}>
<ScaleControl />
</div>
<ControlPanel containerComponent={this.props.containerComponent} />
</MapGL>
);
}
<ControlPanel />
</>
);
}
export function renderToDom(container) {

View File

@ -1,24 +1,23 @@
import * as React from 'react';
import {PureComponent} from 'react';
export default class CityInfo extends PureComponent {
render() {
const {info} = this.props;
const displayName = `${info.city}, ${info.state}`;
function CityInfo(props) {
const {info} = props;
const displayName = `${info.city}, ${info.state}`;
return (
return (
<div>
<div>
<div>
{displayName} |{' '}
<a
target="_new"
href={`http://en.wikipedia.org/w/index.php?title=Special:Search&search=${displayName}`}
>
Wikipedia
</a>
</div>
<img width={240} src={info.image} />
{displayName} |{' '}
<a
target="_new"
href={`http://en.wikipedia.org/w/index.php?title=Special:Search&search=${displayName}`}
>
Wikipedia
</a>
</div>
);
}
<img width={240} src={info.image} />
</div>
);
}
export default React.memo(CityInfo);

View File

@ -1,30 +1,29 @@
import * as React from 'react';
import {PureComponent} from 'react';
export default class ControlPanel extends PureComponent {
render() {
return (
<div className="control-panel">
<h3>Marker, Popup, NavigationControl and FullscreenControl </h3>
<p>
Map showing top 20 most populated cities of the United States. Click on a marker to learn
more.
</p>
<p>
Data source:{' '}
<a href="https://en.wikipedia.org/wiki/List_of_United_States_cities_by_population">
Wikipedia
</a>
</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/controls"
target="_new"
>
View Code
</a>
</div>
function ControlPanel() {
return (
<div className="control-panel">
<h3>Marker, Popup, NavigationControl and FullscreenControl </h3>
<p>
Map showing top 20 most populated cities of the United States. Click on a marker to learn
more.
</p>
<p>
Data source:{' '}
<a href="https://en.wikipedia.org/wiki/List_of_United_States_cities_by_population">
Wikipedia
</a>
</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/controls"
target="_new"
>
View Code
</a>
</div>
);
}
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,5 +1,4 @@
import * as React from 'react';
import {PureComponent} from 'react';
import {Marker} from 'react-map-gl';
const ICON = `M20.2,15.7L20.2,15.7c1.1-1.6,1.8-3.6,1.8-5.7c0-5.6-4.5-10-10-10S2,4.5,2,10c0,2,0.6,3.9,1.6,5.4c0,0.1,0.1,0.2,0.2,0.3
@ -9,26 +8,26 @@ const ICON = `M20.2,15.7L20.2,15.7c1.1-1.6,1.8-3.6,1.8-5.7c0-5.6-4.5-10-10-10S2,
const SIZE = 20;
// Important for perf: the markers never change, avoid rerender when the map viewport changes
export default class Pins extends PureComponent {
render() {
const {data, onClick} = this.props;
function Pins(props) {
const {data, onClick} = props;
return data.map((city, index) => (
<Marker key={`marker-${index}`} longitude={city.longitude} latitude={city.latitude}>
<svg
height={SIZE}
viewBox="0 0 24 24"
style={{
cursor: 'pointer',
fill: '#d00',
stroke: 'none',
transform: `translate(${-SIZE / 2}px,${-SIZE}px)`
}}
onClick={() => onClick(city)}
>
<path d={ICON} />
</svg>
</Marker>
));
}
return data.map((city, index) => (
<Marker key={`marker-${index}`} longitude={city.longitude} latitude={city.latitude}>
<svg
height={SIZE}
viewBox="0 0 24 24"
style={{
cursor: 'pointer',
fill: '#d00',
stroke: 'none',
transform: `translate(${-SIZE / 2}px,${-SIZE}px)`
}}
onClick={() => onClick(city)}
>
<path d={ICON} />
</svg>
</Marker>
));
}
export default React.memo(Pins);

View File

@ -1,7 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Custom Controller
# Example: Custom Controller
This example showcases how to extend the default controller to support custom interactivity.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,6 +1,6 @@
/* global window */
import * as React from 'react';
import {Component} from 'react';
import {useState, useCallback} from 'react';
import {render} from 'react-dom';
import MapGL from 'react-map-gl';
@ -10,34 +10,26 @@ const customController = new MapController();
const MAPBOX_TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
state = {
viewport: {
latitude: 37.773,
longitude: -122.481,
zoom: 15.5,
bearing: 0,
pitch: 0
},
settings: {
invertZoom: false,
invertPan: false,
longPress: false
}
};
export default function App() {
const [viewport, setViewport] = useState({
longitude: -122.45,
latitude: 37.78,
zoom: 15.5,
bearing: 0,
pitch: 0
});
const [settings, setSettings] = useState({
invertZoom: false,
invertPan: false,
longPress: false
});
_onViewportChange = viewport => this.setState({viewport});
const onSettingsChange = useCallback((name, value) => {
setSettings(s => ({...s, [name]: value}));
}, []);
_onSettingsChange = (name, value) => {
this.setState({
settings: {...this.state.settings, [name]: value}
});
};
render() {
const {viewport, settings} = this.state;
return (
return (
<>
<MapGL
{...viewport}
width="100%"
@ -47,13 +39,12 @@ export default class App extends Component {
invertZoom={settings.invertZoom}
invertPan={settings.invertPan}
onPress={settings.longPress ? () => window.alert('pressed') : null} // eslint-disable-line no-alert
onViewportChange={this._onViewportChange}
onViewportChange={setViewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
<ControlPanel settings={settings} onChange={this._onSettingsChange} />
</MapGL>
);
}
/>
<ControlPanel settings={settings} onChange={onSettingsChange} />
</>
);
}
export function renderToDom(container) {

View File

@ -1,47 +1,42 @@
import * as React from 'react';
import {PureComponent} from 'react';
const camelPattern = /(^|[A-Z])[a-z]*/g;
export default class ControlPanel extends PureComponent {
_formatSettingName(name) {
return name.match(camelPattern).join(' ');
}
_renderCheckbox(name, value) {
return (
<div key={name} className="input">
<label>{this._formatSettingName(name)}</label>
<input
type="checkbox"
checked={value}
onChange={evt => this.props.onChange(name, evt.target.checked)}
/>
</div>
);
}
render() {
const {settings} = this.props;
return (
<div className="control-panel">
<h3>Custom Controller</h3>
<p>Override default event handling logic.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/custom-controller"
target="_new"
>
View Code
</a>
</div>
<hr />
{this._renderCheckbox('invertZoom', settings.invertZoom)}
{this._renderCheckbox('invertPan', settings.invertPan)}
{this._renderCheckbox('longPress', settings.longPress)}
</div>
);
}
function formatSettingName(name) {
return name.match(camelPattern).join(' ');
}
function Checkbox({name, value, onChange}) {
return (
<div key={name} className="input">
<label>{formatSettingName(name)}</label>
<input type="checkbox" checked={value} onChange={evt => onChange(name, evt.target.checked)} />
</div>
);
}
function ControlPanel(props) {
const {settings, onChange} = props;
return (
<div className="control-panel">
<h3>Custom Controller</h3>
<p>Override default event handling logic.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/custom-controller"
target="_new"
>
View Code
</a>
</div>
<hr />
<Checkbox name="invertZoom" value={settings.invertZoom} onChange={onChange} />
<Checkbox name="invertPan" value={settings.invertPan} onChange={onChange} />
<Checkbox name="longPress" value={settings.longPress} onChange={onChange} />
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,7 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
# Example: Custom Cursor
## Example: Custom Cursor
This example showcases how to dynamically change the cursor over the map based on interactivity.
This example showcases how to dynamically change the cursor over the map based on interactivity.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,6 +1,6 @@
/* global window */
import * as React from 'react';
import {Component} from 'react';
import {useState, useCallback} from 'react';
import {render} from 'react-dom';
import MapGL from 'react-map-gl';
import ControlPanel from './control-panel';
@ -8,62 +8,49 @@ import MAP_STYLE from '../../map-style-basic-v8.json';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
state = {
mapStyle: '',
viewport: {
latitude: 37.773,
longitude: -122.481,
zoom: 15.5,
bearing: 0,
pitch: 0
},
interactiveLayerIds: []
};
function getCursor({isHovering, isDragging}) {
return isDragging ? 'grabbing' : isHovering ? 'pointer' : 'default';
}
_onViewportChange = viewport => this.setState({viewport});
export default function App() {
const [viewport, setViewport] = useState({
longitude: -122.48,
latitude: 37.78,
zoom: 15.5,
bearing: 0,
pitch: 0
});
const [interactiveLayerIds, setInteractiveLayerIds] = useState([]);
_onInteractiveLayersChange = layerFilter => {
this.setState({
interactiveLayerIds: MAP_STYLE.layers.map(layer => layer.id).filter(layerFilter)
});
};
const onInteractiveLayersChange = useCallback(layerFilter => {
setInteractiveLayerIds(MAP_STYLE.layers.map(layer => layer.id).filter(layerFilter));
}, []);
_onClick = event => {
const onClick = useCallback(event => {
const feature = event.features && event.features[0];
if (feature) {
window.alert(`Clicked layer ${feature.layer.id}`); // eslint-disable-line no-alert
}
};
}, []);
_getCursor = ({isHovering, isDragging}) => {
return isHovering ? 'pointer' : 'default';
};
render() {
const {viewport, interactiveLayerIds} = this.state;
return (
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle={MAP_STYLE}
clickRadius={2}
onClick={this._onClick}
getCursor={this._getCursor}
onClick={onClick}
getCursor={getCursor}
interactiveLayerIds={interactiveLayerIds}
onViewportChange={this._onViewportChange}
onViewportChange={setViewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
<ControlPanel
containerComponent={this.props.containerComponent}
onChange={this._onInteractiveLayersChange}
/>
</MapGL>
);
}
/>
<ControlPanel onChange={onInteractiveLayersChange} />
</>
);
}
export function renderToDom(container) {

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import {PureComponent} from 'react';
import {useState, useEffect} from 'react';
// Layer id patterns by category
const layerSelector = {
@ -18,64 +18,51 @@ function getLayerFilter(categories, layerId) {
return false;
}
export default class StyleControls extends PureComponent {
state = {
categories: {
parks: true,
buildings: true,
roads: true,
labels: true
}
function Checkbox({name, value, onChange}) {
return (
<div key={name} className="input">
<label>{name}</label>
<input type="checkbox" checked={value} onChange={evt => onChange(name, evt.target.checked)} />
</div>
);
}
function StyleControls(props) {
const [categories, setCategories] = useState({
parks: true,
buildings: true,
roads: true,
labels: true
});
useEffect(() => {
const filter = layerId => getLayerFilter(categories, layerId);
props.onChange(filter);
}, [categories]);
const toggleLayer = (name, on) => {
setCategories({...categories, [name]: on});
};
componentDidMount() {
const filter = getLayerFilter.bind(null, this.state.categories);
this.props.onChange(filter);
}
_onToggleLayer(name, event) {
const categories = {
...this.state.categories,
[name]: event.target.checked
};
this.setState({categories});
const filter = getLayerFilter.bind(null, categories);
this.props.onChange(filter);
}
_renderLayerControl(name) {
const {categories} = this.state;
return (
<div key={name} className="input">
<label>{name}</label>
<input
type="checkbox"
checked={categories[name]}
onChange={this._onToggleLayer.bind(this, name)}
/>
return (
<div className="control-panel">
<h3>Custom Cursor</h3>
<p>Customize the cursor based on interactivity.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/custom-cursor"
target="_new"
>
View Code
</a>
</div>
);
}
render() {
return (
<div className="control-panel">
<h3>Custom Cursor</h3>
<p>Customize the cursor based on interactivity.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/custom-cursor"
target="_new"
>
View Code
</a>
</div>
<hr />
<p>Clickable layers</p>
{Object.keys(layerSelector).map(name => this._renderLayerControl(name))}
</div>
);
}
<hr />
<p>Clickable layers</p>
{Object.keys(layerSelector).map(name => (
<Checkbox key={name} name={name} value={categories[name]} onChange={toggleLayer} />
))}
</div>
);
}
export default React.memo(StyleControls);

View File

@ -0,0 +1,12 @@
# Example: DeckGL Overlay
This example demonstrates using [deck.gl](https://deck.gl) to render a data overlay on top of react-map-gl.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -5,7 +5,7 @@
"start-local": "webpack-dev-server --env.local --progress --hot --open"
},
"dependencies": {
"deck.gl": "^7.0.0",
"deck.gl": "^8.0.0",
"react": "^16.3.0",
"react-dom": "^16.3.0",
"react-map-gl": "^6.0.0"

View File

@ -1,60 +1,41 @@
import * as React from 'react';
import {Component} from 'react';
import {useState} from 'react';
import {render} from 'react-dom';
import DeckGL, {ArcLayer} from 'deck.gl';
import MapGL from 'react-map-gl';
const TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
viewport: {
longitude: -122.45,
latitude: 37.78,
zoom: 11,
bearing: 0,
pitch: 30
}
};
}
export default function App() {
const [viewport, setViewport] = useState({
longitude: -122.45,
latitude: 37.75,
zoom: 11,
bearing: 0,
pitch: 60
});
_onViewportChange = viewport => {
this.setState({viewport});
};
const arcLayer = new ArcLayer({
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/bart-segments.json',
getSourcePosition: d => d.from.coordinates,
getTargetPosition: d => d.to.coordinates,
getSourceColor: [255, 200, 0],
getTargetColor: [0, 140, 255],
getWidth: 12
});
render() {
const {viewport} = this.state;
return (
<MapGL
{...viewport}
width="100%"
height="100%"
maxPitch={85}
onViewportChange={this._onViewportChange}
mapboxApiAccessToken={TOKEN}
>
<DeckGL
viewState={viewport}
layers={[
new ArcLayer({
data: [
{
sourcePosition: [-122.41669, 37.7853],
targetPosition: [-122.45669, 37.781]
}
],
strokeWidth: 4,
getSourceColor: x => [0, 0, 255],
getTargetColor: x => [0, 255, 0]
})
]}
/>
</MapGL>
);
}
return (
<MapGL
{...viewport}
width="100%"
height="100%"
maxPitch={85}
onViewportChange={setViewport}
mapboxApiAccessToken={TOKEN}
>
<DeckGL viewState={viewport} layers={[arcLayer]} />
</MapGL>
);
}
export function renderToDom(container) {

View File

@ -1,12 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Draggable Marker
# Example: Draggable Marker
Demonstrates how Marker component can be dragged with react-map-gl.
```
npm install
npm start
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import {Component} from 'react';
import {useState, useCallback} from 'react';
import {render} from 'react-dom';
import MapGL, {Marker, NavigationControl} from 'react-map-gl';
@ -15,66 +15,44 @@ const navStyle = {
padding: '10px'
};
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
viewport: {
latitude: 37.785164,
longitude: -100,
zoom: 3.5,
bearing: 0,
pitch: 0
},
marker: {
latitude: 37.785164,
longitude: -100
},
events: {}
};
}
export default function App() {
const [viewport, setViewport] = useState({
latitude: 40,
longitude: -100,
zoom: 3.5,
bearing: 0,
pitch: 0
});
const [marker, setMarker] = useState({
latitude: 40,
longitude: -100
});
const [events, logEvents] = useState({});
_updateViewport = viewport => {
this.setState({viewport});
};
const onMarkerDragStart = useCallback(event => {
logEvents(_events => ({..._events, onDragStart: event.lngLat}));
}, []);
_logDragEvent(name, event) {
this.setState({
events: {
...this.state.events,
[name]: event.lngLat
}
const onMarkerDrag = useCallback(event => {
logEvents(_events => ({..._events, onDrag: event.lngLat}));
}, []);
const onMarkerDragEnd = useCallback(event => {
logEvents(_events => ({..._events, onDragEnd: event.lngLat}));
setMarker({
longitude: event.lngLat[0],
latitude: event.lngLat[1]
});
}
}, []);
_onMarkerDragStart = event => {
this._logDragEvent('onDragStart', event);
};
_onMarkerDrag = event => {
this._logDragEvent('onDrag', event);
};
_onMarkerDragEnd = event => {
this._logDragEvent('onDragEnd', event);
this.setState({
marker: {
longitude: event.lngLat[0],
latitude: event.lngLat[1]
}
});
};
render() {
const {viewport, marker} = this.state;
return (
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={this._updateViewport}
onViewportChange={setViewport}
mapboxApiAccessToken={TOKEN}
>
<Marker
@ -83,24 +61,20 @@ export default class App extends Component {
offsetTop={-20}
offsetLeft={-10}
draggable
onDragStart={this._onMarkerDragStart}
onDrag={this._onMarkerDrag}
onDragEnd={this._onMarkerDragEnd}
onDragStart={onMarkerDragStart}
onDrag={onMarkerDrag}
onDragEnd={onMarkerDragEnd}
>
<Pin size={20} />
</Marker>
<div className="nav" style={navStyle}>
<NavigationControl onViewportChange={this._updateViewport} />
<NavigationControl />
</div>
<ControlPanel
containerComponent={this.props.containerComponent}
events={this.state.events}
/>
</MapGL>
);
}
<ControlPanel events={events} />
</>
);
}
export function renderToDom(container) {

View File

@ -1,5 +1,4 @@
import * as React from 'react';
import {PureComponent} from 'react';
const eventNames = ['onDragStart', 'onDrag', 'onDragEnd'];
@ -7,32 +6,32 @@ function round5(value) {
return (Math.round(value * 1e5) / 1e5).toFixed(5);
}
export default class ControlPanel extends PureComponent {
renderEvent = eventName => {
const {events = {}} = this.props;
const lngLat = events[eventName];
return (
<div key={eventName}>
<strong>{eventName}:</strong> {lngLat ? lngLat.map(round5).join(', ') : <em>null</em>}
function ControlPanel(props) {
return (
<div className="control-panel">
<h3>Draggable Marker</h3>
<p>Try dragging the marker to another location.</p>
<div>
{eventNames.map(eventName => {
const {events = {}} = props;
const lngLat = events[eventName];
return (
<div key={eventName}>
<strong>{eventName}:</strong> {lngLat ? lngLat.map(round5).join(', ') : <em>null</em>}
</div>
);
})}
</div>
);
};
render() {
return (
<div className="control-panel">
<h3>Draggable Marker</h3>
<p>Try dragging the marker to another location.</p>
<div>{eventNames.map(this.renderEvent)}</div>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/draggable-markers"
target="_new"
>
View Code
</a>
</div>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/draggable-markers"
target="_new"
>
View Code
</a>
</div>
);
}
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,5 +1,4 @@
import * as React from 'react';
import {PureComponent} from 'react';
const ICON = `M20.2,15.7L20.2,15.7c1.1-1.6,1.8-3.6,1.8-5.7c0-5.6-4.5-10-10-10S2,4.5,2,10c0,2,0.6,3.9,1.6,5.4c0,0.1,0.1,0.2,0.2,0.3
c0,0,0.1,0.1,0.1,0.2c0.2,0.3,0.4,0.6,0.7,0.9c2.6,3.1,7.4,7.6,7.4,7.6s4.8-4.5,7.4-7.5c0.2-0.3,0.5-0.6,0.7-0.9
@ -10,14 +9,14 @@ const pinStyle = {
stroke: 'none'
};
export default class Pin extends PureComponent {
render() {
const {size = 20} = this.props;
function Pin(props) {
const {size = 20} = props;
return (
<svg height={size} viewBox="0 0 24 24" style={pinStyle}>
<path d={ICON} />
</svg>
);
}
return (
<svg height={size} viewBox="0 0 24 24" style={pinStyle}>
<path d={ICON} />
</svg>
);
}
export default React.memo(Pin);

View File

@ -1,12 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
# Example: Draw Polygon
## 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.
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.
## Usage
```
yarn
yarn start-local
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -8,7 +8,7 @@
"react": "^16.3.0",
"react-dom": "^16.3.0",
"react-map-gl": "^6.0.0",
"react-map-gl-draw": "^6.0.0",
"react-map-gl-draw": "^0.21.0",
"styled-components": "^4.3.2"
},
"devDependencies": {

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import {Component} from 'react';
import {useState, useRef, useCallback} from 'react';
import {render} from 'react-dom';
import MapGL from 'react-map-gl';
import {Editor, DrawPolygonMode, EditingMode} from 'react-map-gl-draw';
@ -9,101 +9,79 @@ import {getFeatureStyle, getEditHandleStyle} from './style';
const TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
constructor(props) {
super(props);
this._editorRef = null;
this.state = {
viewport: {
longitude: -91.874,
latitude: 42.76,
zoom: 12
},
mode: null,
selectedFeatureIndex: null
};
}
export default function App() {
const [viewport, setViewport] = useState({
longitude: -91.874,
latitude: 42.76,
zoom: 12
});
const [mode, setMode] = useState(null);
const [selectedFeatureIndex, setSelectedFeatureIndex] = useState(null);
const editorRef = useRef(null);
_updateViewport = viewport => {
this.setState({viewport});
};
const onSelect = useCallback(options => {
setSelectedFeatureIndex(options && options.selectedFeatureIndex);
}, []);
_onSelect = options => {
this.setState({selectedFeatureIndex: options && options.selectedFeatureIndex});
};
_onDelete = () => {
const selectedIndex = this.state.selectedFeatureIndex;
if (selectedIndex !== null && selectedIndex >= 0) {
this._editorRef.deleteFeatures(selectedIndex);
const onDelete = useCallback(() => {
if (selectedFeatureIndex !== null && selectedFeatureIndex >= 0) {
editorRef.current.deleteFeatures(selectedFeatureIndex);
}
};
}, [selectedFeatureIndex]);
_onUpdate = ({editType}) => {
const onUpdate = useCallback(({editType}) => {
if (editType === 'addFeature') {
this.setState({
mode: new EditingMode()
});
setMode(new EditingMode());
}
};
}, []);
_renderDrawTools = () => {
// copy from mapbox
return (
<div className="mapboxgl-ctrl-top-left">
<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: new DrawPolygonMode()})}
/>
<button
className="mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_trash"
title="Delete"
onClick={this._onDelete}
/>
</div>
const drawTools = (
<div className="mapboxgl-ctrl-top-left">
<div className="mapboxgl-ctrl-group mapboxgl-ctrl">
<button
className="mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_polygon"
title="Polygon tool (p)"
onClick={() => setMode(new DrawPolygonMode())}
/>
<button
className="mapbox-gl-draw_ctrl-draw-btn mapbox-gl-draw_trash"
title="Delete"
onClick={onDelete}
/>
</div>
);
};
</div>
);
_renderControlPanel = () => {
const features = this._editorRef && this._editorRef.getFeatures();
let featureIndex = this.state.selectedFeatureIndex;
if (features && featureIndex === null) {
featureIndex = features.length - 1;
}
const polygon = features && features.length ? features[featureIndex] : null;
return <ControlPanel containerComponent={this.props.containerComponent} polygon={polygon} />;
};
const features = editorRef.current && editorRef.current.getFeatures();
const selectedFeature =
features && (features[selectedFeatureIndex] || features[features.length - 1]);
render() {
const {viewport, mode} = this.state;
return (
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/satellite-v9"
mapboxApiAccessToken={TOKEN}
onViewportChange={this._updateViewport}
onViewportChange={setViewport}
>
<Editor
ref={_ => (this._editorRef = _)}
ref={editorRef}
style={{width: '100%', height: '100%'}}
clickRadius={12}
mode={mode}
onSelect={this._onSelect}
onUpdate={this._onUpdate}
onSelect={onSelect}
onUpdate={onUpdate}
editHandleShape={'circle'}
featureStyle={getFeatureStyle}
editHandleStyle={getEditHandleStyle}
/>
{this._renderDrawTools()}
{this._renderControlPanel()}
{drawTools}
</MapGL>
);
}
<ControlPanel polygon={selectedFeature} />
</>
);
}
export function renderToDom(container) {

View File

@ -1,29 +1,28 @@
import * as React from 'react';
import {PureComponent} from 'react';
import area from '@turf/area';
export default class ControlPanel extends PureComponent {
render() {
const polygon = this.props.polygon;
const polygonArea = polygon && area(polygon);
return (
<div className="control-panel">
<h3>Draw Polygon</h3>
{polygon && (
<p>
{polygonArea} <br />
square meters
</p>
)}
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/draw-polygon"
target="_new"
>
View Code
</a>
</div>
function ControlPanel(props) {
const polygon = props.polygon;
const polygonArea = polygon && area(polygon);
return (
<div className="control-panel">
<h3>Draw Polygon</h3>
{polygon && (
<p>
{polygonArea} <br />
square meters
</p>
)}
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/draw-polygon"
target="_new"
>
View Code
</a>
</div>
);
}
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,9 +1,14 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Highlight By Filter
# Example: Highlight By Filter
This app reproduces Mapbox's [Highlight features containing similar data](https://www.mapbox.com/mapbox-gl-js/example/query-similar-features/) example.
This example showcases how to dynamically add/remove filters from layers.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import {Component} from 'react';
import {useState, useMemo, useCallback} from 'react';
import {render} from 'react-dom';
import MapGL, {Popup, Source, Layer} from 'react-map-gl';
import ControlPanel from './control-panel';
@ -8,75 +8,59 @@ import {countiesLayer, highlightLayer} from './map-style.js';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
state = {
filter: ['in', 'COUNTY', ''],
viewport: {
latitude: 38.88,
longitude: -98,
zoom: 3,
minZoom: 2,
bearing: 0,
pitch: 0
},
hoverInfo: null
};
export default function App() {
const [viewport, setViewport] = useState({
latitude: 38.88,
longitude: -98,
zoom: 3,
minZoom: 2,
bearing: 0,
pitch: 0
});
const [hoverInfo, setHoverInfo] = useState(null);
_onViewportChange = viewport => this.setState({viewport});
_onHover = event => {
let countyName = '';
let hoverInfo = null;
const county = event.features[0];
if (county) {
hoverInfo = {
lngLat: event.lngLat,
county: county.properties
};
countyName = county.properties.COUNTY;
}
this.setState({
filter: ['in', 'COUNTY', countyName],
hoverInfo
const onHover = useCallback(event => {
const county = event.features && event.features[0];
setHoverInfo({
longitude: event.lngLat[0],
latitude: event.lngLat[1],
countyName: county && county.properties.COUNTY
});
};
}, []);
_renderPopup() {
const {hoverInfo} = this.state;
if (hoverInfo) {
return (
<Popup longitude={hoverInfo.lngLat[0]} latitude={hoverInfo.lngLat[1]} closeButton={false}>
<div className="county-info">{hoverInfo.county.COUNTY}</div>
</Popup>
);
}
return null;
}
const selectedCounty = (hoverInfo && hoverInfo.countyName) || '';
const filter = useMemo(() => ['in', 'COUNTY', selectedCounty], [selectedCounty]);
render() {
const {viewport, filter} = this.state;
return (
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/light-v9"
mapboxApiAccessToken={MAPBOX_TOKEN}
onViewportChange={this._onViewportChange}
onHover={this._onHover}
onViewportChange={setViewport}
onHover={onHover}
interactiveLayerIds={['counties']}
>
<Source type="vector" url="mapbox://mapbox.82pkq93d">
<Layer beforeId="waterway-label" {...countiesLayer} />
<Layer beforeId="waterway-label" {...highlightLayer} filter={filter} />
</Source>
{this._renderPopup()}
<ControlPanel containerComponent={this.props.containerComponent} />
{selectedCounty && (
<Popup
longitude={hoverInfo.longitude}
latitude={hoverInfo.latitude}
closeButton={false}
className="county-info"
>
{selectedCounty}
</Popup>
)}
</MapGL>
);
}
<ControlPanel />
</>
);
}
export function renderToDom(container) {

View File

@ -1,21 +1,20 @@
import * as React from 'react';
import {PureComponent} from 'react';
export default class ControlPanel extends PureComponent {
render() {
return (
<div className="control-panel">
<h3>Highlight Features Containing Similar Data</h3>
<p>Hover over counties to highlight counties that share the same name.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/filter"
target="_new"
>
View Code
</a>
</div>
function ControlPanel() {
return (
<div className="control-panel">
<h3>Highlight Features Containing Similar Data</h3>
<p>Hover over counties to highlight counties that share the same name.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/filter"
target="_new"
>
View Code
</a>
</div>
);
}
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,9 +1,14 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Animated GeoJSON
# Example: Animated GeoJSON
This app reproduces Mapbox's [Animate point along line](https://www.mapbox.com/mapbox-gl-js/example/animate-point-along-line/) example.
This example showcases how to dynamically add and update custom data sources.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,11 +1,10 @@
/* global window */
import * as React from 'react';
import {Component} from 'react';
import {useState, useEffect} from 'react';
import {render} from 'react-dom';
import MapGL, {Source, Layer} from 'react-map-gl';
import ControlPanel from './control-panel';
import {pointOnCircle} from './utils';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
@ -17,47 +16,38 @@ const pointLayer = {
}
};
export default class App extends Component {
state = {
pointData: null,
viewport: {
latitude: 0,
longitude: -100,
zoom: 3,
bearing: 0,
pitch: 0
}
function pointOnCircle({center, angle, radius}) {
return {
type: 'Point',
coordinates: [center[0] + Math.cos(angle) * radius, center[1] + Math.sin(angle) * radius]
};
}
componentDidMount() {
this._animatePoint();
}
export default function App() {
const [viewport, setViewport] = useState({
latitude: 0,
longitude: -100,
zoom: 3,
bearing: 0,
pitch: 0
});
const [pointData, setPointData] = useState(null);
componentWillUnmount() {
window.cancelAnimationFrame(this.animation);
}
useEffect(() => {
const animation = window.requestAnimationFrame(() =>
setPointData(pointOnCircle({center: [-100, 0], angle: Date.now() / 1000, radius: 20}))
);
return () => window.cancelAnimationFrame(animation);
});
animation = null;
_animatePoint = () => {
this.setState({
pointData: pointOnCircle({center: [-100, 0], angle: Date.now() / 1000, radius: 20})
});
this.animation = window.requestAnimationFrame(this._animatePoint);
};
_onViewportChange = viewport => this.setState({viewport});
render() {
const {viewport, pointData} = this.state;
return (
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/light-v9"
onViewportChange={this._onViewportChange}
onViewportChange={setViewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
{pointData && (
@ -65,10 +55,10 @@ export default class App extends Component {
<Layer {...pointLayer} />
</Source>
)}
<ControlPanel containerComponent={this.props.containerComponent} />
</MapGL>
);
}
<ControlPanel />
</>
);
}
export function renderToDom(container) {

View File

@ -1,21 +1,20 @@
import * as React from 'react';
import {PureComponent} from 'react';
export default class ControlPanel extends PureComponent {
render() {
return (
<div className="control-panel">
<h3>Animated GeoJSON</h3>
<p>Render animation by updating GeoJSON data source.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/geojson-animation"
target="_new"
>
View Code
</a>
</div>
function ControlPanel() {
return (
<div className="control-panel">
<h3>Animated GeoJSON</h3>
<p>Render animation by updating GeoJSON data source.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/geojson-animation"
target="_new"
>
View Code
</a>
</div>
);
}
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,6 +0,0 @@
export function pointOnCircle({center, angle, radius}) {
return {
type: 'Point',
coordinates: [center[0] + Math.cos(angle) * radius, center[1] + Math.sin(angle) * radius]
};
}

View File

@ -1,7 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: GeoJSON
# Example: GeoJSON
This example showcases how to dynamically add and update custom data sources.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -4,7 +4,6 @@
"start-local": "webpack-dev-server --env.local --progress --hot --open"
},
"dependencies": {
"d3-request": "^1.0.5",
"d3-scale": "^1.0.6",
"react": "^16.3.0",
"react-dom": "^16.3.0",

View File

@ -1,114 +1,84 @@
import * as React from 'react';
import {Component} from 'react';
import {useState, useEffect, useMemo, useCallback} from 'react';
import {render} from 'react-dom';
import MapGL, {Source, Layer} from 'react-map-gl';
import ControlPanel from './control-panel';
import {dataLayer} from './map-style.js';
import {updatePercentiles} from './utils';
import {json as requestJson} from 'd3-request';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
state = {
year: 2015,
data: null,
hoveredFeature: null,
viewport: {
latitude: 40,
longitude: -100,
zoom: 3,
bearing: 0,
pitch: 0
}
};
export default function App() {
const [viewport, setViewport] = useState({
latitude: 40,
longitude: -100,
zoom: 3,
bearing: 0,
pitch: 0
});
const [year, setYear] = useState(2015);
const [allData, setAllData] = useState(null);
const [hoverInfo, setHoverInfo] = useState(null);
componentDidMount() {
requestJson(
'https://raw.githubusercontent.com/uber/react-map-gl/master/examples/.data/us-income.geojson',
(error, response) => {
if (!error) {
this._loadData(response);
}
}
);
}
useEffect(() => {
/* global fetch */
fetch(
'https://raw.githubusercontent.com/uber/react-map-gl/master/examples/.data/us-income.geojson'
)
.then(resp => resp.json())
.then(json => setAllData(json));
}, []);
_loadData = data => {
this.setState({
data: updatePercentiles(data, f => f.properties.income[this.state.year])
});
};
_updateSettings = (name, value) => {
if (name === 'year') {
this.setState({year: value});
const {data} = this.state;
if (data) {
// trigger update
this.setState({
data: updatePercentiles(data, f => f.properties.income[value])
});
}
}
};
_onViewportChange = viewport => this.setState({viewport});
_onHover = event => {
const onHover = useCallback(event => {
const {
features,
srcEvent: {offsetX, offsetY}
} = event;
const hoveredFeature = features && features.find(f => f.layer.id === 'data');
const hoveredFeature = features && features[0];
this.setState({hoveredFeature, x: offsetX, y: offsetY});
};
_renderTooltip() {
const {hoveredFeature, x, y} = this.state;
return (
hoveredFeature && (
<div className="tooltip" style={{left: x, top: y}}>
<div>State: {hoveredFeature.properties.name}</div>
<div>Median Household Income: {hoveredFeature.properties.value}</div>
<div>Percentile: {(hoveredFeature.properties.percentile / 8) * 100}</div>
</div>
)
setHoverInfo(
hoveredFeature
? {
feature: hoveredFeature,
x: offsetX,
y: offsetY
}
: null
);
}
}, []);
render() {
const {viewport, data} = this.state;
const data = useMemo(() => {
return allData && updatePercentiles(allData, f => f.properties.income[year]);
}, [allData, year]);
return (
<div style={{height: '100%', position: 'relative'}}>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/light-v9"
onViewportChange={this._onViewportChange}
mapboxApiAccessToken={MAPBOX_TOKEN}
onHover={this._onHover}
>
<Source type="geojson" data={data}>
<Layer {...dataLayer} />
</Source>
{this._renderTooltip()}
</MapGL>
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/light-v9"
onViewportChange={setViewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
interactiveLayerIds={['data']}
onHover={onHover}
>
<Source type="geojson" data={data}>
<Layer {...dataLayer} />
</Source>
{hoverInfo && (
<div className="tooltip" style={{left: hoverInfo.x, top: hoverInfo.y}}>
<div>State: {hoverInfo.feature.properties.name}</div>
<div>Median Household Income: {hoverInfo.feature.properties.value}</div>
<div>Percentile: {(hoverInfo.feature.properties.percentile / 8) * 100}</div>
</div>
)}
</MapGL>
<ControlPanel
containerComponent={this.props.containerComponent}
settings={this.state}
onChange={this._updateSettings}
/>
</div>
);
}
<ControlPanel year={year} onChange={value => setYear(value)} />
</>
);
}
export function renderToDom(container) {

View File

@ -1,42 +1,41 @@
import * as React from 'react';
import {PureComponent} from 'react';
export default class ControlPanel extends PureComponent {
render() {
const {settings} = this.props;
function ControlPanel(props) {
const {year} = props;
return (
<div className="control-panel">
<h3>Interactive GeoJSON</h3>
<p>
Map showing median household income by state in year <b>{settings.year}</b>. Hover over a
state to see details.
</p>
<p>
Data source: <a href="www.census.gov">US Census Bureau</a>
</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/geojson"
target="_new"
>
View Code
</a>
</div>
<hr />
<div key={'year'} className="input">
<label>Year</label>
<input
type="range"
value={settings.year}
min={1995}
max={2015}
step={1}
onChange={evt => this.props.onChange('year', evt.target.value)}
/>
</div>
return (
<div className="control-panel">
<h3>Interactive GeoJSON</h3>
<p>
Map showing median household income by state in year <b>{year}</b>. Hover over a state to
see details.
</p>
<p>
Data source: <a href="www.census.gov">US Census Bureau</a>
</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/geojson"
target="_new"
>
View Code
</a>
</div>
);
}
<hr />
<div key={'year'} className="input">
<label>Year</label>
<input
type="range"
value={year}
min={1995}
max={2015}
step={1}
onChange={evt => props.onChange(evt.target.value)}
/>
</div>
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,13 +1,18 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
# react-map-gl Example Using React Class Component
## react-map-gl example with React Component
This example shows a minimal app configuration to use react-map-gl with a React class component.
The configuration showcased here is a bit less straightforward than its browserify
equivalent due to some incompatibilities with mapbox-gl, but has been kept at a
strict minimum.
## Usage
You should keep in mind that it is a development configuration, and probably
should be tweaked a bit for production optimization, there is plenty ressources
on the subject and are not in the scope of this example.
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```
To build a production version:
```bash
npm run build
```

View File

@ -1,11 +1,12 @@
{
"scripts": {
"start": "webpack-dev-server --progress --hot --open"
"start": "webpack-dev-server --progress --hot --open",
"build": "webpack -p"
},
"dependencies": {
"react": "^16.3.0",
"react-dom": "^16.3.0",
"react-map-gl": "^5.1.0"
"react-map-gl": "^6.0.0"
},
"devDependencies": {
"@babel/core": "^7.0.0",

View File

@ -1,13 +1,18 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
# react-map-gl Example Using React Functional Component
## react-map-gl example with React Hooks
This example shows a minimal app configuration to use react-map-gl with a React functional component.
The configuration showcased here is a bit less straightforward than its browserify
equivalent due to some incompatibilities with mapbox-gl, but has been kept at a
strict minimum.
## Usage
You should keep in mind that it is a development configuration, and probably
should be tweaked a bit for production optimization, there is plenty ressources
on the subject and are not in the scope of this example.
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```
To build a production version:
```bash
npm run build
```

View File

@ -21,7 +21,7 @@ function Root() {
width="100vw"
height="100vh"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={nextViewport => setViewport(nextViewport)}
onViewportChange={setViewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
/>
);

View File

@ -5,7 +5,7 @@
"dependencies": {
"react": "^16.8.0",
"react-dom": "^16.8.0",
"react-map-gl": "^5.1.0"
"react-map-gl": "^6.0.0"
},
"devDependencies": {
"@babel/core": "^7.0.0",

View File

@ -1,15 +1,12 @@
## Example: Heatmap layer
# Example: Heatmap layer
This example showcases how to add a heatmap similar as described in https://docs.mapbox.com/mapbox-gl-js/example/heatmap-layer/.
This app reproduces Mapbox's [Create a heatmap layer](https://docs.mapbox.com/mapbox-gl-js/example/heatmap-layer/) example.
### How to run `heatmap layer example`?
## Usage
Install dependencies (only once)
```
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```
and run example
```
MapboxAccessToken={YOUR_MAPBOX_TOKEN} npm run start-local
```
which will open and point your browser to http://localhost:8081/

View File

@ -4,7 +4,6 @@
"start-local": "webpack-dev-server --env.local --progress --hot --open"
},
"dependencies": {
"d3-request": "^1.0.5",
"react": "^16.3.0",
"react-dom": "^16.3.0",
"react-map-gl": "^6.0.0"

View File

@ -1,9 +1,8 @@
import * as React from 'react';
import {Component} from 'react';
import {useState, useEffect, useMemo} from 'react';
import {render} from 'react-dom';
import MapGL, {Source, Layer} from 'react-map-gl';
import ControlPanel from './control-panel';
import {json as requestJson} from 'd3-request';
import {heatmapLayer} from './map-style';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
@ -24,104 +23,66 @@ function filterFeaturesByDay(featureCollection, time) {
return {type: 'FeatureCollection', features};
}
export default class App extends Component {
constructor(props) {
super(props);
const current = new Date().getTime();
export default function App() {
const [viewport, setViewport] = useState({
latitude: 40,
longitude: -100,
zoom: 3,
bearing: 0,
pitch: 0
});
const [allDays, useAllDays] = useState(true);
const [timeRange, setTimeRange] = useState([0, 0]);
const [selectedTime, selectTime] = useState(0);
const [earthquakes, setEarthQuakes] = useState(null);
this.state = {
viewport: {
latitude: 40,
longitude: -100,
zoom: 3,
bearing: 0,
pitch: 0
},
allDay: true,
startTime: current,
endTime: current,
selectedTime: current,
earthquakes: null
};
useEffect(() => {
/* global fetch */
fetch('https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson')
.then(resp => resp.json())
.then(json => {
// Note: In a real application you would do a validation of JSON data before doing anything with it,
// but for demonstration purposes we ingore this part here and just trying to select needed data...
const features = json.features;
const endTime = features[0].properties.time;
const startTime = features[features.length - 1].properties.time;
this._handleChangeDay = this._handleChangeDay.bind(this);
this._handleChangeAllDay = this._handleChangeAllDay.bind(this);
}
componentDidMount() {
requestJson(
'https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson',
(error, response) => {
if (!error) {
// Note: In a real application you would do a validation of JSON data before doing anything with it,
// but for demonstration purposes we ingore this part here and just trying to select needed data...
const features = response.features;
const endTime = features[0].properties.time;
const startTime = features[features.length - 1].properties.time;
this.setState({
data: response,
earthquakes: response,
endTime,
startTime,
selectedTime: endTime
});
}
}
);
}
_onViewportChange = viewport => this.setState({viewport});
_handleChangeDay = time => {
this.setState({selectedTime: time});
if (this.state.earthquakes) {
this.setState({data: filterFeaturesByDay(this.state.earthquakes, time)});
}
};
_handleChangeAllDay = allDay => {
this.setState({allDay});
if (this.state.earthquakes) {
this.setState({
data: allDay
? this.state.earthquakes
: filterFeaturesByDay(this.state.earthquakes, this.state.selectedTime)
setTimeRange([startTime, endTime]);
setEarthQuakes(json);
selectTime(endTime);
});
}
};
}, []);
render() {
const {viewport, data, allDay, selectedTime, startTime, endTime} = this.state;
const data = useMemo(() => {
return allDays ? earthquakes : filterFeaturesByDay(earthquakes, selectedTime);
}, [earthquakes, allDays, selectedTime]);
return (
<div style={{height: '100%', position: 'relative'}}>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={this._onViewportChange}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
{data && (
<Source type="geojson" data={data}>
<Layer {...heatmapLayer} />
</Source>
)}
</MapGL>
<ControlPanel
containerComponent={this.props.containerComponent}
startTime={startTime}
endTime={endTime}
selectedTime={selectedTime}
allDay={allDay}
onChangeDay={this._handleChangeDay}
onChangeAllDay={this._handleChangeAllDay}
/>
</div>
);
}
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={setViewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
{data && (
<Source type="geojson" data={data}>
<Layer {...heatmapLayer} />
</Source>
)}
</MapGL>
<ControlPanel
startTime={timeRange[0]}
endTime={timeRange[1]}
selectedTime={selectedTime}
allDays={allDays}
onChangeTime={selectTime}
onChangeAllDays={useAllDays}
/>
</>
);
}
export function renderToDom(container) {

View File

@ -1,69 +1,70 @@
import * as React from 'react';
import {PureComponent} from 'react';
export default class ControlPanel extends PureComponent {
render() {
const {startTime, endTime, onChangeDay, allDay, onChangeAllDay, selectedTime} = this.props;
const day = 24 * 60 * 60 * 1000;
const days = Math.round((endTime - startTime) / day);
const _onChangeDay = evt => {
const daysToAdd = evt.target.value;
// add selected days to start time to calculate new time
const newTime = startTime + daysToAdd * day;
onChangeDay(newTime);
};
const formatTime = time => {
const date = new Date(time);
return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
};
return (
<div className="control-panel">
<h3>Heatmap</h3>
<p>
Map showing earthquakes
<br />
from <b>{formatTime(startTime)}</b> to <b>{formatTime(endTime)}</b>.
</p>
<hr />
<div className="input">
<label>All Days</label>
<input
type="checkbox"
name="allday"
checked={allDay}
onChange={evt => onChangeAllDay(evt.target.checked)}
/>
</div>
<div className={`input ${allDay ? 'disabled' : ''}`}>
<label>Each Day: {formatTime(selectedTime)}</label>
<input
type="range"
disabled={allDay}
min={1}
max={days}
step={1}
onChange={_onChangeDay}
/>
</div>
<hr />
<p>
Data source:{' '}
<a href="https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson">
earthquakes.geojson
</a>
</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/heatmap"
target="_new"
>
View Code
</a>
</div>
</div>
);
}
function formatTime(time) {
const date = new Date(time);
return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`;
}
function ControlPanel(props) {
const {startTime, endTime, onChangeTime, allDays, onChangeAllDays, selectedTime} = props;
const day = 24 * 60 * 60 * 1000;
const days = Math.round((endTime - startTime) / day);
const selectedDay = Math.round((selectedTime - startTime) / day);
const onSelectDay = evt => {
const daysToAdd = evt.target.value;
// add selected days to start time to calculate new time
const newTime = startTime + daysToAdd * day;
onChangeTime(newTime);
};
return (
<div className="control-panel">
<h3>Heatmap</h3>
<p>
Map showing earthquakes
<br />
from <b>{formatTime(startTime)}</b> to <b>{formatTime(endTime)}</b>.
</p>
<hr />
<div className="input">
<label>All Days</label>
<input
type="checkbox"
name="allday"
checked={allDays}
onChange={evt => onChangeAllDays(evt.target.checked)}
/>
</div>
<div className={`input ${allDays ? 'disabled' : ''}`}>
<label>Each Day: {formatTime(selectedTime)}</label>
<input
type="range"
disabled={allDays}
min={1}
max={days}
value={selectedDay}
step={1}
onChange={onSelectDay}
/>
</div>
<hr />
<p>
Data source:{' '}
<a href="https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson">
earthquakes.geojson
</a>
</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/heatmap"
target="_new"
>
View Code
</a>
</div>
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,7 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Interaction
# Example: Interaction
This example showcases how to toggle/limit user interaction.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,91 +1,62 @@
import * as React from 'react';
import {Component} from 'react';
import {useState, useCallback} from 'react';
import {render} from 'react-dom';
import MapGL, {Marker} from 'react-map-gl';
import MapGL from 'react-map-gl';
import ControlPanel from './control-panel';
import bartStations from './bart-station.json';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
import MARKER_STYLE from './marker-style';
export default function App() {
const [viewport, setViewport] = useState({
latitude: 37.729,
longitude: -122.36,
zoom: 11,
bearing: 0,
pitch: 50
});
const [interactionState, setInteractionState] = useState({});
const [settings, setSettings] = useState({
dragPan: true,
dragRotate: true,
scrollZoom: true,
touchZoom: true,
touchRotate: true,
keyboard: true,
doubleClickZoom: true,
minZoom: 0,
maxZoom: 20,
minPitch: 0,
maxPitch: 85
});
export default class App extends Component {
state = {
viewport: {
latitude: 37.729,
longitude: -122.36,
zoom: 11,
bearing: 0,
pitch: 50
},
interactionState: {},
settings: {
dragPan: true,
dragRotate: true,
scrollZoom: true,
touchZoom: true,
touchRotate: true,
keyboard: true,
doubleClickZoom: true,
minZoom: 0,
maxZoom: 20,
minPitch: 0,
maxPitch: 85
}
};
const updateSettings = useCallback(
(name, value) =>
setSettings(s => ({
...s,
[name]: value
})),
[]
);
_onViewportChange = viewport => this.setState({viewport});
_onInteractionStateChange = interactionState => this.setState({interactionState});
_onSettingChange = (name, value) =>
this.setState({
settings: {...this.state.settings, [name]: value}
});
_renderMarker(station, i) {
const {name, coordinates} = station;
return (
<Marker
key={i}
longitude={coordinates[0]}
latitude={coordinates[1]}
captureDrag={false}
captureDoubleClick={false}
>
<div className="station">
<span>{name}</span>
</div>
</Marker>
);
}
render() {
const {viewport, settings, interactionState} = this.state;
return (
return (
<>
<MapGL
{...viewport}
{...settings}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={this._onViewportChange}
onInteractionStateChange={this._onInteractionStateChange}
onViewportChange={setViewport}
onInteractionStateChange={s => setInteractionState({...s})}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
<style>{MARKER_STYLE}</style>
{bartStations.map(this._renderMarker)}
<ControlPanel
containerComponent={this.props.containerComponent}
settings={settings}
interactionState={{...interactionState}}
onChange={this._onSettingChange}
/>
</MapGL>
);
}
/>
<ControlPanel
settings={settings}
interactionState={interactionState}
onChange={updateSettings}
/>
</>
);
}
export function renderToDom(container) {

View File

@ -1,46 +0,0 @@
[
{"name":"Lafayette (LAFY)","coordinates":[-122.123801,37.893394]},
{"name":"12th St. Oakland City Center (12TH)","coordinates":[-122.271604,37.803664]},
{"name":"16th St. Mission (16TH)","coordinates":[-122.419694,37.765062]},
{"name":"19th St. Oakland (19TH)","coordinates":[-122.269029,37.80787]},
{"name":"24th St. Mission (24TH)","coordinates":[-122.418466,37.752254]},
{"name":"Ashby (ASHB)","coordinates":[-122.26978,37.853024]},
{"name":"Balboa Park (BALB)","coordinates":[-122.447414,37.721981]},
{"name":"Bay Fair (BAYF)","coordinates":[-122.126871,37.697185]},
{"name":"Castro Valley (CAST)","coordinates":[-122.075567,37.690754]},
{"name":"Civic Center/UN Plaza (CIVC)","coordinates":[-122.413756,37.779528]},
{"name":"Colma (COLM)","coordinates":[-122.466233,37.684638]},
{"name":"Coliseum/Oakland Airport (COLS)","coordinates":[-122.197273,37.754006]},
{"name":"Concord (CONC)","coordinates":[-122.029095,37.973737]},
{"name":"Daly City (DALY)","coordinates":[-122.469081,37.706121]},
{"name":"Downtown Berkeley (DBRK)","coordinates":[-122.268045,37.869867]},
{"name":"El Cerrito del Norte (DELN)","coordinates":[-122.317269,37.925655]},
{"name":"Dublin/Pleasanton (DUBL)","coordinates":[-121.900367,37.701695]},
{"name":"Embarcadero (EMBR)","coordinates":[-122.396742,37.792976]},
{"name":"Fremont (FRMT)","coordinates":[-121.9764,37.557355]},
{"name":"Fruitvale (FTVL)","coordinates":[-122.224274,37.774963]},
{"name":"Glen Park (GLEN)","coordinates":[-122.434092,37.732921]},
{"name":"Hayward (HAYW)","coordinates":[-122.087967,37.670399]},
{"name":"Lake Merritt (LAKE)","coordinates":[-122.265609,37.797484]},
{"name":"MacArthur (MCAR)","coordinates":[-122.267227,37.828415]},
{"name":"Millbrae (MLBR)","coordinates":[-122.38666,37.599787]},
{"name":"Montgomery St. (MONT)","coordinates":[-122.401407,37.789256]},
{"name":"North Berkeley (NBRK)","coordinates":[-122.283451,37.87404]},
{"name":"North Concord/Martinez (NCON)","coordinates":[-122.024597,38.003275]},
{"name":"Orinda (ORIN)","coordinates":[-122.183791,37.878361]},
{"name":"Pleasant Hill/Contra Costa Centre (PHIL)","coordinates":[-122.056013,37.928403]},
{"name":"Pittsburg/Bay Point (PITT)","coordinates":[-121.945154,38.018914]},
{"name":"El Cerrito Plaza (PLZA)","coordinates":[-122.299272,37.903059]},
{"name":"Powell St. (POWL)","coordinates":[-122.406857,37.784991]},
{"name":"Richmond (RICH)","coordinates":[-122.353165,37.936887]},
{"name":"Rockridge (ROCK)","coordinates":[-122.251793,37.844601]},
{"name":"San Leandro (SANL)","coordinates":[-122.161311,37.722619]},
{"name":"San Bruno (SBRN)","coordinates":[-122.416038,37.637753]},
{"name":"San Francisco Int'l Airport (SFIA)","coordinates":[-122.392612,37.616035]},
{"name":"South Hayward (SHAY)","coordinates":[-122.057551,37.6348]},
{"name":"South San Francisco (SSAN)","coordinates":[-122.444116,37.664174]},
{"name":"Union City (UCTY)","coordinates":[-122.017867,37.591208]},
{"name":"Walnut Creek (WCRK)","coordinates":[-122.067423,37.905628]},
{"name":"West Dublin/Pleasanton (WDUB)","coordinates":[-121.928099,37.699759]},
{"name":"West Oakland (WOAK)","coordinates":[-122.294582,37.804675]}
]

View File

@ -1,100 +1,88 @@
import * as React from 'react';
import {PureComponent} from 'react';
const camelPattern = /(^|[A-Z])[a-z]*/g;
function formatSettingName(name) {
return name.match(camelPattern).join(' ');
}
export default class ControlPanel extends PureComponent {
_formatSettingName(name) {
return name.match(camelPattern).join(' ');
}
function Checkbox({name, value, onChange}) {
return (
<div className="input">
<label>{formatSettingName(name)}</label>
<input type="checkbox" checked={value} onChange={evt => onChange(name, evt.target.checked)} />
</div>
);
}
_renderCheckbox(name, value) {
return (
<div key={name} className="input">
<label>{this._formatSettingName(name)}</label>
<input
type="checkbox"
checked={value}
onChange={evt => this.props.onChange(name, evt.target.checked)}
/>
</div>
);
}
function NumericInput({name, value, onChange}) {
return (
<div className="input">
<label>{formatSettingName(name)}</label>
<input
type="number"
value={value}
onChange={evt => onChange(name, Number(evt.target.value))}
/>
</div>
);
}
_renderNumericInput(name, value) {
return (
<div key={name} className="input">
<label>{this._formatSettingName(name)}</label>
<input
type="number"
value={value}
onChange={evt => this.props.onChange(name, Number(evt.target.value))}
/>
</div>
);
}
function ControlPanel(props) {
const {settings, interactionState, onChange} = props;
_renderSetting(name, value) {
const renderSetting = (name, value) => {
switch (typeof value) {
case 'boolean':
return this._renderCheckbox(name, value);
return <Checkbox key={name} name={name} value={value} onChange={onChange} />;
case 'number':
return this._renderNumericInput(name, value);
return <NumericInput key={name} name={name} value={value} onChange={onChange} />;
default:
return null;
}
}
};
return (
<div className="control-panel">
<h3>Limit Map Interaction</h3>
<p>Turn interactive features off/on.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/interaction"
target="_new"
>
View Code
</a>
</div>
<hr />
{Object.keys(settings).map(name => renderSetting(name, settings[name]))}
<hr />
_renderInteractionStates({isDragging, isPanning, isRotating, isZooming, inTransition}) {
return (
<div>
<div>
<label>Dragging</label>
<span>{isDragging && 'Yes'}</span>
<span>{interactionState.isDragging && 'Yes'}</span>
</div>
<div>
<label>Transition</label>
<span>{inTransition && 'Yes'}</span>
<span>{interactionState.inTransition && 'Yes'}</span>
</div>
<div>
<label>Panning</label>
<span>{isPanning && 'Yes'}</span>
<span>{interactionState.isPanning && 'Yes'}</span>
</div>
<div>
<label>Rotating</label>
<span>{isRotating && 'Yes'}</span>
<span>{interactionState.isRotating && 'Yes'}</span>
</div>
<div>
<label>Zooming</label>
<span>{isZooming && 'Yes'}</span>
<span>{interactionState.isZooming && 'Yes'}</span>
</div>
</div>
);
}
render() {
const {settings, interactionState} = this.props;
return (
<div className="control-panel">
<h3>Limit Map Interaction</h3>
<p>Turn interactive features off/on.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/interaction"
target="_new"
>
View Code
</a>
</div>
<hr />
{Object.keys(settings).map(name => this._renderSetting(name, settings[name]))}
<hr />
{this._renderInteractionStates(interactionState)}
</div>
);
}
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,30 +0,0 @@
export default `
.station:before {
content: ' ';
display: inline-block;
width: 8px;
height: 8px;
background: red;
border-radius: 8px;
margin: 0 8px;
}
.station {
border-radius: 20px;
padding-right: 12px;
margin: -12px;
color: transparent;
line-height: 24px;
font-size: 13px;
white-space: nowrap;
}
.station span {
display: none;
}
.station:hover {
background: rgba(0,0,0,0.8);
color: #fff;
}
.station:hover span {
display: inline-block;
}
`;

View File

@ -1,7 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
# Example: Layers
## Example: Layers
This example showcases how to dynamically change layer styles and show/hide layers.
This example showcases how to dynamically change layer styles and show/hide layers.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,46 +1,35 @@
import * as React from 'react';
import {Component} from 'react';
import {useState} from 'react';
import {render} from 'react-dom';
import MapGL from 'react-map-gl';
import ControlPanel from './control-panel';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
state = {
mapStyle: '',
viewport: {
latitude: 37.805,
longitude: -122.447,
zoom: 15.5,
bearing: 0,
pitch: 0
}
};
export default function App() {
const [viewport, setViewport] = useState({
latitude: 37.805,
longitude: -122.447,
zoom: 15.5,
bearing: 0,
pitch: 0
});
const [mapStyle, setMapStyle] = useState('');
_onViewportChange = viewport => this.setState({viewport});
_onStyleChange = mapStyle => this.setState({mapStyle});
render() {
const {viewport, mapStyle} = this.state;
return (
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle={mapStyle}
onViewportChange={this._onViewportChange}
onViewportChange={setViewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
<ControlPanel
containerComponent={this.props.containerComponent}
onChange={this._onStyleChange}
/>
</MapGL>
);
}
/>
<ControlPanel onChange={setMapStyle} />
</>
);
}
export function renderToDom(container) {

View File

@ -1,9 +1,10 @@
import * as React from 'react';
import {PureComponent} from 'react';
import {useState, useEffect} from 'react';
import {fromJS} from 'immutable';
import MAP_STYLE from '../../map-style-basic-v8.json';
const defaultMapStyle = fromJS(MAP_STYLE);
const defaultLayers = defaultMapStyle.get('layers');
const categories = ['labels', 'roads', 'buildings', 'parks', 'water', 'background'];
@ -25,107 +26,87 @@ const colorClass = {
symbol: 'text-color'
};
export default class StyleControls extends PureComponent {
constructor(props) {
super(props);
this._defaultLayers = defaultMapStyle.get('layers');
this.state = {
visibility: {
water: true,
parks: true,
buildings: true,
roads: true,
labels: true,
background: true
},
color: {
water: '#DBE2E6',
parks: '#E6EAE9',
buildings: '#c0c0c8',
roads: '#ffffff',
labels: '#78888a',
background: '#EBF0F0'
function getMapStyle({visibility, color}) {
const layers = defaultLayers
.filter(layer => {
const id = layer.get('id');
return categories.every(name => visibility[name] || !layerSelector[name].test(id));
})
.map(layer => {
const id = layer.get('id');
const type = layer.get('type');
const category = categories.find(name => layerSelector[name].test(id));
if (category && colorClass[type]) {
return layer.setIn(['paint', colorClass[type]], color[category]);
}
};
}
return layer;
});
componentDidMount() {
this._updateMapStyle(this.state);
}
_onColorChange(name, event) {
const color = {...this.state.color, [name]: event.target.value};
this.setState({color});
this._updateMapStyle({...this.state, color});
}
_onVisibilityChange(name, event) {
const visibility = {
...this.state.visibility,
[name]: event.target.checked
};
this.setState({visibility});
this._updateMapStyle({...this.state, visibility});
}
_updateMapStyle({visibility, color}) {
const layers = this._defaultLayers
.filter(layer => {
const id = layer.get('id');
return categories.every(name => visibility[name] || !layerSelector[name].test(id));
})
.map(layer => {
const id = layer.get('id');
const type = layer.get('type');
const category = categories.find(name => layerSelector[name].test(id));
if (category && colorClass[type]) {
return layer.setIn(['paint', colorClass[type]], color[category]);
}
return layer;
});
this.props.onChange(defaultMapStyle.set('layers', layers));
}
_renderLayerControl(name) {
const {visibility, color} = this.state;
return (
<div key={name} className="input">
<label>{name}</label>
<input
type="checkbox"
checked={visibility[name]}
onChange={this._onVisibilityChange.bind(this, name)}
/>
<input
type="color"
value={color[name]}
disabled={!visibility[name]}
onChange={this._onColorChange.bind(this, name)}
/>
</div>
);
}
render() {
return (
<div className="control-panel">
<h3>Dynamic Styling</h3>
<p>Dynamically show/hide map layers and change color with Immutable map style.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/layers"
target="_new"
>
View Code
</a>
</div>
<hr />
{categories.map(name => this._renderLayerControl(name))}
</div>
);
}
return defaultMapStyle.set('layers', layers);
}
function StyleControls(props) {
const [visibility, setVisibility] = useState({
water: true,
parks: true,
buildings: true,
roads: true,
labels: true,
background: true
});
const [color, setColor] = useState({
water: '#DBE2E6',
parks: '#E6EAE9',
buildings: '#c0c0c8',
roads: '#ffffff',
labels: '#78888a',
background: '#EBF0F0'
});
useEffect(() => {
props.onChange(getMapStyle({visibility, color}));
}, [visibility, color]);
const onColorChange = (name, value) => {
setColor({...color, [name]: value});
};
const onVisibilityChange = (name, value) => {
setVisibility({...visibility, [name]: value});
};
return (
<div className="control-panel">
<h3>Dynamic Styling</h3>
<p>Dynamically show/hide map layers and change color with Immutable map style.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/layers"
target="_new"
>
View Code
</a>
</div>
<hr />
{categories.map(name => (
<div key={name} className="input">
<label>{name}</label>
<input
type="checkbox"
checked={visibility[name]}
onChange={evt => onVisibilityChange(name, evt.target.checked)}
/>
<input
type="color"
value={color[name]}
disabled={!visibility[name]}
onChange={evt => onColorChange(name, evt.target.value)}
/>
</div>
))}
</div>
);
}
export default React.memo(StyleControls);

View File

@ -1,12 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
# Example: Locate User
## Example: Locate User
Demonstrates how to automatically locate the user and track their current location with react-map-gl.
Demonstrates how to locate the user and track its current location with react-map-gl.
## Usage
```
npm install
npm start
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import {Component} from 'react';
import {useState} from 'react';
import {render} from 'react-dom';
import MapGL, {GeolocateControl} from 'react-map-gl';
@ -11,40 +11,34 @@ const geolocateStyle = {
left: 0,
margin: 10
};
const positionOptions = {enableHighAccuracy: true};
export default class App extends Component {
state = {
viewport: {
latitude: 37.8,
longitude: 96,
zoom: 3,
bearing: 0,
pitch: 0
}
};
export default function App() {
const [viewport, setViewport] = useState({
latitude: 37.8,
longitude: 96,
zoom: 3,
bearing: 0,
pitch: 0
});
_onViewportChange = viewport => this.setState({viewport});
render() {
const {viewport} = this.state;
return (
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={this._onViewportChange}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
<GeolocateControl
style={geolocateStyle}
positionOptions={{enableHighAccuracy: true}}
trackUserLocation={true}
/>
</MapGL>
);
}
return (
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/dark-v9"
onViewportChange={setViewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
<GeolocateControl
style={geolocateStyle}
positionOptions={positionOptions}
trackUserLocation
auto
/>
</MapGL>
);
}
export function renderToDom(container) {

View File

@ -1,7 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Reuse Map
# Example: Reuse Map
This example showcases how to reuse the same map without destroying it.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -7,30 +7,10 @@ body {
height: 100vh;
}
.control-panel {
position: absolute;
top: 0;
right: 0;
max-width: 320px;
background: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
padding: 12px 24px;
.toggle-btn {
position: fixed;
z-index: 1;
margin: 20px;
font-size: 13px;
line-height: 2;
color: #6b6b76;
text-transform: uppercase;
outline: none;
}
label {
display: inline-block;
width: 160px;
}
input {
margin-left: 20px;
max-width: 60px;
}
/* marker */

View File

@ -1,47 +1,31 @@
import * as React from 'react';
import {Component} from 'react';
import {useState} from 'react';
import {render} from 'react-dom';
import BartMap from './bart-map';
const LIGHT_STYLE = 'mapbox://styles/mapbox/light-v9';
const DARK_STYLE = 'mapbox://styles/mapbox/dark-v9';
export default class App extends Component {
state = {
showMap: true,
mapStyleLight: true
export default function App() {
const [showMap, setShowMap] = useState(true);
const [mapStyle, setMapStyle] = useState(LIGHT_STYLE);
const toggleMap = () => {
setShowMap(!showMap);
if (showMap) {
setMapStyle(mapStyle === LIGHT_STYLE ? DARK_STYLE : LIGHT_STYLE);
}
};
_toggleMap() {
let {showMap, mapStyleLight} = this.state;
showMap = !this.state.showMap;
if (showMap) {
mapStyleLight = !mapStyleLight;
}
this.setState({
showMap,
mapStyleLight
});
}
render() {
const {showMap, mapStyleLight} = this.state;
const mapStyle = mapStyleLight ? LIGHT_STYLE : DARK_STYLE;
if (showMap) {
// eslint-disable-next-line no-console, no-undef
console.warn(mapStyle);
}
return (
<div style={{height: '100%'}}>
<button style={{position: 'fixed', zIndex: 1}} onClick={this._toggleMap.bind(this)}>
Toggle Map
</button>
{showMap && <BartMap mapStyle={mapStyle} />}
</div>
);
}
return (
<>
<button className="toggle-btn" onClick={toggleMap}>
Toggle Map
</button>
{showMap && <BartMap mapStyle={mapStyle} />}
</>
);
}
export function renderToDom(container) {

View File

@ -1,54 +1,41 @@
import * as React from 'react';
import {Component} from 'react';
import MapGL, {Marker} from '../../../src';
import {useState} from 'react';
import MapGL, {Marker} from 'react-map-gl';
import bartStations from '../../.data/bart-station.json';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
export default class BartMap extends Component {
state = {
viewState: {
latitude: 37.729,
longitude: -122.36,
zoom: 11,
bearing: 0,
pitch: 50
}
};
_onViewportChange = viewState => this.setState({viewState});
export default function BartMap(props) {
const [viewport, setViewport] = useState({
latitude: 37.73,
longitude: -122.36,
zoom: 11,
bearing: 0,
pitch: 50
});
// eslint-disable-next-line
_onMapLoad = event => console.log(event);
const onMapLoad = event => console.log(event);
_renderMarker(station, i) {
const {name, coordinates} = station;
return (
<Marker key={i} longitude={coordinates[0]} latitude={coordinates[1]}>
<div className="station">
<span>{name}</span>
</div>
</Marker>
);
}
render() {
const {mapStyle} = this.props;
const {viewState} = this.state;
return (
<MapGL
{...viewState}
mapboxApiAccessToken={MAPBOX_TOKEN}
mapStyle={mapStyle}
width="100%"
height="100%"
onViewportChange={this._onViewportChange}
onLoad={this._onMapLoad}
reuseMaps={true}
>
{bartStations.map(this._renderMarker)}
</MapGL>
);
}
return (
<MapGL
{...viewport}
mapboxApiAccessToken={MAPBOX_TOKEN}
mapStyle={props.mapStyle}
width="100%"
height="100%"
onViewportChange={setViewport}
onLoad={onMapLoad}
reuseMaps
>
{bartStations.map(({name, coordinates}, i) => (
<Marker key={i} longitude={coordinates[0]} latitude={coordinates[1]}>
<div className="station">
<span>{name}</span>
</div>
</Marker>
))}
</MapGL>
);
}

View File

@ -1,7 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Viewport Animation
# Example: Viewport Animation
This example showcases how to transition smoothly between one viewport to another.
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import {Component} from 'react';
import {useState, useCallback} from 'react';
import {render} from 'react-dom';
import MapGL, {FlyToInterpolator} from 'react-map-gl';
@ -7,54 +7,39 @@ import ControlPanel from './control-panel';
const MAPBOX_TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
state = {
viewport: {
latitude: 37.7751,
longitude: -122.4193,
zoom: 11,
bearing: 0,
pitch: 0
}
};
export default function App() {
const [viewport, setViewport] = useState({
latitude: 37.7751,
longitude: -122.4193,
zoom: 11,
bearing: 0,
pitch: 0
});
_onViewportChange = viewport =>
this.setState({
viewport: {...this.state.viewport, ...viewport}
});
_goToViewport = ({longitude, latitude}) => {
this._onViewportChange({
const onSelectCity = useCallback(({longitude, latitude}) => {
setViewport({
longitude,
latitude,
zoom: 11,
transitionInterpolator: new FlyToInterpolator({speed: 1.2}),
transitionDuration: 'auto'
});
};
}, []);
render() {
const {viewport, settings} = this.state;
return (
<div style={{height: '100%'}}>
<MapGL
{...viewport}
{...settings}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/light-v9"
onViewportChange={this._onViewportChange}
dragRotate={false}
mapboxApiAccessToken={MAPBOX_TOKEN}
/>
<ControlPanel
containerComponent={this.props.containerComponent}
onViewportChange={this._goToViewport}
/>
</div>
);
}
return (
<>
<MapGL
{...viewport}
width="100%"
height="100%"
mapStyle="mapbox://styles/mapbox/light-v9"
onViewportChange={setViewport}
dragRotate={false}
mapboxApiAccessToken={MAPBOX_TOKEN}
/>
<ControlPanel onSelectCity={onSelectCity} />
</>
);
}
export function renderToDom(container) {

View File

@ -1,41 +1,36 @@
import * as React from 'react';
import {PureComponent} from 'react';
import CITIES from '../../.data/cities.json';
export default class ControlPanel extends PureComponent {
_renderButton = (city, index) => {
return (
<div key={`btn-${index}`} className="input">
<input
type="radio"
name="city"
id={`city-${index}`}
defaultChecked={city.city === 'San Francisco'}
onClick={() => this.props.onViewportChange(city)}
/>
<label htmlFor={`city-${index}`}>{city.city}</label>
function ControlPanel(props) {
return (
<div className="control-panel">
<h3>Camera Transition</h3>
<p>Smooth animate of the viewport.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/viewport-animation"
target="_new"
>
View Code
</a>
</div>
);
};
<hr />
render() {
return (
<div className="control-panel">
<h3>Camera Transition</h3>
<p>Smooth animate of the viewport.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/viewport-animation"
target="_new"
>
View Code
</a>
{CITIES.filter(city => city.state === 'California').map((city, index) => (
<div key={`btn-${index}`} className="input">
<input
type="radio"
name="city"
id={`city-${index}`}
defaultChecked={city.city === 'San Francisco'}
onClick={() => props.onSelectCity(city)}
/>
<label htmlFor={`city-${index}`}>{city.city}</label>
</div>
<hr />
{CITIES.filter(city => city.state === 'California').map(this._renderButton)}
</div>
);
}
))}
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -1,12 +1,12 @@
<div align="center">
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
</div>
## Example: Zoom To Bounds
# Example: Zoom To Bounds
Demonstrates how to zoom to a bounding box with react-map-gl.
```
npm install
npm start
## Usage
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
```bash
npm i
npm run start
```

View File

@ -1,5 +1,5 @@
import * as React from 'react';
import {Component} from 'react';
import {useState} from 'react';
import {render} from 'react-dom';
import MapGL, {LinearInterpolator, WebMercatorViewport} from 'react-map-gl';
import bbox from '@turf/bbox';
@ -9,35 +9,23 @@ import MAP_STYLE from './map-style';
const TOKEN = ''; // Set your mapbox token here
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
viewport: {
latitude: 37.785164,
longitude: -122.4,
zoom: 11,
bearing: 0,
pitch: 0
},
popupInfo: null
};
export default function App() {
const [viewport, setViewport] = useState({
latitude: 37.78,
longitude: -122.4,
zoom: 11,
bearing: 0,
pitch: 0
});
this._map = React.createRef();
}
_updateViewport = viewport => {
this.setState({viewport});
};
_onClick = event => {
const onClick = event => {
const feature = event.features[0];
if (feature) {
// calculate the bounding box of the feature
const [minLng, minLat, maxLng, maxLat] = bbox(feature);
// construct a viewport instance from the current state
const viewport = new WebMercatorViewport(this.state.viewport);
const {longitude, latitude, zoom} = viewport.fitBounds(
const vp = new WebMercatorViewport(viewport);
const {longitude, latitude, zoom} = vp.fitBounds(
[
[minLng, minLat],
[maxLng, maxLat]
@ -47,40 +35,34 @@ export default class App extends Component {
}
);
this.setState({
viewport: {
...this.state.viewport,
longitude,
latitude,
zoom,
transitionInterpolator: new LinearInterpolator({
around: [event.offsetCenter.x, event.offsetCenter.y]
}),
transitionDuration: 1000
}
setViewport({
...viewport,
longitude,
latitude,
zoom,
transitionInterpolator: new LinearInterpolator({
around: [event.offsetCenter.x, event.offsetCenter.y]
}),
transitionDuration: 1000
});
}
};
render() {
const {viewport} = this.state;
return (
return (
<>
<MapGL
ref={this._map}
mapStyle={MAP_STYLE}
interactiveLayerIds={['sf-neighborhoods-fill']}
{...viewport}
width="100%"
height="100%"
onClick={this._onClick}
onViewportChange={this._updateViewport}
mapStyle={MAP_STYLE}
interactiveLayerIds={['sf-neighborhoods-fill']}
onClick={onClick}
onViewportChange={v => setViewport(v)}
mapboxApiAccessToken={TOKEN}
>
<ControlPanel containerComponent={this.props.containerComponent} />
</MapGL>
);
}
/>
<ControlPanel />
</>
);
}
export function renderToDom(container) {

View File

@ -1,21 +1,20 @@
import * as React from 'react';
import {PureComponent} from 'react';
export default class ControlPanel extends PureComponent {
render() {
return (
<div className="control-panel">
<h3>Zoom to Bounding Box</h3>
<p>Click on a San Fransisco Neighborhood to zoom in.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/zoom-to-bounds"
target="_new"
>
View Code
</a>
</div>
function ControlPanel() {
return (
<div className="control-panel">
<h3>Zoom to Bounding Box</h3>
<p>Click on a San Fransisco Neighborhood to zoom in.</p>
<div className="source-link">
<a
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/zoom-to-bounds"
target="_new"
>
View Code
</a>
</div>
);
}
</div>
);
}
export default React.memo(ControlPanel);

View File

@ -62,7 +62,7 @@
margin: 24px;
padding: 12px 24px;
position: absolute;
top: 0;
top: 60px;
right: 0;
outline: none;
cursor: auto;