Load OL3 code only once we need it

This commit is contained in:
Lukas Martinelli 2016-12-22 18:08:42 +01:00
parent 607e920548
commit 3ee1473a49
8 changed files with 123 additions and 46 deletions

View file

@ -110,6 +110,8 @@ export default class App extends React.Component {
const metadata = this.state.mapStyle.metadata || {} const metadata = this.state.mapStyle.metadata || {}
const renderer = metadata['maputnik:renderer'] || 'mbgljs' const renderer = metadata['maputnik:renderer'] || 'mbgljs'
// Check if OL3 code has been loaded?
if(renderer === 'ol3') { if(renderer === 'ol3') {
return <OpenLayers3Map {...mapProps} /> return <OpenLayers3Map {...mapProps} />
} else { } else {

View file

@ -5,7 +5,7 @@ class StringInput extends React.Component {
static propTypes = { static propTypes = {
value: React.PropTypes.string, value: React.PropTypes.string,
style: React.PropTypes.object, style: React.PropTypes.object,
onChange: React.PropTypes.func.isRequired, onChange: React.PropTypes.func,
} }
render() { render() {

View file

@ -1,14 +1,23 @@
import React from 'react' import React from 'react'
import ol from 'openlayers'
import olms from 'ol-mapbox-style'
import Map from './Map' import Map from './Map'
import style from '../../libs/style.js' import style from '../../libs/style.js'
export default class OpenLayers3Map extends Map { class OpenLayers3Map extends Map {
constructor(props) { constructor(props) {
super(props) super(props)
this.state = {
map: null,
ol: null,
olms: null
}
//Load OpenLayers dynamically once we need it
//TODO: Make this more convenient
require.ensure(["openlayers", "ol-mapbox-style"], ()=> {
const ol = require('openlayers')
const olms = require('ol-mapbox-style')
const tilegrid = ol.tilegrid.createXYZ({tileSize: 512, maxZoom: 22}) const tilegrid = ol.tilegrid.createXYZ({tileSize: 512, maxZoom: 22})
this.resolutions = tilegrid.getResolutions() this.resolutions = tilegrid.getResolutions()
this.layer = new ol.layer.VectorTile({ this.layer = new ol.layer.VectorTile({
@ -22,17 +31,31 @@ export default class OpenLayers3Map extends Map {
url: 'http://osm2vectortiles-0.tileserver.com/v2/{z}/{x}/{y}.pbf' url: 'http://osm2vectortiles-0.tileserver.com/v2/{z}/{x}/{y}.pbf'
}) })
}) })
this.setState({
ol: ol,
olms: olms,
})
console.log('Loaded OpenLayers3 renderer')
})
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const olms = this.state.olms
const ol = this.state.ol
if(!olms || !ol) return
const jsonStyle = style.toJSON(nextProps.mapStyle) const jsonStyle = style.toJSON(nextProps.mapStyle)
const styleFunc = olms.getStyleFunction(jsonStyle, 'mapbox', this.resolutions) const styleFunc = olms.getStyleFunction(jsonStyle, 'mapbox', this.resolutions)
this.layer.setStyle(styleFunc) this.layer.setStyle(styleFunc)
this.state.map.render() this.state.map.render()
} }
componentDidMount() { componentDidMount() {
const olms = this.state.olms
const ol = this.state.ol
if(!olms || !ol) return
const jsonStyle = style.toJSON(this.props.mapStyle) const jsonStyle = style.toJSON(this.props.mapStyle)
const styleFunc = olms.getStyleFunction(jsonStyle, 'mapbox', this.resolutions) const styleFunc = olms.getStyleFunction(jsonStyle, 'mapbox', this.resolutions)
this.layer.setStyle(styleFunc) this.layer.setStyle(styleFunc)
@ -49,3 +72,5 @@ export default class OpenLayers3Map extends Map {
this.setState({ map }); this.setState({ map });
} }
} }
export default OpenLayers3Map

View file

@ -23,8 +23,14 @@ class SettingsModal extends React.Component {
this.props.onStyleChanged(changedStyle) this.props.onStyleChanged(changedStyle)
} }
onRendererChange(e) { onRendererChange(renderer) {
const changedStyle = this.props.mapStyle.setIn(['metadata', 'maputnik:renderer'], e.target.value) const changedStyle = {
...this.props.mapStyle,
metadata: {
...this.props.mapStyle.metadata,
'maputnik:renderer': renderer,
}
}
this.props.onStyleChanged(changedStyle) this.props.onStyleChanged(changedStyle)
} }

View file

@ -8,6 +8,7 @@ import StringInput from '../inputs/StringInput'
import SelectInput from '../inputs/SelectInput' import SelectInput from '../inputs/SelectInput'
import SourceTypeEditor from '../sources/SourceTypeEditor' import SourceTypeEditor from '../sources/SourceTypeEditor'
import style from '../../libs/style'
import publicSources from '../../config/tilesets.json' import publicSources from '../../config/tilesets.json'
import colors from '../../config/colors' import colors from '../../config/colors'
import { margins, fontSizes } from '../../config/scales' import { margins, fontSizes } from '../../config/scales'
@ -34,12 +35,15 @@ class PublicSource extends React.Component {
fontSize: fontSizes[4], fontSize: fontSizes[4],
color: colors.lowgray, color: colors.lowgray,
}}> }}>
<Button style={{ <Button
onClick={() => this.props.onSelect(this.props.id)}
style={{
backgroundColor: 'transparent', backgroundColor: 'transparent',
padding: margins[2], padding: margins[2],
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
}}> }}
>
<div> <div>
<span style={{fontWeight: 700}}>{this.props.title}</span> <span style={{fontWeight: 700}}>{this.props.title}</span>
<br/> <br/>
@ -114,9 +118,20 @@ class AddSource extends React.Component {
super(props) super(props)
this.state = { this.state = {
mode: 'tilejson', mode: 'tilejson',
source: {} source: {
id: style.generateId(),
} }
} }
}
onSourceIdChange(newId) {
this.setState({
source: {
...this.state.source,
id: newId,
}
})
}
onSourceChange(source) { onSourceChange(source) {
this.setState({ this.setState({
@ -128,7 +143,8 @@ class AddSource extends React.Component {
return <div> return <div>
<InputBlock label={"Source ID"}> <InputBlock label={"Source ID"}>
<StringInput <StringInput
value={'blubid'} value={this.state.source.id}
onChange={this.onSourceIdChange.bind(this)}
/> />
</InputBlock> </InputBlock>
<InputBlock label={"Source Type"}> <InputBlock label={"Source Type"}>
@ -147,7 +163,7 @@ class AddSource extends React.Component {
mode={this.state.mode} mode={this.state.mode}
source={this.state.source} source={this.state.source}
/> />
<Button onClick={() => this.props.onSourceAdd(this.state.source)}> <Button onClick={() => this.onSourceAdd(this.state.source)}>
Add Source Add Source
</Button> </Button>
</div> </div>
@ -159,6 +175,21 @@ class SourcesModal extends React.Component {
mapStyle: React.PropTypes.object.isRequired, mapStyle: React.PropTypes.object.isRequired,
isOpen: React.PropTypes.bool.isRequired, isOpen: React.PropTypes.bool.isRequired,
onOpenToggle: React.PropTypes.func.isRequired, onOpenToggle: React.PropTypes.func.isRequired,
onStyleChanged: React.PropTypes.func.isRequired,
}
onSourceAdd(source) {
const changedSources = {
...this.props.mapStyle.sources,
[source.id]: source
}
const changedStyle = {
...this.props.mapStyle,
sources: changedSources
}
this.props.onStyleChanged(changedStyle)
} }
render() { render() {
@ -167,12 +198,14 @@ class SourcesModal extends React.Component {
return <SourceEditorLayout sourceId={sourceId} source={source} /> return <SourceEditorLayout sourceId={sourceId} source={source} />
}) })
const tilesetOptions = publicSources.filter(tileset => !(tileset.id in this.props.mapStyle.sources)).map(tileset => { const tilesetOptions = publicSources.filter(source => !(source.id in this.props.mapStyle.sources)).map(source => {
return <PublicSource return <PublicSource
id={tileset.id} key={source.id}
type={tileset.type} id={source.id}
title={tileset.title} type={source.type}
description={tileset.description} title={source.title}
description={source.description}
onSelect={() => this.onSourceAdd(source)}
/> />
}) })
@ -188,7 +221,7 @@ class SourcesModal extends React.Component {
<Heading level={4}>Add New Source</Heading> <Heading level={4}>Add New Source</Heading>
<div style={{maxWidth: 300}}> <div style={{maxWidth: 300}}>
<p style={{color: colors.lowgray, fontSize: fontSizes[5]}}>Add a new source to your style. You can only choose the source type and id at creation time!</p> <p style={{color: colors.lowgray, fontSize: fontSizes[5]}}>Add a new source to your style. You can only choose the source type and id at creation time!</p>
<AddSource /> <AddSource onSourceAdd={this.onSourceAdd.bind(this)} />
</div> </div>
<Heading level={4}>Choose Public Source</Heading> <Heading level={4}>Choose Public Source</Heading>

View file

@ -5,7 +5,7 @@ import StringInput from '../inputs/StringInput'
class TileJSONSourceEditor extends React.Component { class TileJSONSourceEditor extends React.Component {
static propTypes = { static propTypes = {
url: React.PropTypes.string.isRequired, url: React.PropTypes.string.isRequired,
onChange: React.PropTypes.func.isRequired, onChange: React.PropTypes.func,
} }
render() { render() {
@ -23,7 +23,7 @@ class TileURLSourceEditor extends React.Component {
tiles: React.PropTypes.array.isRequired, tiles: React.PropTypes.array.isRequired,
minZoom: React.PropTypes.number.isRequired, minZoom: React.PropTypes.number.isRequired,
maxZoom: React.PropTypes.number.isRequired, maxZoom: React.PropTypes.number.isRequired,
onChange: React.PropTypes.func.isRequired, onChange: React.PropTypes.func,
} }
renderTileUrls() { renderTileUrls() {
@ -59,7 +59,7 @@ class TileURLSourceEditor extends React.Component {
class GeoJSONSourceEditor extends React.Component { class GeoJSONSourceEditor extends React.Component {
static propTypes = { static propTypes = {
data: React.PropTypes.string.isRequired, data: React.PropTypes.string.isRequired,
onChange: React.PropTypes.func.isRequired, onChange: React.PropTypes.func,
} }
render() { render() {

View file

@ -8,9 +8,13 @@ const emptyStyle = ensureMetadataExists({
layers: [], layers: [],
}) })
function generateId() {
return Math.random().toString(36).substr(2, 9)
}
function ensureHasId(style) { function ensureHasId(style) {
if('id' in style) return style if('id' in style) return style
style.id = Math.random().toString(36).substr(2, 9) style.id = generateId()
return style return style
} }
@ -37,4 +41,5 @@ export default {
ensureMetadataExists, ensureMetadataExists,
emptyStyle, emptyStyle,
indexOfLayer, indexOfLayer,
generateId,
} }

View file

@ -28,26 +28,32 @@ module.exports = {
app: './src/index.jsx', app: './src/index.jsx',
vendor: [ vendor: [
'file-saver', 'file-saver',
'immutable',
'mapbox-gl', 'mapbox-gl',
//TODO: Cannot resolve migrations file? //TODO: Build failure because cannot resolve migrations file
//"mapbox-gl-style-spec", //"mapbox-gl-style-spec",
"radium",
"randomcolor", "randomcolor",
"lodash.clonedeep",
"lodash.throttle",
"lodash.topairs",
'color',
'react', 'react',
"react-dom", "react-dom",
"react-color", "react-color",
"react-file-reader-input", "react-file-reader-input",
"react-collapse",
"react-height",
"react-icon-base",
"react-motion",
"react-sortable-hoc",
"request",
//TODO: Icons raise multi vendor errors? //TODO: Icons raise multi vendor errors?
//"react-icons", //"react-icons",
// Open Layers
'openlayers',
'ol-mapbox-style'
] ]
}, },
output: { output: {
path: path.join(__dirname, 'public'), path: path.join(__dirname, 'public'),
filename: '[chunkhash].app.js' filename: '[name].[chunkhash].js',
chunkFilename: '[chunkhash].js'
}, },
resolve: { resolve: {
alias: { alias: {