Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customize color scale and palette #399

Open
heshan0131 opened this issue Feb 28, 2019 · 32 comments
Open

Customize color scale and palette #399

heshan0131 opened this issue Feb 28, 2019 · 32 comments
Labels

Comments

@heshan0131
Copy link
Contributor

heshan0131 commented Feb 28, 2019

Enter Custom Palatte

Color Palette customization (done) #601

User should be able to design custom color palette, they should be able to

  • Click using custom color palette to toggle color palette editor
  • Start from blank, or start from an existing palette, (advanced: paste an array of hex values into an input box)
  • Edit color value using a color picker, or input rgb, hex value
  • Add and remove colors
  • Drag to reorder colors

Color Value customization (in progress)

User should be able to import values next to each color, using dropdown for ordinal values and input box for numeric values. There will be a distribution graph for numeric values, user can drag the slider handle to adjust value range of each bin

Ordinal values

Screen Shot 2019-11-09 at 3 38 35 AM

Implementation Step:

  1. Store value color Map in layer.config.colorUI.colorRange.customPalette, create a new prop colorMap and save it as a JS Map
const colorMap = new Map([["foo", "#FFFFFF"], ["bar", "#000000"], ["foo1", "#FF0000"]]);

only store values manually selected by user. if user is using the "assign rest to one color" option, don't store all the rest of the values to the map, later when create color scale, use the .unknown() method to assign color to the rest of values.

  1. When calculate color based on custom color scale in layer.getVisChannelScale(base-layer.js). Use D3.ordinalScale and pass in the colorMap and a value for unknown
m = d3.scaleOrdinal()
  .domain(colorMap.keys())
  .range(colorMap.values())
.unknown('#fff000') // assign rest value to a color value 

Screen Shot 2019-11-09 at 3 39 12 AM

[Numeric values]

Add more color scale options

  • Add log, sqrt and Jenkins natural break scale

Implementation steps:

  1. add log and sqrt scale (done) Add Log and Sqrt scale #670
    1.5 add Jenkins natural breaks scale (in progress)
  2. Add a 'Use custom color palette ' toggle under the reserved toggle (done) [Feat] add custom color editor #601
  3. Allow edit color value using a color picker, or input rgb, hex value (done) [Feat] add custom color editor #601
  4. Add input box to custom value domain for each color (ordinal and numeric value has different input UI) (not started)
  5. Add distribution graph for numeric values (not started)
@alejokp
Copy link

alejokp commented Mar 20, 2019

I would love this!

@chrisgervang
Copy link
Collaborator

I’d like to recommend react-color as a nice picker component :)

https://casesandberg.github.io/react-color/

@davidwedekind
Copy link

Hi,
Will this also include manual color range specification? It would be very useful not only to have automatic color range determination such as by quantile or quantize but manual range specification like in QGIS. E. g. color "#FFC300" for values between 0 and 50, "#F1920E" for values between 51 and ...
Thx,
David

@vohaha
Copy link

vohaha commented Sep 23, 2019

@heshan0131 Do we have actions/helpers to change layer's color programmatically?

@macrigiuseppe
Copy link
Collaborator

landed

@DavidTrafi
Copy link

Hi,
First of all, the new color palette feature is awesome!!! Thanks for constantly improving kepler. I assumed there was also going to be a function to change the color scale, e. g. based on fixed custom intervals. Is such a function planned/ coming soon?
Thanks for your answer.
Best,
David

@heshan0131
Copy link
Contributor Author

Adding value customization is in progress:

Screen Shot 2019-11-09 at 3 39 12 AM

Screen Shot 2019-11-09 at 3 38 35 AM

@heshan0131 heshan0131 reopened this Nov 9, 2019
@bartekmaciejewski
Copy link

Hi,
I've found this value customization feature important for me. Are you still working on it? Maybe I could help?

Cheers
Bartek

@miketsui3a
Copy link

Is the value customization still in progress?

@heshan0131
Copy link
Contributor Author

This got put away for a little while, we just resumed the work. Should come out in the next month or so

@Stepulin
Copy link

Hi,
sorry to bother you, but I would like to ask you - are there any updates regarding to this function?
Thank you very much in advance

mletic added a commit to mletic/kepler.gl that referenced this issue Jun 29, 2020
* color palette now has an Add value button to map ordinal values for selected color

Signed-off-by: Marko Letic <marc.letic@gmail.com>
mletic added a commit to mletic/kepler.gl that referenced this issue Jun 29, 2020
* color palette now has an Add value button to map ordinal values for selected color

Signed-off-by: Marko Letic <marc.letic@gmail.com>
@lucasvw
Copy link

lucasvw commented Jul 16, 2020

This got put away for a little while, we just resumed the work. Should come out in the next month or so

Hi heshan0131, sorry to bother you on this. Is the value customization still in the pipeline?

@zhenliangma
Copy link

Is the value customization ready to use?

@npahl
Copy link

npahl commented Sep 21, 2020

me too, I would love to use the value customization.

@EzequielPuerta
Copy link

Im waiting for this. it will be awesome...

@diego-ch
Copy link

do we have any updates on this?

@KrimsN
Copy link

KrimsN commented Dec 17, 2020

I'm waiting for the opportunity to set the palette statically from min to max, and not relatively as now.

@jooglyp
Copy link

jooglyp commented Feb 2, 2021

do we have any updates on this?

@nickbroom98
Copy link

Really loved using Kepler so far!! Is there any updates on when value customization might be ready as it would be hugely helpful for my project?

@macrigiuseppe
Copy link
Collaborator

macrigiuseppe commented Jun 10, 2021

This feature is going to be available in studio.unfolded.ai and after beta testing it will be merged into our open source project

@yanzhegeo
Copy link

Kepler is great!! Is there any updates on when value customization might be ready in Kepler.gl?

@gertstahl
Copy link

Any chance this will be available in the open source project soon? Would be greatly appreciated ;) Thanks to the team for an awesome tool!

@xiaofanliang
Copy link

Up vote for this function too! Thanks a lot!

@sinannoureddine
Copy link

I'm reading the comments before not sure if I missed something or if the update still hasn't been published yet.

Any updates on this update, please?

@dancestorm
Copy link

Its almost 2023 and that feature still has not added :(

@yanhann10 yanhann10 removed their assignment Nov 8, 2022
@sinannoureddine
Copy link

This feature is going to be available in studio.unfolded.ai and after beta testing it will be merged into our open source project
Any updates please? This is open since 2019. Just to know if we wait for it lol or we accept that it'll never be updated :(

@KarthicKamalesh
Copy link

KarthicKamalesh commented Jun 6, 2023

Hi @heshan0131 @sinannoureddine @dancestorm @xiaofanliang @mletic
I have a separate column named as count
Can you please tell me how to set the color for the count showing 0 as transaparent and the light color starts from 1 and ends with dark color for 100

New Project

image

@kungf00man
Copy link

Could be relevant for someone working with Kepler in Jupiter. I managed to work out how to bind colors to custom value ranges using colorMap & colorLegends properties:

import pandas as pd
from keplergl import KeplerGl
from numpy import array

# Define color mapping routines ("less or equal" in this example)
class Mapper:

    def __init__(self):

        # Define custom palette & legend values
        self.__ranges = {
            2: { 'color':'#ff0000', 'legend':'<= 2' },
            3: { 'color':'#00ff00', 'legend':'<= 3' },
            4: { 'color':'#0000ff', 'legend':'> 3' }, # Key here must be >= than max value in dataset
        }

        self.__keys = array(list(self.__ranges.keys()))

    # Estimate color based on a value
    def __mapper(self, val:float) -> str:
        return self.__ranges[self.__keys[self.__keys >= val].min()]['color']
    
    # Prepare color map for Kepler config
    def get_color_map(self, for_values: pd.Series) -> list:
        return [[x,y] for x,y in zip(for_values, for_values.apply(self.__mapper))]
    
    # Prepare color legends for Kepler
    def get_color_legends(self) -> dict:
        return { v['color']:v['legend'] for _,v in self.__ranges.items() }
    
    # Prepare custom colors for Kepler
    def get_colors(self) -> list:
        return [v['color'] for _,v in self.__ranges.items()]

# Sample DataFrame
df = pd.DataFrame([[1,1,0],[2,2,0],[3,3,0],[4,4,0]], columns=['x','lon','lat'])

# Init Kepler
kgl = KeplerGl()
kgl.add_data(df)
kgl

# Pull out config
cfg = kgl.config
mapper = Mapper()

# Tweak it with the Mapper
color_range_for_layer = cfg['config']['visState']['layers'][0]['config']['visConfig']['colorRange']
color_range_for_layer['colors'] = mapper.get_colors()
color_range_for_layer['colorMap'] = mapper.get_color_map(df.x)
color_range_for_layer['colorLegends'] = mapper.get_color_legends()

# Re-Init Kepler and enjoy
kgl = KeplerGl(config=cfg)
kgl.add_data(df)
kgl

It's a bit of a hack of course but works fine for me. Not tested on really big datasets :)

@KarthicKamalesh
Copy link

Thanks

@ahinoamp
Copy link

ahinoamp commented Aug 29, 2023

Note - the above code from kungf00man only works for version 0.3.2.
Also - here's an enhacement to automate above code.

# modify the init method from mapper class from above 
import branca.colormap as cm
class Mapper:

    def __init__(self, vmin, vmax, n_steps):
        # Define custom palette & legend values
        stepsize = ((vmax+0.01)-vmin)/(n_steps-1)
        colormap_true_temp = cm.LinearColormap(colors=['blue', 'red'], vmin=vmin, vmax=vmax)
        ranges = {}
        for i in range(n_steps):
            value = np.round(vmin+stepsize*i, 2)
            color = colormap_true_temp(value)[:-2] ## remove the transparency 2 characters
            ranges[value] = {'color': str(color), 'legend': '<= ' + str(value)}
        self.__ranges = ranges

and then also modify the call from above to -->
mapper = Mapper(vmin = 0, vmax = 5, n_steps=5)

@RohithYogi
Copy link

Hi,
Is this feature available ? - Need to create a custom legend - Change scale etc

@starkgate
Copy link

starkgate commented Sep 10, 2024

@ahinoamp @kungf00man Thank you for sharing your findings.

Here is code to get similar functionality (with automatic color scale according to 5% quantiles) for React:

import { scaleLinear } from 'd3-scale';
import { color } from 'd3-color';
import { quantileSorted, ascending } from 'd3-array';

const Mapper = (vmin, vmax, n_steps) => {
  // Define custom color scale using D3
  const minColor = '#a6d96a';
  const maxColor = '#a50026';
  const colorScale = scaleLinear()
    .domain([vmin, vmax])
    .range([minColor, maxColor]);

  const stepSize = (vmax - vmin) / (n_steps - 1);
  const ranges = {};

  for (let i = 0; i < n_steps; i++) {
    const value = (vmin + stepSize * i).toFixed(2); // Rounded to 2 decimals
    const hexColor = color(colorScale(value)).formatHex();
    ranges[value] = {
      color: hexColor,
      legend: `<= ${value}`,
    };
  }

  const keys = Object.keys(ranges);

  // Estimate color based on a value
  const mapper = (val) => {
    const matchingKey = keys.find((key) => Number(key) >= val);
    return ranges[matchingKey]?.color || maxColor; // Default to 'blue' if no match
  };

  // Prepare color map for Kepler config
  const getColorMap = (forValues) => {
    return forValues.map((val) => {
      return [val, mapper(val)]
    });
  };

  // Prepare color legends for Kepler
  const getColorLegends = () => {
    const legends = {};
    Object.entries(ranges).forEach(([key, value]) => {
      legends[value.color] = value.legend;
    });
    return legends;
  };

  // Prepare custom colors for Kepler
  const getColors = () => {
    return Object.values(ranges).map((range) => range.color);
  };

  return { getColorMap, getColorLegends, getColors };
};

function adjustColorRange(predictionData, predictionConfig, parameter) {
  return (dispatch) => {
    const visState = predictionConfig.config.visState;

    if (visState.layers.length === 1) {
      // Extract all parameter values from the features array
      const featureValues = predictionData.features
        .map(d => d.properties[parameter])
        .filter(d => d !== null && !isNaN(d))
        .sort(ascending)
      const minValue = quantileSorted(featureValues, 0.05);
      const maxValue = quantileSorted(featureValues, 0.95);
      const mapper = Mapper(minValue, maxValue, 10);

      visState.layers[0].config.visConfig.strokeColorRange = {
        colors: mapper.getColors(),
        colorMap: mapper.getColorMap(featureValues),
        colorLegends: mapper.getColorLegends(),
      };
    }
  };
}

Call it as follows with your layer's data and config, before dispatching loadRemoteResourceSuccess:

dispatch(adjustColorRange(predictionData, predictionConfig, 'no2'));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests