Merge remote-tracking branch 'upstream/master' into fix/web-driver-tests-v7

Conflicts:
	config/webpack.production.config.js
	package-lock.json
	package.json
This commit is contained in:
orangemug 2018-03-06 07:22:26 +00:00
commit a3fa86f7ee
22 changed files with 5074 additions and 3603 deletions

View file

@ -35,7 +35,7 @@ exports.config = {
mochaOpts: { mochaOpts: {
ui: 'bdd', ui: 'bdd',
// Because we don't know how long the initial build will take... // Because we don't know how long the initial build will take...
timeout: 2*60*1000 timeout: 4*60*1000
}, },
onPrepare: function (config, capabilities) { onPrepare: function (config, capabilities) {
var compiler = webpack(webpackConfig); var compiler = webpack(webpackConfig);

View file

@ -25,8 +25,7 @@ module.exports = {
}, },
module: { module: {
noParse: [ noParse: [
/mapbox-gl\/dist\/mapbox-gl.js/, /mapbox-gl\/dist\/mapbox-gl.js/
/openlayers\/dist\/ol.js/
], ],
loaders: loaders loaders: loaders
}, },

View file

@ -4,9 +4,21 @@ module.exports = [
exclude: /(node_modules|bower_components|public)/, exclude: /(node_modules|bower_components|public)/,
loaders: ['react-hot-loader/webpack'] loaders: ['react-hot-loader/webpack']
}, },
// HACK: This is a massive hack and reaches into the mapbox-gl private API.
// We have to include this for access to `normalizeSourceURL`. We should
// remove this ASAP, see <https://github.com/mapbox/mapbox-gl-js/issues/2416>
{
test: /.*node_modules[\/\\]mapbox-gl[\/\\]src[\/\\]util[\/\\].*\.js/,
loader: 'babel-loader',
query: {
presets: ['env', 'react', 'flow'],
plugins: ['transform-runtime', 'transform-decorators-legacy', 'transform-class-properties'],
}
},
{ {
test: /\.jsx?$/, test: /\.jsx?$/,
exclude: /(.*node_modules(?![\/\\]@mapbox[\/\\]mapbox-gl-style-spec)|bower_components|public)/, // Note: These modules aren't ES5 therefore we much compile them.
exclude: /(.*node_modules(?![\/\\](@mapbox[\/\\]mapbox-gl-style-spec|ol|mapbox-to-ol-style))|bower_components|public)/,
loader: 'babel-loader', loader: 'babel-loader',
query: { query: {
presets: ['env', 'react'], presets: ['env', 'react'],

View file

@ -7,6 +7,7 @@ var WebpackCleanupPlugin = require('webpack-cleanup-plugin');
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
var CopyWebpackPlugin = require('copy-webpack-plugin'); var CopyWebpackPlugin = require('copy-webpack-plugin');
var artifacts = require("../test/artifacts"); var artifacts = require("../test/artifacts");
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
var OUTPATH = artifacts.pathSync("/build"); var OUTPATH = artifacts.pathSync("/build");
@ -43,8 +44,7 @@ module.exports = {
}, },
module: { module: {
noParse: [ noParse: [
/mapbox-gl\/dist\/mapbox-gl.js/, /mapbox-gl\/dist\/mapbox-gl.js/
/openlayers\/dist\/ol.js/
], ],
loaders loaders
}, },
@ -62,12 +62,7 @@ module.exports = {
NODE_ENV: '"production"' NODE_ENV: '"production"'
} }
}), }),
new webpack.optimize.UglifyJsPlugin({ new UglifyJsPlugin(),
compress: {
warnings: false,
screw_ie8: true,
}
}),
new ExtractTextPlugin('[contenthash].css', { new ExtractTextPlugin('[contenthash].css', {
allChunks: true allChunks: true
}), }),

8311
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "maputnik", "name": "maputnik",
"version": "1.1.0-beta3", "version": "1.1.0",
"description": "A MapboxGL visual style editor", "description": "A MapboxGL visual style editor",
"main": "''", "main": "''",
"scripts": { "scripts": {
@ -10,7 +10,8 @@
"test-watch": "cross-env NODE_ENV=test wdio config/wdio.conf.js --watch", "test-watch": "cross-env NODE_ENV=test wdio config/wdio.conf.js --watch",
"start": "webpack-dev-server --progress --profile --colors --config config/webpack.config.js", "start": "webpack-dev-server --progress --profile --colors --config config/webpack.config.js",
"lint": "eslint --ext js --ext jsx {src,test}", "lint": "eslint --ext js --ext jsx {src,test}",
"lint-styles": "stylelint 'src/styles/*.scss'" "lint-styles": "stylelint 'src/styles/*.scss'",
"nsp": "nsp check --reporter summary"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -21,7 +22,7 @@
"homepage": "https://github.com/maputnik/editor#readme", "homepage": "https://github.com/maputnik/editor#readme",
"dependencies": { "dependencies": {
"@mapbox/mapbox-gl-rtl-text": "^0.1.1", "@mapbox/mapbox-gl-rtl-text": "^0.1.1",
"@mapbox/mapbox-gl-style-spec": "^10.0.1", "@mapbox/mapbox-gl-style-spec": "^11.1.1",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"codemirror": "^5.32.0", "codemirror": "^5.32.0",
"color": "^2.0.0", "color": "^2.0.0",
@ -32,12 +33,12 @@
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"mapbox-gl": "^0.43.0", "mapbox-gl": "^0.44.1",
"mapbox-gl-inspect": "^1.2.6", "mapbox-gl-inspect": "^1.3.0",
"maputnik-design": "github:maputnik/design", "maputnik-design": "github:maputnik/design",
"mousetrap": "^1.6.1", "mousetrap": "^1.6.1",
"ol-mapbox-style": "^1.0.1", "ol-mapbox-style": "^2.10.1",
"openlayers": "^4.4.2", "ol": "^4.6.4",
"prop-types": "^15.6.0", "prop-types": "^15.6.0",
"react": "^16.2.0", "react": "^16.2.0",
"react-addons-pure-render-mixin": "^15.6.2", "react-addons-pure-render-mixin": "^15.6.2",
@ -97,6 +98,8 @@
"babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-plugin-transform-runtime": "^6.23.0", "babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.1", "babel-preset-env": "^1.6.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-flow": "^6.23.0",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"babel-register": "^6.26.0", "babel-register": "^6.26.0",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
@ -118,6 +121,7 @@
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mocha": "^4.0.1", "mocha": "^4.0.1",
"node-sass": "^4.6.0", "node-sass": "^4.6.0",
"nsp": "^3.1.0",
"react-hot-loader": "^3.1.1", "react-hot-loader": "^3.1.1",
"sass-loader": "^6.0.6", "sass-loader": "^6.0.6",
"selenium-standalone": "^6.11.0", "selenium-standalone": "^6.11.0",
@ -126,6 +130,7 @@
"stylelint-config-standard": "^15.0.1", "stylelint-config-standard": "^15.0.1",
"transform-loader": "^0.2.4", "transform-loader": "^0.2.4",
"uuid": "^3.1.0", "uuid": "^3.1.0",
"uglifyjs-webpack-plugin": "^1.1.8",
"wdio-mocha-framework": "^0.5.11", "wdio-mocha-framework": "^0.5.11",
"wdio-phantomjs-service": "^0.2.2", "wdio-phantomjs-service": "^0.2.2",
"wdio-selenium-standalone-service": "0.0.9", "wdio-selenium-standalone-service": "0.0.9",

View file

@ -22,6 +22,10 @@ import tokens from '../config/tokens.json'
import isEqual from 'lodash.isequal' import isEqual from 'lodash.isequal'
import Debug from '../libs/debug' import Debug from '../libs/debug'
import MapboxGl from 'mapbox-gl'
import mapboxUtil from 'mapbox-gl/src/util/mapbox'
function updateRootSpec(spec, fieldName, newValues) { function updateRootSpec(spec, fieldName, newValues) {
return { return {
...spec, ...spec,
@ -112,7 +116,9 @@ export default class App extends React.Component {
updateFonts(urlTemplate) { updateFonts(urlTemplate) {
const metadata = this.state.mapStyle.metadata || {} const metadata = this.state.mapStyle.metadata || {}
const accessToken = metadata['maputnik:openmaptiles_access_token'] || tokens.openmaptiles const accessToken = metadata['maputnik:openmaptiles_access_token'] || tokens.openmaptiles
downloadGlyphsMetadata(urlTemplate.replace('{key}', accessToken), fonts => {
let glyphUrl = (typeof urlTemplate === 'string')? urlTemplate.replace('{key}', accessToken): urlTemplate;
downloadGlyphsMetadata(glyphUrl, fonts => {
this.setState({ spec: updateRootSpec(this.state.spec, 'glyphs', fonts)}) this.setState({ spec: updateRootSpec(this.state.spec, 'glyphs', fonts)})
}) })
} }
@ -124,15 +130,17 @@ export default class App extends React.Component {
} }
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 = styleSpec.validate(newStyle, styleSpec.latest) const errors = styleSpec.validate(newStyle, styleSpec.latest)
if(errors.length === 0) { if(errors.length === 0) {
if(newStyle.glyphs !== this.state.mapStyle.glyphs) {
this.updateFonts(newStyle.glyphs)
}
if(newStyle.sprite !== this.state.mapStyle.sprite) {
this.updateIcons(newStyle.sprite)
}
this.revisionStore.addRevision(newStyle) this.revisionStore.addRevision(newStyle)
if(save) this.saveStyle(newStyle) if(save) this.saveStyle(newStyle)
this.setState({ this.setState({
@ -215,13 +223,23 @@ export default class App extends React.Component {
layers: [] layers: []
}; };
if(!this.state.sources.hasOwnProperty(key) && val.type === "vector") { if(!this.state.sources.hasOwnProperty(key) && val.type === "vector" && val.hasOwnProperty("url")) {
const url = val.url; let url = val.url;
try {
url = mapboxUtil.normalizeSourceURL(url, MapboxGl.accessToken);
} catch(err) {
console.warn("Failed to normalizeSourceURL: ", err);
}
fetch(url) fetch(url)
.then((response) => { .then((response) => {
return response.json(); return response.json();
}) })
.then((json) => { .then((json) => {
if(!json.hasOwnProperty("vector_layers")) {
return;
}
// Create new objects before setState // Create new objects before setState
const sources = Object.assign({}, this.state.sources); const sources = Object.assign({}, this.state.sources);
@ -250,7 +268,7 @@ export default class App extends React.Component {
mapRenderer() { mapRenderer() {
const mapProps = { const mapProps = {
mapStyle: style.replaceAccessToken(this.state.mapStyle), mapStyle: style.replaceAccessToken(this.state.mapStyle, {allowFallback: true}),
onDataChange: (e) => { onDataChange: (e) => {
this.layerWatcher.analyzeMap(e.map) this.layerWatcher.analyzeMap(e.map)
this.fetchSources(); this.fetchSources();

View file

@ -22,6 +22,7 @@ import SettingsModal from './modals/SettingsModal'
import ExportModal from './modals/ExportModal' import ExportModal from './modals/ExportModal'
import SourcesModal from './modals/SourcesModal' import SourcesModal from './modals/SourcesModal'
import OpenModal from './modals/OpenModal' import OpenModal from './modals/OpenModal'
import pkgJson from '../../package.json'
import style from '../libs/style' import style from '../libs/style'
@ -137,7 +138,9 @@ export default class Toolbar extends React.Component {
className="maputnik-toolbar-logo" className="maputnik-toolbar-logo"
> >
<img src={logoImage} alt="Maputnik" /> <img src={logoImage} alt="Maputnik" />
<h1>Maputnik</h1> <h1>Maputnik
<span className="maputnik-toolbar-version">v{pkgJson.version}</span>
</h1>
</ToolbarLink> </ToolbarLink>
<div className="maputnik-toolbar__actions"> <div className="maputnik-toolbar__actions">
<ToolbarAction wdKey="nav:open" onClick={this.toggleModal.bind(this, 'open')}> <ToolbarAction wdKey="nav:open" onClick={this.toggleModal.bind(this, 'open')}>

View file

@ -18,6 +18,8 @@ class LayerIcon extends React.Component {
switch(this.props.type) { switch(this.props.type) {
case 'fill-extrusion': return <BackgroundIcon {...iconProps} /> case 'fill-extrusion': return <BackgroundIcon {...iconProps} />
case 'raster': return <FillIcon {...iconProps} /> case 'raster': return <FillIcon {...iconProps} />
case 'hillshade': return <FillIcon {...iconProps} />
case 'heatmap': return <FillIcon {...iconProps} />
case 'fill': return <FillIcon {...iconProps} /> case 'fill': return <FillIcon {...iconProps} />
case 'background': return <BackgroundIcon {...iconProps} /> case 'background': return <BackgroundIcon {...iconProps} />
case 'line': return <LineIcon {...iconProps} /> case 'line': return <LineIcon {...iconProps} />

View file

@ -112,6 +112,11 @@ export default class LayerEditor extends React.Component {
comment = this.props.layer.metadata['maputnik:comment'] comment = this.props.layer.metadata['maputnik:comment']
} }
let sourceLayerIds;
if(this.props.sources.hasOwnProperty(this.props.layer.source)) {
sourceLayerIds = this.props.sources[this.props.layer.source].layers;
}
switch(type) { switch(type) {
case 'layer': return <div> case 'layer': return <div>
<LayerIdBlock <LayerIdBlock
@ -129,8 +134,9 @@ export default class LayerEditor extends React.Component {
onChange={v => this.changeProperty(null, 'source', v)} onChange={v => this.changeProperty(null, 'source', v)}
/> />
} }
{this.props.layer.type !== 'raster' && this.props.layer.type !== 'background' && <LayerSourceLayerBlock {['background', 'raster', 'hillshade', 'heatmap'].indexOf(this.state.type) < 0 &&
sourceLayerIds={this.props.sources[this.props.layer.source].layers} <LayerSourceLayerBlock
sourceLayerIds={sourceLayerIds}
value={this.props.layer['source-layer']} value={this.props.layer['source-layer']}
onChange={v => this.changeProperty(null, 'source-layer', v)} onChange={v => this.changeProperty(null, 'source-layer', v)}
/> />

View file

@ -25,6 +25,8 @@ class LayerTypeBlock extends React.Component {
['raster', 'Raster'], ['raster', 'Raster'],
['circle', 'Circle'], ['circle', 'Circle'],
['fill-extrusion', 'Fill Extrusion'], ['fill-extrusion', 'Fill Extrusion'],
['hillshade', 'Hillshade'],
['heatmap', 'Heatmap'],
]} ]}
onChange={this.props.onChange} onChange={this.props.onChange}
value={this.props.value} value={this.props.value}

View file

@ -38,7 +38,7 @@ function buildInspectStyle(originalMapStyle, coloredLayers, highlightedLayer) {
const sources = {} const sources = {}
Object.keys(originalMapStyle.sources).forEach(sourceId => { Object.keys(originalMapStyle.sources).forEach(sourceId => {
const source = originalMapStyle.sources[sourceId] const source = originalMapStyle.sources[sourceId]
if(source.type !== 'raster') { if(source.type !== 'raster' && source.type !== 'raster-dem') {
sources[sourceId] = source sources[sourceId] = source
} }
}) })

View file

@ -3,66 +3,8 @@ import PropTypes from 'prop-types'
import style from '../../libs/style.js' import style from '../../libs/style.js'
import isEqual from 'lodash.isequal' import isEqual from 'lodash.isequal'
import { loadJSON } from '../../libs/urlopen' import { loadJSON } from '../../libs/urlopen'
import 'openlayers/dist/ol.css' import 'ol/ol.css'
function suitableVectorSource(mapStyle) {
const sources = Object.keys(mapStyle.sources)
.map(sourceId => {
return {
id: sourceId,
source: mapStyle.sources[sourceId]
}
})
.filter(({source}) => (source.type === 'vector' || source.type === 'geojson'))
return sources[0]
}
function toVectorLayer(source, tilegrid, cb) {
function newMVTLayer(tileUrl) {
const ol = require('openlayers')
return new ol.layer.VectorTile({
source: new ol.source.VectorTile({
format: new ol.format.MVT(),
tileGrid: tilegrid,
tilePixelRatio: 8,
url: tileUrl
})
})
}
function newGeoJSONLayer(sourceUrl) {
const ol = require('openlayers')
return new ol.layer.Vector({
source: new ol.source.Vector({
format: new ol.format.GeoJSON(),
url: sourceUrl
})
})
}
if (source.type === 'vector') {
if(!source.tiles) {
sourceFromTileJSON(source.url, tileSource => {
cb(newMVTLayer(tileSource.tiles[0]))
})
} else {
cb(newMVTLayer(source.tiles[0]))
}
} else if (source.type === 'geojson') {
cb(newGeoJSONLayer(source.data))
}
}
function sourceFromTileJSON(url, cb) {
loadJSON(url, null, tilejson => {
if(!tilejson) return
cb({
type: 'vector',
tiles: tilejson.tiles,
minzoom: tilejson.minzoom,
maxzoom: tilejson.maxzoom,
})
})
}
class OpenLayers3Map extends React.Component { class OpenLayers3Map extends React.Component {
static propTypes = { static propTypes = {
@ -79,49 +21,17 @@ class OpenLayers3Map extends React.Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.tilegrid = null
this.resolutions = null
this.layer = null
this.map = null this.map = null
} }
updateStyle(newMapStyle) { updateStyle(newMapStyle) {
const oldSource = suitableVectorSource(this.props.mapStyle) const olms = require('ol-mapbox-style');
const newSource = suitableVectorSource(newMapStyle) const styleFunc = olms.apply(this.map, newMapStyle)
const resolutions = this.resolutions
function setStyleFunc(map, layer) {
const olms = require('ol-mapbox-style')
const styleFunc = olms.getStyleFunction(newMapStyle, newSource.id, resolutions)
layer.setStyle(styleFunc)
//NOTE: We need to mark the source as changed in order
//to trigger a rerender
layer.getSource().changed()
map.render()
}
if(newSource) {
if(this.layer && !isEqual(oldSource, newSource)) {
this.map.removeLayer(this.layer)
this.layer = null
}
if(!this.layer) {
var self = this
toVectorLayer(newSource.source, this.tilegrid, vectorLayer => {
self.layer = vectorLayer
self.map.addLayer(self.layer)
setStyleFunc(self.map, self.layer)
})
} else {
setStyleFunc(this.map, this.layer)
}
}
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
require.ensure(["openlayers", "ol-mapbox-style"], () => { require.ensure(["ol", "ol-mapbox-style"], () => {
if(!this.map || !this.resolutions) return if(!this.map) return
this.updateStyle(nextProps.mapStyle) this.updateStyle(nextProps.mapStyle)
}) })
} }
@ -129,24 +39,22 @@ class OpenLayers3Map extends React.Component {
componentDidMount() { componentDidMount() {
//Load OpenLayers dynamically once we need it //Load OpenLayers dynamically once we need it
//TODO: Make this more convenient //TODO: Make this more convenient
require.ensure(["openlayers", "ol-mapbox-style"], ()=> { require.ensure(["ol", "ol/map", "ol/view", "ol/control/zoom", "ol-mapbox-style"], ()=> {
console.log('Loaded OpenLayers3 renderer') console.log('Loaded OpenLayers3 renderer')
const ol = require('openlayers') const olMap = require('ol/map').default
const olms = require('ol-mapbox-style') const olView = require('ol/view').default
const olZoom = require('ol/control/zoom').default
this.tilegrid = ol.tilegrid.createXYZ({tileSize: 512, maxZoom: 22}) const map = new olMap({
this.resolutions = this.tilegrid.getResolutions()
const map = new ol.Map({
target: this.container, target: this.container,
layers: [], layers: [],
view: new ol.View({ view: new olView({
zoom: 2, zoom: 2,
center: [52.5, -78.4] center: [52.5, -78.4]
}) })
}) })
map.addControl(new ol.control.Zoom()) map.addControl(new olZoom())
this.map = map this.map = map
this.updateStyle(this.props.mapStyle) this.updateStyle(this.props.mapStyle)
}) })

View file

@ -142,7 +142,7 @@ class AddModal extends React.Component {
onChange={v => this.setState({ source: v })} onChange={v => this.setState({ source: v })}
/> />
} }
{this.state.type !== 'background' && this.state.type !== 'raster' && {['background', 'raster', 'hillshade', 'heatmap'].indexOf(this.state.type) < 0 &&
<LayerSourceLayerBlock <LayerSourceLayerBlock
isFixed={true} isFixed={true}
sourceLayerIds={layers} sourceLayerIds={layers}

View file

@ -25,6 +25,7 @@ class Gist extends React.Component {
super(props); super(props);
this.state = { this.state = {
preview: false, preview: false,
public: false,
saving: false, saving: false,
latestGist: null, latestGist: null,
} }
@ -42,7 +43,10 @@ class Gist extends React.Component {
...this.state, ...this.state,
saving: true saving: true
}); });
const preview = this.state.preview && (this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token'];
const preview = this.state.preview;
const mapboxToken = (this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token'];
const mapStyleStr = preview ? const mapStyleStr = preview ?
styleSpec.format(stripAccessTokens(style.replaceAccessToken(this.props.mapStyle))) : styleSpec.format(stripAccessTokens(style.replaceAccessToken(this.props.mapStyle))) :
@ -55,8 +59,8 @@ class Gist extends React.Component {
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>`+styleTitle+` Preview</title> <title>`+styleTitle+` Preview</title>
<link rel="stylesheet" type="text/css" href="https://api.mapbox.com/mapbox-gl-js/v0.43.0/mapbox-gl.css" /> <link rel="stylesheet" type="text/css" href="https://api.mapbox.com/mapbox-gl-js/v0.44.0/mapbox-gl.css" />
<script src="https://api.mapbox.com/mapbox-gl-js/v0.43.0/mapbox-gl.js"></script> <script src="https://api.mapbox.com/mapbox-gl-js/v0.44.0/mapbox-gl.js"></script>
<style> <style>
body { margin:0; padding:0; } body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; } #map { position:absolute; top:0; bottom:0; width:100%; }
@ -65,6 +69,7 @@ class Gist extends React.Component {
<body> <body>
<div id='map'></div> <div id='map'></div>
<script> <script>
mapboxgl.accessToken = '${mapboxToken}';
var map = new mapboxgl.Map({ var map = new mapboxgl.Map({
container: 'map', container: 'map',
style: 'style.json', style: 'style.json',
@ -89,7 +94,7 @@ class Gist extends React.Component {
const gh = new GitHub(); const gh = new GitHub();
let gist = gh.getGist(); // not a gist yet let gist = gh.getGist(); // not a gist yet
gist.create({ gist.create({
public: true, public: this.state.public,
description: styleTitle, description: styleTitle,
files: files files: files
}).then(function({data}) { }).then(function({data}) {
@ -110,6 +115,13 @@ class Gist extends React.Component {
}) })
} }
onPublicChange(value) {
this.setState({
...this.state,
public: value
})
}
changeMetadataProperty(property, value) { changeMetadataProperty(property, value) {
const changedStyle = { const changedStyle = {
...this.props.mapStyle, ...this.props.mapStyle,
@ -162,13 +174,22 @@ class Gist extends React.Component {
<MdFileDownload /> <MdFileDownload />
Save to Gist (anonymous) Save to Gist (anonymous)
</Button> </Button>
{' '} <div className="maputnik-modal-sub-section">
<CheckboxInput <CheckboxInput
value={this.state.preview} value={this.state.public}
name='gist-style-preview' name='gist-style-public'
onChange={this.onPreviewChange.bind(this)} onChange={this.onPublicChange.bind(this)}
/> />
<span> Include preview</span> <span> Public gist</span>
</div>
<div className="maputnik-modal-sub-section">
<CheckboxInput
value={this.state.preview}
name='gist-style-preview'
onChange={this.onPreviewChange.bind(this)}
/>
<span> Include preview</span>
</div>
{this.state.preview ? {this.state.preview ?
<div> <div>
<InputBlock <InputBlock
@ -177,6 +198,12 @@ class Gist extends React.Component {
value={(this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']} value={(this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}/> onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}/>
</InputBlock> </InputBlock>
<InputBlock
label={"Mapbox Access Token: "}>
<StringInput
value={(this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}/>
</InputBlock>
<a target="_blank" rel="noopener noreferrer" href="https://openmaptiles.com/hosting/">Get your free access token</a> <a target="_blank" rel="noopener noreferrer" href="https://openmaptiles.com/hosting/">Get your free access token</a>
</div> </div>
: null} : null}

View file

@ -45,6 +45,10 @@ function editorMode(source) {
if(source.tiles) return 'tilexyz_raster' if(source.tiles) return 'tilexyz_raster'
return 'tilejson_raster' return 'tilejson_raster'
} }
if(source.type === 'raster-dem') {
if(source.tiles) return 'tilexyz_raster-dem'
return 'tilejson_raster-dem'
}
if(source.type === 'vector') { if(source.type === 'vector') {
if(source.tiles) return 'tilexyz_vector' if(source.tiles) return 'tilexyz_vector'
return 'tilejson_vector' return 'tilejson_vector'
@ -127,6 +131,16 @@ class AddSource extends React.Component {
minzoom: source.minzoom || 0, minzoom: source.minzoom || 0,
maxzoom: source.maxzoom || 14 maxzoom: source.maxzoom || 14
} }
case 'tilejson_raster-dem': return {
type: 'raster-dem',
url: source.url || 'http://localhost:3000/tilejson.json'
}
case 'tilexyz_raster-dem': return {
type: 'raster-dem',
tiles: source.tiles || ['http://localhost:3000/{x}/{y}/{z}.pbf'],
minzoom: source.minzoom || 0,
maxzoom: source.maxzoom || 14
}
default: return {} default: return {}
} }
} }
@ -147,6 +161,8 @@ class AddSource extends React.Component {
['tilexyz_vector', 'Vector (XYZ URLs)'], ['tilexyz_vector', 'Vector (XYZ URLs)'],
['tilejson_raster', 'Raster (TileJSON URL)'], ['tilejson_raster', 'Raster (TileJSON URL)'],
['tilexyz_raster', 'Raster (XYZ URL)'], ['tilexyz_raster', 'Raster (XYZ URL)'],
['tilejson_raster-dem', 'Raster DEM (TileJSON URL)'],
['tilexyz_raster-dem', 'Raster DEM (XYZ URLs)'],
]} ]}
onChange={mode => this.setState({mode: mode, source: this.defaultSource(mode)})} onChange={mode => this.setState({mode: mode, source: this.defaultSource(mode)})}
value={this.state.mode} value={this.state.mode}

View file

@ -115,6 +115,8 @@ class SourceTypeEditor extends React.Component {
case 'tilexyz_vector': return <TileURLSourceEditor {...commonProps} /> case 'tilexyz_vector': return <TileURLSourceEditor {...commonProps} />
case 'tilejson_raster': return <TileJSONSourceEditor {...commonProps} /> case 'tilejson_raster': return <TileJSONSourceEditor {...commonProps} />
case 'tilexyz_raster': return <TileURLSourceEditor {...commonProps} /> case 'tilexyz_raster': return <TileURLSourceEditor {...commonProps} />
case 'tilejson_raster-dem': return <TileJSONSourceEditor {...commonProps} />
case 'tilexyz_raster-dem': return <TileURLSourceEditor {...commonProps} />
default: return null default: return null
} }
} }

View file

@ -91,7 +91,8 @@
"circle-stroke-width", "circle-stroke-width",
"circle-pitch-scale", "circle-pitch-scale",
"circle-translate", "circle-translate",
"circle-translate-anchor" "circle-translate-anchor",
"circle-pitch-alignment"
] ]
} }
] ]
@ -147,7 +148,9 @@
"icon-rotate", "icon-rotate",
"icon-padding", "icon-padding",
"icon-keep-upright", "icon-keep-upright",
"icon-offset" "icon-offset",
"icon-anchor",
"icon-pitch-alignment"
] ]
}, },
{ {
@ -194,5 +197,35 @@
] ]
} }
] ]
},
"hillshade": {
"groups": [
{
"title": "Paint properties",
"type": "properties",
"fields": [
"hillshade-illumination-direction",
"hillshade-illumination-anchor",
"hillshade-exaggeration",
"hillshade-shadow-color",
"hillshade-highlight-color",
"hillshade-accent-color"
]
}
]
},
"heatmap": {
"groups": [
{
"title": "Paint properties",
"type": "properties",
"fields": [
"heatmap-radius",
"heatmap-weight",
"heatmap-intensity",
"heatmap-opacity"
]
}
]
} }
} }

View file

@ -54,12 +54,23 @@ function indexOfLayer(layers, layerId) {
return null return null
} }
function replaceAccessToken(mapStyle) { function replaceAccessToken(mapStyle, opts={}) {
const omtSource = mapStyle.sources.openmaptiles const omtSource = mapStyle.sources.openmaptiles
if(!omtSource) return mapStyle if(!omtSource) return mapStyle
if(!omtSource.hasOwnProperty("url")) return mapStyle
const metadata = mapStyle.metadata || {} const metadata = mapStyle.metadata || {}
const accessToken = metadata['maputnik:openmaptiles_access_token'] || tokens.openmaptiles let accessToken = metadata['maputnik:openmaptiles_access_token'];
if(opts.allowFallback && !accessToken) {
accessToken = tokens.openmaptiles;
}
if(!accessToken) {
// Early exit.
return mapStyle;
}
const changedSources = { const changedSources = {
...mapStyle.sources, ...mapStyle.sources,
openmaptiles: { openmaptiles: {

View file

@ -46,6 +46,7 @@
// BUTTON // BUTTON
.maputnik-button { .maputnik-button {
display: inline-block;
cursor: pointer; cursor: pointer;
background-color: $color-midgray; background-color: $color-midgray;
color: $color-lowgray; color: $color-lowgray;

View file

@ -6,6 +6,7 @@
background-color: $color-black; background-color: $color-black;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.3); box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.3);
z-index: 3; z-index: 3;
position: relative;
} }
.maputnik-modal-section { .maputnik-modal-section {
@ -20,6 +21,10 @@
flex-shrink: 0; flex-shrink: 0;
} }
.maputnik-modal-sub-section {
margin-top: $margin-1;
}
.maputnik-modal-section--shrink { .maputnik-modal-section--shrink {
flex-shrink: 1; flex-shrink: 1;
} }
@ -75,6 +80,7 @@
position: fixed; position: fixed;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 9;
@include flex-row; @include flex-row;
} }

View file

@ -36,12 +36,24 @@
cursor: pointer; cursor: pointer;
color: $color-white; color: $color-white;
text-decoration: none; text-decoration: none;
line-height: 20px;
h1 {
position: relative;
}
&:hover { &:hover {
background-color: $color-midgray; background-color: $color-midgray;
} }
} }
.maputnik-toolbar-version {
position: absolute;
font-size: 10px;
bottom: -2px;
margin-left: 4px;
}
.maputnik-toolbar-action { .maputnik-toolbar-action {
@extend .maputnik-toolbar-link; @extend .maputnik-toolbar-link;
} }
@ -56,7 +68,7 @@
} }
.maputnik-toolbar-logo { .maputnik-toolbar-logo {
flex: 0 0 140px; flex: 0 0 170px;
} }
.maputnik-toolbar__inner { .maputnik-toolbar__inner {