From d7d1645d5f3fc648cc32d87b1386b5bb440b6aa8 Mon Sep 17 00:00:00 2001 From: Casey Thomas Date: Wed, 8 Jun 2016 16:03:42 -0400 Subject: [PATCH] Add hotspot map. * Add Mapbox GL and related libraries required to serve maps from Webpack. * Update Webpack config based on Mapbox example and https://mikewilliamson.wordpress.com/2016/02/24/using-mapbox-gl-and-webpack-together/, and https://github.com/uber/react-map-gl/issues/21#issuecomment-224191900. * Create map component and add to SightingsList component. Closes #8 --- package.json | 8 +++-- src/css/app.css | 9 ++++++ src/index.html | 1 + src/js/components/Map.js | 51 ++++++++++++++++++++++++++++++ src/js/components/SightingsList.js | 31 ++++++++++++------ webpack.config.js | 39 +++++++++++++++++++---- 6 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 src/js/components/Map.js diff --git a/package.json b/package.json index 1e809c6..74b200a 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "fbemitter": "^2.0.2", "flux": "^2.1.1", "jquery": "^2.2.0", + "mapbox-gl": "^0.20.1", "object-assign": "^4.0.1", "react": "^15.1.0", "react-dom": "^15.1.0", @@ -28,13 +29,16 @@ "eslint-plugin-import": "^1.7.0", "eslint-plugin-jsx-a11y": "^1.2.0", "eslint-plugin-react": "^5.1.1", + "json-loader": "^0.5.4", + "transform-loader": "^0.2.3", "webpack": "^1.13.1", - "webpack-dev-server": "^1.14.1" + "webpack-dev-server": "^1.14.1", + "webworkify-webpack": "1.0.6" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "bundle": "webpack", - "start": "webpack-dev-server --host 0.0.0.0 --inline --hot --content-base src --progress --colors", + "start": "webpack-dev-server --port 1235 --host 0.0.0.0 --inline --hot --content-base src --progress --colors", "lint": "./node_modules/.bin/eslint src/js" }, "author": "Casey Thomas", diff --git a/src/css/app.css b/src/css/app.css index 9edd82e..15b89ed 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -2,3 +2,12 @@ body { padding-top: 70px; padding-bottom: 30px; } + +#map { + height: 250px; + width: 100%; +} + +.map-container { + margin: 10px 0; +} diff --git a/src/index.html b/src/index.html index e00b805..f055b54 100644 --- a/src/index.html +++ b/src/index.html @@ -13,5 +13,6 @@ + diff --git a/src/js/components/Map.js b/src/js/components/Map.js new file mode 100644 index 0000000..dcc8ef3 --- /dev/null +++ b/src/js/components/Map.js @@ -0,0 +1,51 @@ +import React, { Component, PropTypes } from 'react'; + +import mapboxgl from 'mapbox-gl'; + +export default class HotspotMap extends Component { + componentDidMount() { + mapboxgl.accessToken = + 'pk.eyJ1IjoiY2FzZXlwdCIsImEiOiJjaXA3OTUxODcwMHpic3ZseWtqcDZ1NXV2In0.felyQy7-DwS8zTiILhWf1g'; + + const map = new mapboxgl.Map({ + container: 'map', + style: 'mapbox://styles/mapbox/outdoors-v9', + center: this.props.position, + zoom: 15, + }); + + map.on('load', () => { + map.addSource('symbols', { + type: 'geojson', + data: { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [ + this.props.position[0], + this.props.position[1], + ], + }, + }, + }); + + map.addLayer({ + id: 'symbols', + type: 'symbol', + source: 'symbols', + layout: { + 'icon-image': 'marker-15', + }, + }); + }); + } + + render() { + return
; + } +} + +HotspotMap.propTypes = { + position: PropTypes.arrayOf(PropTypes.number.isRequired).isRequired, +}; diff --git a/src/js/components/SightingsList.js b/src/js/components/SightingsList.js index 78aec9a..5b9a1f5 100644 --- a/src/js/components/SightingsList.js +++ b/src/js/components/SightingsList.js @@ -6,6 +6,7 @@ import { Actions } from '../actions/Actions'; import { Store } from '../stores/Store'; import Sighting from './Sighting.js'; import Loading from './Loading'; +import Map from './Map'; function getState() { return { @@ -70,17 +71,27 @@ export default class SightingsList extends Component { howMany={s.howMany} /> ); + const position = [ + this.state.sightings[0].lng, + this.state.sightings[0].lat, + ]; + content = ( - - - - - - - - {sightings} - -
DateSpeciesCount
+
+
+ +
+ + + + + + + + {sightings} + +
DateSpeciesCount
+
); } else if (this.state.sightings && this.state.sightings.length === 0) { content = No recent sightings; diff --git a/webpack.config.js b/webpack.config.js index a9264f8..6f4a10b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,3 +1,5 @@ +const path = require('path'); + module.exports = { entry: './src/js/main.js', output: { @@ -6,14 +8,37 @@ module.exports = { publicPath: '/static/', }, devtool: 'source-map', + resolve: { + alias: { + webworkify: 'webworkify-webpack', + }, + }, module: { - loaders: [{ - test: /\.js?$/, - exclude: /node_modules/, - loader: 'babel', - query: { - presets: ['react', 'es2015', 'react-hmre'], + loaders: [ + { + test: /\.js?$/, + exclude: /node_modules/, + loader: 'babel', + query: { + presets: ['react', 'es2015', 'react-hmre'], + }, + }, + { + test: /\.json$/, + loader: 'json-loader', + }, + { + test: /\.js$/, + include: path.resolve('node_modules/mapbox-gl-shaders/index.js'), + loader: 'transform/cacheable?brfs', + }, + ], + postLoaders: [ + { + include: /node_modules\/mapbox-gl-shaders/, + loader: 'transform', + query: 'brfs', }, - }], + ], }, };