From 821e6042e1ea45ff571b10a13e7b57c010912c08 Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 5 Jul 2024 11:48:54 +0200 Subject: [PATCH] reworked map colors on tif_map and extended ColorGradientPicker functionality #6 --- src/lib/ColorGradientPicker.svelte | 489 ++++++++++++++++------------- src/lib/tempresults/tif_map.svelte | 79 +++-- 2 files changed, 323 insertions(+), 245 deletions(-) diff --git a/src/lib/ColorGradientPicker.svelte b/src/lib/ColorGradientPicker.svelte index 689b2782..b5671267 100644 --- a/src/lib/ColorGradientPicker.svelte +++ b/src/lib/ColorGradientPicker.svelte @@ -18,148 +18,171 @@ export let cmin = -100; export let cmax = 100; - export let CENTER_VALUE = 0; + // export let CENTER_VALUE = 0; export let show_in_bounds = true; export let horizontal = true; - let value_stops: any[] = []; - export let color_stops: any[] = []; - export let odd_middle_mode: boolean = true; - - function hexToRgb(hex) { - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result - ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } - : null; - } + let value_steps: any[] = []; + export let color_steps: any[] = []; + + const DATA_MODES = ['divergent', 'sequential', 'categorical']; + export let data_mode: string = DATA_MODES[0]; + + // const VALUE_MODES = ['categorized', 'direct'] + // export let value_mode: string = VALUE_MODES[0]; - function rgbToHex(c) { + // called once on init, to display default colors and values + update_color_and_value_steps(); + + /** + * Returns a hex-string from an rgba input parameter c. + * @param c color value of the format: [r, g, b, a] + */ + function rgbToHex(c: number[]) { return '#' + ((1 << 24) | (c[0] << 16) | (c[1] << 8) | c[2]).toString(16).slice(1); } - export function set_bounds(min = cmin, max = cmax) { + /** + * Sets cmin, cmax and updates color- and value-stops. Used from + * outside of the component. + * @param min + * @param max + */ + export function set_bounds(min: number = cmin, max: number = cmax) { cmin = min; cmax = max; - set_color_stops(); - console.log('SETTING BOUNDS: ', cmin, ' ', cmax); + update_color_and_value_steps(); } - function set_custom_bounds(e?) { + /** + * Tries to set cmin, cmax and update color- and value-stops. Used from + * inside of the component (input field). + * @param e + */ + function set_custom_bounds(e: Event) { + if (!e.target) { + return; + } + try { - var custom_abs_max = Math.abs(parseFloat(e.target.value)); + const target = e.target as HTMLInputElement; + var custom_abs_max = Math.abs(parseFloat(target.value)); + if (isNaN(custom_abs_max)) { throw new Error('NaN value entered as custom abs max.'); } + cmin = -custom_abs_max; cmax = custom_abs_max; - console.log('SETTING BOUNDS: ', cmin, ' ', cmax); - console.log('SETTING BOUNDS: ', typeof cmin, ' ', typeof cmax); - set_color_stops(); + + // console.log('SETTING BOUNDS: ', cmin, ' ', cmax); + + update_color_and_value_steps(); } catch (error) { console.log(error); } } - function set_color_scheme(e?) { - if (!e) { + /** + * Sets the currently selected color_scheme. + * @param e + */ + function set_color_scheme(e: Event) { + if (!e.target) { return; } + const target = e.target as HTMLSelectElement; - var scheme = e.target.value; - console.log('Type: ', typeof scheme); + var chosen_scheme = target.value; - if (scheme == 'prec_div') { + if (chosen_scheme == 'prec_div') { color_scheme = prec_div; - } else if (scheme == 'chem_div') { + } else if (chosen_scheme == 'chem_div') { color_scheme = chem_div; - } else if (scheme == 'cryo_div') { + } else if (chosen_scheme == 'cryo_div') { color_scheme = cryo_div; - } else if (scheme == 'misc_div') { + } else if (chosen_scheme == 'misc_div') { color_scheme = misc_div; - } else if (scheme == 'slev_div') { + } else if (chosen_scheme == 'slev_div') { color_scheme = slev_div; - } else if (scheme == 'temp_div') { + } else if (chosen_scheme == 'temp_div') { color_scheme = temp_div; - } else if (scheme == 'wind_div') { + } else if (chosen_scheme == 'wind_div') { color_scheme = wind_div; } else { color_scheme = []; + throw Error('Color scheme not imported or misspelled!'); } - console.log('Set color scheme to: ', scheme, color_scheme); - set_color_stops(); + // console.log('Set color scheme to: ', chosen_scheme, color_scheme); + update_color_and_value_steps(); } + /** + * Returns a list of color-stops (value-stops with according colors). + * The format depends on the chosen data_mode. + */ export function get_color_stops() { + if (data_mode == 'divergent') { + return get_color_stops_divergent(); + } else { + return get_color_stops_normal(); + } + } + + /** + * Returns color stops with foregoing fix values, in the format: + * [[value1], [color1], [value2], [color2], ..., [value_n], [color_n]] + */ + function get_color_stops_normal() { const stops = new Array(steps * 2); - console.log('value_stops: ', value_stops); - console.log('color_stops: ', color_stops); for (let i = 0; i < steps; i++) { - stops[i * 2] = value_stops[i]; - stops[i * 2 + 1] = color_stops[i]; + stops[i * 2] = value_steps[i]; + stops[i * 2 + 1] = color_steps[i]; } // console.log("STOPS: ", stops); return stops; - // stepwidth per color change - // const delta = (max - min) / (n - 1); - // // helper array holding ranges and colors (return value) - // const stops = new Array(n * 2); - - // // #steps times iterate and fill stops with [range1, color1, ..., rangeN, colorN] - // for (let i = 0; i < n; i++) { - // stops[i * 2] = min + i * delta; - // stops[i * 2 + 1] = color_stops[i]; - // } - - // return stops; - // n = Math.round(n); - // var scheme_len = color_scheme.length; - // var stepwidth = Math.floor(scheme_len/n); - - // console.log(scheme_len, stepwidth); } - function get_color_stops_linear() {} - - function get_color_stops_from_middle(min, max) { - var abs_max = Math.max(Math.abs(min), Math.abs(max)); - + /** + * Returns color stops with foregoing value ranges, in the format: + * [[value1, value2], [color1], [value2, value3], [color2], ..., [value_n-1, value_n], [color_n]] + */ + function get_color_stops_divergent() { const stops = new Array(steps * 2); + for (let i = 0; i < steps; i++) { - var cur_value = -abs_max + i * Math.round((abs_max * 2) / steps); - stops[i * 2] = cur_value; - stops[i * 2 + 1] = color_stops[i]; + stops[i * 2] = [value_steps[i], value_steps[i + 1]]; + stops[i * 2 + 1] = color_steps[i]; } - // console.log("stops: ", stops); return stops; } - function change_steps(e?) { - var n = parseInt(e.target.value); - console.log('Changing steps: ', steps); - - if (odd_middle_mode) { - if (steps >= n) { - if (n % 2 == 0) { - steps = n - 1; - } else { - steps = n; - } - } else if (steps < n) { - if (n % 2 == 0) { - steps = n + 1; - } else { - steps = n; - } - } + /** + * Sets the number of steps used for color- and value-stops. + * Clamps all values between 2 and 21. + * In divergent mode, it rounds to the next odd number. + * @param e + */ + function change_steps(e: Event) { + if (!e.target) { + return; + } + const target = e.target as HTMLInputElement; + + var n = parseInt(target.value); + + if (data_mode == 'divergent') { + var con_steps = steps >= n ? -1 : 1; + var con_n = n % 2 == 0 ? 1 : 0; + // round to next odd number (in appropriate direction) + steps = n + con_n * con_steps; + + // divergent lower bounds check if (steps < 3) { steps = 3; } @@ -167,34 +190,41 @@ steps = n; } - if (steps < 1) { - steps = 1; + // clamp + if (steps < 2) { + steps = 2; } else if (steps > 21) { steps = 21; } - console.log(' to: ', steps); + input_steps = steps; - set_color_stops(); + update_color_and_value_steps(); } - set_color_stops(); - // get_color_stops_from_middle(-10, 100); - - function set_color_stops() { - if (odd_middle_mode) { - set_color_stops_from_middle(); - set_value_stops_from_middle(); + /** + * Updates color_steps and value_steps according to the current data_mode. + * Also in the end, dispatches the 'color_stops_changed' event. + * + * Called everytime color_steps or value_steps needs to be recalculated. + */ + function update_color_and_value_steps() { + if (data_mode == 'divergent') { + set_color_steps_divergent(); + set_value_steps_divergent(); } else { - set_color_stops_zero_to_end(); - set_value_stops_zero_to_end(); + // not functional rn + set_color_steps_zero_to_end(); + set_value_steps_zero_to_end(); } dispatch('color_stops_changed', {}); } - function set_color_stops_zero_to_end() { - color_stops = []; + function set_color_steps_zero_to_end() { + throw Error('Not implemented yet'); + + color_steps = []; // console.log("setting color stops to n=", n) var scheme_len = color_scheme.length; @@ -202,23 +232,31 @@ // console.log("scheme_len: ", scheme_len) // console.log("stepwidth: ", stepwidth) - color_stops = [...color_stops, color_scheme[0]]; + color_steps = [...color_steps, color_scheme[0]]; for (let i = 1; i < steps - 1; i += 1) { - // color_stops.push(color_scheme[i]); + // color_steps.push(color_scheme[i]); var cur_index = Math.round((scheme_len / (steps - 1)) * i); // console.log("i: ", i, " index: ", cur_index) - color_stops = [...color_stops, color_scheme[cur_index]]; + color_steps = [...color_steps, color_scheme[cur_index]]; } - color_stops = [...color_stops, color_scheme[color_scheme.length - 1]]; + color_steps = [...color_steps, color_scheme[color_scheme.length - 1]]; - // console.log("stops len: ", color_stops.length) - // console.log(color_stops); - // color_stops = color_stops; + // console.log("stops len: ", color_steps.length) + // console.log(color_steps); + // color_steps = color_steps; } - function set_color_stops_from_middle() { - color_stops = []; + /** + * Updates color_steps based on the number of steps chosen. + * Picks #steps colors from the chosen color-scheme. This is + * a two-part process where #Math.floor(steps/2) colors are + * picked between color_scheme[0] - color_scheme[middle] and + * color_scheme[end] - color_scheme[middle]. Thus the first-, + * last- and center-element of the color_scheme is always present. + */ + function set_color_steps_divergent() { + color_steps = []; var scheme_len = color_scheme.length; var middle = Math.ceil(scheme_len / 2); var step_half = Math.floor(steps / 2); @@ -226,52 +264,41 @@ // console.log("middle:", middle) // console.log("step_half:", step_half) - color_stops = [...color_stops, color_scheme[0]]; + color_steps = [...color_steps, color_scheme[0]]; for (let i = 1; i < step_half; i += 1) { var cur_index = Math.round((scheme_len / (steps - 1)) * i); // console.log("i: ", i, " index: ", cur_index) - color_stops = [...color_stops, color_scheme[cur_index]]; + color_steps = [...color_steps, color_scheme[cur_index]]; } - color_stops = [...color_stops, color_scheme[middle]]; + color_steps = [...color_steps, color_scheme[middle]]; for (let i = 1; i < step_half; i += 1) { var cur_index = Math.round((scheme_len / (steps - 1)) * i) + middle; // console.log("i: ", i, " index: ", cur_index) - color_stops = [...color_stops, color_scheme[cur_index]]; + color_steps = [...color_steps, color_scheme[cur_index]]; } - color_stops = [...color_stops, color_scheme[color_scheme.length - 1]]; + color_steps = [...color_steps, color_scheme[color_scheme.length - 1]]; - // console.log(color_stops) + // console.log(color_steps) } - function set_value_stops() { - if (odd_middle_mode) { - set_value_stops_from_middle(); - } else { - set_value_stops_zero_to_end(); - } + function set_value_steps_zero_to_end() { + throw Error('Not implemented yet'); } - function set_value_stops_zero_to_end() {} - - function set_value_stops_from_middle() { + /** + * Updates value_steps based on the number of steps chosen. + * Sets a list of evenly values between cmin and cmax, resulting + * in #(steps+1) values. + */ + function set_value_steps_divergent() { var abs_max = Math.max(Math.abs(cmin), Math.abs(cmax)); - var step_half = Math.floor(steps / 2); - - value_stops = []; + value_steps = []; - value_stops = [...value_stops, -abs_max]; - for (let i = 1; i < step_half; i++) { - var cur_value = (step_half - i) * -Math.round(abs_max / step_half); - value_stops = [...value_stops, cur_value]; + value_steps = [...value_steps, -abs_max]; + for (let i = 1; i < steps; i++) { + value_steps = [...value_steps, -abs_max + i * ((abs_max * 2) / steps)]; } - value_stops = [...value_stops, CENTER_VALUE]; - for (let i = 1; i < step_half; i++) { - var cur_value = i * Math.round(abs_max / step_half); - value_stops = [...value_stops, cur_value]; - } - value_stops = [...value_stops, abs_max]; - - console.log('value_stops: ', value_stops); + value_steps = [...value_steps, abs_max]; } @@ -279,87 +306,112 @@
-
-
- {#if show_in_bounds} - - {:else} - - {/if} - - {#if horizontal} - - {:else} - - {/if} -
-
- - -
-
- - +
+
+
+ {#if show_in_bounds} + + {:else} + + {/if} +
+ +
+ {#if horizontal} + + {:else} + + {/if} +
+ +
+ + + + +
+
+ + + + +
+ +
+ + + + +
- {#key color_stops} - {#each color_stops as color, i} - + {#key color_steps} + {#each color_steps as color, i} + - {#if show_in_bounds && (cmin > value_stops[i] || cmax < value_stops[i])} + {#if show_in_bounds && (cmin > value_steps[i] || cmax < value_steps[i])} {:else} @@ -372,20 +424,23 @@
- {#key color_stops} - {#each value_stops as value, i} + {#key color_steps} + {#each value_steps as value, i} {#if horizontal}
{#if i == 0} -
+
{value.toFixed(2)}
- {:else if i == Math.floor(value_stops.length / 2)} -
- {value.toFixed(2)} + {:else if i == Math.floor(value_steps.length / 2)} +
+ 0.0
- {:else if i == value_stops.length - 1} -
+ {:else if i == value_steps.length - 1} +
{value.toFixed(2)}
{/if} diff --git a/src/lib/tempresults/tif_map.svelte b/src/lib/tempresults/tif_map.svelte index 43016b7f..98851986 100644 --- a/src/lib/tempresults/tif_map.svelte +++ b/src/lib/tempresults/tif_map.svelte @@ -12,7 +12,7 @@ let metadata_loaded: boolean = false; let selected_file: string = ''; - let folder_data: any[] = []; + let folder_data: any[any] = []; let foldertype: string = 'water_budget'; let selected_tif_url: string = ''; let horizontal_scala: boolean = true; @@ -30,22 +30,6 @@ let map: Map; let dif_mode: boolean = false; - var TWENTY_FOUR = 86400000; - var TWELF = 43200000; - var days_1 = 180; - var days_2 = 540.5; - var days_3 = 900; - var days_4 = 1260; - - console.log(Date.parse('days since 1950-01-01T00:00:00+00:00')); // NaN - console.log(Date.parse('1950-01-01T00:00:00+00:00')); // -631152000000 - console.log(new Date(Date.parse('1950-01-01T00:00:00+00:00')).toString()); // Sun Jan 01 1950 01:00:00 GMT+0100 (Central European Standard Time) - - var current_date = new Date(Date.parse('1950-01-01T00:00:00+00:00') + days_1 * TWELF * 2); - console.log(current_date.toString()); // Fri Jun 30 1950 01:00:00 GMT+0100 (Central European Summer Time) - var current_date = new Date(Date.parse('1950-01-01T00:00:00+00:00') + days_2 * TWELF * 2); - console.log(current_date.toString()); // Mon Jun 25 1951 13:00:00 GMT+0100 (Central European Summer Time) - let base_view = new View({ center: [0, 0], zoom: 2 @@ -62,12 +46,12 @@ }; let cg_picker: ColorGradientPicker; - let color_stops: any; + let color_steps: any; - $: color_stops, rebuild_map(); + $: color_steps, rebuild_map(); function rebuild_map() { - if (!color_stops) { + if (!color_steps) { return; } } @@ -86,6 +70,7 @@ onMount(() => { initialize_map(); + generate_openlayers_case_stops(cg_picker.get_color_stops()); }); function initialize_map() { @@ -201,11 +186,42 @@ visualize_band(); } + function generate_openlayers_case_stops(color_stops: any[]) { + var color_cases = ['case', ['==', ['band', 1], 0], [0, 0, 0, 0]]; + for (let i = 0; i < color_stops.length; i++) { + if (i % 2 == 0) { + // console.log("Current UB: ", color_stops[i], " current LB: ", color_stops[i][1]); + color_cases.push(['between', ['band', 1], color_stops[i][0], color_stops[i][1]]); + } else { + color_cases.push(color_stops[i]); + } + } + + // TODO: + // - maybe add case here for datapoints that exceed the min-/max-values (should only + // be needed when custom min/max is set) + + // fallback value for the 'case' operator + color_cases.push([0, 0, 0, 0]); + + // console.log("Generated ol-stops: ", color_cases) + return color_cases; + } + function visualize_band() { console.log('Visualizing: '); console.log('selected_band: ', selected_band, ' ', typeof selected_band); console.log('selected_band_dif: ', selected_band_dif, ' ', typeof selected_band_dif); console.log('dif_mode: ', dif_mode); + + // Important note: + // band selection is a bit tricky here.. + // - band numbers always start at 1 + // - in the GeoTIFF source itself, bands are indexed as they are in the file + // - in the TileLayer however they are referenced as they are loaded into the source + // - thus when loading 2 bands into the source with e.g. band-number 4 and 50, + // they will be re-indexed in the TileLayer to 1 and 2 respectively + const source = new GeoTIFF_OL({ normalize: false, sources: [ @@ -221,6 +237,7 @@ } ] }); + var info = []; if (dif_mode) { info = ['-', ['band', 1], ['band', 2]]; @@ -230,14 +247,20 @@ const layerbandinfo = info; - const color_thing = [ - 'case', - ['==', ['band', 1], 0], - [0, 0, 0, 0], + const color_thing = generate_openlayers_case_stops(cg_picker.get_color_stops()); + + // const color_thing = [ + // 'case', + // ['==', ['band', 1], 0], + // [0, 0, 0, 0], + // ['>', ['band', 1], 0], + // [0, 0, 0, 0], + + // // https://openlayers.org/workshop/en/cog/ndvi.html + // ['interpolate', ['linear'], layerbandinfo, [...cg_picker.get_color_stops()]] + // ]; - // https://openlayers.org/workshop/en/cog/ndvi.html - ['interpolate', ['linear'], layerbandinfo, ...cg_picker.get_color_stops()] - ]; + console.log('COLOR_THING: ', color_thing); // const color_thing = [ // // https://openlayers.org/workshop/en/cog/ndvi.html // 'interpolate', ['linear'], layerbandinfo, ...cg_picker.get_color_stops() @@ -339,7 +362,7 @@