mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-27 09:25:24 +01:00
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:
commit
a3fa86f7ee
22 changed files with 5074 additions and 3603 deletions
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
},
|
},
|
||||||
|
|
|
@ -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'],
|
||||||
|
|
|
@ -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
8311
package-lock.json
generated
File diff suppressed because it is too large
Load diff
19
package.json
19
package.json
|
@ -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",
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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')}>
|
||||||
|
|
|
@ -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} />
|
||||||
|
|
|
@ -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)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue