mirror of
https://github.com/maputnik/editor.git
synced 2026-01-04 12:30:00 +00:00
Restructure and rename components
This commit is contained in:
41
src/libs/apistore.js
Normal file
41
src/libs/apistore.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import request from 'request'
|
||||
import style from './style.js'
|
||||
|
||||
export class ApiStyleStore {
|
||||
supported(cb) {
|
||||
request('http://localhost:8000/styles', (error, response, body) => {
|
||||
cb(error === undefined)
|
||||
})
|
||||
}
|
||||
|
||||
latestStyle(cb) {
|
||||
if(this.latestStyleId) {
|
||||
request('http://localhost:8000/styles/' + this.latestStyleId, (error, response, body) => {
|
||||
cb(JSON.parse(body))
|
||||
})
|
||||
} else {
|
||||
request('http://localhost:8000/styles', (error, response, body) => {
|
||||
if (!error && response.statusCode == 200) {
|
||||
const styleIds = JSON.parse(body);
|
||||
this.latestStyleId = styleIds[0];
|
||||
request('http://localhost:8000/styles/' + this.latestStyleId, (error, response, body) => {
|
||||
cb(style.fromJSON(JSON.parse(body)))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Save current style replacing previous version
|
||||
save(mapStyle) {
|
||||
const id = mapStyle.get('id')
|
||||
request.put({
|
||||
url: 'http://localhost:8000/styles/' + id,
|
||||
json: true,
|
||||
body: style.toJSON(mapStyle)
|
||||
}, (error, response, body) => {
|
||||
console.log('Saved style');
|
||||
})
|
||||
return mapStyle
|
||||
}
|
||||
}
|
||||
66
src/libs/layerwatcher.js
Normal file
66
src/libs/layerwatcher.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import Immutable from 'immutable'
|
||||
import throttle from 'lodash.throttle'
|
||||
import entries from 'lodash.topairs'
|
||||
|
||||
/** Listens to map events to build up a store of available vector
|
||||
* layers contained in the tiles */
|
||||
export default class LayerWatcher {
|
||||
constructor() {
|
||||
this._sources = {}
|
||||
this._vectorLayers = {}
|
||||
this._map= null
|
||||
|
||||
// Since we scan over all features we want to avoid this as much as
|
||||
// possible and only do it after a batch of data has loaded because
|
||||
// we only care eventuall about knowing the fields in the vector layers
|
||||
this.throttledAnalyzeVectorLayerFields = throttle(this.analyzeVectorLayerFields, 5000)
|
||||
}
|
||||
|
||||
/** Set the map as soon as the map is initialized */
|
||||
set map(m) {
|
||||
|
||||
this._map = m
|
||||
//TODO: At some point we need to unsubscribe when new map is set
|
||||
this._map.on('data', (e) => {
|
||||
if(e.dataType !== 'tile') return
|
||||
|
||||
this._sources[e.source.id] = e.source.vectorLayerIds
|
||||
this.throttledAnalyzeVectorLayerFields()
|
||||
})
|
||||
}
|
||||
|
||||
analyzeVectorLayerFields() {
|
||||
Object.keys(this._sources).forEach(sourceId => {
|
||||
this._sources[sourceId].forEach(vectorLayerId => {
|
||||
const knownProperties = this._vectorLayers[vectorLayerId] || {}
|
||||
const params = { sourceLayer: vectorLayerId }
|
||||
this._map.querySourceFeatures(sourceId, params).forEach(feature => {
|
||||
Object.keys(feature.properties).forEach(propertyName => {
|
||||
const knownPropertyValues = knownProperties[propertyName] || {}
|
||||
knownPropertyValues[feature.properties[propertyName]] = {}
|
||||
knownProperties[propertyName] = knownPropertyValues
|
||||
})
|
||||
})
|
||||
|
||||
this._vectorLayers[vectorLayerId] = knownProperties
|
||||
})
|
||||
})
|
||||
|
||||
console.log(this.vectorLayers.toJSON())
|
||||
}
|
||||
|
||||
/** Access all known sources and their vector tile ids */
|
||||
get sources() {
|
||||
return Immutable.Map(Object.keys(this._sources).map(key => {
|
||||
return [key, Immutable.Set(this._sources[key])]
|
||||
}))
|
||||
}
|
||||
|
||||
get vectorLayers() {
|
||||
return Immutable.Map(entries(this._vectorLayers).map(([key, layer]) => {
|
||||
return [key, Immutable.Map(entries(layer).map(([propId, values]) => {
|
||||
return [propId, Immutable.Set(Object.keys(values))]
|
||||
}))]
|
||||
}))
|
||||
}
|
||||
}
|
||||
78
src/libs/style.js
Normal file
78
src/libs/style.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import Immutable from 'immutable'
|
||||
import spec from 'mapbox-gl-style-spec/reference/latest.min.js'
|
||||
import diffJSONStyles from 'mapbox-gl-style-spec/lib/diff'
|
||||
|
||||
// Standard JSON to Immutable conversion except layers
|
||||
// are stored in an OrderedMap to make lookups id fast
|
||||
// It also ensures that every style has an id and
|
||||
// a created date for future reference
|
||||
function fromJSON(jsonStyle) {
|
||||
if (typeof jsonStyle === 'string' || jsonStyle instanceof String) {
|
||||
jsonStyle = JSON.parse(jsonStyle)
|
||||
}
|
||||
|
||||
return Immutable.Map(Object.keys(jsonStyle).map(key => {
|
||||
const val = jsonStyle[key]
|
||||
if(key === "layers") {
|
||||
return [key, Immutable.OrderedMap(val.map(l => [l.id, Immutable.fromJS(l)]))]
|
||||
} else if(key === "sources" || key === "metadata" || key === "transition") {
|
||||
return [key, Immutable.fromJS(val)]
|
||||
} else {
|
||||
return [key, val]
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
// Empty style is always used if no style could be restored or fetched
|
||||
const emptyStyle = ensureMetadataExists(fromJSON({
|
||||
version: 8,
|
||||
sources: {},
|
||||
layers: [],
|
||||
}))
|
||||
|
||||
function ensureHasId(style) {
|
||||
if(style.has('id')) return style
|
||||
return style.set('id', Math.random().toString(36).substr(2, 9))
|
||||
}
|
||||
|
||||
function ensureHasTimestamp(style) {
|
||||
if(style.has('id')) return style
|
||||
return style.set('created', new Date().toJSON())
|
||||
}
|
||||
|
||||
function ensureMetadataExists(style) {
|
||||
return ensureHasId(ensureHasTimestamp(style))
|
||||
}
|
||||
|
||||
// Compare style with other style and return changes
|
||||
//TODO: Write own diff algo that operates on immutable collections
|
||||
// Should be able to improve performance since we can only compare
|
||||
// by reference
|
||||
function diffStyles(before, after) {
|
||||
return diffJSONStyles(toJSON(before), toJSON(after))
|
||||
}
|
||||
|
||||
// Turns immutable style back into JSON with the original order of the
|
||||
// layers preserved
|
||||
function toJSON(mapStyle) {
|
||||
const jsonStyle = {}
|
||||
for(let [key, value] of mapStyle.entries()) {
|
||||
if(key === "layers") {
|
||||
jsonStyle[key] = value.toIndexedSeq().toJS()
|
||||
} else if(key === 'sources' || key === "metadata" || key === "transition") {
|
||||
jsonStyle[key] = value.toJS()
|
||||
} else {
|
||||
jsonStyle[key] = value
|
||||
}
|
||||
}
|
||||
return jsonStyle
|
||||
}
|
||||
|
||||
export default {
|
||||
toJSON,
|
||||
fromJSON,
|
||||
diffStyles,
|
||||
ensureMetadataExists,
|
||||
emptyStyle,
|
||||
}
|
||||
118
src/libs/stylestore.js
Normal file
118
src/libs/stylestore.js
Normal file
@@ -0,0 +1,118 @@
|
||||
import { colorizeLayers } from './style.js'
|
||||
import Immutable from 'immutable'
|
||||
import style from './style.js'
|
||||
|
||||
const storagePrefix = "maputnik"
|
||||
const stylePrefix = 'style'
|
||||
const storageKeys = {
|
||||
latest: [storagePrefix, 'latest_style'].join(':'),
|
||||
accessToken: [storagePrefix, 'access_token'].join(':')
|
||||
}
|
||||
|
||||
const defaultStyleUrl = "https://raw.githubusercontent.com/osm2vectortiles/mapbox-gl-styles/master/styles/basic-v9-cdn.json"
|
||||
// Fetch a default style via URL and return it or a fallback style via callback
|
||||
export function loadDefaultStyle(cb) {
|
||||
console.log('Load default style')
|
||||
var request = new XMLHttpRequest()
|
||||
request.open('GET', defaultStyleUrl, true)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status >= 200 && request.status < 400) {
|
||||
cb(style.ensureMetadataExists(style.fromJSON(request.responseText)))
|
||||
} else {
|
||||
cb(style.emptyStyle)
|
||||
}
|
||||
}
|
||||
|
||||
request.onerror = function() {
|
||||
console.log('Could not fetch default style')
|
||||
cb(style.emptyStyle)
|
||||
}
|
||||
|
||||
request.send()
|
||||
}
|
||||
|
||||
// Return style ids and dates of all styles stored in local storage
|
||||
function loadStoredStyles() {
|
||||
const styles = []
|
||||
for (let i = 0; i < window.localStorage.length; i++) {
|
||||
const key = window.localStorage.key(i)
|
||||
if(isStyleKey(key)) {
|
||||
styles.push(fromKey(key))
|
||||
}
|
||||
}
|
||||
return styles
|
||||
}
|
||||
|
||||
function isStyleKey(key) {
|
||||
const parts = key.split(":")
|
||||
return parts.length == 3 && parts[0] === storagePrefix && parts[1] === stylePrefix
|
||||
}
|
||||
|
||||
// Load style id from key
|
||||
function fromKey(key) {
|
||||
if(!isStyleKey(key)) {
|
||||
throw "Key is not a valid style key"
|
||||
}
|
||||
|
||||
const parts = key.split(":")
|
||||
const styleId = parts[2]
|
||||
return styleId
|
||||
}
|
||||
|
||||
// Calculate key that identifies the style with a version
|
||||
function styleKey(styleId) {
|
||||
return [storagePrefix, stylePrefix, styleId].join(":")
|
||||
}
|
||||
|
||||
// Store style independent settings
|
||||
export class SettingsStore {
|
||||
get accessToken() {
|
||||
const token = window.localStorage.getItem(storageKeys.accessToken)
|
||||
return token ? token : ""
|
||||
}
|
||||
set accessToken(val) {
|
||||
window.localStorage.setItem(storageKeys.accessToken, val)
|
||||
}
|
||||
}
|
||||
|
||||
// Manages many possible styles that are stored in the local storage
|
||||
export class StyleStore {
|
||||
// Tile store will load all items from local storage and
|
||||
// assume they do not change will working on it
|
||||
constructor() {
|
||||
this.mapStyles = loadStoredStyles()
|
||||
}
|
||||
|
||||
supported(cb) {
|
||||
cb(window.localStorage !== undefined)
|
||||
}
|
||||
|
||||
// Delete entire style history
|
||||
purge() {
|
||||
for (let i = 0; i < window.localStorage.length; i++) {
|
||||
const key = window.localStorage.key(i)
|
||||
if(key.startsWith(storagePrefix)) {
|
||||
window.localStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the last edited style
|
||||
latestStyle(cb) {
|
||||
if(this.mapStyles.length === 0) return cb(style.emptyStyle)
|
||||
const styleId = window.localStorage.getItem(storageKeys.latest)
|
||||
const styleItem = window.localStorage.getItem(styleKey(styleId))
|
||||
|
||||
if(styleItem) return cb(style.fromJSON(styleItem))
|
||||
cb(style.emptyStyle)
|
||||
}
|
||||
|
||||
// Save current style replacing previous version
|
||||
save(mapStyle) {
|
||||
const key = styleKey(mapStyle.get('id'))
|
||||
window.localStorage.setItem(key, JSON.stringify(style.toJSON(mapStyle)))
|
||||
window.localStorage.setItem(storageKeys.latest, mapStyle.get('id'))
|
||||
return mapStyle
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user