Search code examples
javascriptalgorithmlogarithm

How to visualise a range of values logarithmically?


I am visualising bubbles for cities, bigger if the city has a bigger value. Eg:

London: 14500
New York: 100
Tokyo: 1100

The values range from ~100-15000

I am having trouble creating a function that will return reasonable values, so that the 100 value bubbles aren't too small. The only way I could think to do this is to set a minimum size, eg:

if (size < 5) { size = 5 }

However, this causes the cities with values of ~100 to look very similar to cities with values of ~1000. I'd like the values of approx 0-15000 to return as values between 0.5 and 1 (or something similar). How would this be done?

Here's what I have so far, but like I said it's causing values of 100 and values of 1000 to both be under the min value:

var minBulletSize = 7.5;
var maxBulletSize = 20;
var maxSquare = maxBulletSize * maxBulletSize * 2 * Math.PI;
var minSquare = minBulletSize * minBulletSize * 2 * Math.PI;

// create circle for each location
for (var i = 0; i < mapData.length; i++) {
    var dataItem = mapData[i];
    var value = dataItem.value;
    // calculate size of a bubble
    var square = (value/1000 - minBulletSize) / (maxBulletSize - minBulletSize) * (maxSquare - minSquare) + minSquare;
    if (square < minSquare) {
        square = minSquare;
    }
    if (square > maxSquare) {
        square = maxSquare;
    }
    var size = Math.sqrt(square / (Math.PI * 2));
    var id = dataItem.code;
}

Solution

  • I have taken a look at how to make a logarithmic function to look "logarithmic" within the constraints of 0.5 and 1 :

    Math.log10(x / 0.8 + 1) / 3 + 0.5 where x is in between 0 to 24.5.

    This is purely a function that seems to look good for me where you can get very dynamic numbers early although a clear growth can be seen in larger numbers.

    Feel free to mess around with the numbers, this is VERY subjective.

    Next you will need to fit in your 100~15000 range within 0 to 24.5. I would simply do a x = (x - 100) / 608.16 to get your range to be within 0 to 24.5.

    var minBulletSize = 7.5;
    var maxBulletSize = 20;
    var maxSquare = maxBulletSize * maxBulletSize * 2 * Math.PI;
    var minSquare = minBulletSize * minBulletSize * 2 * Math.PI;
    
    // create circle for each location
    for (var i = 0; i < mapData.length; i++) {
        var dataItem = mapData[i];
        var value = dataItem.value;
        // assuming value is in between 100 and 15000
        value = (value - 100) / 608.16;
        value = Math.log10(value / 0.8 + 1) / 3.0 + 0.5;
        // value will be in between 0.5 and 1 on a logarithmic scale.
        // Do what you want with it :)
    }
    

    Tinker the values within the functions until you find a perfect curve for you.