> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.ZcJ7L0TOgs5/rev.1889
 * 
 * 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 bigger = -1;
  String name;
  int count = 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;  
  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);
    this.name = line[1];
    
    //if (dept==26 && Dept.deptList[this.dept].bigger>=0) println(nb+" "+name+" "+population+"hab : bigger was "+Dept.deptList[this.dept].bigger+":"+Place.placeList[Dept.deptList[this.dept].bigger].name);

    this.x = float(line[4]);
    this.y = float(line[5]);
    this.population = int(line[6]);
    
    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 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));
}

// permute 2 villes dans la liste
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[];
  //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];
  sort(Place.sortedPlaceList, 0, Place.sortedPlaceList.length-1);
  
 // println("07 "+Dept.deptList[7].name+" : "+Dept.deptList[7].bigger+" "+Place.placeList[Dept.deptList[7].bigger].name);
  println("x " +Place.minx+" - "+Place.maxx);
  //println("y " +Place.miny+" - "+Place.maxy);
  //println("s " +Place.mins+" - "+Place.maxs);
  //println("p " +Place.minp+" - "+Place.maxp+" => "+log10(Place.maxp));

  smooth();
  maxdisp = Place.placeList.length;
  noLoop();
}

void draw(){
  if (focus==-1)
    drawFrance();
  else
    drawDept(focus);
  image(img, 0, 0, 80, 40);
  text ("36550 cities of France",5, 60);  
}

void drawFrance(){
  background(255);
  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);
    if (p.id == Dept.deptList[p.dept].bigger){
      po = po*2;
      fill(255, 0, 255);
      strokeWeight(1);
      stroke(0, 0, 0);
      rect(mapX(p.x)-textWidth(p.name)/2, mapY(p.y)-11-po, textWidth(p.name)+2, 12);
      fill(0, 0, 0);
      text (p.name, mapX(p.x)-textWidth(p.name)/2+1, mapY(p.y)-po);
    }
    else fill(int(random(255)), 255-p.altitude/20, 255-po*40);
    noStroke();
    ellipse(mapX(p.x), mapY(p.y), 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);
  }
}


void drawDept(int dept){
  Dept dd = Dept.deptList[dept];
  println("draw "+dept+" "+dd.minx+"-"+dd.maxx);
  background(255);
  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);
    if (p.dept==dept &&p.id == Dept.deptList[p.dept].bigger){
      po = po*2;
      fill(255, 0, 255);
      strokeWeight(1);
      stroke(0, 0, 0);
      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(int(random(255)), 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);
  }
}  



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 mouseClicked(){
  float mind = 10000;
  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=1651;loop();break;}
      if (d<mind) {mind=d; labelindex = i;}
    }
  } else 
    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=1651;loop();break;}
      if (d<mind) {mind=d; labelindex = i;}
    }
  redraw();
}