views:

171

answers:

2

Google maps api v3 allows "styles" to be applied to the map, including setting the color of various features. However, the color format it uses is HSL (or what seems like it):

  • hue (an RGB hex string)
  • lightness (a floating point value between -100 and 100)
  • saturation (a floating point value between -100 and 100)

(from the docs)

I managed to find RGB to HSL converters online, but I am unsure how to specify the converted values in a way that google maps will accept. For instance, a typical HSL value given by a converter would be: 209° 72% 49%

How does that HSL value map to the parameters I specified from the google maps api? i.e. how does a hue degree value map to an RGB hex string and how does a percentage map to a floating point value between -100 and 100?

I am still uncertain how to do the conversion. I need to, given an RGB value, quickly convert it to what google maps expects so that the color will be identical...

A: 

From the linked page:

Note: while hue takes an HTML hex color value, it only uses this value to determine the basic color (its orientation around the color wheel), not its saturation or lightness, which are indicated separately as percentage changes. For example, the hue for pure green may be defined as "#00ff00" or "#000100" within the hue property and both hues will be identical. (Both values point to pure green in the HSL color model.) RGB hue values which consist of equal parts Red, Green and Blue — such as "#000000" (black) and "#FFFFFF" (white) and all the pure shades of grey — do not indicate a hue whatsoever, as none of those values indicate an orientation in the HSL coordinate space. To indicate black, white or grey, you must remove all saturation (set the value to -100) and adjust lightness instead.

At least as I read it, that means you need to convert your angle based on a color wheel. For example, let's assume 0 degrees is pure red, 120 degrees is pure blue and 240 degrees is pure green. You'd then take your angle, figure out which two primaries it falls between, and interpolate to determine how much of each primary to use. In theory you should probably use a quadratic interpolation -- but chances are that you can get by reasonably well with linear.

Using that, 90 degrees (for example) is 90/120 = 3/4ths of the way from red to blue, so your hex number for the hue would be 0x00010003 -- or any other number that had green set to 0, and a 1:3 ratio between red and blue.

Jerry Coffin
If one is using an RGB to HSL converter to calculate the angle, then one already has the RGB value... The RGB value could just be hexified and sent as-is as the H value, and then just use the converter for S, L.Or am I misunderstanding.
Andrew Shelansky
I have a related design question.If the system that wants HSL colors accepts an RGB value as the H, why on earth does it need an S and an L to go with... it seems like if it is going to do colorspace conversion to calculate the H, it might as well just use the color specifiedOr does using HSL in this manner provide something that is missing in the RGB space?
Andrew Shelansky
@Andrew: as to using the RGB value directly, maybe -- but maybe not. In particular, I'm pretty sure it really expects at least one of the components to be set to 0, which won't normally be the case for a raw RGB color.
Jerry Coffin
I suspect they're using HSL for (a degree of) device independence. An RGB value (by itself) is basically referenced to the colors produced by a given device, so 0,0,0 means the darkest black it can produce, and 255,255,255 means the brightest white it can produce -- but (for example) those will vary quite a bit between a monitor and a printer (and even on the same printer will vary depending on the paper you feed it). An HSL value is a bit easier to reference to some "neutral" color space (CIE XYZ is better for that but harder to visualize, explain, or convert to/from RGB).
Jerry Coffin
+2  A: 

Since the hue argument expects RGB, you can use the original color as the hue.

rgb2hsl.py:

#!/usr/bin/env python

def rgb2hsl(r, g, b):
    #Hue: the RGB string
    H = (r<<16) + (g<<8) + b
    H = "0x%06X" % H

    #convert to [0 - 1] range
    r = float(r) / 0xFF
    g = float(g) / 0xFF
    b = float(b) / 0xFF

    #http://en.wikipedia.org/wiki/HSL_and_HSV#Lightness
    M = max(r,g,b)
    m = min(r,g,b)
    C = M - m

    #Lightness
    L = (M + m) / 2

    #Saturation (HSL)
    if L == 0:
        S = 0
    elif L <= .5:
        S = C/(2*L)
    else:
        S = C/(2 - 2*L)

    #gmaps wants values from -100 to 100
    S = int(round(S * 200 - 100))
    L = int(round(L * 200 - 100))

    return (H, S, L)


def main(r, g, b):
    r = int(r, base=16)
    g = int(g, base=16)
    b = int(b, base=16)
    print rgb2hsl(r,g,b)

if __name__ == '__main__':
    from sys import argv
    main(*argv[1:])

Example:

$ ./rgb2hsl.py F0 FF FF
('0xF0FFFF', 100, 94)

Result:

Below is a screenshot showing the body set to a rgb background color (#2800E2 in this case), and a google map with styled road-geometry, using the values calculated as above ('0x2800E2', 100, -11).

It's pretty clear that google uses your styling to create around six different colors centered on the given color, with the outlines being closest to the input. I believe this is as close as it gets.

alt text


From experimentation with: http://gmaps-samples-v3.googlecode.com/svn/trunk/styledmaps/wizard/index.html

For water, gmaps subtracts a gamma of .5. To get the exact color you want, use the calculations above, and add that .5 gamma back.

like:

{
  featureType: "water",
  elementType: "geometry",
  stylers: [
    { hue: "#2800e2" },
    { saturation: 100 },
    { lightness: -11 },
    { gamma: 0.5 },
  ]
}
bukzor
This didnt quite work. Looking the comment in your script it looks like you assumed gmaps wants a value from 0-100. Actually, gmaps wants a value from -100 to 100. Maybe you can modify it accordingly? Thanks for your help!!
aeq
hmm..i tried scaling your solution to a value between -100 to 100 by adding (right before you return L and S): S=2*S-100 and L=2*L-100, but the right colors still didn't show up...maybe derived the formula wrong ..
aeq
@aeq: I've modified it and updated the example per your description.
bukzor
hmm..your modification is pretty much the same thing i mentioned in my comment. The converted value shows up on the map pretty close to the original RGB value. But its still a little off. #2800E2 shows up slightly purple for instance...I wonder why its still off
aeq
Interesting. I'm actually trying to style the color of the water on the map so there isn't really a concept of an outline or border. Strange that they do it this way...thanks
aeq
@aeq: I made a correction to the calculation of S. I was using the HSV formula earlier.
bukzor
You script is calculating the S and L correctly. I confirmed by trying an online rgb to HSL converter and scaling the values to be between -100 to 100 and I get the same output as your script. So it could be that gmaps is doing something different with the H value, or they are adjusting it somehow..or maybe im just missing something
aeq
@aeq: It's pretty clear that google uses your styling colors only as a basis for a theme. I would bet that they simply shift the HSL values in a standard way for each element ( water = H, S-10, L+10 ). You could possibly reverse-engineer the shift to counter-act it.
bukzor
@aeq: i guess the bounty just expires if it isn't awareded manually and the answer has less than two votes :(
bukzor