Commit c095554f authored by anneb's avatar anneb

add color selection to legend form

parent 28bb4d5a
...@@ -31,6 +31,28 @@ ...@@ -31,6 +31,28 @@
#attrinfo div { #attrinfo div {
margin: 5px; margin: 5px;
} }
#colorschemes {
display: flex;
align-content: center;
}
#colorschemes div {
padding: 2px;
margin: 2px;
cursor: pointer;
}
#colorschemes div:hover {
background-color: lightgray;
}
#colorschemes div.selected {
background-color: darkgray;
}
#colorschemes svg {
display: block;
}
#colorschemes rect {
stroke: #333;
stroke-width: 0.5;
}
</style> </style>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.2.0/mapbox-gl.js'></script> <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.2.0/mapbox-gl.js'></script>
...@@ -43,6 +65,7 @@ ...@@ -43,6 +65,7 @@
let map = null; let map = null;
let globalStats = null; let globalStats = null;
let selectedColorScheme = 0;
function init() { function init() {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
...@@ -54,6 +77,8 @@ ...@@ -54,6 +77,8 @@
document.querySelector('#tablename').innerHTML = `Layer: ${fullTableName}<br>Geometry: ${geomColumn}`; document.querySelector('#tablename').innerHTML = `Layer: ${fullTableName}<br>Geometry: ${geomColumn}`;
document.querySelector('#columnname').innerHTML = `Attribute: ${attrName} (${attrType})`; document.querySelector('#columnname').innerHTML = `Attribute: ${attrName} (${attrType})`;
document.querySelector('#back').innerHTML = `<a href="tableinfo.html?table=${fullTableName}&geom_column=${geomColumn}">Back to layer info</a>`; document.querySelector('#back').innerHTML = `<a href="tableinfo.html?table=${fullTableName}&geom_column=${geomColumn}">Back to layer info</a>`;
document.querySelectorAll('[name="colorscheme"]').forEach(radio=>radio.onchange=updateLegendColorSchemes);
document.querySelector('#classcount').onchange = classCountChanged;
initMap(); initMap();
...@@ -344,11 +369,11 @@ ...@@ -344,11 +369,11 @@
return false; return false;
} }
// legendTypes 'div', 'qual', 'seq' // schemeTypes 'div', 'qual', 'seq'
// for diverging, qualitative and sequential legends // for diverging, qualitative and sequential legends
function getColorSchemes(numClasses, legendType) { function getColorSchemes(numClasses, schemeType) {
for (; numClasses > 2; numClasses--) { for (; numClasses > 2; numClasses--) {
let result = colorbrewer.filter(scheme=>scheme.type===legendType && scheme.sets.length > numClasses - 3) let result = colorbrewer.filter(scheme=>scheme.type===schemeType && scheme.sets.length > numClasses - 3)
.map(scheme=>{ .map(scheme=>{
const result = scheme.sets[numClasses - 3]; const result = scheme.sets[numClasses - 3];
result.name = scheme.name; result.name = scheme.name;
...@@ -360,7 +385,7 @@ ...@@ -360,7 +385,7 @@
} }
} }
if (numClasses === 2) { if (numClasses === 2) {
return colorbrewer.filter(scheme=>scheme.type===legendType) return colorbrewer.filter(scheme=>scheme.type===schemeType)
.map(scheme=>{ .map(scheme=>{
const result = {colors: [scheme.sets[0].colors[0],scheme.sets[0].colors[2]]} const result = {colors: [scheme.sets[0].colors[0],scheme.sets[0].colors[2]]}
result.name = scheme.name; result.name = scheme.name;
...@@ -371,6 +396,43 @@ ...@@ -371,6 +396,43 @@
return []; return [];
} }
function selectColorScheme(schemeIndex)
{
let schemeDiv = document.querySelector('#colorschemes');
schemeDiv.querySelectorAll('div').forEach(div=>div.classList.remove('selected'));
schemeDiv.querySelectorAll('div').forEach((div,index)=>{if (index === schemeIndex) {div.classList.add('selected')}});
selectedColorScheme = schemeIndex;
}
function classCountChanged() {
updateLegendColorSchemes();
}
// display the available colorschemes for current scheme type and class number
function updateLegendColorSchemes() {
let schemeDiv = document.querySelector('#colorschemes');
schemeDiv.innerHTML = '';
let classCount = Number(document.querySelector('#classcount').value);
let schemeType = document.querySelector('input[name="colorscheme"]:checked').value;
let schemes = getColorSchemes(classCount, schemeType);
classCount = schemes[0].colors.length;
if (selectedColorScheme > schemes.length - 1) {
selectedColorScheme = schemes.length - 1;
}
schemes.forEach((scheme, schemeIndex) => {
let ramp = document.createElement('div');
if (schemeIndex === selectedColorScheme) {
ramp.classList.add('selected');
selectedColorScheme = schemeIndex;
}
let svg = `<svg width="15" height="${classCount * 15}">${
scheme.colors.map((color, index)=>`<rect fill="${color}" width="15" height="15" y="${index * 15}"></rect>`).join('\n')
}</svg>`;
ramp.innerHTML = svg;
ramp.onclick = ()=>selectColorScheme(schemeIndex);
schemeDiv.appendChild(ramp);
})
}
function classButton() { function classButton() {
let classType = document.querySelector('input[name="classtype"]:checked').value; let classType = document.querySelector('input[name="classtype"]:checked').value;
...@@ -383,6 +445,7 @@ ...@@ -383,6 +445,7 @@
const layerType = map.getLayer('attrlayer').type; const layerType = map.getLayer('attrlayer').type;
const rowCount = globalStats.percentiles.reduce((result, percentile)=>result + percentile.count, 0); const rowCount = globalStats.percentiles.reduce((result, percentile)=>result + percentile.count, 0);
let mapboxPaint; let mapboxPaint;
let schemeType = document.querySelector('input[name="colorscheme"]:checked').value;
switch(classType) { switch(classType) {
case 'mostfrequent': case 'mostfrequent':
let classValues = globalStats.values.filter(value=>value.value !== null); let classValues = globalStats.values.filter(value=>value.value !== null);
...@@ -395,19 +458,26 @@ ...@@ -395,19 +458,26 @@
count: rowCount - classValuesRowCount count: rowCount - classValuesRowCount
}) })
} }
const schemes = getColorSchemes(classCount, 'qual'); const schemes = getColorSchemes(classCount, schemeType);
if (globalStats.datatype === 'numeric') {
mapboxPaint = [
"match",
["to-number", ["get",`${globalStats.column}`], 0]
];
} else {
mapboxPaint = [ mapboxPaint = [
"match", "match",
["get",`${globalStats.column}`] ["get",`${globalStats.column}`]
]; ];
}
classValues.forEach((value, index) => { classValues.forEach((value, index) => {
addLegendLine(schemes[0].colors[index], value.value, layerType); addLegendLine(schemes[selectedColorScheme].colors[index], value.value, layerType);
if (index < classValues.length - 1 || !needsSlice) { if (index < classValues.length - 1 || !needsSlice) {
mapboxPaint.push(value.value); mapboxPaint.push(value.value);
mapboxPaint.push(schemes[0].colors[index]); mapboxPaint.push(schemes[selectedColorScheme].colors[index]);
} }
}); });
mapboxPaint.push(schemes[0].colors[classValues.length -1]); mapboxPaint.push(schemes[selectedColorScheme].colors[classValues.length -1]);
break; break;
case 'quantile': case 'quantile':
let percentileBreaks = globalStats.percentiles.reduce((result, percentile)=>{ let percentileBreaks = globalStats.percentiles.reduce((result, percentile)=>{
...@@ -423,9 +493,9 @@ ...@@ -423,9 +493,9 @@
result.push(Object.assign({}, percentile)); result.push(Object.assign({}, percentile));
return result; return result;
},[]); },[]);
const legendType = (typeof percentileBreaks[0].from === "string") ? 'qual' : 'seq'; //const schemeType = (typeof percentileBreaks[0].from === "string") ? 'qual' : 'seq';
const seqSchemes = getColorSchemes(classCount, legendType); const seqSchemes = getColorSchemes(classCount, schemeType);
classCount = seqSchemes[0].colors.length; classCount = seqSchemes[selectedColorScheme].colors.length;
if (classCount > percentileBreaks.length) { if (classCount > percentileBreaks.length) {
classCount = percentileBreaks.length classCount = percentileBreaks.length
} else { } else {
...@@ -448,16 +518,20 @@ ...@@ -448,16 +518,20 @@
} }
mapboxPaint = ["case"] mapboxPaint = ["case"]
percentileBreaks.forEach((brk, index, arr)=>{ percentileBreaks.forEach((brk, index, arr)=>{
addLegendLine(seqSchemes[0].colors[index], `${brk.from} - ${brk.to}`, layerType); addLegendLine(seqSchemes[selectedColorScheme].colors[index], `${brk.from} - ${brk.to}`, layerType);
mapboxPaint.push(["<",["get", globalStats.column],brk.to],seqSchemes[0].colors[index]) if (globalStats.datatype === 'numeric') {
mapboxPaint.push(["<",["to-number", ["get", `${globalStats.column}`], 0],brk.to],seqSchemes[selectedColorScheme].colors[index]);
} else {
mapboxPaint.push(["<",["get", `${globalStats.column}`],brk.to],seqSchemes[selectedColorScheme].colors[index]);
}
}) })
mapboxPaint.push(seqSchemes[0].colors[classCount - 1]) mapboxPaint.push(seqSchemes[selectedColorScheme].colors[classCount - 1])
break; break;
case 'interval': case 'interval':
const min = globalStats.percentiles[0].from; const min = globalStats.percentiles[0].from;
const max = globalStats.percentiles[globalStats.percentiles.length - 1].to; const max = globalStats.percentiles[globalStats.percentiles.length - 1].to;
if (typeof min === "number") { if (typeof min === "number") {
const intervalSchemes = getColorSchemes(classCount, 'seq'); const intervalSchemes = getColorSchemes(classCount, schemeType);
classCount = intervalSchemes[0].colors.length; classCount = intervalSchemes[0].colors.length;
let classTicks = getIntervalClassTicks(min, max, classCount); let classTicks = getIntervalClassTicks(min, max, classCount);
mapboxPaint = ["case"]; mapboxPaint = ["case"];
...@@ -468,10 +542,15 @@ ...@@ -468,10 +542,15 @@
legendFrom = Math.floor(legendFrom); legendFrom = Math.floor(legendFrom);
legendTo = Math.floor(legendTo); legendTo = Math.floor(legendTo);
} }
addLegendLine(intervalSchemes[0].colors[index], `${(legendFrom)} - ${legendTo}`, layerType); addLegendLine(intervalSchemes[selectedColorScheme].colors[index], `${(legendFrom)} - ${legendTo}`, layerType);
mapboxPaint.push(["<", ["get", globalStats.column],legendTo], intervalSchemes[0].colors[index]); if (globalStats.datatype === 'numeric') {
// convert javscript string to number
mapboxPaint.push(["<", ["to-number",["get", globalStats.column], 0],legendTo], intervalSchemes[selectedColorScheme].colors[index]);
} else {
mapboxPaint.push(["<", ["get", globalStats.column],legendTo], intervalSchemes[selectedColorScheme].colors[index]);
}
}) })
mapboxPaint.push(intervalSchemes[0].colors[classCount - 1]) mapboxPaint.push(intervalSchemes[selectedColorScheme].colors[classCount - 1])
} }
break; break;
} }
...@@ -532,11 +611,12 @@ ...@@ -532,11 +611,12 @@
<input type="radio" name="classtype" id="quantile" value="quantile" checked><label for="quantile">quantile</label> <input type="radio" name="classtype" id="quantile" value="quantile" checked><label for="quantile">quantile</label>
<input type="radio" name="classtype" id="mostfrequent" value="mostfrequent"><label for="mostfrequent">most frequent</label><br> <input type="radio" name="classtype" id="mostfrequent" value="mostfrequent"><label for="mostfrequent">most frequent</label><br>
Color schemes:<br> Color schemes:<br>
<input type="radio" name="colorscheme" id="seqential" value="seq"><label for="sequential">sequential</label> <input type="radio" name="colorscheme" id="sequential" value="seq"><label for="sequential">sequential</label>
<input type="radio" name="colorscheme" id="diverting" value="div"><label for="diverting">diverting</label> <input type="radio" name="colorscheme" id="diverting" value="div"><label for="diverting">diverting</label>
<input type="radio" name="colorscheme" id="diverting" value="qual"><label for="qualitative">qualitative</label><br> <input type="radio" name="colorscheme" id="qualitative" value="qual" checked><label for="qualitative">qualitative</label><br>
<input type="checkbox" id="hidenulls" name="hidenulls" checked><label for="hidenulls">Hide no-data</label><br> <input type="checkbox" id="hidenulls" name="hidenulls" checked><label for="hidenulls">Hide no-data</label><br>
<button onclick="classButton()">update legend</button><br> <button onclick="classButton()">update legend</button><br>
<div id="colorschemes"></div>
</div> </div>
</div> </div>
<div id="featureinfo"></div> <div id="featureinfo"></div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment