/* built with Studio Sketchpad:
* https://sketchpad.cc
*
* observe the evolution of this sketch:
* https://studio.sketchpad.cc/sp/pad/view/ro.okGZJqh0yTT/rev.3108
*
* authors:
*
* frederic.vernier
* license (unless otherwise specified):
* creative commons attribution-share alike 3.0 license.
* https://creativecommons.org/licenses/by-sa/3.0/
*/
/* @pjs preload="/static/uploaded_resources/p.2418/logoUPSUD.png"; */
/* @pjs preload="/static/uploaded_resources/p.2418/villes40.csv,/static/uploaded_resources/p.2418/villes41.csv,/static/uploaded_resources/p.2418/villes42.csv,/static/uploaded_resources/p.2418/depts.csv"; */
/////////////////////////////////////////////////////////////////////
static class Dept{
static Dept deptList[] = new Dept[100];
float maxx = 0;
float minx = 0;
float maxy = 0;
float miny = 0;
int nb = 0;
int population=0;
int nbCities = 0;
int bigger = -1;
String name;
int count = 0;
float cx=0;
float cy=0;
Dept(String line[]){
if (line[1].equals("2A")) {
this.nb = 20;this.name="Corse";}
else if (line[1].equals("2B")) {
this.nb = 20;this.name="Corse";
} else {
this.nb = int(line[1]);
this.name = line[2];
}
if (this.nb<100)
deptList[this.nb] = this;
}
int mapX(float xi, int width){
return int(map(xi, this.minx, this.maxx, 0, width));
}
int mapY(float yi, int height){
return int(map(yi, miny, maxy, height, 0));
}
}
/////////////////////////////////////////////////////////////////////
static class Place {
int postalCode, inseecode, altitude, dept;
String name;
float x;
float y;
int population;
float surface;
int id;
static Place placeList[];
static Place sortedPlaceList[];
static float maxx = 0;
static float minx = 0;
static float maxy = 0;
static float miny = 0;
static int maxa = 0;
static int mina = 0;
static int maxp = 0;
static int minp = 0;
static float maxs = 0;
static float mins = 0;
static int nb = 0;
static Place get(int code){
for(int i=Place.sortedPlaceList.length-1; i>=0; i--){
Place p = Place.sortedPlaceList[i];
if (p.inseecode == code) {
//println("found at "+i);
return p;
}
}
return null;
}
static Place search(String name){
for(int i=Place.sortedPlaceList.length-1; i>=0; i--){
Place p = Place.sortedPlaceList[i];
if (p.name==name) {
//println("found at "+i);
return p;
}
}
return null;
}
Place(String line[]){
this.inseecode = int(line[0]);
this.altitude = int(line[2]);
if (line[3].startsWith("2A")) {
this.postalCode=20000+int(line[3].substring(2));
} else if (line[3]=="2B") {
this.postalCode=20000+int(line[3].substring(2));
} else if (line[3].indexOf("|")>=0) {
this.postalCode = int(line[3].substring(0, line[3].indexOf("|")));
} else
this.postalCode = int(line[3]);
this.dept = int(postalCode/1000);
Dept.deptList[this.dept].nbCities++;
this.name = line[1];
this.x = float(line[4]);
this.y = float(line[5]);
this.population = int(line[6]);
Dept.deptList[this.dept].population+=this.population;
Dept.deptList[this.dept].cx+=this.x;
Dept.deptList[this.dept].cy+=this.y;
if (Dept.deptList[this.dept]!=null){
if (Dept.deptList[this.dept].bigger==-1)
Dept.deptList[this.dept].bigger = nb;
else if (Place.placeList[Dept.deptList[this.dept].bigger].population<population)
Dept.deptList[this.dept].bigger = nb;
if(Dept.deptList[this.dept].count==0){
Dept.deptList[this.dept].maxx = this.x;
Dept.deptList[this.dept].minx = this.x;
Dept.deptList[this.dept].maxy = this.y;
Dept.deptList[this.dept].miny = this.y;
} else{
Dept.deptList[this.dept].miny = min(Dept.deptList[this.dept].miny, y);
Dept.deptList[this.dept].maxy = max(Dept.deptList[this.dept].maxy, y);
Dept.deptList[this.dept].minx = min(Dept.deptList[this.dept].minx, x);
Dept.deptList[this.dept].maxx = max(Dept.deptList[this.dept].maxx, x);
}
Dept.deptList[this.dept].count++;
} else
println("problem with dept |"+this.dept+"|");
this.surface = float(line[7]);
if (nb==0){
miny = y;
maxy = y;
minx = x;
maxx = x;
} else {
miny = min(miny, y);
maxy = max(maxy, y);
minx = min(minx, x);
maxx = max(maxx, x);
minp = min(minp, this.population);
maxp = max(maxp, this.population);
mins = min(mins, this.surface);
maxs = max(maxs, this.surface);
mina = min(mina, this.altitude);
maxa = max(maxa, this.altitude);
}
placeList[nb] = this;
id = nb;
nb++;
}
}
/////////////////////////////////////////////////////////////////////
PImage img;
int labelindex = -1;
// min/max boundary of all points
float minX, maxX;
float minY, maxY;
int totalCount; // total number of places
int maxdisp = 0;
int focus = -1;
int preFocus = 26;
float lastClick = 0;
int clickCount = 0;
int mapX(float xi){
return int(map(xi, Place.minx, Place.maxx, 0, width));
}
int mapY(float yi){
return int(map(yi, Place.miny, Place.maxy, height, 0));
}
int mapS(float si){
return int(map(si, Place.mins, Place.maxs, 3, 255));
}
// swap 2 cities in the list
void permute(Place[] list, int i, int j){
Place tmp = list[i];
list[i]= list[j];
list[j]=tmp;
}
// quicksort the cities according to population
void sort(Place[] list, int min, int max){
if (min<max) {
permute(list, min, max);
int li = min;
for (int i=min; i<max; i++){
if (list[i].population<list[max].population) {
permute(list, i, li); li++;
}
}
permute(list, li, max);
sort(list, min, li-1);
sort(list, li+1, max);
}
}
// Calculates the base-10 logarithm of a number
float log10 (int x) {
return (log(x) / log(10));
}
/////////////////////////////////////////////////////////////////////
void setup() {
size(800,800);
String lines1[], lines2[], lines3[], lines4[];
background(255);
//text ("Loading 1/4", 5, 60);
//if(online) {
img = loadImage("/static/uploaded_resources/p.2418/logoUPSUD.png");
lines1 = loadStrings("/static/uploaded_resources/p.2418/villes40.csv");
lines2 = loadStrings("/static/uploaded_resources/p.2418/villes41.csv");
lines3 = loadStrings("/static/uploaded_resources/p.2418/villes42.csv");
lines4 = loadStrings("/static/uploaded_resources/p.2418/depts.csv");
//} else {
// img = loadImage("logoUPSUD.png");
// lines1 = loadStrings("villes40.csv");
// lines2 = loadStrings("villes41.csv");
// lines3 = loadStrings("villes42.csv");
// lines4 = loadStrings("depts.csv");
//}
Place.placeList = new Place[lines1.length+lines2.length+lines3.length-1];
Place.sortedPlaceList = new Place[lines1.length+lines2.length+lines3.length-1];
for(int i=0; i<lines4.length; i++)
new Dept(split(lines4[i], ';'));
for(int i=1; i<lines1.length; i++)
new Place(split(lines1[i], ';'));
for(int i=0; i<lines2.length; i++)
new Place(split(lines2[i], ';'));
for(int i=0; i<lines3.length; i++)
new Place(split(lines3[i], ';'));
for(int i=Place.placeList.length-1; i>=0; i--)
Place.sortedPlaceList[i] = Place.placeList[i];
for (int i=0; i<Dept.deptList.length; i++){
if (Dept.deptList[i]!=null && Dept.deptList[i].nbCities>0) {
Dept.deptList[i].cx = Dept.deptList[i].cx/Dept.deptList[i].nbCities;
Dept.deptList[i].cy = Dept.deptList[i].cy/Dept.deptList[i].nbCities;
}
}
sort(Place.sortedPlaceList, 0, Place.sortedPlaceList.length-1);
smooth();
maxdisp = 0;
//font = loadFont("Arial");
//textFont(font, 16);
noLoop();
//Place p = Place.get(2001);
//println("0ville= "+p.name);
}
void draw(){
if (focus==-1)
drawFrance();
else
drawDept(focus);
image(img, 0, 0, 80, 40);
}
void drawRailroad(Place c1, Place c2,int size) {
if (c1==null || c2==null) return;
strokeWeight(1);
stroke(0, 0, 0);
float x1 = mapX(Dept.deptList[c1.dept].cx);
float y1 = mapY(Dept.deptList[c1.dept].cy);
if (c1.dept==preFocus){
x1 = mapX(c1.x);
y1 = mapY(c1.y);
}
float x2 = mapX(Dept.deptList[c2.dept].cx);
float y2 = mapY(Dept.deptList[c2.dept].cy);
if (c2.dept==preFocus){
x2 = mapX(c2.x);
y2 = mapY(c2.y);
}
float s = size/2;
float t = s+1;
float d = dist(x1, y1, x2, y2);
float dx = (x2-x1)/d;
float dy = (y2-y1)/d;
line (x1-s*dy, y1+s*dx, x2-s*dy, y2+s*dx);
line (x1+s*dy, y1-s*dx, x2+s*dy, y2-s*dx);
for (int i=0; i<d; i+=4){
line (x1+dx*i-dy*t, y1+dy*i+dx*t, x1+dx*i+dy*t, y1+dy*i-dx*t);
}
}
void drawFrance(){
if (maxdisp==0) maxdisp = 1651;
background(255);
colorMode(RGB, 255, 255, 255);
Place p0 = Place.search("Paris");
Place p1 = Place.search("Le Creusot");
Place p2 = Place.search("Mâcon");
Place p3 = Place.search("Lyon");
Place p4 = Place.search("Valence");
Place p5 = Place.search("Avignon");
Place p6 = Place.search("Marseille");
Place p7 = Place.search("Nîmes");
drawRailroad(p0, p1, 4);
drawRailroad(p1, p2, 4);
drawRailroad(p2, p3, 4);
drawRailroad(p3, p4, 4);
drawRailroad(p4, p5, 4);
drawRailroad(p5, p6, 4);
drawRailroad(p5, p7, 4);
p0 = Place.search("Torcy");
p1 = Place.search("Reims");
p2 = Place.search("Les Trois-Domaines");
p3 = Place.get(57422);
drawRailroad(p0, p1, 4);
drawRailroad(p1, p2, 4);
drawRailroad(p2, p3, 4);
p0 = Place.search("Bobigny");
p1 = Place.search("Ablaincourt-Pressoir");
p2 = Place.search("Arras");
p3 = Place.search("Lille");
p4 = Place.search("Calais");
drawRailroad(p0, p1, 4);
drawRailroad(p1, p2, 4);
drawRailroad(p2, p3, 4);
drawRailroad(p3, p4, 4);
p0 = Place.search("Paris");
p1 = Place.search("Massy");
p2 = Place.search("Tours");
p3 = Place.search("Le Mans");
drawRailroad(p0, p1, 4);
drawRailroad(p1, p2, 4);
drawRailroad(p1, p3, 4);
p0 = Place.search("Belfort");
p1 = Place.search("Dijon");
drawRailroad(p0, p1, 4);
p0 = Place.search("Le Mans");
p1 = Place.search("Laval");
p2 = Place.search("Rennes");
p3 = Place.search("Saint-Brieuc");
p4 = Place.search("Guingamp");
p5 = Place.search("Morlaix");
p6 = Place.search("Brest");
p7 = Place.search("Redon");
p8 = Place.search("Vannes");
p9 = Place.search("Lorient");
pa = Place.search("Quimper");
pb = Place.search("Nantes");
pc = Place.search("Saint-Nazaire");
pd = Place.search("Clisson");
pe = Place.search("Ancenis");
pf = Place.search("Angers");
pg = Place.search("Sablé-sur-Sarthe");
ph = Place.search("Saint-Malo");
pi = Place.search("Saumur");
pj = Place.search("Tours");
pk = Place.search("La Roche-sur-Yon");
pl = Place.search("Châtellerault");
pm = Place.search("Poitiers");
pn = Place.search("Niort");
po = Place.search("La Rochelle");
pp = Place.search("Angoulême");
pq = Place.search("Libourne");
pr = Place.search("Bordeaux");
ps = Place.search("Arcachon");
pt = Place.search("Dax");
pu = Place.search("Bayonne");
pv = Place.search("Hendaye");
pw = Place.search("Pau");
px = Place.search("Tarbes");
py = Place.search("Saint-Gaudens");
pz = Place.search("Toulouse");
drawRailroad(p0, p1, 2);
drawRailroad(p1, p2, 2);
drawRailroad(p2, ph, 2);
drawRailroad(p2, p3, 2);
drawRailroad(p3, p4, 2);
drawRailroad(p4, p5, 2);
drawRailroad(p5, p6, 2);
drawRailroad(p2, p7, 2);
drawRailroad(p7, p8, 2);
drawRailroad(p8, p9, 2);
drawRailroad(p9, pa, 2);
drawRailroad(p7, pb, 2);
drawRailroad(pb, pc, 2);
drawRailroad(pb, pd, 2);
drawRailroad(pb, pe, 2);
drawRailroad(pe, pf, 2);
drawRailroad(pf, pg, 2);
drawRailroad(pg, p0, 2);
drawRailroad(p0, pg, 2);
drawRailroad(pf, pi, 2);
drawRailroad(pi, pj, 2);
drawRailroad(pb, pk, 2);
drawRailroad(pj, pl, 3);
drawRailroad(pl, pm, 3);
drawRailroad(pm, pn, 2);
drawRailroad(pn, po, 2);
drawRailroad(pm, pp, 3);
drawRailroad(pp, pq, 3);
drawRailroad(pq, pr, 3);
drawRailroad(pr, ps, 2);
drawRailroad(ps, pt, 2);
drawRailroad(pt, pu, 2);
drawRailroad(pu, pv, 2);
drawRailroad(pt, pw, 2);
drawRailroad(pw, px, 2);
drawRailroad(px, py, 2);
drawRailroad(py, pz, 2);
p0 = Place.search("Paris");
p1 = Place.search("Mantes-la-Jolie");
p2 = Place.search("Évreux");
p3 = Place.search("Bernay");
p4 = Place.search("Lisieux");
p5 = Place.search("Caen");
p6 = Place.search("Bayeux");
p7 = Place.search("Cherbourg-Octeville");
p8 = Place.search("Rouen");
p9 = Place.search("Le Havre");
pa = Place.search("Amiens");
pb = Place.search("Creil");
pc = Place.search("Compiègne");
pd = Place.search("Tergnier");
pe = Place.search("Saint-Quentin");
pe = Place.search("Busigny");
pf = Place.search("Avesnes");
pg = Place.search("Cambrai");
ph = Place.search("Valenciennes");
pi = Place.search("Lilles");
pj = Place.search("Charleville-Mézières");
pk = Place.search("Reims");
pl = Place.search("Épernay");
pm = Place.search("Château-Thierry");
pn = Place.search("Meaux");
drawRailroad(p1, p2, 2);
drawRailroad(p1, p2, 2);
drawRailroad(p0, p1, 2);
drawRailroad(p1, p2, 2);
drawRailroad(p2, p3, 2);
drawRailroad(p3, p4, 2);
drawRailroad(p4, p5, 2);
drawRailroad(p5, p6, 2);
drawRailroad(p6, p7, 2);
drawRailroad(p1, p8, 2);
drawRailroad(p8, p9, 2);
drawRailroad(p8, pa, 2);
drawRailroad(pa, pb, 2);
drawRailroad(pb, p0, 2);
drawRailroad(pb, pc, 2);
drawRailroad(pc, pd, 2);
drawRailroad(pd, pe, 2);
drawRailroad(pe, pf, 2);
drawRailroad(pe, pg, 2);
drawRailroad(pg, ph, 2);
drawRailroad(ph, pi, 2);
drawRailroad(pf, pj, 2);
drawRailroad(pj, pk, 2);
drawRailroad(pk, pl, 2);
drawRailroad(pl, pm, 2);
drawRailroad(pm, pn, 2);
drawRailroad(pn, p0, 2);
colorMode(HSB, 255, 255, 255);
//for(Place p:listPlaces){
for(int i=Place.sortedPlaceList.length-maxdisp; i< Place.sortedPlaceList.length; i++){
Place p = Place.sortedPlaceList[i];
stroke(128, 128, 128);
int w = mapS(p.surface);
int po = int(log10(p.population+2));
//line(mapX(p.x), mapY(p.y), mapX(p.x), mapY(p.y)-int(p.altitude/100));
strokeWeight(po);
randomSeed(p.dept);random(255);
float dx= mapX((Dept.deptList[p.dept].cx+p.x)/2);
float dy= mapY((Dept.deptList[p.dept].cy+p.y)/2);
if (p.dept==preFocus){
dx= mapX(p.x);
dy= mapY(p.y);
}
if (p.id == Dept.deptList[p.dept].bigger && p.dept==preFocus){
String txt = p.dept+":"+p.name;
po = po*2;
fill(255, 0, 255);
strokeWeight(1);
stroke(0, 0, 0);
fill(int(random(255)), 128, 192);
rect(dx-textWidth(txt)/2, dy-11-po, textWidth(txt)+2, 12);
fill(0, 0, 0);
text (txt, dx-textWidth(txt)/2+1, dy-po);
}
else fill(int(random(255)), 255-p.altitude/20, 255-po*40);
noStroke();
ellipse(dx, dy, po, po);
//ellipse(mapX(p.x), mapY(p.y), p.population/10000, p.population/10000);
}
if (labelindex>=0) {
Place p = Place.sortedPlaceList[labelindex];
colorMode(RGB, 255, 255, 255);
fill(255, 255, 255);
strokeWeight(1);
stroke(0, 0, 0);
rect(mapX(p.x), mapY(p.y)-11, 2+textWidth(p.postalCode +" "+p.name), 12*4);
fill(0, 0, 0);
text (p.postalCode +" "+p.name, 1+mapX(p.x), mapY(p.y)+12*0);
text (p.altitude +" m", 1+mapX(p.x), mapY(p.y)+12*1);
text (p.population +" hab.", 1+mapX(p.x), mapY(p.y)+12*2);
text (p.surface+" ha", 1+mapX(p.x), mapY(p.y)+12*3);
}
if (maxdisp==Place.placeList.length){
noLoop();
} else {
maxdisp = min(maxdisp+5000, Place.placeList.length);
if (Place.placeList.length-maxdisp<2500)
maxdisp = Place.placeList.length;
loop();
}
}
void drawDept(int dept){
Dept dd = Dept.deptList[dept];
//println("draw "+dept+" "+dd.minx+"-"+dd.maxx);
background(255);
text (dd.nbCities+" cities of "+dd.name,5, 60);
colorMode(HSB, 255, 255, 255);
//for(Place p:listPlaces){
for (int i=0; i<Place.sortedPlaceList.length; i++){
Place p = Place.sortedPlaceList[i];
stroke(128, 128, 128);
int w = mapS(p.surface);
int po = 1+int(log10(p.population+2));
//line(mapX(p.x), mapY(p.y), mapX(p.x), mapY(p.y)-int(p.altitude/100));
strokeWeight(po);
randomSeed(p.dept);random(255);
int col = int(random(255));
if (p.dept==dept && p.population>4000){
po = po*2;
strokeWeight(1);
stroke(0, 0, 0);
if (p.id == Dept.deptList[p.dept].bigger)
fill(col, 128, 192);
else
fill(255, 0, 255);
rect(dd.mapX(p.x, width)-textWidth(p.name)/2, dd.mapY(p.y, height)-11-po, textWidth(p.name)+2, 12);
fill(0, 0, 0);
text (p.name, dd.mapX(p.x, width)-textWidth(p.name)/2+1, dd.mapY(p.y, height)-po);
}
else fill(col, 255-p.altitude/20, 255-po*40);
noStroke();
if (p.dept==dept)
ellipse(dd.mapX(p.x, width), dd.mapY(p.y, height), po, po);
//ellipse(mapX(p.x), mapY(p.y), p.population/10000, p.population/10000);
}
if (labelindex>=0) {
Place p = Place.sortedPlaceList[labelindex];
colorMode(RGB, 255, 255, 255);
fill(255, 255, 255);
strokeWeight(1);
stroke(0, 0, 0);
rect(dd.mapX(p.x, width), dd.mapY(p.y, height)-11, 2+textWidth(p.postalCode +" "+p.name), 12*4);
fill(0, 0, 0);
text (p.postalCode +" "+p.name, 1+dd.mapX(p.x, width), dd.mapY(p.y, height)+12*0);
text (p.altitude +" m", 1+dd.mapX(p.x, width), dd.mapY(p.y, height)+12*1);
text (p.population +" hab.", 1+dd.mapX(p.x, width), dd.mapY(p.y, height)+12*2);
text (p.surface+" ha", 1+dd.mapX(p.x, width), dd.mapY(p.y, height)+12*3);
}
if (maxdisp==Place.placeList.length){
noLoop();
} else {
maxdisp = min(maxdisp+5000, Place.placeList.length);
if (Place.placeList.length-maxdisp<2500)
maxdisp = Place.placeList.length;
loop();
}
}
void keyTyped(){
println(""+key+" "+keyCode);
}
void keyPressed(){
if(key=='+'){
println("+");
} else if(key=='-'){
println("-");
} else if(keyCode==LEFT){
println("L");
} else if(keyCode==RIGHT){
println("R");
} else if(keyCode==UP){
println("U");
} else if(keyCode==DOWN){
println("D");
}
}
void mouseMoved(){
if (focus<=0) {
int avFoc = preFocus;
float mind = 10000;
for (int i=0; i<Dept.deptList.length; i++){
if (Dept.deptList[i]!=null) {
float d = dist(mapX(Dept.deptList[i].cx), mapY(Dept.deptList[i].cy), mouseX, mouseY);
if (d<mind){
preFocus = i;
mind = d;
}
}
}
if (avFoc != preFocus){
maxdisp = 0;
redraw();
}
}
}
void mouseClicked(){
float m = millis();
if (m-lastClick<350) clickCount++; else clickCount=1;
//println("mc "+(m-lastClick)+" => "+ clickCount);
lastClick= m;
float mind = 10000;
if (focus>0 &&clickCount >=2){
focus= -1;
maxdisp = 0;
labelindex = -1;
} else if (focus>0){
Dept dd = Dept.deptList[focus];
for(int i=Place.sortedPlaceList.length-1; i>=0; i--){
Place p = Place.sortedPlaceList[i];
int po = int(log10(p.population+2));
float d = dist(dd.mapX(p.x, width), dd.mapY(p.y, height), mouseX, mouseY);
if (d<po){labelindex = i;maxdisp=0;loop();break;}
if (d<mind) {mind=d; labelindex = i;}
}
} else {
int prevli = labelindex;
for(int i=Place.sortedPlaceList.length-1; i>=0; i--){
Place p = Place.sortedPlaceList[i];
int po = int(log10(p.population+2));
float d = dist(mapX(p.x), mapY(p.y), mouseX, mouseY);
if (d<po){labelindex = i;maxdisp=0;loop();break;}
if (d<mind) {mind=d; labelindex = i;}
}
if (clickCount >=2 && prevli== labelindex) {
focus = int(Place.sortedPlaceList[labelindex].postalCode/1000);
labelindex=-1;
}
}
redraw();
}