diff --git a/config.js b/config.js
index f69936d..6e99844 100644
--- a/config.js
+++ b/config.js
@@ -1,4 +1,5 @@
'use strict';
+import {Color} from './js/colorlib.js';
export const gradeColors = {
grade_flat: '#fff',
@@ -13,3 +14,18 @@ export const gradeColors = {
grade_8: '#a3008d',
grade_9: '#690097',
}
+
+export const speedGradient = [
+ new Color('#370065'),
+ new Color('#79146e'),
+ new Color('#d53354'),
+ new Color('#fa8256'),
+ new Color('#fbffb2'),
+]
+
+export const altitudeGradient = [
+ new Color('#340042'),
+ new Color('#235f7b'),
+ new Color('#51c34e'),
+ new Color('#fce51e'),
+]
\ No newline at end of file
diff --git a/index.html b/index.html
index 7183ddd..1e92fa6 100644
--- a/index.html
+++ b/index.html
@@ -239,6 +239,9 @@
Map Settings
diff --git a/index.js b/index.js
index 2852191..ccc5eff 100644
--- a/index.js
+++ b/index.js
@@ -288,6 +288,8 @@ function establishDimensions(){
x2: maxX,
y1: minY,
y2: maxY,
+ minAlt: minAlt,
+ maxAlt: maxAlt,
width: width,
height: height,
fixY: yIn => {
@@ -503,8 +505,11 @@ function sortTracks(){
}
}
-function setTrackColorMode(mode){
- let modeFunction = null;
+export function setTrackColorMode(mode){
+ let modeFunction;
+ const keyElement = document.getElementById('legendKeyColors');
+ let gradientString = '';
+ keyElement.innerHTML = '';
switch(mode){
case 'Track Type':
modeFunction = (section, curve) => {
@@ -515,30 +520,73 @@ function setTrackColorMode(mode){
else if(curve.isTurntable) finalColor = '#b4f';
section.element.setAttribute('stroke', finalColor);
- }
+ };
+ keyElement.innerHTML = `
+
Designated Yard
+
Other Yard
+
Point
+
Turntable
+
Other Tracks`;
break;
case 'Grade':
modeFunction = section => {
section.element.setAttribute('stroke', Config.gradeColors['grade_'+section.gradeClass]);
};
+ for(let grade in Config.gradeColors){
+ keyElement.innerHTML += `
${(Utils.classToGrade(grade.replace('grade_',''))*100).toFixed(1)}%`;
+ }
+ break;
+ case 'Altitude':
+ modeFunction = section => {
+ section.element.setAttribute('stroke', Color.blendGradient(Config.altitudeGradient, (section.position.y-MapMatrix.minAlt)/(MapMatrix.maxAlt-MapMatrix.minAlt)).hex);
+ }
+ gradientString = 'linear-gradient(to right';
+ for(let col of Config.altitudeGradient) gradientString += ', '+col.hex;
+ gradientString += ')';
+
+ keyElement.innerHTML = `
+
+
${Math.floor(MapMatrix.minAlt)}m
+
+
${Math.ceil(MapMatrix.maxAlt)}m
+
+ `;
+ break;
+ case 'Speed':
+ modeFunction = section => {
+ section.element.setAttribute('stroke', Color.blendGradient(Config.speedGradient, (section.postedSpeed+10)/110).hex);
+ };
+
+ gradientString = 'linear-gradient(to right';
+ for(let col of Config.speedGradient) gradientString += ', '+col.hex;
+ gradientString += ')';
+ keyElement.innerHTML = `
+
+ `;
break;
case 'Track Random':
modeFunction = (section, curve) => {
- section.element.setAttribute('stroke', curve.randomColor);
- }
+ section.element.setAttribute('stroke', curve.randomColor.hex);
+ };
+ keyElement.innerHTML = 'Random per continuous track section';
break;
- case 'Section Random':
+ case 'Curve Random':
modeFunction = section => {
- section.element.setAttribute('stroke', section.randomColor);
- }
+ section.element.setAttribute('stroke', section.randomColor.hex);
+ };
+ keyElement.innerHTML = 'Random per individual track bezier curve';
break;
default:
modeFunction = section => {
section.element.setAttribute('stroke', '#fff');
- }
+ };
break;
}
- if(modeFunction !== null){
+ if(modeFunction != null){
for(let curve of mapData){
for(let section of curve.points){
if(!section.element) continue;
diff --git a/js/utillib.js b/js/utillib.js
index f74ac3e..04b0f4f 100644
--- a/js/utillib.js
+++ b/js/utillib.js
@@ -26,6 +26,12 @@ export function gradeToClass(grade){
return 'flat';
}
+export function classToGrade(className){
+ let classNum = Math.min(Math.max(Number(className), 0), gradeIncrements.length-1);
+ if(Number.isNaN(classNum)) return 0;
+ return gradeIncrements[gradeIncrements.length-1-classNum];
+}
+
/**
* Outputs an easy to read time interval between `date1` and `date2`
* @param {Date} date
diff --git a/legend.js b/legend.js
index 01e8e7e..5f1cb08 100644
--- a/legend.js
+++ b/legend.js
@@ -2,8 +2,27 @@
import * as Utils from './js/utillib.js';
import * as Config from './config.js';
+import { Color } from './js/colorlib.js';
+import { setTrackColorMode } from './index.js';
let settingEntries = [
+ {
+ label: 'Color Mode',
+ id: 'dropdown_trackColor',
+ options: [
+ 'None',
+ 'Grade',
+ 'Altitude',
+ 'Speed',
+ 'Track Type',
+ 'Track Random',
+ 'Curve Random',
+ ],
+ default: 'Grade',
+ func: state =>{
+ setTrackColorMode(state);
+ }
+ },
{
label: 'Signage',
id: 'toggle_signage',
@@ -56,15 +75,6 @@ let settingEntries = [
},
]
},
- {
- label: 'Track Color',
- id: 'radio_trackColor',
- options: [
- 'None',
- 'Grade',
- ],
- default: 1,
- },
];
const legendKey = document.getElementById('legendKey');
@@ -93,21 +103,15 @@ document.addEventListener('DOMContentLoaded', async () => {
function addSettingEntry(thisSetting, parent, indent=0){
thisSetting.divContainer = document.createElement('div');
- thisSetting.inputElement = document.createElement('input');
- thisSetting.inputElement.style.margin = `0 0.5em 0 ${indent}em`;
thisSetting.labelElement = document.createElement('label');
- parent.appendChild(thisSetting.divContainer);
- thisSetting.divContainer.appendChild(thisSetting.inputElement);
thisSetting.divContainer.appendChild(thisSetting.labelElement);
-
+
let settingType = thisSetting.id.split('_')[0];
switch(settingType){
case 'toggle':
+ thisSetting.inputElement = document.createElement('input');
thisSetting.inputElement.type = 'checkbox';
- thisSetting.inputElement.id = thisSetting.id;
thisSetting.inputElement.checked = thisSetting.state;
- thisSetting.labelElement.innerHTML = thisSetting.label;
- thisSetting.labelElement.htmlFor = thisSetting.id;
if(thisSetting.func){
thisSetting.inputElement.addEventListener('input', e =>{
@@ -120,10 +124,50 @@ function addSettingEntry(thisSetting, parent, indent=0){
});
}
break;
+ case 'dropdown':
+ thisSetting.inputElement = document.createElement('select');
+ for(let inputEntry of thisSetting.options){
+ let optionElement = document.createElement('option');
+ if(typeof(inputEntry) == 'object'){
+ optionElement.value = inputEntry[0];
+ optionElement.innerHTML = inputEntry[1];
+ }else{
+ optionElement.value = inputEntry;
+ optionElement.innerHTML = inputEntry;
+ }
+ if(inputEntry == thisSetting.default) optionElement.selected = true;
+ thisSetting.inputElement.appendChild(optionElement);
+ }
+ if(thisSetting.func){
+ thisSetting.inputElement.addEventListener('change', e =>{
+ thisSetting.func(e.target.value);
+ })
+ }
+ break;
+ case 'slider':
+ thisSetting.inputElement = document.createElement('input');
+ thisSetting.inputElement.type = 'range';
+ thisSetting.inputElement.min = thisSetting.min ?? 0;
+ thisSetting.inputElement.max = thisSetting.max ?? 100;
+ thisSetting.inputElement.step = thisSetting.step ?? 1;
+ if(thisSetting.func){
+ thisSetting.inputElement.addEventListener('input', e => {
+ thisSetting.func(e.target.value);
+ });
+ }
+ break;
default:
thisSetting.divContainer.remove();
return;
}
+ parent.appendChild(thisSetting.divContainer);
+ thisSetting.labelElement.innerHTML = thisSetting.label;
+ thisSetting.labelElement.htmlFor = thisSetting.id;
+ thisSetting.inputElement.id = thisSetting.id;
+
+ thisSetting.inputElement.style.margin = `0 0.5em 0 ${indent}em`;
+ thisSetting.divContainer.prepend(thisSetting.inputElement);
+
if(thisSetting.children){
for(let child of thisSetting.children){
addSettingEntry(child, parent, indent+1);
diff --git a/style.css b/style.css
index 8c0e54f..f6ca653 100644
--- a/style.css
+++ b/style.css
@@ -90,6 +90,10 @@ a:visited{
columns: 2;
}
+#legendKeyColors{
+ overflow-wrap:break-word;
+}
+
#legend{
transition: transform 400ms cubic-bezier(0.445, 0.050, 0.550, 0.950);
display: flex;
@@ -143,10 +147,19 @@ a:visited{
background: linear-gradient(#444, #333);
}
-.inlineSvg {
+.colorKey, .gradientKey{
+ border:1px #858585 solid;
+ border-radius: 4px;
+ display: inline-block;
+ margin: 2px 1px;
+ padding: 1px 3px;
+}
+
+.inlineSvg, .colorKey>span{
width: 1em;
height: 1em;
vertical-align: -0.1em;
+ display: inline-block;
}
.poi{