Implement adding public and custom sources

This commit is contained in:
Lukas Martinelli 2016-12-28 15:57:30 +01:00
parent ba271e1fc6
commit 80678af691
4 changed files with 80 additions and 49 deletions

View file

@ -5,6 +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,
default: React.PropTypes.number,
onChange: React.PropTypes.func, onChange: React.PropTypes.func,
} }
@ -15,6 +16,7 @@ class StringInput extends React.Component {
...this.props.style ...this.props.style
}} }}
value={this.props.value} value={this.props.value}
placeholder={this.props.default}
onChange={e => this.props.onChange(e.target.value)} onChange={e => this.props.onChange(e.target.value)}
/> />
} }

View file

@ -118,19 +118,30 @@ class AddSource extends React.Component {
super(props) super(props)
this.state = { this.state = {
mode: 'tilejson', mode: 'tilejson',
source: { sourceId: style.generateId(),
id: style.generateId(), source: this.defaultSource('tilejson'),
}
} }
} }
onSourceIdChange(newId) { defaultSource(mode) {
this.setState({ const source = (this.state || {}).source || {}
source: { switch(mode) {
...this.state.source, case 'geojson': return {
id: newId, type: 'geojson',
data: source.data || 'http://localhost:3000/geojson.json'
} }
}) case 'tilejson': return {
type: 'vector',
url: source.url || 'http://localhost:3000/tilejson.json'
}
case 'tilexyz': return {
type: 'vector',
tiles: source.tiles || ['http://localhost:3000/{x}/{y}/{z}.pbf'],
minZoom: source.minZoom || 0,
maxZoom: source.maxZoom || 14
}
default: return {}
}
} }
onSourceChange(source) { onSourceChange(source) {
@ -143,8 +154,8 @@ class AddSource extends React.Component {
return <div> return <div>
<InputBlock label={"Source ID"}> <InputBlock label={"Source ID"}>
<StringInput <StringInput
value={this.state.source.id} value={this.state.sourceId}
onChange={this.onSourceIdChange.bind(this)} onChange={v => this.setState({ sourceId: v})}
/> />
</InputBlock> </InputBlock>
<InputBlock label={"Source Type"}> <InputBlock label={"Source Type"}>
@ -154,7 +165,7 @@ class AddSource extends React.Component {
['tilejson', 'Vector (TileJSON URL)'], ['tilejson', 'Vector (TileJSON URL)'],
['tilexyz', 'Vector (XYZ URLs)'], ['tilexyz', 'Vector (XYZ URLs)'],
]} ]}
onChange={v => this.setState({mode: v})} onChange={mode => this.setState({mode: mode, source: this.defaultSource(mode)})}
value={this.state.mode} value={this.state.mode}
/> />
</InputBlock> </InputBlock>
@ -163,7 +174,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.onSourceAdd(this.state.source)}> <Button onClick={() => this.props.onSourceAdd(this.state.sourceId, this.state.source)}>
Add Source Add Source
</Button> </Button>
</div> </div>
@ -178,10 +189,10 @@ class SourcesModal extends React.Component {
onStyleChanged: React.PropTypes.func.isRequired, onStyleChanged: React.PropTypes.func.isRequired,
} }
onSourceAdd(source) { onSourceAdd(sourceId, source) {
const changedSources = { const changedSources = {
...this.props.mapStyle.sources, ...this.props.mapStyle.sources,
[source.id]: source [sourceId]: source
} }
const changedStyle = { const changedStyle = {
@ -203,6 +214,12 @@ class SourcesModal extends React.Component {
this.props.onStyleChanged(changedStyle) this.props.onStyleChanged(changedStyle)
} }
stripTitle(source) {
const strippedSource = {...source}
delete strippedSource['title']
return strippedSource
}
render() { render() {
const activeSources = Object.keys(this.props.mapStyle.sources).map(sourceId => { const activeSources = Object.keys(this.props.mapStyle.sources).map(sourceId => {
const source = this.props.mapStyle.sources[sourceId] const source = this.props.mapStyle.sources[sourceId]
@ -214,14 +231,14 @@ class SourcesModal extends React.Component {
/> />
}) })
const tilesetOptions = publicSources.filter(source => !(source.id in this.props.mapStyle.sources)).map(source => { const tilesetOptions = Object.keys(publicSources).filter(sourceId => !(sourceId in this.props.mapStyle.sources)).map(sourceId => {
const source = publicSources[sourceId]
return <PublicSource return <PublicSource
key={source.id} key={sourceId}
id={source.id} id={sourceId}
type={source.type} type={source.type}
title={source.title} title={source.title}
description={source.description} onSelect={() => this.onSourceAdd(sourceId, this.stripTitle(source))}
onSelect={() => this.onSourceAdd(source)}
/> />
}) })

View file

@ -1,18 +1,22 @@
import React from 'react' import React from 'react'
import InputBlock from '../inputs/InputBlock' import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput' import StringInput from '../inputs/StringInput'
import NumberInput from '../inputs/NumberInput'
class TileJSONSourceEditor extends React.Component { class TileJSONSourceEditor extends React.Component {
static propTypes = { static propTypes = {
url: React.PropTypes.string.isRequired, source: React.PropTypes.object.isRequired,
onChange: React.PropTypes.func, onChange: React.PropTypes.func,
} }
render() { render() {
return <InputBlock label={"TileJSON URL"}> return <InputBlock label={"TileJSON URL"}>
<StringInput <StringInput
value={this.props.url} value={this.props.source.url}
onChange={this.props.onChange} onChange={url => this.props.onChange({
...this.props.source,
url: url
})}
/> />
</InputBlock> </InputBlock>
} }
@ -20,15 +24,14 @@ class TileJSONSourceEditor extends React.Component {
class TileURLSourceEditor extends React.Component { class TileURLSourceEditor extends React.Component {
static propTypes = { static propTypes = {
tiles: React.PropTypes.array.isRequired, source: React.PropTypes.object.isRequired,
minZoom: React.PropTypes.number.isRequired,
maxZoom: React.PropTypes.number.isRequired,
onChange: React.PropTypes.func, onChange: React.PropTypes.func,
} }
renderTileUrls() { renderTileUrls() {
const prefix = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th'] const prefix = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th']
return this.props.tiles.map((tileUrl, tileIndex) => { const tiles = this.props.source.tiles || []
return tiles.map((tileUrl, tileIndex) => {
return <InputBlock key={tileIndex} label={prefix[tileIndex] + " Tile URL"}> return <InputBlock key={tileIndex} label={prefix[tileIndex] + " Tile URL"}>
<StringInput <StringInput
value={tileUrl} value={tileUrl}
@ -38,17 +41,24 @@ class TileURLSourceEditor extends React.Component {
} }
render() { render() {
console.log(this.props.tiles)
return <div> return <div>
{this.renderTileUrls()} {this.renderTileUrls()}
<InputBlock label={"Min Zoom"}> <InputBlock label={"Min Zoom"}>
<StringInput <NumberInput
value={this.props.minZoom} value={this.props.source.minZoom}
onChange={minZoom => this.props.onChange({
...this.props.source,
minZoom: minZoom
})}
/> />
</InputBlock> </InputBlock>
<InputBlock label={"Max Zoom"}> <InputBlock label={"Max Zoom"}>
<StringInput <NumberInput
value={this.props.maxZoom} value={this.props.source.maxZoom}
onChange={maxZoom => this.props.onChange({
...this.props.source,
maxZoom: maxZoom
})}
/> />
</InputBlock> </InputBlock>
</div> </div>
@ -58,15 +68,18 @@ class TileURLSourceEditor extends React.Component {
class GeoJSONSourceEditor extends React.Component { class GeoJSONSourceEditor extends React.Component {
static propTypes = { static propTypes = {
data: React.PropTypes.string.isRequired, source: React.PropTypes.object.isRequired,
onChange: React.PropTypes.func, onChange: React.PropTypes.func,
} }
render() { render() {
return <InputBlock label={"GeoJSON Data"}> return <InputBlock label={"GeoJSON Data"}>
<StringInput <StringInput
value={this.props.data} value={this.props.source.data}
onChange={this.props.onChange} onChange={data => this.props.onChange({
...this.props.source,
data: data
})}
/> />
</InputBlock> </InputBlock>
} }
@ -80,11 +93,14 @@ class SourceTypeEditor extends React.Component {
} }
render() { render() {
const source = this.props.source const commonProps = {
source: this.props.source,
onChange: this.props.onChange,
}
switch(this.props.mode) { switch(this.props.mode) {
case 'geojson': return <GeoJSONSourceEditor data={source.data || 'http://localhost:3000/mygeojson.json'} /> case 'geojson': return <GeoJSONSourceEditor {...commonProps} />
case 'tilejson': return <TileJSONSourceEditor url={source.url || 'http://localhost:3000/tiles.json'}/> case 'tilejson': return <TileJSONSourceEditor {...commonProps} />
case 'tilexyz': return <TileURLSourceEditor tiles={source.tiles || ['http://localhost:3000/{x}/{y}/{z}.pbf']} minZoom={source.minZoom || 0} maxZoom={source.maxZoom || 14}/> case 'tilexyz': return <TileURLSourceEditor {...commonProps} />
default: return null default: return null
} }
} }

View file

@ -1,12 +1,10 @@
[ {
{ "mapbox-streets": {
"id": "mapbox-streets",
"type": "vector", "type": "vector",
"url": "mapbox://mapbox.mapbox-streets-v7", "url": "mapbox://mapbox.mapbox-streets-v7",
"title": "Mapbox Streets" "title": "Mapbox Streets"
}, },
{ "tilezen": {
"id": "tilezen",
"type": "vector", "type": "vector",
"tiles": [ "tiles": [
"http://tile.mapzen.com/mapzen/vector/v1/{layers}/{z}/{x}/{y}.pbf?api_key=mapzen-RVcyVL7" "http://tile.mapzen.com/mapzen/vector/v1/{layers}/{z}/{x}/{y}.pbf?api_key=mapzen-RVcyVL7"
@ -15,16 +13,14 @@
"maxZoom": 15, "maxZoom": 15,
"title": "Mapzen Vector Tile Service" "title": "Mapzen Vector Tile Service"
}, },
{ "openmaptiles": {
"id": "openmaptiles",
"type": "vector", "type": "vector",
"url": "https://free.tilehosting.com/data/v3.json?key=25ItXg7aI5wurYDtttD", "url": "https://free.tilehosting.com/data/v3.json?key=25ItXg7aI5wurYDtttD",
"title": "OpenMapTiles CDN" "title": "OpenMapTiles CDN"
}, },
{ "swissnames-landscape": {
"id": "swissnames-landscape",
"type": "geojson", "type": "geojson",
"data": "http://swissnames.lukasmartinelli.ch/data/landscape.geojson", "data": "http://swissnames.lukasmartinelli.ch/data/landscape.geojson",
"title": "Landscape Names GeoJSON" "title": "Landscape Names GeoJSON"
} }
] }