Skip to content

Commit 86a41e3

Browse files
justenPalmerARolek
authored andcommitted
Domain request headers and bug fixes (#63)
* added customizable domain headers to stylesheet resources * fixed localStorage existing data bug - closes #62 * updated README
1 parent 9b5ced6 commit 86a41e3

File tree

18 files changed

+448
-169
lines changed

18 files changed

+448
-169
lines changed

README.md

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,42 @@
11
# fresco
2-
An open source vector map style editor utilizing Mapbox GL.
32

4-
[Try it](https://fresco.netlify.com/)
3+
Fresco is an open source [Mapbox Vector Tile Style](https://docs.mapbox.com/mapbox-gl-js/style-spec) editor that allows cartographers to craft stylesheets for use with [Mapbox GL](https://docs.mapbox.com/mapbox-gl-js/api/) maps. Unlike other style editors, Fresco does not attempt to hide the complexity of Mapbox GL Styles but rather exposes an interactive JSON code editor to allow for maximum control and flexibility. This allows the user to implement more complex styles concepts like data driven styles with [expressions](https://docs.mapbox.com/help/tutorials/mapbox-gl-js-expressions/). When using Fresco, it may be helpful to have the [Mapbox Style Spec](https://docs.mapbox.com/mapbox-gl-js/style-spec/) available as a reference.
4+
5+
Styles created and modified with Fresco are saved to the browser's local storage and are auto-saved on changes.
6+
7+
Give it a try: [https://fresco.gospatial.org/](https://fresco.gospatial.org/)
8+
9+
![map editing screen shot](/docs/img/osm-screenshot.png)
510

611
## Features
712

8-
- Style editor for Mapbox-gl styles
9-
- Styles stored in localStorage (in the browser)
10-
- JSON editor
11-
- Layer property editor
12-
- MapboxGL expression support
13+
- Interactive JSON code editor.
14+
- Style editor for Mapbox GL styles.
15+
- Mapbox GL layer style expression editor.
16+
- Auto save on changes.
17+
- Styles persisted to local storage (in the browser).
18+
- Mapbox GL style error parser. Displays the error at the error location in the style.
19+
- Integrated Mapbox GL style spec attributes (info on style fields).
20+
- Custom domain header configurations. Useful for domains which require `Authorization` headers.
21+
22+
## Usage
1323

14-
## Installation
24+
Fresco may be used in the browser by visiting [https://fresco.gospatial.org/](https://fresco.gospatial.org/) or by downloading a pre compiled binary from the [releases](https://github.com/go-spatial/fresco/releases) page.
25+
26+
## Running from source
27+
28+
Fresco is built on top of React. To run Freco from source use the following steps:
1529

1630
1. Download the latest version of [Node.js](https://nodejs.org/en/download/)
1731
2. Clone this repository to your computer
1832
3. Navigate to this repo on your computer
1933
4. Run `npm install`
2034
5. To startup, run `npm start` - Fresco should open in a browser window
2135

22-
![map editing screenshot](/docs/img/osm-screenshot.png)
36+
## Contributing
37+
38+
Contributions are welcome! Fork the repo and send in a PR with any bug fixes or features.
39+
40+
## Looking for a vector tile server?
2341

42+
If you're looking to create vector tiles that can be styled with Fresco, check out [tegola](https://github.com/go-spatial/tegola)!

src/middleware/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {applyMiddleware} from 'redux';
2-
import logger from 'redux-logger';
2+
//import logger from 'redux-logger';
33
import thunk from 'redux-thunk';
44

55
import error from './error';
66

7-
export default applyMiddleware(logger,error,thunk);
7+
export default applyMiddleware(/*logger,*/error,thunk);

src/model/Msource.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import Store from '../Store';
22
import SourceReader from '../utility/SourceReader';
33
import MaterialColor from '../utility/MaterialColor';
4+
import Url from '../utility/Url';
45

56
import styleSpec from '../vendor/style-spec/style-spec';
67

78
import Mstyle from './Mstyle';
89

910
export default {
1011

11-
add:function(source,key,makeLayers){
12+
add:function(source, key, makeLayers, headers){
1213
return new Promise((resolve,reject)=>{
1314
if (!source.url) throw new Error('no source.url');
1415
if (!source.type) throw new Error('no source.type');
@@ -22,7 +23,7 @@ export default {
2223
//set key on source
2324
key = key || source.url;
2425

25-
SourceReader.load(source.url).then((sourceJson)=>{
26+
SourceReader.load(source.url, {headers:headers.toJS()}).then((sourceJson)=>{
2627

2728
Store.dispatch({
2829
type:'SOURCE_ADD',
@@ -35,6 +36,13 @@ export default {
3536
payload:sourceJson
3637
});
3738

39+
const domain = Url.getDomain(source.url)
40+
Store.dispatch({
41+
type:'STYLE_STORE_SETIN',
42+
key:['domains', domain],
43+
payload:headers
44+
});
45+
3846
if (makeLayers){
3947
const sourceLayers = sourceJson.vector_layers;
4048
if (sourceLayers) this.setupInitialLayers(key, sourceLayers);

src/model/Mstyle.js

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Store from '../Store';
66
import MapboxError from '../utility/MapboxError';
77
import LocalStorage from '../utility/LocalStorage';
88
import Uid from '../utility/Uid';
9+
import Url from '../utility/Url';
910

1011
const Mstyle = {
1112

@@ -104,6 +105,46 @@ const Mstyle = {
104105
get:function(){
105106
return Store.getState().style;
106107
},
108+
getDomains:function(){
109+
const style = this.get()
110+
111+
let domains = [];
112+
const glyphs = style.getIn(['rec','glyphs'])
113+
const glyphsDomain = Url.getDomain(glyphs)
114+
if (glyphsDomain) domains.push(glyphsDomain)
115+
116+
const sprites = style.getIn(['rec','sprites'])
117+
const spritesDomain = Url.getDomain(sprites)
118+
if (spritesDomain && !domains.includes(spritesDomain)) domains.push(spritesDomain)
119+
120+
// loop through sources and get all domains (from tiles too)
121+
const sources = style.getIn(['rec','sources'])
122+
sources.forEach((source, sourceKey)=>{
123+
const url = source.get('url');
124+
if (url){
125+
const domain = Url.getDomain(url)
126+
if (domain && !domains.includes(domain)) domains.push(domain)
127+
const sourceJson = Store.getState().style.getIn(['rec','_store','sourceJson',url])
128+
129+
let tiles;
130+
if (source.has('tiles')){
131+
tiles = source.get('tiles')
132+
} else if (sourceJson && sourceJson.has('tiles')){
133+
tiles = sourceJson.get('tiles')
134+
}
135+
136+
tiles && tiles.forEach((tile)=>{
137+
const domain = Url.getDomain(tile)
138+
if (domain && !domains.includes(domain)) domains.push(domain)
139+
})
140+
}
141+
})
142+
143+
return domains
144+
},
145+
getStore:function(){
146+
return this.get().getIn(['rec','_store'])
147+
},
107148
getJS:function(){
108149
return this.get().toJS();
109150
},
@@ -115,8 +156,6 @@ const Mstyle = {
115156
getJSforMapbox:function(){
116157

117158
// apply interactive transformations to style
118-
119-
120159

121160
return this.get().get('rec').delete('_store').toJS();
122161
},
@@ -128,6 +167,12 @@ const Mstyle = {
128167

129168
if (all._config) delete all._config;
130169

170+
// remove all non-styles from all
171+
for (let i in all){
172+
if (!all[i] || !all[i].id || !all[i].layers) // not a mapbox style
173+
delete all[i]
174+
}
175+
131176
Store.dispatch({
132177
type:'STYLES_DEFINE',
133178
payload:all
@@ -220,6 +265,26 @@ const Mstyle = {
220265
});
221266
},
222267

268+
setDomains:function(domains){
269+
//console.log('setJSON source:',source);
270+
return new Promise((resolve,reject)=>{
271+
Store.dispatch({
272+
type:'STYLE_STORE_SETIN',
273+
key:['domains'],
274+
payload:domains
275+
});
276+
277+
Mstyle.save();
278+
279+
Store.dispatch({
280+
type:'SOURCE_RELOAD',
281+
payload:{}
282+
});
283+
284+
return resolve();
285+
});
286+
},
287+
223288
validate:function(){
224289
return new Promise((resolve,reject)=>{
225290
const errors = validateStyle(this.getJSforMapbox());

src/model/MstyleSource.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export default {
2222
},NameFromURL.get(sourceUrl),true).then((source)=>{
2323
//console.log('added source:',source);
2424
const json = Msource.getJson(source.url);
25-
console.log('added source json:',json);
2625
const center = [
2726
json.getIn(['center',0]),
2827
json.getIn(['center',1])
@@ -47,7 +46,6 @@ export default {
4746
},
4847

4948
setStyleSourceJSON:function(style){
50-
console.log('style',style);
5149
if (!style.has('sources') || style.get('sources').size < 1) return;
5250

5351
style.get('sources').keySeq().map((key)=>{

src/utility/LocalStorage.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ export default {
1313
}
1414
} catch(e){
1515
console.error('localstorage json parse error:',e);
16-
window.localStorage.clear();
1716
}
18-
console.log('items:',items);
1917
return items;
2018
},
2119

src/utility/SourceReader.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import axios from 'axios';
22

33
export default {
44

5-
load:(sourceUrl)=>{
6-
return new Promise((resolve,reject)=>{
7-
axios.get(sourceUrl)
5+
load:(sourceUrl, {headers})=>{
6+
return new Promise((resolve, reject)=>{
7+
console.log('headers:',headers)
8+
axios.get(sourceUrl, {headers})
89
.then(function (response) {
910
//if (!response.data.maps) return reject('no maps defined on source');
1011
return resolve(response.data);

src/utility/Url.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const getDomain = (url)=>{
2+
if (!url) return null
3+
var matches = url.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
4+
var domain = matches && matches[1];
5+
return domain || null
6+
}
7+
8+
module.exports = {
9+
getDomain
10+
}

src/view/Vfield/VfieldJSON.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ export default class VfieldJSON extends React.Component {
122122
const val = this.cm.getValue();
123123
const json = this.strToJson(val);
124124
if (json) return handle.change(json);
125-
console.log('perform lint!');
126125
this.cm.performLint();
127126
},
128127
focus(){

src/view/Vmap/Vmapbox.jsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Mconfig from '../../model/Mconfig';
1010
import Msource from '../../model/Msource';
1111
import Mstyle from '../../model/Mstyle';
1212
import Mlayer from '../../model/Mlayer';
13+
import Url from '../../utility/Url';
1314
import VmapboxControls from './VmapboxControls';
1415
import VmapboxInspector from './VmapboxInspector';
1516

@@ -157,6 +158,21 @@ class Vmapbox extends React.Component {
157158
}
158159

159160
transformRequest = (url, resourceType)=>{
161+
162+
const store = Mstyle.getStore()
163+
const domains = store.getIn(['domains'])
164+
165+
const domain = Url.getDomain(url)
166+
167+
if (domains && domains.has(domain)){
168+
const headers = domains.getIn([domain]).toJS()
169+
return {
170+
url: url,
171+
headers
172+
}
173+
}
174+
175+
/*
160176
if (resourceType === 'Source') {
161177
const sources = Msource.get(); // get all sources
162178
@@ -217,6 +233,7 @@ class Vmapbox extends React.Component {
217233
headers: settings.get('headers').toJS()
218234
}
219235
}
236+
*/
220237
}
221238
};
222239

0 commit comments

Comments
 (0)