mirror of
https://github.com/visgl/react-map-gl.git
synced 2026-01-18 15:54:22 +00:00
Website and examples polish (#294)
- Add geojson example - Add introduction, credit and link to source to all examples - CSS bug fixes - Remove unused website components - Header image
This commit is contained in:
parent
5e65c18ee5
commit
dd5baee74e
@ -3,15 +3,18 @@ body {
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.city-pin {
|
||||
cursor: pointer;
|
||||
fill: #d00;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.nav {
|
||||
.options-panel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 10px;
|
||||
max-width: 320px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
padding: 12px 24px;
|
||||
margin: 20px;
|
||||
font-size: 13px;
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@ -3,9 +3,11 @@ import React, {Component} from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import MapGL, {Marker, Popup, NavigationControl} from 'react-map-gl';
|
||||
|
||||
import ControlPanel from './control-panel';
|
||||
import CityPin from './city-pin';
|
||||
import CityInfo from './city-info';
|
||||
|
||||
import CITIES from './cities.json';
|
||||
import CITIES from '../../data/cities.json';
|
||||
|
||||
const token = process.env.MapboxAccessToken; // eslint-disable-line
|
||||
|
||||
@ -16,7 +18,7 @@ if (!token) {
|
||||
const navStyle = {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
padding: '10px'
|
||||
};
|
||||
|
||||
@ -28,29 +30,16 @@ export default class App extends Component {
|
||||
viewport: {
|
||||
latitude: 37.785164,
|
||||
longitude: -100,
|
||||
zoom: 4,
|
||||
zoom: 3.5,
|
||||
bearing: 0,
|
||||
pitch: 0,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
width: 500,
|
||||
height: 500,
|
||||
},
|
||||
popupInfo: null
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.width !== this.state.viewport.width ||
|
||||
nextProps.height !== this.state.viewport.height) {
|
||||
this.setState({
|
||||
viewport: {
|
||||
...this.state.viewport,
|
||||
width: nextProps.width,
|
||||
height: nextProps.height
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('resize', this._resize);
|
||||
this._resize();
|
||||
@ -60,19 +49,18 @@ export default class App extends Component {
|
||||
window.removeEventListener('resize', this._resize);
|
||||
}
|
||||
|
||||
_updateViewport = (viewport) => {
|
||||
this.setState({viewport});
|
||||
}
|
||||
|
||||
_resize = () => {
|
||||
const {widthOffset, heightOffset} = this.props;
|
||||
this.setState({
|
||||
viewport: {
|
||||
...this.state.viewport,
|
||||
width: window.innerWidth - widthOffset,
|
||||
height: window.innerHeight - heightOffset,
|
||||
width: this.props.width || window.innerWidth,
|
||||
height: this.props.height || window.innerHeight
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
_updateViewport = (viewport) => {
|
||||
this.setState({viewport});
|
||||
}
|
||||
|
||||
_renderCityMarker = (city, index) => {
|
||||
@ -94,7 +82,7 @@ export default class App extends Component {
|
||||
longitude={popupInfo.longitude}
|
||||
latitude={popupInfo.latitude}
|
||||
onClose={() => this.setState({popupInfo: null})} >
|
||||
{popupInfo.city}, {popupInfo.state}
|
||||
<CityInfo info={popupInfo} />
|
||||
</Popup>
|
||||
);
|
||||
}
|
||||
@ -118,15 +106,10 @@ export default class App extends Component {
|
||||
<NavigationControl onViewportChange={this._updateViewport} />
|
||||
</div>
|
||||
|
||||
<ControlPanel />
|
||||
|
||||
</MapGL>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Used to render properly in docs. Ignore these props or remove if you're
|
||||
// copying this as a starting point.
|
||||
App.defaultProps = {
|
||||
widthOffset: 0,
|
||||
heightOffset: 0
|
||||
};
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
[
|
||||
{"city":"New York","state":"New York","latitude":40.6643,"longitude":-73.9385},
|
||||
{"city":"Los Angeles","state":"California","latitude":34.0194,"longitude":-118.4108},
|
||||
{"city":"Chicago","state":"Illinois","latitude":41.8376,"longitude":-87.6818},
|
||||
{"city":"Houston","state":"Texas","latitude":29.7805,"longitude":-95.3863},
|
||||
{"city":"Phoenix","state":"Arizona","latitude":33.5722,"longitude":-112.0880},
|
||||
{"city":"Philadelphia","state":"Pennsylvania","latitude":40.0094,"longitude":-75.1333},
|
||||
{"city":"San Antonio","state":"Texas","latitude":29.4724,"longitude":-98.5251},
|
||||
{"city":"San Diego","state":"California","latitude":32.8153,"longitude":-117.1350},
|
||||
{"city":"Dallas","state":"Texas","latitude":32.7757,"longitude":-96.7967},
|
||||
{"city":"San Jose","state":"California","latitude":37.2969,"longitude":-121.8193},
|
||||
{"city":"Austin","state":"Texas","latitude":30.3072,"longitude":-97.7560},
|
||||
{"city":"Jacksonville","state":"Florida","latitude":30.3370,"longitude":-81.6613},
|
||||
{"city":"San Francisco","state":"California","latitude":37.7751,"longitude":-122.4193},
|
||||
{"city":"Columbus","state":"Ohio","latitude":39.9848,"longitude":-82.9850},
|
||||
{"city":"Indianapolis","state":"Indiana","latitude":39.7767,"longitude":-86.1459},
|
||||
{"city":"Fort Worth","state":"Texas","latitude":32.7795,"longitude":-97.3463},
|
||||
{"city":"Charlotte","state":"North Carolina","latitude":35.2087,"longitude":-80.8307},
|
||||
{"city":"Seattle","state":"Washington","latitude":47.6205,"longitude":-122.3509},
|
||||
{"city":"Denver","state":"Colorado","latitude":39.7618,"longitude":-104.8806},
|
||||
{"city":"El Paso","state":"Texas","latitude":31.8484,"longitude":-106.4270}
|
||||
]
|
||||
21
examples/controls/src/city-info.js
Normal file
21
examples/controls/src/city-info.js
Normal file
@ -0,0 +1,21 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
|
||||
export default class CityInfo extends PureComponent {
|
||||
|
||||
render() {
|
||||
const {info} = this.props;
|
||||
const displayName = `${info.city}, ${info.state}`;
|
||||
|
||||
return (
|
||||
<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} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -4,14 +4,20 @@ 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,
|
||||
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
|
||||
C20.1,15.8,20.2,15.8,20.2,15.7z`;
|
||||
|
||||
const pinStyle = {
|
||||
cursor: 'pointer',
|
||||
fill: '#d00',
|
||||
stroke: 'none'
|
||||
};
|
||||
|
||||
export default class CityPin extends PureComponent {
|
||||
|
||||
render() {
|
||||
const {size = 20, onClick} = this.props;
|
||||
|
||||
return (
|
||||
<svg height={size} viewBox='0 0 24 24' className="city-pin"
|
||||
style={{transform: `translate(${-size/2}px,${-size}px)`}}
|
||||
<svg height={size} viewBox='0 0 24 24'
|
||||
style={{...pinStyle, transform: `translate(${-size/2}px,${-size}px)`}}
|
||||
onClick={onClick} >
|
||||
<path d={ICON}/>
|
||||
</svg>
|
||||
|
||||
16
examples/controls/src/control-panel.js
Normal file
16
examples/controls/src/control-panel.js
Normal file
@ -0,0 +1,16 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
|
||||
export default class ControlPanel extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div className="options-panel" tabIndex="0">
|
||||
<h3>Marker, Popup, and NavigationControl</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/uber/react-map-gl/tree/master/examples/controls" target="_new">View Code ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,22 @@
|
||||
[
|
||||
{
|
||||
"cityName": "San Francisco",
|
||||
"latitude": 37.7749,
|
||||
"longitude": -122.4194
|
||||
},
|
||||
{
|
||||
"cityName": "Pittsburgh",
|
||||
"latitude": 40.4406,
|
||||
"longitude": -79.9959
|
||||
}
|
||||
{"city":"New York","population":"8,175,133","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Above_Gotham.jpg/240px-Above_Gotham.jpg","state":"New York","latitude":40.6643,"longitude":-73.9385},
|
||||
{"city":"Los Angeles","population":"3,792,621","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/5/57/LA_Skyline_Mountains2.jpg/240px-LA_Skyline_Mountains2.jpg","state":"California","latitude":34.0194,"longitude":-118.4108},
|
||||
{"city":"Chicago","population":"2,695,598","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/8/85/2008-06-10_3000x1000_chicago_skyline.jpg/240px-2008-06-10_3000x1000_chicago_skyline.jpg","state":"Illinois","latitude":41.8376,"longitude":-87.6818},
|
||||
{"city":"Houston","population":"2,100,263","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Aerial_views_of_the_Houston%2C_Texas%2C_28005u.jpg/240px-Aerial_views_of_the_Houston%2C_Texas%2C_28005u.jpg","state":"Texas","latitude":29.7805,"longitude":-95.3863},
|
||||
{"city":"Phoenix","population":"1,445,632","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Downtown_Phoenix_Aerial_Looking_Northeast.jpg/207px-Downtown_Phoenix_Aerial_Looking_Northeast.jpg","state":"Arizona","latitude":33.5722,"longitude":-112.0880},
|
||||
{"city":"Philadelphia","population":"1,526,006","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Philly_skyline.jpg/240px-Philly_skyline.jpg","state":"Pennsylvania","latitude":40.0094,"longitude":-75.1333},
|
||||
{"city":"San Antonio","population":"1,327,407","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/f/ff/Downtown_San_Antonio_View.JPG/240px-Downtown_San_Antonio_View.JPG","state":"Texas","latitude":29.4724,"longitude":-98.5251},
|
||||
{"city":"San Diego","population":"1,307,402","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/5/53/US_Navy_110604-N-NS602-574_Navy_and_Marine_Corps_personnel%2C_along_with_community_leaders_from_the_greater_San_Diego_area_come_together_to_commemora.jpg/240px-US_Navy_110604-N-NS602-574_Navy_and_Marine_Corps_personnel%2C_along_with_community_leaders_from_the_greater_San_Diego_area_come_together_to_commemora.jpg","state":"California","latitude":32.8153,"longitude":-117.1350},
|
||||
{"city":"Dallas","population":"1,197,816","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/a/ab/Dallas_skyline_daytime.jpg/240px-Dallas_skyline_daytime.jpg","state":"Texas","latitude":32.7757,"longitude":-96.7967},
|
||||
{"city":"San Jose","population":"945,942","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/1/1e/Downtown_San_Jose_skyline.PNG/240px-Downtown_San_Jose_skyline.PNG","state":"California","latitude":37.2969,"longitude":-121.8193},
|
||||
{"city":"Austin","population":"790,390","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/9/97/Austin2012-12-01.JPG/240px-Austin2012-12-01.JPG","state":"Texas","latitude":30.3072,"longitude":-97.7560},
|
||||
{"city":"Jacksonville","population":"821,784","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/Skyline_of_Jacksonville_FL%2C_South_view_20160706_1.jpg/240px-Skyline_of_Jacksonville_FL%2C_South_view_20160706_1.jpg","state":"Florida","latitude":30.3370,"longitude":-81.6613},
|
||||
{"city":"San Francisco","population":"805,235","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/San_Francisco_skyline_from_Coit_Tower.jpg/240px-San_Francisco_skyline_from_Coit_Tower.jpg","state":"California","latitude":37.7751,"longitude":-122.4193},
|
||||
{"city":"Columbus","population":"787,033","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Columbus-ohio-skyline-panorama.jpg/240px-Columbus-ohio-skyline-panorama.jpg","state":"Ohio","latitude":39.9848,"longitude":-82.9850},
|
||||
{"city":"Indianapolis","population":"820,445","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/1/16/Downtown_indy_from_parking_garage_zoom.JPG/213px-Downtown_indy_from_parking_garage_zoom.JPG","state":"Indiana","latitude":39.7767,"longitude":-86.1459},
|
||||
{"city":"Fort Worth","population":"741,206","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/d/db/FortWorthTexasSkylineW.jpg/240px-FortWorthTexasSkylineW.jpg","state":"Texas","latitude":32.7795,"longitude":-97.3463},
|
||||
{"city":"Charlotte","population":"731,424","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/7/7d/Charlotte_skyline45647.jpg/222px-Charlotte_skyline45647.jpg","state":"North Carolina","latitude":35.2087,"longitude":-80.8307},
|
||||
{"city":"Seattle","population":"608,660","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/3/36/SeattleI5Skyline.jpg/240px-SeattleI5Skyline.jpg","state":"Washington","latitude":47.6205,"longitude":-122.3509},
|
||||
{"city":"Denver","population":"600,158","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/DenverCP.JPG/240px-DenverCP.JPG","state":"Colorado","latitude":39.7618,"longitude":-104.8806},
|
||||
{"city":"El Paso","population":"649,121","image":"http://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Downtown_El_Paso_at_sunset.jpeg/240px-Downtown_El_Paso_at_sunset.jpeg","state":"Texas","latitude":31.8484,"longitude":-106.4270}
|
||||
]
|
||||
56
examples/data/us-income.geojson
Normal file
56
examples/data/us-income.geojson
Normal file
File diff suppressed because one or more lines are too long
7
examples/geojson-animation/README.md
Normal file
7
examples/geojson-animation/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
<div align="center">
|
||||
<img src="https://avatars3.githubusercontent.com/u/2105791?v=3&s=200" />
|
||||
</div>
|
||||
|
||||
## Example: GeoJSON
|
||||
|
||||
This example showcases how to dynamically add and update custom data sources.
|
||||
20
examples/geojson-animation/app.css
Normal file
20
examples/geojson-animation/app.css
Normal file
@ -0,0 +1,20 @@
|
||||
body {
|
||||
margin: 0;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.options-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;
|
||||
margin: 20px;
|
||||
font-size: 13px;
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
}
|
||||
11
examples/geojson-animation/index.html
Normal file
11
examples/geojson-animation/index.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='UTF-8' />
|
||||
<title>react-map-gl GeoJSON Example</title>
|
||||
<link rel="stylesheet" type="text/css" href="app.css">
|
||||
</head>
|
||||
<body>
|
||||
<script src='bundle.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
28
examples/geojson-animation/package.json
Normal file
28
examples/geojson-animation/package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --progress --hot --open",
|
||||
"start-local": "webpack-dev-server --env.local --progress --hot --open"
|
||||
},
|
||||
"dependencies": {
|
||||
"immutable": "^3.8.1",
|
||||
"react": "^15.4.1",
|
||||
"react-dom": "^15.4.1",
|
||||
"react-map-gl": ">=3.0.0-alpha.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.21.0",
|
||||
"babel-loader": "^6.2.10",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"babel-preset-react": "^6.16.0",
|
||||
"babel-preset-stage-2": "^6.18.0",
|
||||
"webpack": "^2.4.0",
|
||||
"webpack-dev-server": "^2.4.0"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"es2015",
|
||||
"stage-2",
|
||||
"react"
|
||||
]
|
||||
}
|
||||
}
|
||||
92
examples/geojson-animation/src/app.js
Normal file
92
examples/geojson-animation/src/app.js
Normal file
@ -0,0 +1,92 @@
|
||||
/* global window */
|
||||
import React, {Component} from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import MapGL from 'react-map-gl';
|
||||
|
||||
import ControlPanel from './control-panel';
|
||||
import {defaultMapStyle, pointLayer} from './map-style.js';
|
||||
import {pointOnCircle} from './utils';
|
||||
import {fromJS} from 'immutable';
|
||||
|
||||
const token = process.env.MapboxAccessToken; // eslint-disable-line
|
||||
|
||||
if (!token) {
|
||||
throw new Error('Please specify a valid mapbox token');
|
||||
}
|
||||
|
||||
let animation = null;
|
||||
|
||||
export default class App extends Component {
|
||||
|
||||
state = {
|
||||
mapStyle: defaultMapStyle,
|
||||
viewport: {
|
||||
latitude: 0,
|
||||
longitude: -100,
|
||||
zoom: 3,
|
||||
bearing: 0,
|
||||
pitch: 0,
|
||||
width: 500,
|
||||
height: 500
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('resize', this._resize);
|
||||
this._resize();
|
||||
animation = window.requestAnimationFrame(this._animatePoint);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this._resize);
|
||||
window.cancelAnimationFrame(animation);
|
||||
}
|
||||
|
||||
_resize = () => {
|
||||
this.setState({
|
||||
viewport: {
|
||||
...this.state.viewport,
|
||||
width: this.props.width || window.innerWidth,
|
||||
height: this.props.height || window.innerHeight
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
_animatePoint = () => {
|
||||
this._updatePointData(pointOnCircle({center: [-100, 0], angle: Date.now() / 1000, radius: 20}));
|
||||
animation = window.requestAnimationFrame(this._animatePoint);
|
||||
}
|
||||
|
||||
_updatePointData = pointData => {
|
||||
let {mapStyle} = this.state;
|
||||
if (!mapStyle.hasIn(['source', 'point'])) {
|
||||
mapStyle = mapStyle
|
||||
// Add geojson source to map
|
||||
.setIn(['sources', 'point'], fromJS({type: 'geojson'}))
|
||||
// Add point layer to map
|
||||
.set('layers', mapStyle.get('layers').push(pointLayer));
|
||||
}
|
||||
// Update data source
|
||||
mapStyle = mapStyle.setIn(['sources', 'point', 'data'], pointData);
|
||||
|
||||
this.setState({mapStyle});
|
||||
}
|
||||
|
||||
_onViewportChange = viewport => this.setState({viewport});
|
||||
|
||||
render() {
|
||||
|
||||
const {viewport, mapStyle} = this.state;
|
||||
|
||||
return (
|
||||
<MapGL
|
||||
{...viewport}
|
||||
mapStyle={mapStyle}
|
||||
onViewportChange={this._onViewportChange}
|
||||
mapboxApiAccessToken={token} >
|
||||
<ControlPanel />
|
||||
</MapGL>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
15
examples/geojson-animation/src/control-panel.js
Normal file
15
examples/geojson-animation/src/control-panel.js
Normal file
@ -0,0 +1,15 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
|
||||
export default class ControlPanel extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div className="options-panel" tabIndex="0">
|
||||
<h3>Animated GeoJSON</h3>
|
||||
<p>Render animation by updating GeoJSON data source.</p>
|
||||
<div className="source-link">
|
||||
<a href="https://github.com/uber/react-map-gl/tree/master/examples/geojson-animation" target="_new">View Code ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
14
examples/geojson-animation/src/map-style.js
Normal file
14
examples/geojson-animation/src/map-style.js
Normal file
@ -0,0 +1,14 @@
|
||||
import {fromJS} from 'immutable';
|
||||
import MAP_STYLE from '../../map-style-basic-v8.json';
|
||||
|
||||
export const pointLayer = fromJS({
|
||||
id: 'point',
|
||||
source: 'point',
|
||||
type: 'circle',
|
||||
paint: {
|
||||
'circle-radius': 10,
|
||||
'circle-color': '#007cbf'
|
||||
}
|
||||
});
|
||||
|
||||
export const defaultMapStyle = fromJS(MAP_STYLE);
|
||||
6
examples/geojson-animation/src/root.js
Normal file
6
examples/geojson-animation/src/root.js
Normal file
@ -0,0 +1,6 @@
|
||||
/* global document */
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './app';
|
||||
|
||||
ReactDOM.render(<App/>, document.body.appendChild(document.createElement('div')));
|
||||
9
examples/geojson-animation/src/utils.js
Normal file
9
examples/geojson-animation/src/utils.js
Normal file
@ -0,0 +1,9 @@
|
||||
export function pointOnCircle({center, angle, radius}) {
|
||||
return {
|
||||
type: 'Point',
|
||||
coordinates: [
|
||||
center[0] + Math.cos(angle) * radius,
|
||||
center[1] + Math.sin(angle) * radius
|
||||
]
|
||||
};
|
||||
}
|
||||
40
examples/geojson-animation/webpack.config.js
Normal file
40
examples/geojson-animation/webpack.config.js
Normal file
@ -0,0 +1,40 @@
|
||||
// NOTE: To use this example standalone (e.g. outside of deck.gl repo)
|
||||
// delete the local development overrides at the bottom of this file
|
||||
|
||||
// avoid destructuring for older Node version support
|
||||
const resolve = require('path').resolve;
|
||||
const webpack = require('webpack');
|
||||
|
||||
const config = {
|
||||
entry: {
|
||||
app: resolve('./src/root.js')
|
||||
},
|
||||
|
||||
devtool: 'source-map',
|
||||
|
||||
module: {
|
||||
rules: [{
|
||||
// Compile ES2015 using bable
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
include: [resolve('.')],
|
||||
exclude: [/node_modules/]
|
||||
}]
|
||||
},
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
// From mapbox-gl-js README. Required for non-browserify bundlers (e.g. webpack):
|
||||
'mapbox-gl$': resolve('./node_modules/mapbox-gl/dist/mapbox-gl.js')
|
||||
}
|
||||
},
|
||||
|
||||
// Optional: Enables reading mapbox token from environment variable
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin(['MapboxAccessToken'])
|
||||
]
|
||||
};
|
||||
|
||||
// Enables bundling against src in this repo rather than the installed version
|
||||
module.exports = env => env && env.local ?
|
||||
require('../webpack.config.local')(config)(env) : config;
|
||||
42
examples/geojson/app.css
Normal file
42
examples/geojson/app.css
Normal file
@ -0,0 +1,42 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.options-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;
|
||||
margin: 20px;
|
||||
font-size: 13px;
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
outline: none;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
input {
|
||||
margin-left: 20px;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
margin: 8px;
|
||||
padding: 4px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: #fff;
|
||||
max-width: 300px;
|
||||
font-size: 10px;
|
||||
z-index: 9;
|
||||
pointer-events: none;
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset='UTF-8' />
|
||||
<title>react-map-gl GeoJSON Example</title>
|
||||
<style>body {margin: 0}</style>
|
||||
<link rel="stylesheet" type="text/css" href="app.css">
|
||||
</head>
|
||||
<body>
|
||||
<script src='bundle.js'></script>
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
"start-local": "webpack-dev-server --env.local --progress --hot --open"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-request": "^1.0.5",
|
||||
"d3-scale": "^1.0.6",
|
||||
"immutable": "^3.8.1",
|
||||
"react": "^15.4.1",
|
||||
"react-dom": "^15.4.1",
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
/* global window */
|
||||
/* global window, fetch */
|
||||
import React, {Component} from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import MapGL from 'react-map-gl';
|
||||
import ControlPanel from './control-panel';
|
||||
|
||||
import {defaultMapStyle, pointLayer} from './map-style.js';
|
||||
import {pointOnCircle} from './utils';
|
||||
import {defaultMapStyle, dataLayer} from './map-style.js';
|
||||
import {updatePercentiles} from './utils';
|
||||
import {fromJS} from 'immutable';
|
||||
import {json as requestJson} from 'd3-request';
|
||||
|
||||
const token = process.env.MapboxAccessToken; // eslint-disable-line
|
||||
|
||||
@ -13,85 +15,116 @@ if (!token) {
|
||||
throw new Error('Please specify a valid mapbox token');
|
||||
}
|
||||
|
||||
let animation = null;
|
||||
|
||||
export default class App extends Component {
|
||||
|
||||
state = {
|
||||
mapStyle: defaultMapStyle,
|
||||
year: 2015,
|
||||
data: null,
|
||||
hoveredFeature: null,
|
||||
viewport: {
|
||||
latitude: 0,
|
||||
latitude: 40,
|
||||
longitude: -100,
|
||||
zoom: 3,
|
||||
bearing: 0,
|
||||
pitch: 0,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
width: 500,
|
||||
height: 500
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('resize', this._resize);
|
||||
this._resize();
|
||||
animation = window.requestAnimationFrame(this._animatePoint);
|
||||
|
||||
requestJson('data/us-income.geojson', (error, response) => {
|
||||
if (!error) {
|
||||
this._loadData(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.cancelAnimationFrame(animation);
|
||||
}
|
||||
|
||||
_animatePoint = () => {
|
||||
this._updatePointData(pointOnCircle({center: [-100, 0], angle: Date.now() / 1000, radius: 20}));
|
||||
animation = window.requestAnimationFrame(this._animatePoint);
|
||||
}
|
||||
|
||||
_updatePointData = pointData => {
|
||||
let {mapStyle} = this.state;
|
||||
if (!mapStyle.hasIn(['source', 'point'])) {
|
||||
mapStyle = mapStyle
|
||||
// Add geojson source to map
|
||||
.setIn(['sources', 'point'], fromJS({type: 'geojson'}))
|
||||
// Add point layer to map
|
||||
.set('layers', mapStyle.get('layers').push(pointLayer));
|
||||
}
|
||||
// Update data source
|
||||
mapStyle = mapStyle.setIn(['sources', 'point', 'data'], pointData);
|
||||
|
||||
this.setState({mapStyle});
|
||||
window.removeEventListener('resize', this._resize);
|
||||
}
|
||||
|
||||
_resize = () => {
|
||||
const {widthOffset, heightOffset} = this.props;
|
||||
this.setState({
|
||||
viewport: {
|
||||
...this.state.viewport,
|
||||
width: window.innerWidth - widthOffset,
|
||||
height: window.innerHeight - heightOffset
|
||||
width: this.props.width || window.innerWidth,
|
||||
height: this.props.height || window.innerHeight
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
_loadData = data => {
|
||||
|
||||
updatePercentiles(data, f => f.properties.income[this.state.year]);
|
||||
|
||||
const mapStyle = defaultMapStyle
|
||||
// Add geojson source to map
|
||||
.setIn(['sources', 'incomeByState'], fromJS({type: 'geojson', data}))
|
||||
// Add point layer to map
|
||||
.set('layers', defaultMapStyle.get('layers').push(dataLayer));
|
||||
|
||||
this.setState({data, mapStyle});
|
||||
};
|
||||
|
||||
_updateSettings = (name, value) => {
|
||||
if (name === 'year') {
|
||||
this.setState({year: value});
|
||||
|
||||
const {data, mapStyle} = this.state;
|
||||
if (data) {
|
||||
updatePercentiles(data, f => f.properties.income[value]);
|
||||
const newMapStyle = mapStyle.setIn(['sources', 'incomeByState', 'data'], fromJS(data));
|
||||
this.setState({mapStyle: newMapStyle});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_onViewportChange = viewport => this.setState({viewport});
|
||||
|
||||
_onHover = event => {
|
||||
const {features, srcEvent: {offsetX, offsetY}} = event;
|
||||
const hoveredFeature = features && features.find(f => f.layer.id === 'data');
|
||||
|
||||
this.setState({hoveredFeature, x: offsetX, y: offsetY});
|
||||
};
|
||||
|
||||
_renderTooltip() {
|
||||
const {hoveredFeature, year, 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>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const {viewport, mapStyle} = this.state;
|
||||
|
||||
return (
|
||||
<MapGL
|
||||
{...viewport}
|
||||
mapStyle={mapStyle}
|
||||
onViewportChange={this._onViewportChange}
|
||||
mapboxApiAccessToken={token} >
|
||||
</MapGL>
|
||||
<div>
|
||||
<MapGL
|
||||
{...viewport}
|
||||
mapStyle={mapStyle}
|
||||
onViewportChange={this._onViewportChange}
|
||||
mapboxApiAccessToken={token}
|
||||
onHover={this._onHover} >
|
||||
|
||||
{this._renderTooltip()}
|
||||
|
||||
</MapGL>
|
||||
|
||||
<ControlPanel settings={this.state} onChange={this._updateSettings} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Used to render properly in docs. Ignore these props or remove if you're
|
||||
// copying this as a starting point.
|
||||
App.defaultProps = {
|
||||
widthOffset: 0,
|
||||
heightOffset: 0
|
||||
};
|
||||
|
||||
27
examples/geojson/src/control-panel.js
Normal file
27
examples/geojson/src/control-panel.js
Normal file
@ -0,0 +1,27 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
|
||||
export default class ControlPanel extends PureComponent {
|
||||
render() {
|
||||
const {settings} = this.props;
|
||||
|
||||
return (
|
||||
<div className="options-panel" tabIndex="0">
|
||||
<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/uber/react-map-gl/tree/master/examples/geojson" target="_new">View Code ↗</a>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<div key={name} 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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,28 @@
|
||||
import {fromJS} from 'immutable';
|
||||
import MAP_STYLE from '../../map-style-basic-v8.json';
|
||||
|
||||
export const pointLayer = fromJS({
|
||||
id: 'point',
|
||||
source: 'point',
|
||||
type: 'circle',
|
||||
// For more information on data-driven styles, see https://www.mapbox.com/help/gl-dds-ref/
|
||||
export const dataLayer = fromJS({
|
||||
id: 'data',
|
||||
source: 'incomeByState',
|
||||
type: 'fill',
|
||||
interactive: true,
|
||||
paint: {
|
||||
'circle-radius': 10,
|
||||
'circle-color': '#007cbf'
|
||||
'fill-color': {
|
||||
property: 'percentile',
|
||||
stops: [
|
||||
[0, '#3288bd'],
|
||||
[1, '#66c2a5'],
|
||||
[2, '#abdda4'],
|
||||
[3, '#e6f598'],
|
||||
[4, '#ffffbf'],
|
||||
[5, '#fee08b'],
|
||||
[6, '#fdae61'],
|
||||
[7, '#f46d43'],
|
||||
[8, '#d53e4f']
|
||||
]
|
||||
},
|
||||
'fill-opacity': 0.8
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
export function pointOnCircle({center, angle, radius}) {
|
||||
return {
|
||||
type: 'Point',
|
||||
coordinates: [
|
||||
center[0] + Math.cos(angle) * radius,
|
||||
center[1] + Math.sin(angle) * radius
|
||||
]
|
||||
};
|
||||
import {range} from 'd3-array';
|
||||
import {scaleQuantile} from 'd3-scale';
|
||||
|
||||
export function updatePercentiles(featureCollection, accessor) {
|
||||
const {features} = featureCollection;
|
||||
const scale = scaleQuantile().domain(features.map(accessor)).range(range(9));
|
||||
features.forEach(f => {
|
||||
const value = accessor(f);
|
||||
f.properties.value = value;
|
||||
f.properties.percentile = scale(value);
|
||||
});
|
||||
}
|
||||
|
||||
@ -10,6 +10,13 @@ const config = {
|
||||
app: resolve('./src/root.js')
|
||||
},
|
||||
|
||||
devServer: {
|
||||
contentBase: [
|
||||
__dirname,
|
||||
resolve(__dirname, '../')
|
||||
]
|
||||
},
|
||||
|
||||
devtool: 'source-map',
|
||||
|
||||
module: {
|
||||
|
||||
@ -3,8 +3,11 @@ body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
.options-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;
|
||||
@ -13,6 +16,7 @@ body {
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
label {
|
||||
@ -24,32 +28,3 @@ input {
|
||||
margin-left: 20px;
|
||||
max-width: 60px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ if (!token) {
|
||||
throw new Error('Please specify a valid mapbox token');
|
||||
}
|
||||
|
||||
import MARKER_STYLE from './marker-style';
|
||||
|
||||
export default class App extends Component {
|
||||
|
||||
state = {
|
||||
@ -21,8 +23,8 @@ export default class App extends Component {
|
||||
zoom: 11,
|
||||
bearing: 0,
|
||||
pitch: 50,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
width: 500,
|
||||
height: 500
|
||||
},
|
||||
settings: {
|
||||
dragPan: true,
|
||||
@ -42,13 +44,16 @@ export default class App extends Component {
|
||||
this._resize();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this._resize);
|
||||
}
|
||||
|
||||
_resize = () => {
|
||||
const {widthOffset, heightOffset} = this.props;
|
||||
this.setState({
|
||||
viewport: {
|
||||
...this.state.viewport,
|
||||
width: window.innerWidth - widthOffset,
|
||||
height: window.innerHeight - heightOffset
|
||||
width: this.props.width || window.innerWidth,
|
||||
height: this.props.height || window.innerHeight
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -79,6 +84,7 @@ export default class App extends Component {
|
||||
mapStyle="mapbox://styles/mapbox/dark-v9"
|
||||
onViewportChange={this._onViewportChange}
|
||||
mapboxApiAccessToken={token} >
|
||||
<style>{MARKER_STYLE}</style>
|
||||
{ bartStations.map(this._renderMarker) }
|
||||
<ControlPanel settings={settings} onChange={this._onSettingChange} />
|
||||
</MapGL>
|
||||
@ -86,10 +92,3 @@ export default class App extends Component {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Used to render properly in docs. Ignore these props or remove if you're
|
||||
// copying this as a starting point.
|
||||
App.defaultProps = {
|
||||
widthOffset: 0,
|
||||
heightOffset: 0
|
||||
};
|
||||
|
||||
@ -10,7 +10,7 @@ export default class ControlPanel extends PureComponent {
|
||||
|
||||
_renderCheckbox(name, value) {
|
||||
return (
|
||||
<div key={name}>
|
||||
<div key={name} className="input">
|
||||
<label>{this._formatSettingName(name)}</label>
|
||||
<input type="checkbox" checked={value}
|
||||
onChange={evt => this.props.onChange(name, evt.target.checked)} />
|
||||
@ -20,7 +20,7 @@ export default class ControlPanel extends PureComponent {
|
||||
|
||||
_renderNumericInput(name, value) {
|
||||
return (
|
||||
<div key={name}>
|
||||
<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))} />
|
||||
@ -43,7 +43,14 @@ export default class ControlPanel extends PureComponent {
|
||||
const {settings} = this.props;
|
||||
|
||||
return (
|
||||
<div className="control-panel">
|
||||
<div className="options-panel" tabIndex="0">
|
||||
<h3>Limit Map Interaction</h3>
|
||||
<p>Turn interactive features off/on.</p>
|
||||
<div className="source-link">
|
||||
<a href="https://github.com/uber/react-map-gl/tree/master/examples/interaction" target="_new">View Code ↗</a>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
{ Object.keys(settings).map(name => this._renderSetting(name, settings[name])) }
|
||||
</div>
|
||||
);
|
||||
|
||||
30
examples/interaction/src/marker-style.js
Normal file
30
examples/interaction/src/marker-style.js
Normal file
@ -0,0 +1,30 @@
|
||||
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;
|
||||
}
|
||||
`;
|
||||
@ -1,20 +0,0 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import {Marker} from 'react-map-gl';
|
||||
import bartStations from './bart-station.json';
|
||||
|
||||
export default class Markers extends PureComponent {
|
||||
|
||||
_renderMarker(station, i) {
|
||||
const {name, coordinates} = station;
|
||||
return (
|
||||
<Marker key={i} longitude={coordinates[0]} latitude={coordinates[1]} >
|
||||
{name}
|
||||
</Marker>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return bartStations.map(this._renderMarker);
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,8 +3,11 @@ body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
.options-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;
|
||||
@ -13,6 +16,7 @@ body {
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
label {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import React, {Component} from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import MapGL from 'react-map-gl';
|
||||
import StyleControls from './style-controls';
|
||||
import ControlPanel from './control-panel';
|
||||
|
||||
const token = process.env.MapboxAccessToken; // eslint-disable-line
|
||||
|
||||
@ -20,8 +20,8 @@ export default class App extends Component {
|
||||
zoom: 15.5,
|
||||
bearing: 0,
|
||||
pitch: 0,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
width: 500,
|
||||
height: 500
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,13 +30,16 @@ export default class App extends Component {
|
||||
this._resize();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this._resize);
|
||||
}
|
||||
|
||||
_resize = () => {
|
||||
const {widthOffset, heightOffset} = this.props;
|
||||
this.setState({
|
||||
viewport: {
|
||||
...this.state.viewport,
|
||||
width: window.innerWidth - widthOffset,
|
||||
height: window.innerHeight - heightOffset
|
||||
width: this.props.width || window.innerWidth,
|
||||
height: this.props.height || window.innerHeight
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -55,16 +58,9 @@ export default class App extends Component {
|
||||
mapStyle={mapStyle}
|
||||
onViewportChange={this._onViewportChange}
|
||||
mapboxApiAccessToken={token} >
|
||||
<StyleControls onChange={this._onStyleChange} />
|
||||
<ControlPanel onChange={this._onStyleChange} />
|
||||
</MapGL>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Used to render properly in docs. Ignore these props or remove if you're
|
||||
// copying this as a starting point.
|
||||
App.defaultProps = {
|
||||
widthOffset: 0,
|
||||
heightOffset: 0
|
||||
};
|
||||
|
||||
@ -91,7 +91,7 @@ export default class StyleControls extends PureComponent {
|
||||
const {visibility, color} = this.state;
|
||||
|
||||
return (
|
||||
<div key={name}>
|
||||
<div key={name} className="input">
|
||||
<label>{name}</label>
|
||||
<input type="checkbox" checked={visibility[name]}
|
||||
onChange={this._onVisibilityChange.bind(this, name)} />
|
||||
@ -103,7 +103,13 @@ export default class StyleControls extends PureComponent {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="control-panel">
|
||||
<div className="options-panel" tabIndex="0">
|
||||
<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/uber/react-map-gl/tree/master/examples/layers" target="_new">View Code ↗</a>
|
||||
</div>
|
||||
<hr />
|
||||
{ categories.map(name => this._renderLayerControl(name)) }
|
||||
</div>
|
||||
);
|
||||
@ -8,8 +8,10 @@ import ClickExample from '../views/click';
|
||||
// Standalone
|
||||
import Controls from '../../controls/src/app';
|
||||
import GeoJson from '../../geojson/src/app';
|
||||
import GeoJsonAnimation from '../../geojson-animation/src/app';
|
||||
import Interaction from '../../interaction/src/app';
|
||||
import Layers from '../../layers/src/app';
|
||||
import ViewportAnimation from '../../viewport-animation/src/app';
|
||||
|
||||
export const BASIC_EXAMPLES = 'basicExamples';
|
||||
export const STANDALONE_EXAMPLES = 'standalonExamples';
|
||||
@ -17,6 +19,39 @@ export const STANDALONE_EXAMPLES = 'standalonExamples';
|
||||
export const DEFAULT_EXAMPLE = 'markerExample';
|
||||
|
||||
// TOC
|
||||
export const standaloneExamples = [
|
||||
{
|
||||
path: 'layersExample',
|
||||
name: 'Dynamic Styling',
|
||||
component: Layers
|
||||
},
|
||||
{
|
||||
path: 'controlsExample',
|
||||
name: 'Markers & Popups',
|
||||
component: Controls
|
||||
},
|
||||
{
|
||||
path: 'geojsonExample',
|
||||
name: 'GeoJSON',
|
||||
component: GeoJson
|
||||
},
|
||||
{
|
||||
path: 'geojsonAnimationExample',
|
||||
name: 'GeoJSON Animation',
|
||||
component: GeoJsonAnimation
|
||||
},
|
||||
{
|
||||
path: 'interactionExample',
|
||||
name: 'Limit Map Interaction',
|
||||
component: Interaction
|
||||
},
|
||||
{
|
||||
path: 'viewportAnimationExample',
|
||||
name: 'Camera Transition',
|
||||
component: ViewportAnimation
|
||||
}
|
||||
];
|
||||
|
||||
export default [
|
||||
{
|
||||
path: BASIC_EXAMPLES,
|
||||
@ -56,28 +91,7 @@ export default [
|
||||
},
|
||||
{
|
||||
path: STANDALONE_EXAMPLES,
|
||||
name: 'Advanced Examples',
|
||||
children: [
|
||||
{
|
||||
path: 'controlsExample',
|
||||
name: 'Controls',
|
||||
component: Controls
|
||||
},
|
||||
{
|
||||
path: 'geoJsonExample',
|
||||
name: 'GeoJSON',
|
||||
component: GeoJson
|
||||
},
|
||||
{
|
||||
path: 'interactionExample',
|
||||
name: 'Interaction',
|
||||
component: Interaction
|
||||
},
|
||||
{
|
||||
path: 'layersExample',
|
||||
name: 'Layers',
|
||||
component: Layers
|
||||
}
|
||||
]
|
||||
name: 'Standalone Examples',
|
||||
children: standaloneExamples
|
||||
}
|
||||
];
|
||||
|
||||
@ -33,7 +33,7 @@ export default class App extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentWillMount() {
|
||||
window.onresize = () => this.setState({
|
||||
viewport: {
|
||||
width: window.innerWidth - 240,
|
||||
@ -52,7 +52,7 @@ export default class App extends Component {
|
||||
const ExampleComponent = component;
|
||||
return (
|
||||
<div className="flexbox-item flexbox-item--fill">
|
||||
<ExampleComponent widthOffset={240} {...viewport} />
|
||||
<ExampleComponent {...viewport} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
"d3-array": "^1.0.1",
|
||||
"d3-color": "^1.0.1",
|
||||
"d3-random": "^1.0.2",
|
||||
"d3-request": "^1.0.5",
|
||||
"d3-scale": "^1.0.3",
|
||||
"immutable": "^3.8.1",
|
||||
"react": "^15.4.1",
|
||||
|
||||
@ -105,6 +105,9 @@
|
||||
box-shadow: 0 0 4px rgba(0, 0, 0, 0.15);
|
||||
margin: 24px;
|
||||
padding: 12px 24px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
hr {
|
||||
margin: 12px -24px;
|
||||
@ -128,8 +131,9 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
label {
|
||||
text-transform: uppercase;
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
width: 50%;
|
||||
margin-right: 10%;
|
||||
color: $black-40;
|
||||
margin-bottom: 4px;
|
||||
@ -138,10 +142,13 @@
|
||||
font-size: 0.9em;
|
||||
display: inline-block;
|
||||
padding: 0 4px;
|
||||
width: 50%;
|
||||
width: 40%;
|
||||
height: 20px;
|
||||
line-height: 1.833;
|
||||
}
|
||||
input[type="checkbox"], input[type="radio"], input[type="color"] {
|
||||
width: 20%;
|
||||
}
|
||||
input {
|
||||
border: solid 1px #ccc;
|
||||
|
||||
@ -268,39 +275,3 @@ code {
|
||||
padding: 40px 12px 96px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-demo {
|
||||
position: relative;
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.tooltip {
|
||||
padding: 4px 12px;
|
||||
max-width: 240px;
|
||||
max-height: 320px;
|
||||
box-sizing: content-box;
|
||||
overflow-y: hidden;
|
||||
|
||||
h5 {
|
||||
font-size: 1em;
|
||||
}
|
||||
p {
|
||||
display: none;
|
||||
text-indent: 4px;
|
||||
}
|
||||
}
|
||||
.tooltip.interactive {
|
||||
border: solid 4px transparent;
|
||||
margin: -4px;
|
||||
background: $white;
|
||||
color: $black;
|
||||
pointer-events: all;
|
||||
width: 240px;
|
||||
overflow-y: auto;
|
||||
|
||||
p {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,40 +12,6 @@ $topbar-height: 64px;
|
||||
@import 'gallery';
|
||||
@import '../node_modules/mapbox-gl/dist/mapbox-gl.css';
|
||||
|
||||
.control-panel {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
padding: 12px 24px;
|
||||
margin: 20px;
|
||||
font-size: 13px;
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.control-panel label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.control-panel input {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.city-pin {
|
||||
cursor: pointer;
|
||||
fill: #d00;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.nav {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: ff-clan-web-pro, "Helvetica Neue", Helvetica, sans-serif !important;
|
||||
font-size: 12px;
|
||||
@ -279,12 +245,6 @@ hr.short {
|
||||
bottom: 24px;
|
||||
color: $white-40;
|
||||
}
|
||||
.mapboxgl-ctrl-attrib {
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 4px;
|
||||
z-index: 9;
|
||||
}
|
||||
.overlays {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
@ -25,7 +25,12 @@ const config = {
|
||||
devServer: {
|
||||
stats: {
|
||||
warnings: false
|
||||
}
|
||||
},
|
||||
|
||||
contentBase: [
|
||||
__dirname,
|
||||
resolve(__dirname, '../')
|
||||
]
|
||||
},
|
||||
|
||||
devtool: 'source-maps',
|
||||
|
||||
@ -3,10 +3,11 @@ body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
.options-panel {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
max-width: 320px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
padding: 12px 24px;
|
||||
@ -15,6 +16,7 @@ body {
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.btn {
|
||||
|
||||
@ -2,9 +2,10 @@
|
||||
import React, {Component} from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import MapGL from 'react-map-gl';
|
||||
import {PerspectiveMercatorViewport} from 'viewport-mercator-project';
|
||||
import TWEEN from 'tween.js';
|
||||
|
||||
import CITIES from './cities.json';
|
||||
import ControlPanel from './control-panel';
|
||||
|
||||
const token = process.env.MapboxAccessToken; // eslint-disable-line
|
||||
|
||||
@ -19,30 +20,17 @@ function animate() {
|
||||
}
|
||||
animate();
|
||||
|
||||
export default class Root extends Component {
|
||||
export default class App extends Component {
|
||||
|
||||
state = {
|
||||
viewport: {
|
||||
latitude: 37.785164,
|
||||
longitude: -100,
|
||||
zoom: 4,
|
||||
latitude: 37.7751,
|
||||
longitude: -122.4193,
|
||||
zoom: 11,
|
||||
bearing: 0,
|
||||
pitch: 0,
|
||||
width: this.props.width,
|
||||
height: this.props.height
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.width !== this.state.viewport.width ||
|
||||
nextProps.height !== this.state.viewport.height) {
|
||||
this.setState({
|
||||
viewport: {
|
||||
...this.state.viewport,
|
||||
width: nextProps.width,
|
||||
height: nextProps.height
|
||||
}
|
||||
});
|
||||
width: 500,
|
||||
height: 500
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,12 +39,16 @@ export default class Root extends Component {
|
||||
this._resize();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this._resize);
|
||||
}
|
||||
|
||||
_resize = () => {
|
||||
this.setState({
|
||||
viewport: {
|
||||
...this.state.viewport,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
width: this.props.width || window.innerWidth,
|
||||
height: this.props.height || window.innerHeight
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -79,15 +71,6 @@ export default class Root extends Component {
|
||||
|
||||
_onViewportChange = viewport => this.setState({viewport});
|
||||
|
||||
_renderButton = (city, index) => {
|
||||
return (
|
||||
<div key={`btn-${index}`} className="btn"
|
||||
onClick={() => this._easeTo(city)} >
|
||||
{city.city}, {city.state}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
const {viewport, settings} = this.state;
|
||||
@ -101,17 +84,9 @@ export default class Root extends Component {
|
||||
onViewportChange={this._onViewportChange}
|
||||
dragToRotate={false}
|
||||
mapboxApiAccessToken={token} />
|
||||
|
||||
<div className="control-panel">
|
||||
{ CITIES.map(this._renderButton) }
|
||||
</div>
|
||||
<ControlPanel onViewportChange={this._easeTo} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
App.defaultProps = {
|
||||
width: 500,
|
||||
height: 500
|
||||
};
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
[
|
||||
{"city":"New York","state":"New York","latitude":40.6643,"longitude":-73.9385},
|
||||
{"city":"Los Angeles","state":"California","latitude":34.0194,"longitude":-118.4108},
|
||||
{"city":"Chicago","state":"Illinois","latitude":41.8376,"longitude":-87.6818},
|
||||
{"city":"Houston","state":"Texas","latitude":29.7805,"longitude":-95.3863},
|
||||
{"city":"Phoenix","state":"Arizona","latitude":33.5722,"longitude":-112.0880},
|
||||
{"city":"Philadelphia","state":"Pennsylvania","latitude":40.0094,"longitude":-75.1333},
|
||||
{"city":"San Antonio","state":"Texas","latitude":29.4724,"longitude":-98.5251},
|
||||
{"city":"San Diego","state":"California","latitude":32.8153,"longitude":-117.1350},
|
||||
{"city":"Dallas","state":"Texas","latitude":32.7757,"longitude":-96.7967},
|
||||
{"city":"San Jose","state":"California","latitude":37.2969,"longitude":-121.8193},
|
||||
{"city":"Austin","state":"Texas","latitude":30.3072,"longitude":-97.7560},
|
||||
{"city":"Jacksonville","state":"Florida","latitude":30.3370,"longitude":-81.6613},
|
||||
{"city":"San Francisco","state":"California","latitude":37.7751,"longitude":-122.4193},
|
||||
{"city":"Columbus","state":"Ohio","latitude":39.9848,"longitude":-82.9850},
|
||||
{"city":"Indianapolis","state":"Indiana","latitude":39.7767,"longitude":-86.1459},
|
||||
{"city":"Fort Worth","state":"Texas","latitude":32.7795,"longitude":-97.3463},
|
||||
{"city":"Charlotte","state":"North Carolina","latitude":35.2087,"longitude":-80.8307},
|
||||
{"city":"Seattle","state":"Washington","latitude":47.6205,"longitude":-122.3509},
|
||||
{"city":"Denver","state":"Colorado","latitude":39.7618,"longitude":-104.8806},
|
||||
{"city":"El Paso","state":"Texas","latitude":31.8484,"longitude":-106.4270}
|
||||
]
|
||||
33
examples/viewport-animation/src/control-panel.js
Normal file
33
examples/viewport-animation/src/control-panel.js
Normal file
@ -0,0 +1,33 @@
|
||||
import React, {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'}
|
||||
onChange={() => this.props.onViewportChange(city)} />
|
||||
<label htmlFor={`city-${index}`}>{city.city}</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="options-panel" tabIndex="0">
|
||||
<h3>Camera Transition</h3>
|
||||
<p>Smooth animate of the viewport.</p>
|
||||
<div className="source-link">
|
||||
<a href="https://github.com/uber/react-map-gl/tree/master/examples/viewport-animation" target="_new">View Code ↗</a>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
{ CITIES.filter(city => city.state === 'California').map(this._renderButton) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,8 @@
|
||||
"babel-polyfill": "^6.1.19",
|
||||
"babel-register": "^6.22.0",
|
||||
"d3-color": "^1.0.1",
|
||||
"d3-request": "^1.0.2",
|
||||
"d3-request": "^1.0.5",
|
||||
"d3-scale": "^1.0.6",
|
||||
"highlight.js": "^9.7.0",
|
||||
"immutable": "^3.7.5",
|
||||
"marked": "^0.3.6",
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import {request, json, text} from 'd3-request';
|
||||
|
||||
import {StreamParser} from '../utils/worker-utils';
|
||||
import {text} from 'd3-request';
|
||||
|
||||
const loadContentSuccess = (name, content) => {
|
||||
const payload = {};
|
||||
@ -26,92 +24,6 @@ export const loadContent = filename => {
|
||||
};
|
||||
};
|
||||
|
||||
const loadDataStart = owner => ({type: 'LOAD_DATA_START', owner});
|
||||
|
||||
const loadDataSuccess = (context, index, data, meta) => {
|
||||
|
||||
if (context.isArray) {
|
||||
context.resultData = context.resultData.slice(0);
|
||||
context.resultData[index] = data;
|
||||
} else {
|
||||
context.resultData = data;
|
||||
}
|
||||
context.resultMeta = {...context.resultMeta, ...meta};
|
||||
|
||||
return {
|
||||
type: 'LOAD_DATA_SUCCESS',
|
||||
payload: {
|
||||
owner: context.owner,
|
||||
data: context.resultData,
|
||||
meta: context.resultMeta
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* loads data for a demo
|
||||
* @param {String} owner - identifier of the demo
|
||||
* @param {Object | Array} source - an object or array of objects specifying
|
||||
* the data that needs to be loaded
|
||||
* {String} source.url - (required) url of the data file
|
||||
* {String} source.worker - (optional) url of a web worker
|
||||
* if specified, then the loaded file content will be passed to the worker
|
||||
* if not specified, then the loaded file will be parsed as JSON or text
|
||||
* based on its extension
|
||||
*/
|
||||
export const loadData = (owner, source) => {
|
||||
|
||||
return (dispatch, getState) => {
|
||||
if (getState().vis.owner === owner) {
|
||||
// already loading / loaded
|
||||
return;
|
||||
}
|
||||
|
||||
const isArray = Array.isArray(source);
|
||||
|
||||
if (!isArray) {
|
||||
source = [source];
|
||||
}
|
||||
const context = {
|
||||
owner,
|
||||
resultData: [],
|
||||
resultMeta: [],
|
||||
isArray
|
||||
};
|
||||
|
||||
dispatch(loadDataStart(owner));
|
||||
|
||||
source.forEach(({url, worker}, index) => {
|
||||
if (worker) {
|
||||
const req = request(url);
|
||||
// use a web worker to parse data
|
||||
const dataParser = new StreamParser(worker, (data, meta) => {
|
||||
dispatch(loadDataSuccess(context, index, data, meta));
|
||||
});
|
||||
|
||||
req.on('progress', dataParser.onProgress)
|
||||
.on('load', dataParser.onLoad)
|
||||
.get();
|
||||
} else if (/\.(json|geojson)$/.test(url)) {
|
||||
// load as json
|
||||
json(url, (error, response) => {
|
||||
if (!error) {
|
||||
dispatch(loadDataSuccess(context, index, response, {}));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// load as plain text
|
||||
text(url, (error, response) => {
|
||||
if (!error) {
|
||||
dispatch(loadDataSuccess(context, index, response, {}));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
export const updateMap = viewport => ({type: 'UPDATE_MAP', viewport});
|
||||
export const updateMeta = meta => ({type: 'UPDATE_META', meta});
|
||||
export const updateParam = (name, value) => ({type: 'UPDATE_PARAM', payload: {name, value}});
|
||||
|
||||
@ -10,20 +10,14 @@ export default class Examples extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
viewport: {
|
||||
width: window.innerWidth - WIDTH_OFFSET,
|
||||
height: window.innerHeight - HEIGHT_OFFSET
|
||||
}
|
||||
viewport: this._getSize()
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.onresize = () => {
|
||||
this.setState({
|
||||
viewport: {
|
||||
width: window.innerWidth - WIDTH_OFFSET,
|
||||
height: window.innerHeight - HEIGHT_OFFSET
|
||||
}
|
||||
viewport: this._getSize()
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -32,6 +26,15 @@ export default class Examples extends Component {
|
||||
window.onresize = null;
|
||||
}
|
||||
|
||||
_getSize() {
|
||||
const {innerWidth, innerHeight} = window;
|
||||
|
||||
return {
|
||||
width: innerWidth >= 576 ? innerWidth - WIDTH_OFFSET : innerWidth,
|
||||
height: innerHeight - HEIGHT_OFFSET
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {viewport} = this.state;
|
||||
const {route: {childComponent}} = this.props;
|
||||
@ -39,8 +42,6 @@ export default class Examples extends Component {
|
||||
return (
|
||||
<div className="flexbox-item flexbox-item--fill">
|
||||
<ExampleComponent
|
||||
widthOffset={WIDTH_OFFSET}
|
||||
heightOffset={HEIGHT_OFFSET}
|
||||
{...viewport} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -6,13 +6,12 @@ export default class Home extends Component {
|
||||
return (
|
||||
<div className="home-wrapper">
|
||||
|
||||
<section ref="banner" id="banner">
|
||||
<div className="container soft-left">
|
||||
<section id="banner" style={{backgroundImage: 'url(images/hero.jpg)'}}>
|
||||
<div className="container soft">
|
||||
<h1>react-map-gl</h1>
|
||||
<p>A React port of Mapbox GL JS</p>
|
||||
<a href="#/documentation/getting-started" className="btn">Get started</a>
|
||||
</div>
|
||||
<div ref="fps" className="fps" />
|
||||
</section>
|
||||
|
||||
<section id="features">
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import GenericInput from './input';
|
||||
// import * as Demos from './demos';
|
||||
import {updateParam} from '../actions/app-actions';
|
||||
|
||||
class InfoPanel extends Component {
|
||||
|
||||
render() {
|
||||
const {demo, hasFocus, onInteract, params, owner, meta} = this.props;
|
||||
// const DemoComponent = Demos[demo];
|
||||
// const metaLoaded = owner === demo ? meta : {};
|
||||
|
||||
return (
|
||||
<div className={`options-panel top-right ${hasFocus ? 'focus' : ''}`} onClick={onInteract}>
|
||||
|
||||
{/* {DemoComponent.renderInfo(metaLoaded)} */}
|
||||
|
||||
{Object.keys(params).length > 0 && <hr />}
|
||||
|
||||
{Object.keys(params).map((name, i) => (
|
||||
<GenericInput key={i}
|
||||
name={name}
|
||||
{...params[name]}
|
||||
onChange={this.props.updateParam} />
|
||||
))}
|
||||
|
||||
{this.props.children}
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(state => state.vis, {updateParam})(InfoPanel);
|
||||
@ -192,8 +192,7 @@ export default class MarkdownPage extends PureComponent {
|
||||
}
|
||||
|
||||
MarkdownPage.propTypes = {
|
||||
content: PropTypes.string,
|
||||
renderDemo: PropTypes.func.isRequired
|
||||
content: PropTypes.string
|
||||
};
|
||||
|
||||
MarkdownPage.defaultProps = {
|
||||
|
||||
@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import autobind from 'autobind-decorator';
|
||||
|
||||
import InfoPanel from './info-panel';
|
||||
import MarkdownPage from './markdown-page';
|
||||
import {loadContent, updateMap} from '../actions/app-actions';
|
||||
|
||||
@ -38,23 +37,6 @@ class Page extends Component {
|
||||
return content;
|
||||
}
|
||||
|
||||
@autobind _renderDemo(name, sourceLink) {
|
||||
const {mapHasFocus} = this.state;
|
||||
|
||||
return (
|
||||
<div className="demo">
|
||||
<InfoPanel
|
||||
demo={name}
|
||||
hasFocus={!mapHasFocus}
|
||||
onInteract={this._onMapBlur} >
|
||||
{sourceLink && (<div className="source-link">
|
||||
<a href={sourceLink} target="_new">View Code ↗</a>
|
||||
</div>)}
|
||||
</InfoPanel>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// replaces the current query string in react-router
|
||||
@autobind _updateQueryString(queryString) {
|
||||
const {location: {pathname, search}} = this.props;
|
||||
@ -77,8 +59,7 @@ class Page extends Component {
|
||||
} else if (typeof content === 'string') {
|
||||
child = (<MarkdownPage content={contents[content]}
|
||||
query={query}
|
||||
updateQueryString={this._updateQueryString}
|
||||
renderDemo={this._renderDemo} />);
|
||||
updateQueryString={this._updateQueryString} />);
|
||||
}
|
||||
|
||||
return <div className="page">{child}</div>;
|
||||
|
||||
@ -28,7 +28,7 @@ export default class TableOfContents extends Component {
|
||||
// is external link
|
||||
return (
|
||||
<li key={`page-${i}`}>
|
||||
<a className="link" href={page.external} target="_blank" >{page.name}</a>
|
||||
<a className="link" href={page.external} target="_new" >{page.name}</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import ExamplesToc from '../../../examples/main/constants/toc';
|
||||
import {standaloneExamples} from '../../../examples/main/constants/toc';
|
||||
import ExamplesComponent from '../components/examples';
|
||||
import PagesComponent from '../components/page';
|
||||
|
||||
@ -14,7 +14,7 @@ function generatePath(tree, parentPath = '') {
|
||||
tree.forEach(branch => generatePath(branch, parentPath));
|
||||
}
|
||||
if (tree.name) {
|
||||
tree.path = tree.name.match(/(GeoJson|3D|API|([A-Z]|^)[a-z'0-9]+|\d+)/g)
|
||||
tree.path = tree.name.match(/(GeoJSON|3D|API|([A-Z]|^)[a-z'0-9]+|\d+)/g)
|
||||
.join('-')
|
||||
.toLowerCase()
|
||||
.replace(/[^\w-]/g, '');
|
||||
@ -31,7 +31,7 @@ function generatePath(tree, parentPath = '') {
|
||||
const examplePages = {
|
||||
title: 'Examples',
|
||||
pageComponent: ExamplesComponent,
|
||||
paths: generatePath(ExamplesToc)
|
||||
paths: generatePath(standaloneExamples)
|
||||
};
|
||||
|
||||
const docPages = {
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import {handleActions} from 'redux-actions';
|
||||
|
||||
import {DEFAULT_VIEWPORT_STATE} from '../constants/defaults';
|
||||
import ViewportAnimation from '../utils/map-utils';
|
||||
|
||||
ViewportAnimation.init();
|
||||
|
||||
export default handleActions({
|
||||
|
||||
|
||||
BIN
website/src/static/images/hero.jpg
Normal file
BIN
website/src/static/images/hero.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 621 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 440 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB |
@ -1,142 +0,0 @@
|
||||
{
|
||||
"marker-1": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-2": {
|
||||
"x": 128,
|
||||
"y": 0,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-3": {
|
||||
"x": 256,
|
||||
"y": 0,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-4": {
|
||||
"x": 384,
|
||||
"y": 0,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-5": {
|
||||
"x": 0,
|
||||
"y": 128,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-6": {
|
||||
"x": 128,
|
||||
"y": 128,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-7": {
|
||||
"x": 256,
|
||||
"y": 128,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-8": {
|
||||
"x": 384,
|
||||
"y": 128,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-9": {
|
||||
"x": 0,
|
||||
"y": 256,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-10": {
|
||||
"x": 128,
|
||||
"y": 256,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-20": {
|
||||
"x": 256,
|
||||
"y": 256,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-30": {
|
||||
"x": 384,
|
||||
"y": 256,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-40": {
|
||||
"x": 0,
|
||||
"y": 384,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-50": {
|
||||
"x": 128,
|
||||
"y": 384,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-60": {
|
||||
"x": 256,
|
||||
"y": 384,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-70": {
|
||||
"x": 384,
|
||||
"y": 384,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-80": {
|
||||
"x": 0,
|
||||
"y": 512,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-90": {
|
||||
"x": 128,
|
||||
"y": 512,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker-100": {
|
||||
"x": 256,
|
||||
"y": 512,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
},
|
||||
"marker": {
|
||||
"x": 384,
|
||||
"y": 512,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"anchorY": 128
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,6 @@
|
||||
|
||||
<link rel="icon" type="img/ico" href="favicon.ico">
|
||||
<link rel="stylesheet" id="font-link" href="https://d1a3f4spazzrp4.cloudfront.net/uber-fonts/3.1.0/refresh.css">
|
||||
<link rel="stylesheet" type="text/css" href="node_modules/mapbox-gl/dist/mapbox-gl.css" />
|
||||
|
||||
<!-— facebook open graph tags -->
|
||||
<meta property="og:url" content="https://uber.github.io/deck.gl/" />
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
### Core Layers
|
||||
|
||||
<div>
|
||||
<div class="thumb">
|
||||
<div class="bg-black" data-title="Flights at Heathrow" data-name="LineLayer">
|
||||
<a href="#/examples/core-layers/line-layer">
|
||||
<img src="images/demo-thumb-line.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="thumb">
|
||||
<div class="bg-black" data-title="Road Safety in UK" data-name="HexagonLayer">
|
||||
<a href="#/examples/core-layers/hexagon-layer">
|
||||
<img src="images/demo-thumb-heatmap.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="thumb">
|
||||
<div data-title="Vancouver Property Value" data-name="GeoJsonLayer">
|
||||
<a href="#/examples/core-layers/geojson-layer">
|
||||
<img src="images/demo-thumb-geojson.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="thumb">
|
||||
<div class="bg-black" data-title="Access Public Transit in California" data-name="ScreenGridLayer">
|
||||
<a href="#/examples/core-layers/screen-grid-layer">
|
||||
<img src="images/demo-thumb-screengrid.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="thumb">
|
||||
<div data-title="US County-to-County Migration" data-name="ArcLayer">
|
||||
<a href="#/examples/core-layers/arc-layer">
|
||||
<img src="images/demo-thumb-arc.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="thumb">
|
||||
<div data-title="Every Person in NYC" data-name="ScatterplotLayer">
|
||||
<a href="#/examples/core-layers/scatterplot-layer">
|
||||
<img src="images/demo-thumb-scatterplot.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
### Custom Layers
|
||||
|
||||
<div>
|
||||
<div class="thumb">
|
||||
<div data-title="US County-to-County Migration" data-name="BrushingLayer">
|
||||
<a href="#/examples/custom-layers/brushing-layer">
|
||||
<img src="images/demo-thumb-brushing.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="thumb">
|
||||
<div class="bg-black" data-title="Taxi Trips in NYC" data-name="TripsLayer">
|
||||
<a href="#/examples/custom-layers/trip-routes">
|
||||
<img src="images/demo-thumb-trip.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
### Beyond Maps
|
||||
|
||||
<div>
|
||||
<div class="thumb">
|
||||
<div data-title="3D Surface Explorer" data-name="PlotLayer">
|
||||
<a href="#/examples/beyond-maps/3d-surface-explorer">
|
||||
<img src="images/demo-thumb-plot.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="thumb">
|
||||
<div data-title="3D Indoor Scan" data-name="PointCloudLayer">
|
||||
<a href="https://gnavvy.github.io/point-cloud-example/" target="_blank">
|
||||
<img src="images/demo-thumb-point-cloud.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,4 +1,3 @@
|
||||
/* from deck.gl */
|
||||
|
||||
.gallery-wrapper {
|
||||
position: fixed;;
|
||||
@ -106,6 +105,10 @@
|
||||
box-shadow: 0 0 4px rgba(0, 0, 0, 0.15);
|
||||
margin: 24px;
|
||||
padding: 12px 24px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
outline: none;
|
||||
|
||||
hr {
|
||||
margin: 12px -24px;
|
||||
@ -129,8 +132,9 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
label {
|
||||
text-transform: uppercase;
|
||||
display: inline-block;
|
||||
width: 40%;
|
||||
width: 50%;
|
||||
margin-right: 10%;
|
||||
color: $black-40;
|
||||
margin-bottom: 4px;
|
||||
@ -139,10 +143,13 @@
|
||||
font-size: 0.9em;
|
||||
display: inline-block;
|
||||
padding: 0 4px;
|
||||
width: 50%;
|
||||
width: 40%;
|
||||
height: 20px;
|
||||
line-height: 1.833;
|
||||
}
|
||||
input[type="checkbox"], input[type="radio"], input[type="color"] {
|
||||
width: 20%;
|
||||
}
|
||||
input {
|
||||
border: solid 1px #ccc;
|
||||
|
||||
@ -244,7 +251,7 @@ code {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.options-panel:not(.focus) {
|
||||
.options-panel:not(:focus) {
|
||||
cursor: pointer;
|
||||
>div > *, hr, .input {
|
||||
display: none;
|
||||
@ -269,39 +276,3 @@ code {
|
||||
padding: 40px 12px 96px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-demo {
|
||||
position: relative;
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.tooltip {
|
||||
padding: 4px 12px;
|
||||
max-width: 240px;
|
||||
max-height: 320px;
|
||||
box-sizing: content-box;
|
||||
overflow-y: hidden;
|
||||
|
||||
h5 {
|
||||
font-size: 1em;
|
||||
}
|
||||
p {
|
||||
display: none;
|
||||
text-indent: 4px;
|
||||
}
|
||||
}
|
||||
.tooltip.interactive {
|
||||
border: solid 4px transparent;
|
||||
margin: -4px;
|
||||
background: $white;
|
||||
color: $black;
|
||||
pointer-events: all;
|
||||
width: 240px;
|
||||
overflow-y: auto;
|
||||
|
||||
p {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,24 +48,30 @@
|
||||
}
|
||||
|
||||
#banner {
|
||||
height: 80vh;
|
||||
background: $black-20;
|
||||
color: $white;
|
||||
position: relative;
|
||||
@include venderPrefix(user-select, none);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
|
||||
> .fps > div {
|
||||
position: absolute !important;
|
||||
bottom: 24px !important;
|
||||
right: 10px !important;
|
||||
top: inherit !important;
|
||||
left: inherit !important;
|
||||
}
|
||||
>.hero {
|
||||
&:before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
@include linearGradientH(background, rgba(0,0,0,0.75), transparent, 0%, 60%);
|
||||
display: block;
|
||||
}
|
||||
|
||||
> .container {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
p {
|
||||
@ -155,16 +161,21 @@
|
||||
@media screen and (max-width: 576px) {
|
||||
.home-wrapper {
|
||||
h1 {
|
||||
font-size: 3.6em;
|
||||
font-size: 3.2em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
}
|
||||
#banner {
|
||||
height: 100vh;
|
||||
p {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
@include linearGradientH(background, rgba(0,0,0,0.75), transparent);
|
||||
}
|
||||
}
|
||||
#features {
|
||||
.container >div {
|
||||
|
||||
@ -7,13 +7,13 @@
|
||||
#{$property_name}: linear-gradient(to bottom, $top 0%,$bottom 100%); /* W3C */
|
||||
}
|
||||
|
||||
@mixin linearGradientH($property_name, $left, $right){
|
||||
@mixin linearGradientH($property_name, $left, $right, $let_position: 0%, $right_position: 100%){
|
||||
#{$property_name}: $left; /* Old browsers */
|
||||
#{$property_name}: -moz-linear-gradient(left, $left 0%, $right 100%); /* FF3.6+ */
|
||||
#{$property_name}: -webkit-linear-gradient(left, $left 0%,$right 100%); /* Chrome10+,Safari5.1+ */
|
||||
#{$property_name}: -o-linear-gradient(left, $left 0%,$right 100%); /* Opera 11.10+ */
|
||||
#{$property_name}: -ms-linear-gradient(left, $left 0%,$right 100%); /* IE10+ */
|
||||
#{$property_name}: linear-gradient(to right, $left 0%,$right 100%); /* W3C */
|
||||
#{$property_name}: -moz-linear-gradient(left, $left $let_position, $right $right_position); /* FF3.6+ */
|
||||
#{$property_name}: -webkit-linear-gradient(left, $left $let_position,$right $right_position); /* Chrome10+,Safari5.1+ */
|
||||
#{$property_name}: -o-linear-gradient(left, $left $let_position,$right $right_position); /* Opera 11.10+ */
|
||||
#{$property_name}: -ms-linear-gradient(left, $left $let_position,$right $right_position); /* IE10+ */
|
||||
#{$property_name}: linear-gradient(to right, $left $let_position,$right $right_position); /* W3C */
|
||||
}
|
||||
|
||||
@mixin venderPrefix($property_name, $value) {
|
||||
|
||||
@ -16,44 +16,6 @@ $topbar-height: 64px;
|
||||
@import 'gallery';
|
||||
@import '../../node_modules/mapbox-gl/dist/mapbox-gl.css';
|
||||
|
||||
.mapboxgl-ctrl.mapboxgl-ctrl-attrib {
|
||||
width: 115px;
|
||||
};
|
||||
|
||||
.control-panel {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
padding: 12px 24px;
|
||||
margin: 20px;
|
||||
font-size: 13px;
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.control-panel label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.control-panel input {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.city-pin {
|
||||
cursor: pointer;
|
||||
fill: #d00;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.nav {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: ff-clan-web-pro, "Helvetica Neue", Helvetica, sans-serif !important;
|
||||
font-size: 12px;
|
||||
@ -226,60 +188,6 @@ hr.short {
|
||||
background: $black;
|
||||
color: $white;
|
||||
}
|
||||
.thumb {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
width: 32%;
|
||||
display: inline-block;
|
||||
line-height: 0;
|
||||
|
||||
img {
|
||||
transition: opacity 0.4s;
|
||||
}
|
||||
>div:before, >div:after {
|
||||
display: block;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
transition: opacity 0.4s;
|
||||
opacity: 0;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
box-sizing: border-box;
|
||||
line-height: 1.5;
|
||||
}
|
||||
>div:before {
|
||||
content: attr(data-title);
|
||||
font-size: 1.4em;
|
||||
font-weight: 100;
|
||||
width: 100%;
|
||||
padding: 12%;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
>div:after {
|
||||
font-size: 0.833em;
|
||||
content: attr(data-name);
|
||||
padding: 5%;
|
||||
left: 0;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
margin: 10%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border: solid 2px;
|
||||
@include linearGradientV(border-image, $primary, $secondary);
|
||||
border-image-slice: 2;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
>div:hover img {
|
||||
opacity: 0.2;
|
||||
}
|
||||
>div:hover:before, >div:hover:after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.mapbox-tip {
|
||||
position: absolute;
|
||||
@ -287,26 +195,10 @@ hr.short {
|
||||
bottom: 24px;
|
||||
color: $white-40;
|
||||
}
|
||||
.mapboxgl-ctrl-attrib {
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 4px;
|
||||
z-index: 9;
|
||||
}
|
||||
.overlays {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
.thumb {
|
||||
width: 49%;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.thumb {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 576px) {
|
||||
.container {
|
||||
padding: 0 12px;
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
import {rgb} from 'd3-color';
|
||||
|
||||
export const normalizeParam = p => {
|
||||
if (p.type === 'function') {
|
||||
let displayValue = p.value.toString();
|
||||
// pretty print function code:
|
||||
// convert `function funcName(d) {...}` to `d => {...}`
|
||||
displayValue = displayValue.replace(/^function (\w+)?\((\w*?)\)/, '$2 =>');
|
||||
// convert `function funcName(d, i) {...}` to `(d, i) => {...}`
|
||||
displayValue = displayValue.replace(/^function (\w+)?(\(.*?\))/, '$2 =>');
|
||||
// convert `d => {return 1}` to `d => 1`
|
||||
displayValue = displayValue.replace(/\{\s*return\s*(.*?);?\s*\}$/, '$1');
|
||||
return {...p, displayValue};
|
||||
}
|
||||
if (p.type === 'json') {
|
||||
return {...p, displayValue: JSON.stringify(p.value)};
|
||||
}
|
||||
if (p.type === 'color') {
|
||||
return {...p, displayValue: colorToHex(p.value)};
|
||||
}
|
||||
return {...p, displayValue: String(p.value)};
|
||||
};
|
||||
|
||||
export const readableInteger = x => {
|
||||
if (!x) {
|
||||
return 0;
|
||||
}
|
||||
if (x < 1000) {
|
||||
return x.toString();
|
||||
}
|
||||
x /= 1000;
|
||||
if (x < 1000) {
|
||||
return `${x.toFixed(1)}K`;
|
||||
}
|
||||
x /= 1000;
|
||||
return `${x.toFixed(1)}M`;
|
||||
};
|
||||
|
||||
export function colorToHex(color) {
|
||||
return colorToRGBArray(color).reduce(
|
||||
(acc, v) => `${acc}${v < 16 ? '0' : ''}${v.toString(16)}`,
|
||||
'#'
|
||||
);
|
||||
}
|
||||
|
||||
export function colorToRGBArray(color) {
|
||||
if (Array.isArray(color)) {
|
||||
return color.slice(0, 3);
|
||||
}
|
||||
const c = rgb(color);
|
||||
return [c.r, c.g, c.b];
|
||||
}
|
||||
@ -1,80 +0,0 @@
|
||||
const blackList = [
|
||||
'projectionMode',
|
||||
'modelMatrix'
|
||||
];
|
||||
|
||||
/* eslint-disable complexity */
|
||||
/*
|
||||
* infer parameter type from a prop
|
||||
*/
|
||||
export function propToParam(key, value) {
|
||||
if (blackList.indexOf(key) >= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const param = {
|
||||
name: key,
|
||||
displayName: key,
|
||||
value
|
||||
};
|
||||
|
||||
switch (typeof value) {
|
||||
case 'boolean':
|
||||
return {...param, type: 'checkbox'};
|
||||
case 'number':
|
||||
if (/pixels|width|height|size|scale|radius|limit/i.test(key)) {
|
||||
param.max = 100;
|
||||
param.step = 1;
|
||||
} else {
|
||||
param.max = 1;
|
||||
param.step = 0.01;
|
||||
}
|
||||
return {...param, type: 'range', min: 0};
|
||||
case 'function':
|
||||
if (key.indexOf('get') === 0) {
|
||||
// is accessor
|
||||
return {...param, type: 'function'};
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
if (/\.(png|jpg|jpeg|gif)/i.test(value)) {
|
||||
return {...param, type: 'link'};
|
||||
}
|
||||
break;
|
||||
case 'object':
|
||||
if (/color/i.test(key) && value && Number.isFinite(value[0])) {
|
||||
return {...param, type: 'color'};
|
||||
}
|
||||
if (/mapping|domain|range/i.test(key)) {
|
||||
return {...param, type: 'json'};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/* eslint-enable complexity */
|
||||
|
||||
/*
|
||||
* get array of parameters from a layer's default props
|
||||
* sorted by type
|
||||
*/
|
||||
export function getLayerParams(layer) {
|
||||
const paramsMap = {};
|
||||
const paramsArray = [];
|
||||
|
||||
Object.keys(layer.props).forEach(key => {
|
||||
const param = propToParam(key, layer.props[key]);
|
||||
if (param) {
|
||||
paramsArray.push(param);
|
||||
}
|
||||
});
|
||||
|
||||
paramsArray.sort((p1, p2) => p1.type.localeCompare(p2.type));
|
||||
|
||||
paramsArray.forEach(param => {
|
||||
paramsMap[param.name] = param;
|
||||
});
|
||||
|
||||
return paramsMap;
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
/* global window */
|
||||
import TWEEN from 'tween.js';
|
||||
|
||||
let viewportTween;
|
||||
|
||||
const animate = () => {
|
||||
TWEEN.update();
|
||||
window.requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
// get a linear tween
|
||||
const ease = (fromState, toState, duration) =>
|
||||
new TWEEN.Tween(fromState).to(toState, duration);
|
||||
|
||||
// fly to new viewport
|
||||
const fly = (fromViewport, toViewport, duration, onUpdate) => {
|
||||
const fromState = {};
|
||||
const nanState = {};
|
||||
const toState = {};
|
||||
|
||||
Object.keys(toViewport).forEach(key => {
|
||||
const v0 = fromViewport[key];
|
||||
const v1 = toViewport[key];
|
||||
if (Number.isFinite(v0) && Number.isFinite(v1)) {
|
||||
fromState[key] = v0;
|
||||
toState[key] = v1;
|
||||
} else {
|
||||
nanState[key] = v1;
|
||||
}
|
||||
});
|
||||
|
||||
if (viewportTween) {
|
||||
TWEEN.remove(viewportTween);
|
||||
}
|
||||
|
||||
viewportTween = new TWEEN.Tween(fromState)
|
||||
.to(toState, duration)
|
||||
.onUpdate(function update() {
|
||||
onUpdate({...this, ...nanState}); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
return viewportTween;
|
||||
};
|
||||
|
||||
export default {
|
||||
init: animate,
|
||||
Easing: TWEEN.Easing,
|
||||
ease,
|
||||
fly
|
||||
};
|
||||
@ -1,45 +0,0 @@
|
||||
/* global Worker */
|
||||
|
||||
const workers = {};
|
||||
|
||||
export function StreamParser(workerUrl, callback) {
|
||||
let parsedLength = 0;
|
||||
|
||||
if (workers[workerUrl]) {
|
||||
workers[workerUrl].terminate();
|
||||
}
|
||||
|
||||
const workerInstance = new Worker(workerUrl);
|
||||
workers[workerUrl] = workerInstance;
|
||||
let streamedData = [];
|
||||
|
||||
workerInstance.onmessage = e => {
|
||||
const {action, data, meta} = e.data;
|
||||
if (action === 'end') {
|
||||
workerInstance.terminate();
|
||||
} else if (action === 'add' && data && data.length) {
|
||||
streamedData = streamedData.concat(data);
|
||||
callback(streamedData, meta); // eslint-disable-line callback-return
|
||||
}
|
||||
};
|
||||
|
||||
this.onProgress = e => {
|
||||
const {responseText} = e.target;
|
||||
const lineBreak = responseText.lastIndexOf('\n') + 1;
|
||||
|
||||
workerInstance.postMessage({
|
||||
event: 'progress',
|
||||
text: responseText.slice(parsedLength, lineBreak)
|
||||
});
|
||||
|
||||
parsedLength = lineBreak;
|
||||
};
|
||||
|
||||
this.onLoad = target => {
|
||||
const {responseText} = target;
|
||||
workerInstance.postMessage({
|
||||
event: 'load',
|
||||
text: responseText.slice(parsedLength)
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -69,6 +69,10 @@ module.exports = {
|
||||
from: '../docs',
|
||||
to: 'docs'
|
||||
},
|
||||
{
|
||||
from: '../examples/data',
|
||||
to: 'data'
|
||||
},
|
||||
{
|
||||
from: './src/static'
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user