Abstract L and SVG pane to React

This commit is contained in:
Franc Camps-Febrer
2018-12-19 07:48:13 +01:00
parent 1ddaf2a856
commit 8765b118ce
4 changed files with 126 additions and 109 deletions

103
src/components/Map.jsx Normal file
View File

@@ -0,0 +1,103 @@
import React from 'react';
import hash from 'object-hash';
import MapLogic from '../js/map/map.js'
import MapDefsMarkers from './MapDefsMarkers.jsx';
class Map extends React.Component {
constructor() {
super();
this.state = {
isInitialized: false,
map: null
}
}
initializeMap() {
/**
* Creates a Leaflet map and a tilelayer for the map background
* @param {string} id: DOM element to create map onto
* @param {array} center: [lat, long] coordinates the map will be centered on
* @param {number} zoom: zoom level
*/
const map = L.map(this.props.mapId)
.setView(this.props.app.mapAnchor, 14)
.setMinZoom(10)
.setMaxZoom(18)
.setMaxBounds([[180, -180], [-180, 180]])
let s
if (process.env.MAPBOX_TOKEN && process.env.MAPBOX_TOKEN !== 'your_token') {
s = L.tileLayer(
`http://a.tiles.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}@2x.png?access_token=${process.env.MAPBOX_TOKEN}`
);
} else {
// eslint-disable-next-line
alert(`No mapbox token specified in config.
Timemap does not currently support any other tiling layer,
so you will need to sign up for one at:
https://www.mapbox.com/
Stop and start the development process in terminal after you have added your token to config.js`
)
return
}
s = s.addTo(map);
map.keyboard.disable();
this.setState({ map });
}
componentDidMount(){
if (this.state.map === null) {
this.initializeMap();
}
}
componentDidUpdate() {
if (!this.state.isInitialized) {
const pane = d3.select(this.state.map.getPanes().overlayPane);
const boundingClient = d3.select(`#${this.props.mapId}`).node().getBoundingClientRect();
const width = boundingClient.width;
const height = boundingClient.height;
let svg = pane.append('svg')
.attr('class', 'leaflet-svg')
.attr('width', width)
.attr('height', height);
let g = svg.append('g');
this.state.map.on('zoomstart', () => {
svg.classed('hide', true);
});
this.state.map.on('zoomend', () => {
svg.classed('hide', false);
});
this.mapLogic = new MapLogic(this.state.map, svg, g, this.props.app, this.props.ui, this.props.methods)
this.mapLogic.update(this.props.domain, this.props.app)
this.setState({ isInitialized: true })
}
}
componentWillReceiveProps(nextProps) {
if (hash(nextProps) !== hash(this.props)) {
this.mapLogic.update(nextProps.domain, nextProps.app)
}
}
render() {
return (
<div id={this.props.mapId} />
);
}
}
export default Map;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const MapDefsMarkers = ({}) => (
<defs>
<marker id="arrow" viewBox="0 0 6 6" refX="3" refY="3" markerWidth="6" markerHeight="6" orient="auto">
<path d="M0,3v-3l6,3l-6,3z" style="fill: red;"></path>
</marker>
<marker id="arrow-off" viewBox="0 0 6 6" refX="3" refY="3" markerWidth="6" markerHeight="6" orient="auto">
<path d="M0,3v-3l6,3l-6,3z" style="fill: black; fill-opacity: 0.2;"></path>
</marker>
</defs>
);
export default MapDefsMarkers;

View File

@@ -3,7 +3,7 @@ import { connect } from 'react-redux'
import * as selectors from '../selectors'
import hash from 'object-hash';
import Map from '../js/map/map.js'
import Map from './Map.jsx';
import { areEqual } from '../js/utilities.js'
class Viewport extends React.Component {
@@ -11,22 +11,17 @@ class Viewport extends React.Component {
super(props)
}
componentDidMount() {
this.map = new Map(this.props.app, this.props.ui, this.props.methods)
this.map.update(this.props.domain, this.props.app)
}
componentWillReceiveProps(nextProps) {
if (hash(nextProps) !== hash(this.props)) {
this.map.update(nextProps.domain, nextProps.app)
}
}
render() {
const classes = this.props.app.narrative ? 'map-wrapper narrative-mode' : 'map-wrapper';
return (
<div className={classes}>
<div id="map" />
<Map
mapId="map"
domain={this.props.domain}
app={this.props.app}
ui={this.props.ui}
methods={this.props.methods}
/>
</div>
)
}

View File

@@ -5,8 +5,7 @@ import {
import hash from 'object-hash';
import 'leaflet-polylinedecorator';
export default function(newApp, ui, methods) {
let svg, g, defs;
export default function(lMap, svg, g, newApp, ui, methods) {
const domain = {
locations: [],
@@ -24,14 +23,8 @@ export default function(newApp, ui, methods) {
const getCategoryColor = methods.getCategoryColor;
const narrativeProps = ui.narratives;
// Map Settings
const center = newApp.mapAnchor;
const maxBoundaries = [[180, -180], [-180, 180]];
const zoomLevel = 14;
// Initialize layer
const sitesLayer = L.layerGroup();
const pathLayer = L.layerGroup();
// Icons for markPoint flags (a yellow ring around a location)
const eventCircleMarkers = {};
@@ -44,94 +37,6 @@ export default function(newApp, ui, methods) {
direction: 'top',
};
/**
* Creates a Leaflet map and a tilelayer for the map background
* @param {string} id: DOM element to create map onto
* @param {array} center: [lat, long] coordinates the map will be centered on
* @param {number} zoom: zoom level
*/
function initBackgroundMap(id, zoom) {
/* http://bl.ocks.org/sumbera/10463358 */
const map = L.map(id)
.setView(center, zoom)
.setMinZoom(10)
.setMaxZoom(19)
.setMaxBounds(maxBoundaries)
// NB: configure tile endpoint
let s
if (process.env.MAPBOX_TOKEN && process.env.MAPBOX_TOKEN !== 'your_token') {
s = L.tileLayer(
`http://a.tiles.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}@2x.png?access_token=${process.env.MAPBOX_TOKEN}`
);
} else {
// eslint-disable-next-line
alert(`No mapbox token specified in config.
Timemap does not currently support any other tiling layer,
so you will need to sign up for one at:
https://www.mapbox.com/
Stop and start the development process in terminal after you have added your token to config.js`)
return
}
s = s.addTo(map);
map.keyboard.disable();
const pane = d3.select(map.getPanes().overlayPane);
const boundingClient = d3.select(`#${id}`).node().getBoundingClientRect();
const width = boundingClient.width;
const height = boundingClient.height;
svg = pane.append('svg')
.attr('class', 'leaflet-svg')
.attr('width', width)
.attr('height', height);
g = svg.append('g');
svg.insert('defs', 'g')
.append('marker')
.attr('id', 'arrow')
.attr('viewBox', '0 0 6 6')
.attr('refX', 3)
.attr('refY', 3)
.attr('markerWidth', 6)
.attr('markerHeight', 6)
.attr('orient', 'auto')
.append('path')
.style('fill', 'red')
.attr('d', 'M0,3v-3l6,3l-6,3z');
svg.insert('defs', 'g')
.append('marker')
.attr('id', 'arrow-off')
.attr('viewBox', '0 0 6 6')
.attr('refX', 3)
.attr('refY', 3)
.attr('markerWidth', 6)
.attr('markerHeight', 6)
.attr('orient', 'auto')
.append('path')
.style('fill', 'black')
.style('fill-opacity', 0.2)
.attr('d', 'M0,3v-3l6,3l-6,3z');
map.on('zoomstart', () => {
svg.classed('hide', true);
});
map.on('zoomend', () => {
svg.classed('hide', false);
});
return map;
}
// Initialize leaflet map and layers for each type of data
const lMap = initBackgroundMap(ui.dom.map, zoomLevel);
function projectPoint(location) {
const latLng = new L.LatLng(location[0], location[1]);
return {