Skip to content

Instantly share code, notes, and snippets.

@michalskop
Last active May 12, 2017 03:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michalskop/fa5ee2191da7a10436960af0038fb6b2 to your computer and use it in GitHub Desktop.
Save michalskop/fa5ee2191da7a10436960af0038fb6b2 to your computer and use it in GitHub Desktop.
CZ senate cartogram
<script src="http://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>
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 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]])
<script src="http://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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment