UNPKG

thematic-earth

Version:

HTML-based, whole-Earth thematic maps using locally hosted data layers

2 lines 8.15 kB
/* Copyright (c) 2023 Read Write Tools. Legal use subject to the Thematic Earth Software License Agreement. */ import TessManager from'./tess-manager.class.js';import TessAssignedValue from'./tess-assigned-value.class.js';import*as TessValidator from'./tess-validator.js';import cssTokenizer from'./css-tokenizer.js';import cssParser from'./css-parser.js';import expect from'../dev/expect.js';import terminal from'../dev/terminal.js';export default class Visualizer{constructor(e,t){this.thematicEarthElement=e,this.tessManager=new TessManager,this.tessSourceFilenames=[],this.mapScale=t,this.allFeaturesNeedRestyling=!1,this.registerListeners(),Object.seal(this)}registerListeners(){this.thematicEarthElement.signal.listen('carte/mapScale',(e=>{expect(e,'Number'),this.mapScale=e,this.allFeaturesNeedRestyling=!0}))}computeStyle(e,t,a,r,s,n,l){return this.tessManager.computeStyle(e,this.mapScale,t,a,r,s,n,l)}overlayCanvasParams(e,t,a){this.tessManager.overlayCanvasParams(e,t,a)}runCourtesyValidator(e,t,a,r,s,n){this.tessManager.runCourtesyValidator(e,t,a,r,s,n)}getAllOverridesForFeature(e,t,a){expect(e,'Map'),expect(t,['GeneralFeature','GreatCircleFeature','HemisphereFeature','LabelFeature','LineFeature','PointFeature','PolygonFeature']),expect(a,'Layer'),this.tessManager.getAllOverridesForFeature(e,this.mapScale,t,a)}getAllRangeRestrictionsOfFeature(e,t){return expect(e,['GeneralFeature','GreatCircleFeature','HemisphereFeature','LabelFeature','LineFeature','PointFeature','PolygonFeature']),expect(t,'Layer'),this.tessManager.getAllRangeRestrictionsOfFeature(this.mapScale,e,t)}getAllSelectorsOfFeature(e,t){return expect(e,['GeneralFeature','GreatCircleFeature','HemisphereFeature','LabelFeature','LineFeature','PointFeature','PolygonFeature']),expect(t,'Layer'),this.tessManager.getAllSelectorsOfFeature(this.mapScale,e,t)}exportTess(e,t,a,r){return expect(e,'Array'),expect(t,'Boolean'),expect(a,'String'),expect(r,'Boolean'),this.tessManager.exportTess(e,t,a,r)}async addTessFile(e){try{var t=await fetch(e,{cache:'no-cache',referrerPolicy:'no-referrer'});if(200!=t.status&&304!=t.status)throw new Error(`Request for ${e} returned with ${t.status}`);var a=await t.text();this.addTessDeclarations(a,e),-1==e.indexOf('thematic-earth.tess')&&this.thematicEarthElement.signal.broadcast('visualization/requestValidation',e)}catch(e){terminal.caught(e)}}addTessDeclarations(e,t){var a=(t=t??'').substr(t.lastIndexOf('/')+1);this.tessSourceFilenames.push(a);var r=cssTokenizer(e,{loc:!0}),s=cssParser(r);if('STYLESHEET'!=s.type)return terminal.logic(`Expected 'STYLESHEET', but found ${s.type}`);var n=this.tessManager.theAnyMapScaleRule();this.decodeSheet(n,s,a),this.thematicEarthElement.signal.broadcast('visualization/tessRulesAdded',t)}prepareAssignedValue(e,t,a,r,s){expect(e,'String'),expect(t,'String'),expect(a,'String'),expect(r,'String'),expect(s,'String');var n=this.tessManager.ruleExists(e,t)?this.tessManager.getRule(e,t):this.tessManager.createNewRule(e,t);n.selectorExists(a)?n.getDeclarationsBySelector(a):n.createNewDeclarations(a);return r in TessValidator.multiTable&&(s=s.split(',')),new TessAssignedValue(n,a,r,s,'user',0,0)}addPreparsedRule(e,t,a,r,s){expect(e,'String'),expect(t,'String'),expect(a,'String'),expect(r,'String'),expect(s,'String');var n=this.tessManager.ruleExists(e,t)?this.tessManager.getRule(e,t):this.tessManager.createNewRule(e,t),l=n.selectorExists(a)?n.getDeclarationsBySelector(a):n.createNewDeclarations(a);r in TessValidator.multiTable&&(s=s.split(','));var i=new TessAssignedValue(n,a,r,s,'(user)',0,0);return l.set(r,i),i}decodeSheet(e,t,a){expect(e,'RangeRestrictedRule');TessAssignedValue.formatMinMax(e);for(let h=0;h<t.value.length;h++){var r=t.value[h];if('AT-RULE'!=r.type)if('STYLE-RULE'==r.type){var s=this.determineSelectorNames(r.selector);for(let t=0;t<r.value.length;t++){var n=r.value[t];if('DECLARATION'==n.type){var l=n.name,i=n.value,o=this.determinePropertyValue(l,i);for(let t=0;t<s.length;t++){var c=s[t],u=e.selectorExists(c)?e.getDeclarationsBySelector(c):e.createNewDeclarations(c),p=new TessAssignedValue(e,c,l,o.value,a,o.line,o.column);u.set(l,p)}}else terminal.logic(`Expected 'DECLARATION', but found ${n.type} . . . skipping`)}}else terminal.logic(`Expected 'AT-RULE' or 'STYLE-RULE', but found ${r.type} . . . skipping`);else{if('scale'!=r.name)continue;var g=this.determineValidMapScales(r.prelude),m=this.tessManager.ruleExists(g.minScale,g.maxScale)?this.tessManager.getRule(g.minScale,g.maxScale):this.tessManager.createNewRule(g.minScale,g.maxScale);this.decodeSheet(m,r,a)}}}determineValidMapScales(e){for(var t={minScale:'min',maxScale:'max'},a=0;a<e.length;a++)if('FUNCTION'!=e[a].type||'from'!=e[a].name&&'to'!=e[a].name){if('IDENT'==e[a].tokenType&&'and'==e[a].value)continue}else{for(var r=e[a].name,s='',n=0;n<e[a].value.length;n++){var l=e[a].value[n];if('FUNCTION-ARG'==l.type){var i=l.value[0];if('IDENT'==i.tokenType)s=i.value;else{if(':'==i.tokenType)continue;'NUMBER'==i.tokenType&&(s=i.value)}}}'from'==r?t.minScale=s:'to'==r&&(t.maxScale=s)}return t}determineSelectorNames(e){for(var t=[],a='',r=0;r<e.length;r++)if('DELIM'==e[r].tokenType&&'.'==e[r].value)a+=e[r].value;else if('HASH'==e[r].tokenType)a+='#'+e[r].value;else if('IDENT'==e[r].tokenType)a+=e[r].value;else if('BLOCK'==e[r].type&&'['==e[r].name){for(var s='',n=e[r].value,l=0;l<n.length;l++)'IDENT'==n[l].tokenType||'DELIM'==n[l].tokenType||'NUMBER'==n[l].tokenType?s+=n[l].value:'STRING'==n[l].tokenType&&(s+=`"${n[l].value}"`);a+=`[${s}]`}else'DELIM'==e[r].tokenType&&','==e[r].value&&(t.push(a),a='');return t.push(a),t}determinePropertyValue(e,t){for(var a='',r=null,s=0;s<t.length;s++){var n=t[s];if('fill-pattern'==e||'stroke-brush'==e){if('WHITESPACE'==n.tokenType)continue;if('IDENT'==n.tokenType)a=n.value.split(' '),r=n.loc;else if('STRING'==n.tokenType)a=n.value.split(' '),r=n.loc;else if('BLOCK'==n.type&&'('==n.name){for(var l=n.value,i=[],o=0;o<l.length;o++)('STRING'==l[o].tokenType||'IDENT'==l[o].tokenType)&&(i.push(l[o].value),r=l[o].loc);a=i}else a=['none'],r=n.loc}else if('fill-color'==e){if('WHITESPACE'==n.tokenType)continue;if('IDENT'==n.tokenType)a=n.value,r=n.loc;else if('HASH'==n.tokenType)a='#'+n.value,r=n.loc;else if('BLOCK'==n.type&&'('==n.name){var c=[];for(l=n.value,o=0;o<l.length;o++)'HASH'==l[o].tokenType&&(c.push('#'+l[o].value),r=l[o].loc);a=c}else'FUNCTION'==n.type?[a,r]=this.decodeFunction(e,n.name,n.value):(a='#000',r=n.loc)}else if('FUNCTION'==n.type)[a,r]=this.decodeFunction(e,n.name,n.value);else switch(n.tokenType){case'WHITESPACE':r=n.loc;continue;case'DIMENSION':a=n.repr,r=n.loc;break;case'HASH':a='#'+n.value,r=n.loc;break;case'NUMBER':case'STRING':a=n.value,r=n.loc;break;case'PERCENTAGE':a=n.value+'%',r=n.loc;break;case'IDENT':a='fill-pattern'==e||'brush-stroke'==e?[n.value]:n.value,r=n.loc;break;default:continue}}return{value:a,line:r.start.line,column:r.start.column}}decodeFunction(e,t,a){if(expect(name,'String'),expect(a,'Array'),'rgb'==t||'RGB'==t){if(3!=a.length)return terminal.abnormal(`TESS declarations expected 3 arguments to ${t} for property ${e}`),[null,null];return[`#${convertIntegerToHex(getFunctionArg(a[0]))}${convertIntegerToHex(getFunctionArg(a[1]))}${convertIntegerToHex(getFunctionArg(a[2]))}`,a[0].value[0].loc]}if('rgba'==t||'RGBA'==t){if(4!=a.length)return terminal.abnormal(`TESS declarations expected 4 arguments to ${t} for property ${e}`),null;return[`#${convertIntegerToHex(getFunctionArg(a[0]))}${convertIntegerToHex(getFunctionArg(a[1]))}${convertIntegerToHex(getFunctionArg(a[2]))}${convertIntegerToHex(Math.round(255*getFunctionArg(a[3]),0))}`,a[0].value[0].loc]}return terminal.abnormal(`TESS declarations unhandled function ${t} for property ${e}`),[null,null]}}function getFunctionArg(e){expect(e,'CSSParserRule');var t=e.value;expect(t,'Array');for(let e=0;e<t.length;e++){if('WHITESPACE'!=t[e].tokenType)return'NUMBER'==t[e].tokenType?t[e].value:(terminal.logic(`Visualizer:getFunctionArg() unhandled tokenType ${t[e].tokenType}`),0)}return 0}function convertIntegerToHex(e){return expect(e,'Number'),isNaN(e)||e<0||e>255?'00':e.toString(16).padStart(2,'0').toUpperCase()}