mirror of
https://github.com/rastapasta/mapscii.git
synced 2024-11-25 01:23:58 +01:00
Unpolished translation of Styler.coffee
This commit is contained in:
parent
2d22e33ff1
commit
554ae593be
@ -1,112 +0,0 @@
|
|||||||
###
|
|
||||||
termap - Terminal Map Viewer
|
|
||||||
by Michael Strassburger <codepoet@cpan.org>
|
|
||||||
|
|
||||||
Minimalistic parser and compiler for Mapbox (Studio) Map Style files
|
|
||||||
See: https://www.mapbox.com/mapbox-gl-style-spec/
|
|
||||||
|
|
||||||
Compiles layer filter instructions into a chain of true/false returning
|
|
||||||
anonymous functions to improve rendering speed compared to realtime parsing.
|
|
||||||
###
|
|
||||||
|
|
||||||
fs = require 'fs'
|
|
||||||
|
|
||||||
module.exports = class Styler
|
|
||||||
styleById: {}
|
|
||||||
styleByLayer: {}
|
|
||||||
|
|
||||||
constructor: (file) ->
|
|
||||||
json = JSON.parse fs.readFileSync(file).toString()
|
|
||||||
@styleName = json.name
|
|
||||||
|
|
||||||
@_replaceConstants json.constants, json.layers if json.constants
|
|
||||||
|
|
||||||
for style in json.layers
|
|
||||||
if style.ref and @styleById[style.ref]
|
|
||||||
for ref in ['type', 'source-layer', 'minzoom', 'maxzoom', 'filter']
|
|
||||||
if @styleById[style.ref][ref] and not style[ref]
|
|
||||||
style[ref] = @styleById[style.ref][ref]
|
|
||||||
|
|
||||||
style.appliesTo = @_compileFilter style.filter
|
|
||||||
|
|
||||||
@styleByLayer[style['source-layer']] ?= []
|
|
||||||
@styleByLayer[style['source-layer']].push style
|
|
||||||
@styleById[style.id] = style
|
|
||||||
|
|
||||||
getStyleFor: (layer, feature, zoom) ->
|
|
||||||
return false unless @styleByLayer[layer]
|
|
||||||
|
|
||||||
for style in @styleByLayer[layer]
|
|
||||||
if style.appliesTo feature
|
|
||||||
return style
|
|
||||||
|
|
||||||
return false
|
|
||||||
|
|
||||||
_replaceConstants: (constants, tree) ->
|
|
||||||
for id, node of tree
|
|
||||||
switch typeof node
|
|
||||||
when 'object'
|
|
||||||
continue if node.constructor.name.match /Stream/
|
|
||||||
@_replaceConstants constants, node
|
|
||||||
|
|
||||||
when 'string'
|
|
||||||
if node.charAt(0) is '@'
|
|
||||||
tree[id] = constants[node]
|
|
||||||
null
|
|
||||||
|
|
||||||
_compileFilter: (filter) ->
|
|
||||||
switch filter?[0]
|
|
||||||
when "all"
|
|
||||||
filters = (@_compileFilter subFilter for subFilter in filter[1..])
|
|
||||||
(feature) ->
|
|
||||||
return false for appliesTo in filters when not appliesTo feature
|
|
||||||
true
|
|
||||||
|
|
||||||
when "any"
|
|
||||||
filters = (@_compileFilter subFilter for subFilter in filter[1..])
|
|
||||||
(feature) ->
|
|
||||||
return true for appliesTo in filters when appliesTo feature
|
|
||||||
false
|
|
||||||
|
|
||||||
when "none"
|
|
||||||
filters = (@_compileFilter subFilter for subFilter in filter[1..])
|
|
||||||
(feature) ->
|
|
||||||
return false for appliesTo in filters when appliesTo feature
|
|
||||||
true
|
|
||||||
|
|
||||||
when "=="
|
|
||||||
(feature) -> feature.properties[filter[1]] is filter[2]
|
|
||||||
|
|
||||||
when "!="
|
|
||||||
(feature) -> feature.properties[filter[1]] isnt filter[2]
|
|
||||||
|
|
||||||
when "in"
|
|
||||||
(feature) ->
|
|
||||||
return true for value in filter[2..] when feature.properties[filter[1]] is value
|
|
||||||
false
|
|
||||||
|
|
||||||
when "!in"
|
|
||||||
(feature) ->
|
|
||||||
return false for value in filter[2..] when feature.properties[filter[1]] is value
|
|
||||||
true
|
|
||||||
|
|
||||||
when "has"
|
|
||||||
(feature) -> !!feature.properties[filter[1]]
|
|
||||||
|
|
||||||
when "!has"
|
|
||||||
(feature) -> !feature.properties[filter[1]]
|
|
||||||
|
|
||||||
when ">"
|
|
||||||
(feature) -> feature.properties[filter[1]] > filter[2]
|
|
||||||
|
|
||||||
when ">="
|
|
||||||
(feature) -> feature.properties[filter[1]] >= filter[2]
|
|
||||||
|
|
||||||
when "<"
|
|
||||||
(feature) -> feature.properties[filter[1]] < filter[2]
|
|
||||||
|
|
||||||
when "<="
|
|
||||||
(feature) -> feature.properties[filter[1]] <= filter[2]
|
|
||||||
|
|
||||||
else
|
|
||||||
-> true
|
|
163
src/Styler.js
Normal file
163
src/Styler.js
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
termap - Terminal Map Viewer
|
||||||
|
by Michael Strassburger <codepoet@cpan.org>
|
||||||
|
|
||||||
|
Minimalistic parser and compiler for Mapbox (Studio) Map Style files
|
||||||
|
See: https://www.mapbox.com/mapbox-gl-style-spec/
|
||||||
|
|
||||||
|
Compiles layer filter instructions into a chain of true/false returning
|
||||||
|
anonymous functions to improve rendering speed compared to realtime parsing.
|
||||||
|
*/
|
||||||
|
var Styler;
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
module.exports = class Styler {
|
||||||
|
constructor(file) {
|
||||||
|
this.styleById = {};
|
||||||
|
this.styleByLayer = {};
|
||||||
|
var base, name;
|
||||||
|
const json = JSON.parse(fs.readFileSync(file).toString());
|
||||||
|
this.styleName = json.name;
|
||||||
|
if (json.constants) {
|
||||||
|
this._replaceConstants(json.constants, json.layers);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const style of json.layers) {
|
||||||
|
if (style.ref && this.styleById[style.ref]) {
|
||||||
|
for (const ref of ['type', 'source-layer', 'minzoom', 'maxzoom', 'filter']) {
|
||||||
|
if (this.styleById[style.ref][ref] && !style[ref]) {
|
||||||
|
style[ref] = this.styleById[style.ref][ref];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
style.appliesTo = this._compileFilter(style.filter);
|
||||||
|
|
||||||
|
//TODO Better translation of: @styleByLayer[style['source-layer']] ?= []
|
||||||
|
if ((base = this.styleByLayer)[name = style['source-layer']] == null) {
|
||||||
|
base[name] = [];
|
||||||
|
}
|
||||||
|
this.styleByLayer[style['source-layer']].push(style);
|
||||||
|
this.styleById[style.id] = style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getStyleFor(layer, feature, zoom) {
|
||||||
|
if (!this.styleByLayer[layer]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const style of this.styleByLayer[layer]) {
|
||||||
|
if (style.appliesTo(feature)) {
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_replaceConstants(constants, tree) {
|
||||||
|
for (const id in tree) {
|
||||||
|
const node = tree[id];
|
||||||
|
switch (typeof node) {
|
||||||
|
case 'object':
|
||||||
|
if (node.constructor.name.match(/Stream/)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this._replaceConstants(constants, node);
|
||||||
|
break;
|
||||||
|
case 'string':
|
||||||
|
if (node.charAt(0) === '@') {
|
||||||
|
tree[id] = constants[node];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO Better translation of the long cases.
|
||||||
|
_compileFilter(filter) {
|
||||||
|
var filters;
|
||||||
|
switch (filter != null ? filter[0] : void 0) {
|
||||||
|
case 'all':
|
||||||
|
filter = filter.slice(1);
|
||||||
|
filters = (() => {
|
||||||
|
return filter.map((sub) => this._compileFilter(sub));
|
||||||
|
}).call(this);
|
||||||
|
//TODO Use Array.prototype.find
|
||||||
|
return (feature) => {
|
||||||
|
for (const appliesTo of filters) {
|
||||||
|
if (!appliesTo(feature)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
case 'any':
|
||||||
|
filter = filter.slice(1);
|
||||||
|
filters = (() => {
|
||||||
|
return filter.map((sub) => this._compileFilter(sub));
|
||||||
|
}).call(this);
|
||||||
|
//TODO Use Array.prototype.find
|
||||||
|
return (feature) => {
|
||||||
|
for (const appliesTo of filters) {
|
||||||
|
if (appliesTo(feature)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
case 'none':
|
||||||
|
filter = filter.slice(1);
|
||||||
|
filters = (() => {
|
||||||
|
return filter.map((sub) => this._compileFilter(sub));
|
||||||
|
}).call(this);
|
||||||
|
return (feature) => {
|
||||||
|
for (const appliesTo of filters) {
|
||||||
|
if (appliesTo(feature)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
case '==':
|
||||||
|
return (feature) => feature.properties[filter[1]] === filter[2];
|
||||||
|
case "!=":
|
||||||
|
return (feature) => feature.properties[filter[1]] !== filter[2];
|
||||||
|
case "in":
|
||||||
|
return (feature) => {
|
||||||
|
filter = filter.slice(2);
|
||||||
|
for (const value of filter) {
|
||||||
|
if (feature.properties[filter[1]] === value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
case '!in':
|
||||||
|
return (feature) => {
|
||||||
|
filter = filter.slice(2);
|
||||||
|
for (const value of filter) {
|
||||||
|
if (feature.properties[filter[1]] === value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
case "has":
|
||||||
|
return (feature) => !!feature.properties[filter[1]];
|
||||||
|
case "!has":
|
||||||
|
return (feature) => !feature.properties[filter[1]];
|
||||||
|
case ">":
|
||||||
|
return (feature) => feature.properties[filter[1]] > filter[2];
|
||||||
|
case ">=":
|
||||||
|
return (feature) => feature.properties[filter[1]] >= filter[2];
|
||||||
|
case "<":
|
||||||
|
return (feature) => feature.properties[filter[1]] < filter[2];
|
||||||
|
case "<=":
|
||||||
|
return (feature) => feature.properties[filter[1]] <= filter[2];
|
||||||
|
default:
|
||||||
|
return () => true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user