function limitBounds(b, maxWidth, maxHeight) {
    var w = b[1][0] - b[0][0];

    if (w > maxWidth) {
        var c = (b[1][0] + b[0][0]) / 2;

        maxWidth /= 2;
        b[0][0] = c - maxWidth;
        b[1][0] = c + maxWidth;
    }

    var h = b[1][1] - b[0][1];

    if (h > maxHeight) {
        var ch = (b[1][1] + b[0][1]) / 2;

        maxHeight /= 2;
        b[0][1] = ch - maxHeight;
        b[1][1] = ch + maxHeight;
    }
}

function groupBounds(path, features, width, height, maxWidth, maxHeight) {
    var r = features.length ? [[[], []], [[], []]] : [[[0], [0]], [[width], [height]]];

    features.forEach(function (feature) {
        var b = path.bounds(feature);

        limitBounds(b, maxWidth, maxHeight);

        r[0][0].push(b[0][0]);
        r[0][1].push(b[0][1]);
        r[1][0].push(b[1][0]);
        r[1][1].push(b[1][1]);
    });

    r[0][0] = Math.min.apply(this, r[0][0]);
    r[0][1] = Math.min.apply(this, r[0][1]);
    r[1][0] = Math.max.apply(this, r[1][0]);
    r[1][1] = Math.max.apply(this, r[1][1]);

    return r;
}

function normalize(v, minMax) {
    return minMax.diff ? (v - minMax.min) / minMax.diff : 0.5;
}

function getValuesMinMax(values) {
    var v = [];
    var s = 0;

    for (var k in values) {
        v.push(values[k]);
        s += values[k];
    }

    var r = {
        min: Math.min.apply(this, v),
        max: Math.max.apply(this, v)
    };

    r.diff = r.max - r.min;
    r.sum = s;

    return r;
}

function getScale(size, width, height, scaleMargin, maxScale) {
    var marginSize = [(size[0] + width * scaleMargin), (size[1] + width * scaleMargin)];
    var sizeRatio = marginSize[0] / marginSize[1];
    var boxRatio = width / height;
    var r;

    if (sizeRatio >= boxRatio) {
        r = width / marginSize[0];
    } else {
        r = height / marginSize[1];
    }

    if (r < 1) {
        r = 1;
    } else if (r > maxScale) {
        r = maxScale;
    }

    return r;
}

function setContextSyle(ctx, opt) {
    ctx.fillStyle = opt.fillStyle;
    ctx.strokeStyle = opt.strokeStyle;
    ctx.shadowColor= opt.shadowColor;
    ctx.shadowOffsetY = opt.shadowOffsetY;
    ctx.shadowBlur = opt.shadowBlur;
}