mirror of
https://github.com/rastapasta/mapscii.git
synced 2024-11-21 23:53:08 +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