block by michalskop fa5ee2191da7a10436960af0038fb6b2

CZ senate cartogram

Full Screen

index.html

<script src="//d3js.org/d3.v3.min.js"></script>

<p id="chart">

<script>

// set margins, size
var margin = {top: 0, right: 0, bottom: 0, left: 0},
    width = 1040 - margin.left - margin.right,
    height = 620 - margin.top - margin.bottom;
// append the svg canvas to the page
var svg = d3.select("#chart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");
// set up scales
var x = d3.scale.linear()
                    .domain([-260,260])
                    .range([0, width])
var y = d3.scale.linear()
                    .domain([-145, 165])
                    .range([height, 0])
var r = d3.scale.linear()
                    .domain([0,50])
                    .range([0,100])

d3.csv("centroids.csv", function (error, data) {

    data.forEach(function(d) {
        d.x = parseFloat(d.x);
        d.y = parseFloat(d.y);
        d.x_original = d.x;
        d.y_original = d.y;
        d.x_next = d.x;
        d.y_next = d.y;
        d.r = 20;
    })

    var circles = svg.selectAll(".circle")
        .data(data)
            .enter()
        .append("circle")
            .attr("cx", function(d) {
                return x(d.x)
            })
            .attr("cy", function(d) {
                return y(d.y)
            })
            .attr("r", function(d) {
                return r(d.r*.8)
            })
            .attr("fill", function(d) {
                return d.color
            })
            .attr("stroke",function(d) {
                return "#000"
            })
            .attr("stroke-width",function(d) {
                if (d.region == 'Praha' || d.region == 'Brno' || d.region == 'Ostrava' || d.region == 'Plze')
                    return 10
                else
                    return 2
            })
            .attr("fill-opacity",function(d, i) {
                return .5
            })
            .attr("title", function(d) {
                return d.name + ": " + d.senator
            })

    function distance(d1, d2){
        return Math.sqrt((d1.x-d2.x)*(d1.x-d2.x) + (d1.y-d2.y)*(d1.y-d2.y))
    }
    function unitvector(d1, d2) {
        var distx = d1.x - d2.x;
        var disty = d1.y - d2.y;
        var size = Math.sqrt(distx*distx + disty*disty);
        if (size > 0) {
            return [distx/size, disty/size]
        } else {
            return [0, 0]
        }

    }
    function repulsive(d1, d2, a=0.05) {
        var gap = distance(d1, d2) - d1.r - d2.r;
        if (gap < 0) {
            var unitv = unitvector(d1, d2);
            return {'dx': -1*unitv[0]*gap*a, 'dy': -1*unitv[1]*gap*a }
        } else {
            return {'dx': 0, 'dy': 0}
        }
    }
    function attractive(d, b=0.1) {
        var orig = {'x': d.x_original, 'y': d.y_original}
        var dist = distance(d, orig);
        var unitv = unitvector(d, orig);
        return {'dx': -1*unitv[0]*dist*b, 'dy': -1*unitv[1]*dist*b }
    }
    conflicted = []
    for (k=0; k<1000; k++) {
        for (var i=0; i<data.length; i++) {
            data[i].x_next = data[i].x;
            data[i].y_next = data[i].y;
        }
        for (var i=0; i<data.length; i++) {
            conflicted[i] = false;
            for (var j=i+1; j<data.length; j++) {
                repuls = repulsive(data[i], data[j])
                data[i]['x_next'] += repuls['dx'];
                data[i]['y_next'] += repuls['dy'];
                data[j]['x_next'] += -1*repuls['dx'];
                data[j]['y_next'] += -1*repuls['dy'];
                if (repuls['dx'] != 0 || repuls['dy'] != 0) {
                    conflicted[i] = true;
                    conflicted[j] = true;
                }
            }
        }
        for (var i=0; i<data.length; i++) {
            if (conflicted[i]) {
                attract = attractive(data[i], 0.01);
            } else {
                attract = attractive(data[i])
            }
                data[i]['x_next'] += attract['dx'];
                data[i]['y_next'] += attract['dy'];
        }
        for (var i=0; i<data.length; i++) {
            data[i].x = data[i].x_next;
            data[i].y = data[i].y_next;
        }
        d3.selectAll("circle")
            .transition()
            .duration(100)
            .delay(function() {
                    return k * 50
            })
            .attr("cx", function(d) {
                return x(d.x)
            })
            .attr("cy", function(d) {
                return y(d.y)
            })
    }

    nothing = 0
})

</script>

cartogram_forces.html

<script src="//d3js.org/d3.v3.min.js"></script>

<p id="chart">

<script>

// set margins, size
var margin = {top: 0, right: 0, bottom: 0, left: 0},
    width = 1040 - margin.left - margin.right,
    height = 620 - margin.top - margin.bottom;
// append the svg canvas to the page
var svg = d3.select("#chart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");
// set up scales
var x = d3.scale.linear()
                    .domain([-260,260])
                    .range([0, width])
var y = d3.scale.linear()
                    .domain([-145, 165])
                    .range([height, 0])
var r = d3.scale.linear()
                    .domain([0,50])
                    .range([0,100])

d3.csv("centroids.csv", function (error, data) {

    data.forEach(function(d) {
        d.x = parseFloat(d.x);
        d.y = parseFloat(d.y);
        d.x_original = d.x;
        d.y_original = d.y;
        d.x_next = d.x;
        d.y_next = d.y;
        d.r = 20;
    })

    var circles = svg.selectAll(".circle")
        .data(data)
            .enter()
        .append("circle")
            .attr("cx", function(d) {
                return x(d.x)
            })
            .attr("cy", function(d) {
                return y(d.y)
            })
            .attr("r", function(d) {
                return r(d.r*.8)
            })
            .attr("fill", function(d) {
                return d.color
            })
            .attr("stroke",function(d) {
                return "#000"
            })
            .attr("stroke-width",function(d) {
                if (d.region == 'Praha' || d.region == 'Brno' || d.region == 'Ostrava' || d.region == 'Plze')
                    return 10
                else
                    return 2
            })
            .attr("fill-opacity",function(d, i) {
                return .5
            })
            .attr("title", function(d) {
                return d.name + ": " + d.senator
            })

    function distance(d1, d2){
        return Math.sqrt((d1.x-d2.x)*(d1.x-d2.x) + (d1.y-d2.y)*(d1.y-d2.y))
    }
    function unitvector(d1, d2) {
        var distx = d1.x - d2.x;
        var disty = d1.y - d2.y;
        var size = Math.sqrt(distx*distx + disty*disty);
        if (size > 0) {
            return [distx/size, disty/size]
        } else {
            return [0, 0]
        }

    }
    function repulsive(d1, d2, a=0.05) {
        var gap = distance(d1, d2) - d1.r - d2.r;
        if (gap < 0) {
            var unitv = unitvector(d1, d2);
            return {'dx': -1*unitv[0]*gap*a, 'dy': -1*unitv[1]*gap*a }
        } else {
            return {'dx': 0, 'dy': 0}
        }
    }
    function attractive(d, b=0.1) {
        var orig = {'x': d.x_original, 'y': d.y_original}
        var dist = distance(d, orig);
        var unitv = unitvector(d, orig);
        return {'dx': -1*unitv[0]*dist*b, 'dy': -1*unitv[1]*dist*b }
    }
    conflicted = []
    for (k=0; k<1000; k++) {
        for (var i=0; i<data.length; i++) {
            data[i].x_next = data[i].x;
            data[i].y_next = data[i].y;
        }
        for (var i=0; i<data.length; i++) {
            conflicted[i] = false;
            for (var j=i+1; j<data.length; j++) {
                repuls = repulsive(data[i], data[j])
                data[i]['x_next'] += repuls['dx'];
                data[i]['y_next'] += repuls['dy'];
                data[j]['x_next'] += -1*repuls['dx'];
                data[j]['y_next'] += -1*repuls['dy'];
                if (repuls['dx'] != 0 || repuls['dy'] != 0) {
                    conflicted[i] = true;
                    conflicted[j] = true;
                }
            }
        }
        for (var i=0; i<data.length; i++) {
            if (conflicted[i]) {
                attract = attractive(data[i], 0.01);
            } else {
                attract = attractive(data[i])
            }
                data[i]['x_next'] += attract['dx'];
                data[i]['y_next'] += attract['dy'];
        }
        for (var i=0; i<data.length; i++) {
            data[i].x = data[i].x_next;
            data[i].y = data[i].y_next;
        }
        d3.selectAll("circle")
            .transition()
            .duration(100)
            .delay(function() {
                    return k * 50
            })
            .attr("cx", function(d) {
                return x(d.x)
            })
            .attr("cy", function(d) {
                return y(d.y)
            })
    }

    nothing = 0
})

</script>

centroids.csv

id,longitude,latitude,x,y,name,senator,party,color,region
1,12.9923260929,50.2369228535,-188.3910807716,46.3739120772,Karlovy Vary,Jan Horník ,STAN ,#5d8c00,
2,12.7039611942,50.1469085881,-209.065402183,36.3617153481,Sokolov,Zdenìk Berka ,ÈSSD ,orange,
3,12.6371979349,49.8938711897,-213.8519940588,8.2166185636,Cheb,Miroslav Nenutil ,ÈSSD ,orange,
4,13.6028544485,50.5488528714,-144.6192503148,81.069576028,Most,Alena Dernerová ,BEZPP ,pink,
5,13.2987258983,50.4335024752,-166.4237467217,68.2392668099,Chomutov,Václav Homolka ,KSÈM ,red,
6,13.6760119656,50.2057124598,-139.3742221237,42.9024111944,Louny,Zdeòka Hamousová ,BEZPP ,#261060,
7,13.4769618986,49.5257953058,-153.6451166833,-32.72409393,Plzeò-mìsto,Václav Chaloupek ,OPAT ,#5d8c00,Plze
8,13.4300783967,49.8704796711,-157.0064293499,5.6148033399,Rokycany,Milada Emmerová ,ÈSSD ,orange,
9,13.3667989195,49.743236239,-161.5432514681,-8.5383563688,Plzeò-mìsto,Lumír Aschenbrenner ,ODS ,#004494,Plze
10,14.2980083174,48.8580173136,-94.7801936871,-107.0003722244,Èeský Krumlov,Tomáš Jirsa ,ODS ,#004494,
11,13.1751008448,49.3446218251,-175.2870449298,-52.8758390119,Domažlice,Jan Látka ,ÈSSD ,orange,
12,13.9098710783,49.2025579408,-122.6076930444,-68.6774628029,Strakonice,Karel Kratochvíle ,ÈSSD ,orange,
13,14.5342184548,49.3935684614,-77.8451078805,-47.4315536039,Tábor,Jaroslav Vìtrovský ,BEZPP ,#261060,
14,14.6477891475,49.0227308529,-69.7026570708,-88.679449962,Èeské Budìjovice,Jiøí Šesták ,HOPB ,#5d8c00,
15,15.096425761,49.266602495,-37.5376550673,-61.5538510885,Pelhøimov,Milan Štìch ,ÈSSD ,orange,
16,14.1604445,49.949004321,-104.6428315715,14.3490216151,Beroun,Jiøí Oberfalzer ,ODS ,#004494,
17,14.417801255,49.9969272334,-86.1916390217,19.6794392383,Praha 12,Tomáš Grulich ,ODS ,#004494,Praha
18,14.2158197948,49.6766923695,-100.6726998145,-15.9399644299,Pøíbram,Jiøí Burian ,ODS ,#004494,
19,14.5815333106,50.0351641042,-74.4528692985,23.9324881494,Praha 11,Ladislav Kos ,HPP 11 ,yellow,Praha
20,14.4421936341,50.040826304,-84.4428274022,24.5622889729,Praha 4,Eva Syková ,BEZPP ,orange,Praha
21,14.3495604452,50.0478117988,-91.0841638785,25.3392785704,Praha 5,Václav Láska ,SZ ,#27d07d,Praha
22,14.5827459621,50.0677348371,-74.3659282467,27.5552981942,Praha 10,Renata Chmelová ,BEZPP ,yellow,Praha
23,14.4788402017,50.1410789147,-81.8154517368,35.7132866053,Praha 8,Daniela Filipiová ,ODS ,#004494,Praha
24,14.5928862773,50.103856395,-73.6389183504,31.5730629543,Praha 9,Zuzana Baudyšová ,BEZPP ,#261060,Praha
25,14.3075417834,50.0897839327,-94.0966918419,30.0077970536,Praha 6,Jiøí Rùžièka ,BEZPP ,#5d8c00,Praha
26,14.4890506764,50.0745574139,-81.0834117522,28.3141665897,Praha 2,Libor Michálek ,BEZPP ,black,Praha
27,14.3646233366,50.1009102828,-90.0042298839,31.24536985,Praha 1,Václav Hampl ,BEZPP ,yellow,Praha
28,14.497568573,50.2574368018,-80.4727211602,48.6556580238,Mìlník,Petr Holeèek ,BEZPP ,#5d8c00,
29,14.1654287455,50.4405202689,-104.2854860903,69.0198489921,Litomìøice,Hassan Mezian ,ÈSSD ,orange,
30,14.1231117704,50.1431450357,-107.3194016227,35.9430991795,Kladno,Jiøí Dienstbier ,ÈSSD ,orange,
31,14.0393212791,50.685952837,-113.3267608928,96.3190681074,Ústí nad Labem,Jaroslav Doubrava ,S.cz ,pink,
32,13.7948404523,50.6237589701,-130.8548137708,89.4013064843,Teplice,Jaroslav Kubera ,ODS ,#004494,
33,14.3604583688,50.848985871,-90.3028372482,114.4530694462,Dìèín,Zbynìk Linhart ,BEZPP ,#5d8c00,
34,15.1034527699,50.8281444009,-37.0338536589,112.1348935627,Liberec,Michael Canov ,SLK ,#5d8c00,
35,15.3581209559,50.6720379901,-18.775418067,94.7713335976,Jablonec nad Nisou,Jaroslav Zeman ,ODS ,#004494,
36,14.6650474988,50.6340720799,-68.4653195721,90.5484233742,Èeská Lípa,Jiøí Vosecký ,SLK ,#5d8c00,
37,15.3036853474,50.3330433339,-22.6781790181,57.0652969808,Jièín,Tomáš Czernin ,TOP 09 ,#5d8c00,
38,14.9333061213,50.4310788343,-49.2325176347,67.9696876605,Mladá Boleslav,Jaromír Jermáø ,ÈSSD ,orange,
39,15.847136685,50.5744691032,16.2845646343,83.9188438802,Trutnov,Jiøí Hlavatý ,BEZPP ,#261060,
40,15.2595460313,49.768798068,-25.8427472836,-5.6951396938,Kutná Hora,Jaromír Strnad ,ÈSSD ,orange,
41,14.7114770913,49.8569077331,-65.1365499406,4.1052102445,Benešov,Ludìk Jeništa ,BEZPP ,#5d8c00,
42,15.0481773929,50.0648297763,-40.9968218137,27.2321711878,Kolín,Emilie Tøísková ,ÈSSD ,orange,
43,15.5885241041,50.0577897576,-2.2566643566,26.4491169477,Pardubice,Miluše Horská ,BEZPP ,yellow,
44,15.7384690792,49.7622170097,8.4936406324,-6.4271442274,Chrudim,Jan Veleba ,SPO ,pink,
45,15.6446873645,50.2290658105,1.7699605945,45.4999810353,Hradec Králové,Jaroslav Malý ,BEZPP ,orange,
46,16.5041015716,49.9932316588,63.3856621726,19.2683841738,Ústí nad Orlicí,Petr Šilar ,KDU-ÈSL ,yellow,
47,16.0909502673,50.4123373258,33.7647794158,65.8850884093,Náchod,Lubomír Franc ,ÈSSD ,orange,
48,16.1643352442,50.1613332504,39.0261153316,37.9661561038,Rychnov nad Knìžnou,Miroslav Antl ,BEZPP ,orange,
49,16.6054036303,49.4470022087,70.6485132714,-41.4881713248,Blansko,Jaromíra Vítková ,KDU-ÈSL ,yellow,
50,16.3828820433,49.7629883282,54.6948280969,-6.3413512478,Svitavy,Radko Martínek ,ÈSSD ,orange,
51,16.0752129065,49.4998378809,32.6364893311,-35.6113123414,Žïár nad Sázavou,František Bradáè ,KDU-ÈSL ,yellow,
52,15.5210509882,49.2516634571,-7.0941494001,-63.2155053249,Jihlava,Miloš Vystrèil ,ODS ,#004494,
53,15.8890299748,49.1504470548,19.2881040461,-74.4737045396,Tøebíè,František Bublan ,BEZPP ,orange,
54,16.1186781789,48.9121533327,35.7527320383,-100.9788769571,Znojmo,Pavel Štohl ,BEZPP ,orange,
55,16.4081219668,49.1999519058,56.5044044074,-68.9673294735,Brno-mìsto,Jan Žaloudík ,BEZPP ,orange,
56,16.7510143063,48.8669627002,81.0880706893,-106.005385818,Bøeclav,Jan Hajda ,ÈSSD ,orange,
57,16.8743247327,49.2229554776,89.9288117107,-66.4086651767,Vyškov,Ivo Bárek ,ÈSSD ,orange,
58,16.659187789,49.1764798536,74.504568533,-71.5781023695,Brno-mìsto,Jiøí Dušek ,BEZPP ,#261060,Brno
59,16.5677603082,49.1861453918,67.9496752965,-70.503014219,Brno-mìsto,Eliška Wagnerová ,BEZPP ,#27d07d,Brno
60,16.5907774315,49.2413774157,69.5998879518,-64.3596114296,Brno-mìsto,Zdenìk Papoušek ,BEZPP ,yellow,Brno
61,17.4329898667,49.6247249866,129.9823084899,-21.7202444709,Olomouc,Lumír Kantor ,BEZPP ,yellow,
62,17.0641417821,49.4707703167,103.5377450691,-38.844468444,Prostìjov,Božena Sekaninová ,ÈSSD ,orange,
63,17.6821450631,49.5490731602,147.8454903008,-30.1349214649,Pøerov,Jitka Seitlová ,BEZPP ,yellow,
64,17.50151842,50.0165319416,134.8954631202,21.8600513376,Bruntál,Ladislav Václavec ,BEZPP ,#261060,
65,17.0566790037,50.134177467,103.0027011679,34.9456454814,Šumperk,Zdenìk Brož ,BEZPP ,yellow,
66,17.0964801688,49.7476034081,105.8562457037,-8.0526005156,Olomouc,Alena Šromová ,KDU-ÈSL ,yellow,
67,18.0637894311,49.6597050828,175.2074832603,-17.8294433487,Nový Jièín,Petr Orel ,SZ ,#27d07d,
68,17.9327863677,49.8889221955,165.8152186287,7.6661468886,Opava,Vladimír Plaèek ,ÈSSD ,orange,
69,18.3719216278,49.5850286293,197.2990211057,-26.1356305921,Frýdek-Místek,Jiøí Carbol ,KDU-ÈSL ,yellow,
70,18.2901637124,49.8000144227,191.4373873623,-2.2229757798,Ostrava-mìsto,Zdenìk Nytra ,BEZPP ,#004494,Ostrava
71,18.2035330258,49.7838842983,185.2264002861,-4.0171133891,Ostrava-mìsto,Leopold Sulovský ,BEZPP ,#5d8c00,Ostrava
72,18.1897256638,49.8847598112,184.2364814689,7.2031690378,Ostrava-mìsto,Peter Koliba ,BEZPP ,#261060,Ostrava
73,18.6400291863,49.620614563,216.5209925153,-22.1774427707,Frýdek-Místek,Jiøí Cieñcia³a ,BEZPP ,orange,
74,18.3987752728,49.8601537135,199.2242931835,4.4662574033,Karviná,Petr Vícha ,ÈSSD ,orange,
75,18.5067119083,49.833539535,206.9628102624,1.5059889402,Karviná,Radek Sušil ,ÈSSD ,orange,
76,17.3977401721,49.2787882727,127.455081639,-60.198439211,Kromìøíž,Šárka Jelínková ,KDU-ÈSL ,yellow,
77,18.0964574261,49.3941884441,177.5496151616,-47.3625935561,Vsetín,Jiøí Èunek ,KDU-ÈSL ,yellow,
78,17.8528455889,49.237391308,160.0838644954,-64.8029821971,Zlín,František Èuba ,SPO ,pink,
79,17.1281431886,48.9522141616,108.1263259065,-96.5229510154,Hodonín,Anna Hubáèková ,BEZPP ,yellow,
80,17.7377978623,49.0902916902,151.8355177352,-81.1647255904,Zlín,Patrik Kunèar ,KDU-ÈSL ,yellow,
81,17.4804807684,48.988455103,133.3871686889,-92.4919073492,Uherské Hradištì,Ivo Valenta ,BEZPP ,#004494,

extract_centers.py

# extract centroids for obvody
import csv
import json

# length of a degree, latitude 50 (longitude, latitude)
degree_length = [71.695, 111.229]
center = [15.62, 49.82]

with open("centroids.json") as fin:
    data = json.load(fin)

with open("centroids.csv", "w") as fout:
    csvw = csv.writer(fout)
    csvw.writerow(["id", "longitude", "latitude", "x", "y"])
    for row in data['features']:
        geo = [row['geometry']['coordinates'][0], row['geometry']['coordinates'][1]]
        coordinates = [
            (geo[0] - center[0]) * degree_length[0],
            (geo[1] - center[1]) * degree_length[1]
        ]
        csvw.writerow([row['properties']['obvod'], geo[0], geo[1], coordinates[0], coordinates[1]])