> show canvas only <


/* 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();
}