mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-29 07:30:32 +01:00
Load icon and font metadata from endpoint
This commit is contained in:
parent
09b6b2dffe
commit
0fb59ca544
5 changed files with 80 additions and 17 deletions
|
@ -10,6 +10,7 @@ import Toolbar from './Toolbar'
|
||||||
import AppLayout from './AppLayout'
|
import AppLayout from './AppLayout'
|
||||||
import MessagePanel from './MessagePanel'
|
import MessagePanel from './MessagePanel'
|
||||||
|
|
||||||
|
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
|
||||||
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
|
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
|
||||||
import validateStyleMin from 'mapbox-gl-style-spec/lib/validate_style.min'
|
import validateStyleMin from 'mapbox-gl-style-spec/lib/validate_style.min'
|
||||||
import formatStyle from 'mapbox-gl-style-spec/lib/format'
|
import formatStyle from 'mapbox-gl-style-spec/lib/format'
|
||||||
|
@ -21,6 +22,18 @@ import { ApiStyleStore } from '../libs/apistore'
|
||||||
import { RevisionStore } from '../libs/revisions'
|
import { RevisionStore } from '../libs/revisions'
|
||||||
import LayerWatcher from '../libs/layerwatcher'
|
import LayerWatcher from '../libs/layerwatcher'
|
||||||
|
|
||||||
|
function updateRootSpec(spec, fieldName, newValues) {
|
||||||
|
return {
|
||||||
|
...spec,
|
||||||
|
$root: {
|
||||||
|
...spec.$root,
|
||||||
|
[fieldName]: {
|
||||||
|
...spec.$root[fieldName],
|
||||||
|
values: newValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default class App extends React.Component {
|
export default class App extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -52,6 +65,7 @@ export default class App extends React.Component {
|
||||||
sources: {},
|
sources: {},
|
||||||
vectorLayers: {},
|
vectorLayers: {},
|
||||||
inspectModeEnabled: false,
|
inspectModeEnabled: false,
|
||||||
|
spec: GlSpec,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.layerWatcher = new LayerWatcher({
|
this.layerWatcher = new LayerWatcher({
|
||||||
|
@ -85,7 +99,26 @@ export default class App extends React.Component {
|
||||||
this.styleStore.save(snapshotStyle)
|
this.styleStore.save(snapshotStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFonts(urlTemplate) {
|
||||||
|
downloadGlyphsMetadata(urlTemplate, fonts => {
|
||||||
|
this.setState({ spec: updateRootSpec(this.state.spec, 'glyphs', fonts)})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIcons(baseUrl) {
|
||||||
|
downloadSpriteMetadata(baseUrl, icons => {
|
||||||
|
this.setState({ spec: updateRootSpec(this.state.spec, 'sprite', icons)})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onStyleChanged(newStyle, save=true) {
|
onStyleChanged(newStyle, save=true) {
|
||||||
|
if(newStyle.glyphs !== this.state.mapStyle.glyphs) {
|
||||||
|
this.updateFonts(newStyle.glyphs)
|
||||||
|
}
|
||||||
|
if(newStyle.sprite !== this.state.mapStyle.sprite) {
|
||||||
|
this.updateIcons(newStyle.sprite)
|
||||||
|
}
|
||||||
|
|
||||||
const errors = validateStyleMin(newStyle, GlSpec)
|
const errors = validateStyleMin(newStyle, GlSpec)
|
||||||
if(errors.length === 0) {
|
if(errors.length === 0) {
|
||||||
this.revisionStore.addRevision(newStyle)
|
this.revisionStore.addRevision(newStyle)
|
||||||
|
@ -212,6 +245,7 @@ export default class App extends React.Component {
|
||||||
layer={selectedLayer}
|
layer={selectedLayer}
|
||||||
sources={this.state.sources}
|
sources={this.state.sources}
|
||||||
vectorLayers={this.state.vectorLayers}
|
vectorLayers={this.state.vectorLayers}
|
||||||
|
spec={this.state.spec}
|
||||||
onLayerChanged={this.onLayerChanged.bind(this)}
|
onLayerChanged={this.onLayerChanged.bind(this)}
|
||||||
onLayerIdChange={this.onLayerIdChange.bind(this)}
|
onLayerIdChange={this.onLayerIdChange.bind(this)}
|
||||||
/> : null
|
/> : null
|
||||||
|
|
|
@ -1,20 +1,34 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
|
|
||||||
|
|
||||||
import ZoomSpecField from './ZoomSpecField'
|
import ZoomSpecField from './ZoomSpecField'
|
||||||
import colors from '../../config/colors'
|
import colors from '../../config/colors'
|
||||||
import { margins } from '../../config/scales'
|
import { margins } from '../../config/scales'
|
||||||
|
|
||||||
|
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
|
||||||
|
|
||||||
/** Extract field spec by {@fieldName} from the {@layerType} in the
|
/** Extract field spec by {@fieldName} from the {@layerType} in the
|
||||||
* style specification from either the paint or layout group */
|
* style specification from either the paint or layout group */
|
||||||
function getFieldSpec(layerType, fieldName) {
|
function getFieldSpec(spec, layerType, fieldName) {
|
||||||
const groupName = getGroupName(layerType, fieldName)
|
const groupName = getGroupName(spec, layerType, fieldName)
|
||||||
const group = GlSpec[groupName + '_' + layerType]
|
const group = spec[groupName + '_' + layerType]
|
||||||
return group[fieldName]
|
const fieldSpec = group[fieldName]
|
||||||
|
if(iconProperties.indexOf(fieldName) >= 0) {
|
||||||
|
return {
|
||||||
|
...fieldSpec,
|
||||||
|
values: spec.$root.sprite.values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fieldName === 'text-font') {
|
||||||
|
return {
|
||||||
|
...fieldSpec,
|
||||||
|
values: spec.$root.glyphs.values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fieldSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGroupName(layerType, fieldName) {
|
function getGroupName(spec, layerType, fieldName) {
|
||||||
const paint = GlSpec['paint_' + layerType] || {}
|
const paint = spec['paint_' + layerType] || {}
|
||||||
if (fieldName in paint) {
|
if (fieldName in paint) {
|
||||||
return 'paint'
|
return 'paint'
|
||||||
} else {
|
} else {
|
||||||
|
@ -27,16 +41,17 @@ export default class PropertyGroup extends React.Component {
|
||||||
layer: React.PropTypes.object.isRequired,
|
layer: React.PropTypes.object.isRequired,
|
||||||
groupFields: React.PropTypes.array.isRequired,
|
groupFields: React.PropTypes.array.isRequired,
|
||||||
onChange: React.PropTypes.func.isRequired,
|
onChange: React.PropTypes.func.isRequired,
|
||||||
|
spec: React.PropTypes.object.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
onPropertyChange(property, newValue) {
|
onPropertyChange(property, newValue) {
|
||||||
const group = getGroupName(this.props.layer.type, property)
|
const group = getGroupName(this.props.spec, this.props.layer.type, property)
|
||||||
this.props.onChange(group , property, newValue)
|
this.props.onChange(group , property, newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const fields = this.props.groupFields.map(fieldName => {
|
const fields = this.props.groupFields.map(fieldName => {
|
||||||
const fieldSpec = getFieldSpec(this.props.layer.type, fieldName)
|
const fieldSpec = getFieldSpec(this.props.spec, this.props.layer.type, fieldName)
|
||||||
|
|
||||||
const paint = this.props.layer.paint || {}
|
const paint = this.props.layer.paint || {}
|
||||||
const layout = this.props.layer.layout || {}
|
const layout = this.props.layer.layout || {}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import color from 'color'
|
import color from 'color'
|
||||||
|
|
||||||
import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js'
|
|
||||||
import ColorField from './ColorField'
|
import ColorField from './ColorField'
|
||||||
import NumberInput from '../inputs/NumberInput'
|
import NumberInput from '../inputs/NumberInput'
|
||||||
import CheckboxInput from '../inputs/CheckboxInput'
|
import CheckboxInput from '../inputs/CheckboxInput'
|
||||||
|
@ -10,8 +9,10 @@ import SelectInput from '../inputs/SelectInput'
|
||||||
import MultiButtonInput from '../inputs/MultiButtonInput'
|
import MultiButtonInput from '../inputs/MultiButtonInput'
|
||||||
import ArrayInput from '../inputs/ArrayInput'
|
import ArrayInput from '../inputs/ArrayInput'
|
||||||
import FontInput from '../inputs/FontInput'
|
import FontInput from '../inputs/FontInput'
|
||||||
|
import IconInput from '../inputs/IconInput'
|
||||||
import capitalize from 'lodash.capitalize'
|
import capitalize from 'lodash.capitalize'
|
||||||
|
|
||||||
|
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
|
||||||
|
|
||||||
import input from '../../config/input.js'
|
import input from '../../config/input.js'
|
||||||
|
|
||||||
|
@ -78,11 +79,17 @@ export default class SpecField extends React.Component {
|
||||||
options={options}
|
options={options}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
case 'string': return (
|
case 'string':
|
||||||
<StringInput
|
if(iconProperties.indexOf(this.props.fieldName) >= 0) {
|
||||||
{...commonProps}
|
return <IconInput
|
||||||
/>
|
{...commonProps}
|
||||||
)
|
icons={this.props.fieldSpec.values}
|
||||||
|
/>
|
||||||
|
} else {
|
||||||
|
return <StringInput
|
||||||
|
{...commonProps}
|
||||||
|
/>
|
||||||
|
}
|
||||||
case 'color': return (
|
case 'color': return (
|
||||||
<ColorField
|
<ColorField
|
||||||
{...commonProps}
|
{...commonProps}
|
||||||
|
@ -97,6 +104,7 @@ export default class SpecField extends React.Component {
|
||||||
if(this.props.fieldName === 'text-font') {
|
if(this.props.fieldName === 'text-font') {
|
||||||
return <FontInput
|
return <FontInput
|
||||||
{...commonProps}
|
{...commonProps}
|
||||||
|
fonts={this.props.fieldSpec.values}
|
||||||
/>
|
/>
|
||||||
} else {
|
} else {
|
||||||
return <ArrayInput
|
return <ArrayInput
|
||||||
|
|
|
@ -9,10 +9,15 @@ import fontStacks from '../../config/fontstacks.json'
|
||||||
class FontInput extends React.Component {
|
class FontInput extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
value: React.PropTypes.array.isRequired,
|
value: React.PropTypes.array.isRequired,
|
||||||
|
fonts: React.PropTypes.array,
|
||||||
style: React.PropTypes.object,
|
style: React.PropTypes.object,
|
||||||
onChange: React.PropTypes.func.isRequired,
|
onChange: React.PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
fonts: []
|
||||||
|
}
|
||||||
|
|
||||||
get values() {
|
get values() {
|
||||||
return this.props.value || this.props.default.slice(1) || []
|
return this.props.value || this.props.default.slice(1) || []
|
||||||
}
|
}
|
||||||
|
@ -28,7 +33,7 @@ class FontInput extends React.Component {
|
||||||
return <AutocompleteInput
|
return <AutocompleteInput
|
||||||
key={i}
|
key={i}
|
||||||
value={value}
|
value={value}
|
||||||
options={fontStacks.map(f => [f, f])}
|
options={this.props.fonts.map(f => [f, f])}
|
||||||
onChange={this.changeFont.bind(this, i)}
|
onChange={this.changeFont.bind(this, i)}
|
||||||
wrapperStyle={{
|
wrapperStyle={{
|
||||||
display: 'block',
|
display: 'block',
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
|
|
||||||
import JSONEditor from './JSONEditor'
|
import JSONEditor from './JSONEditor'
|
||||||
import FilterEditor from '../filter/FilterEditor'
|
import FilterEditor from '../filter/FilterEditor'
|
||||||
import PropertyGroup from '../fields/PropertyGroup'
|
import PropertyGroup from '../fields/PropertyGroup'
|
||||||
|
@ -47,6 +46,7 @@ export default class LayerEditor extends React.Component {
|
||||||
layer: React.PropTypes.object.isRequired,
|
layer: React.PropTypes.object.isRequired,
|
||||||
sources: React.PropTypes.object,
|
sources: React.PropTypes.object,
|
||||||
vectorLayers: React.PropTypes.object,
|
vectorLayers: React.PropTypes.object,
|
||||||
|
spec: React.PropTypes.object.isRequired,
|
||||||
onLayerChanged: React.PropTypes.func,
|
onLayerChanged: React.PropTypes.func,
|
||||||
onLayerIdChange: React.PropTypes.func,
|
onLayerIdChange: React.PropTypes.func,
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,7 @@ export default class LayerEditor extends React.Component {
|
||||||
case 'properties': return <PropertyGroup
|
case 'properties': return <PropertyGroup
|
||||||
layer={this.props.layer}
|
layer={this.props.layer}
|
||||||
groupFields={fields}
|
groupFields={fields}
|
||||||
|
spec={this.props.spec}
|
||||||
onChange={this.changeProperty.bind(this)}
|
onChange={this.changeProperty.bind(this)}
|
||||||
/>
|
/>
|
||||||
case 'jsoneditor': return <JSONEditor
|
case 'jsoneditor': return <JSONEditor
|
||||||
|
|
Loading…
Reference in a new issue