How to slow down drawing points


#1

Hey folks,

New to processing. I’m reading a large number of X,Y points out of a CSV file and drawing them to the screen using the point command. inside a for loop The points represent GPS locations on a race track. I’d like to slow down the display of these points but can’t figure out how. I see that the delay() command does not work - although to be honest I don’t understand why.

// The following short CSV file called "XXX.csv" is parsed 
// in the code below. It must be in the project's "data" folder.

Table table;  // Define a table 

void setup() {
  
boolean debug = false;  // a debug variable that will be used to determine if we are in debug state.   
int num_corners = 15;                                 // A varible to set the number of corners 
int closest_corner = 0; 
int last_corner_passed = 0;
float R = 6371e3;
float a;
float c;

    
    float lat;
    float lon;
    int speed;

float[] corner_lat = new float[num_corners];          //Create an array for the coordinates for each corner's latitude
float[] corner_lon = new float[num_corners];
float[] dist = new float[num_corners];

corner_lat[1] = radians(43.792018);  //Assign latitude and lognitude for each corner
corner_lon[1] = radians(-87.989872); // 
corner_lat[3] = radians(43.791582);
corner_lon[3] = radians(-87.995253);
corner_lat[5] = radians( 43.801770);
corner_lon[5] = radians(-87.992557);
corner_lat[6] = radians(43.801646);
corner_lon[6] = radians(-87.996056);
corner_lat[7] = radians( 43.799580);
corner_lon[7] = radians(-87.996246);
corner_lat[8] = radians( 43.797146);
corner_lon[8] = radians(-87.999966);
corner_lat[11] = radians(43.797010);
corner_lon[11] = radians(-88.002596);
corner_lat[12] = radians(43.804927);
corner_lon[12] = radians(-87.997457);
corner_lat[14] = radians( 43.803919);
corner_lon[14] = radians(-87.990119);
  
background(0);
     size(650, 650);
     stroke(150);

table = loadTable("test.csv", "header");  
println(table.getRowCount() + " total rows in table"); 
   
  for (TableRow row : table.rows()) {
   
    lat = row.getFloat("lat");
    lon = row.getFloat("lon");
    speed = row.getInt("speed");

  
  float lat2 = radians(lat);    //  current gps position 
  float lon2 = radians(lon);    //  currrent gps position 
  
  closest_corner = 0; 
  float closest_distance = 10000;

  for (int i = 0; i < num_corners; i = i+1){ 
  
  float p1= corner_lat[i];
  float p2 = lat2;
  float delta_p = (lat2-corner_lat[i]);
  float delta_lam = (lon2-corner_lon[i]);
  a = sin(delta_p/2)*sin(delta_p/2)+cos(p1) * cos(p2)*sin(delta_lam/2)*sin(delta_lam/2);
  c = 2 * atan2(sqrt(a), sqrt(1-a));
  dist[i] = R*c;
  
  if (dist[i] < closest_distance){
    closest_corner = i;
    closest_distance = dist[i];
  }
  
  if (dist[i] < 20) {
    last_corner_passed = i;
  }
  
  if (dist[i] > 10000){
    dist[i] = 0;
  }



    
  }
  println(); 
  println("current position = ", lat, ", ", lon); 
  println("closest corner = ", closest_corner); 
  println("last corner passed ", last_corner_passed); 
    for (int j = 0; j < num_corners; j = j+1){
      
    point((corner_lat[1]-lat2)*1000000+400,(corner_lon[1]-(lon2))*1000000+200);
    if (dist[j]>0) {
    println("distance to corner ", j, dist[j]);  
    }
    }
    
  };
}

#2

hi,
-a- sorry, but pls. repair your post and
paste your code again using the

</>

code formatter.

-b- your code is little bit complex and requires a csv file
so it could not be tested here.

-c- but most of it has nothing to do with the question:
for loop in draw()
how to slow down?

! you can not.

-d-
try read and play

/* slow down the show of a 
 for loop 
 inside draw
 is not possible.
 and it does not matter if background is used or not.
 */
// -A-
/*
void setup() {}
 void draw() {
 //background(0, 0, 80);
 for ( int i =0; i <10; i++ ) text(i, 30, 55);
 }
 */

/* 
 but if we make a loop over the elements at each draw,
 and slow down the draw you can see something
 */
// -B-
/*
int i = 0;
 void setup() {
 frameRate(1);
 }
 
 void draw() {
 background(0, 0, 80);
 text(i, 30, 55);
 i++;
 if ( i > 10 ) i = 0;
 }
 */

/*
but for a timed show better make a timer
 */
// -C-
long startT, stopT=2000;
int i=0;
void setup() {
  startT = millis();
}
void draw() {
  background(0, 0, 80);
  text(i, 30, 55);
  mytimer();
}

void mytimer() {
  if ( millis() >= startT + stopT ) {
    startT += stopT;
    // what you need to be done now
    i++;
    if ( i > 10 ) i = 0;
  }
}

/*
so the draw loop can run ( with 60 FPS )
with background what cleans the screen

and what you show is decided externally 
by a timer function,
here just a different number...
*/

#3

Sorry! new to processing and new to posting on here. I really struggled to format my code.

I will try to understand your comments. Here is a link to the CSV file:

These are GPS points from a race car lap. My objective is to plot out the position of the race car but the entire race plots at once.

Thank you for your help.


#4

I think I understand your code but I am struggling to use it in my case.

I am reading XY points out of a CSV file, doing a bit of math and then plotting each XY point.

However I would like to leave the previous XY point on the screen so I slowly draw the entire map of the race track. i will keep trying to understand your comments. thank you for any suggestions.

Dave


#5

did you get the timercode running inside your project?

now disable the

for table row loop

and use only one row depending on the global / timed / i

TableRow row = table.getRow(i);

and limit i to the number of total rows in table
https://processing.org/reference/Table_getRowCount_.html
crows = table.getRowCount();


#6

I am sorry… Stuggling to make this work but i’m sure it’s my own lack of familiarity. This code will clear the background each time - correct?

My objective is to draw out each successive X,Y Point, leaving all the others that were previously drawn in place.

For example, every 1/2 second I’d like to add one more point to the screen on top of the previous ones that were already there.

With your code suggestion, I would still blank the screen entirely and then redraw only the new point, correct?


#7

hm, so you not draw only one point: row(i)
( like the CAR current position )
you want draw all points UP TO i
ok so you need the for loop
but like

for ( int k =0; k < i ; k++) {
TableRow row = table.getRow(k);
 }

i hope i understand you now.


yes, with

draw()
background()

the whole screen is cleared and redrawn 60 times per sec.
that is the usual concept,
sorry i miss the correct words
but there are also concepts use

setup()
background()

draw()

what would add up graphic changes ( like painting )

that basic decision to go what way depends on many things,
common is:

  • with painting, the canvas is the memory
  • with redrawing you need saved shapes or arrays.

( and you have all memory in your loaded table )


#8

and i used it like

// https://discourse.processing.org/t/how-to-slow-down-drawing-points/8234
Table table;
int crows;
long startT, stopT=100;
int i=0;                  // timed global for current row
boolean debug = false;

void setup() {
  size(500, 500);
  table = loadTable("test.csv", "tsv,header");
  crows = table.getRowCount();
  println( crows + " total rows in table");
  for (int k = 0; k<crows; k++) {
    TableRow row = table.getRow(k);
    if (debug) println(" lat "+row.getFloat("lat") + " lon "+row.getFloat("lon"));
  }
}

void draw() {
  background(0, 200, 0);
  translate(width/2, height/2+100);
  float offx=43.799, offy=-87.989, zoom=20000;

  for (int k =0; k < crows; k++ ) {
    TableRow row = table.getRow(k);
    float lat = row.getFloat("lat");
    float lon = row.getFloat("lon");
    float speed = row.getFloat("speed");   // also float?? why int
    float posx = (lat-offx)*zoom;
    float posy = (lon-offy)*zoom;
    if (debug) println("posx "+posx+" posy "+posy);
    if ( k < i ) { 
      stroke(200, 0, 200);
      strokeWeight(6);
    } else {   
      stroke(0, 0, 200);
      strokeWeight(3);
    }
    point(posx, posy);
  } 
  mytimer();
}

void mytimer() {
  if ( millis() >= startT + stopT ) {
    startT += stopT;                         // what you need to be done now
    i++;
    if ( i > crows ) i = 0;
  }
}


#9

but i understand that you want to make it more
LIVE, so any constant delta T between the records
is not that funny.

i try to read the csv file,
and add 2 more columns
way and time
calculate delta way [m] from lat lon
calculate time [sec] from way and speed
( but i not know the unit of that speed.?? )
and that time i use for the timer…

now it looks much better:

// https://discourse.processing.org/t/how-to-slow-down-drawing-points/8234
Table table;
int crows;
long startT, stopT=100;
int i=0;                                     // timed global for current row
boolean debug = true; //false;
float speedf = 3.6; //1 ;                           // if the speed is in m/s ? so time is in sec. if km/h 1/1000/3600
void setup() {
  size(500, 500);
  table = loadTable("test.csv", "header");          // changed from tsv to CSV ( by excel )
  crows = table.getRowCount();

  table.addColumn("way", Table.FLOAT);
  table.addColumn("time", Table.FLOAT);


  println( crows + " total rows in table");
  for (int k = 0; k < crows; k++) {
    TableRow row = table.getRow(k);
    if ( k < crows-1) {
      row.setFloat("way",way(k));
      row.setFloat("time",speedf*row.getFloat("way")/row.getFloat("speed"));  
    }
    if (debug) println("# "+k+" lat "+row.getFloat("lat") + 
                       " lon "+row.getFloat("lon") + 
                       " speed "+row.getFloat("speed") +
                       " way "+row.getFloat("way") +
                       " time "+row.getFloat("time")      );
  }
}

float way(int i) {
    float delta = 0;
    float R = 6371e3;   // meter
    float lat1 = radians(table.getFloat(i,"lat"));
    float lat2 = radians(table.getFloat(i+1,"lat"));
    float lon1 = radians(table.getFloat(i,"lon"));
    float lon2 = radians(table.getFloat(i+1,"lon"));
    float speed1 = table.getFloat(i,"speed");
    float x = (lon2-lon1)*cos((lat1+lat2)/2);
    float y = lat2 - lat1;
    delta = sqrt(sq(x) + sq(y))*R;
    return delta;
}

/*
//https://www.movable-type.co.uk/scripts/latlong.html
 
 var φ1 = lat1.toRadians(), φ2 = lat2.toRadians(), Δλ = (lon2-lon1).toRadians(), R = 6371e3; // gives d in metres
 var d = Math.acos( Math.sin(φ1)*Math.sin(φ2) + Math.cos(φ1)*Math.cos(φ2) * Math.cos(Δλ) ) * R;
 
 
 OR
 var x = (λ2-λ1) * Math.cos((φ1+φ2)/2);
 var y = (φ2-φ1);
 var d = Math.sqrt(x*x + y*y) * R;
 
 */


void draw() {
  background(0, 200, 0);
  fill(0);
  text("speed "+nf(table.getFloat(i,"speed"),1,1)+" ?km/h?",width - 150, height-20);
  translate(width/2, height/2+100);
  float offx=43.799, offy=-87.989, zoom=20000;

  for (int k =0; k < crows; k++ ) {
    TableRow row = table.getRow(k);
    float lat = row.getFloat("lat");
    float lon = row.getFloat("lon");
//    float speed = row.getFloat("speed");   // also float?? why int
    float posx = (lat-offx)*zoom;
    float posy = (lon-offy)*zoom;
    //if (debug) println("posx "+posx+" posy "+posy);
    if ( k < i ) { 
      stroke(200, 0, 200);
      strokeWeight(6);
    } else {   
      stroke(0, 0, 200);
      strokeWeight(3);
    }
    point(posx, posy);
  }
  mytimer();
}

void mytimer() {
  if ( millis() >= startT + stopT ) {
    startT += stopT;                         // what you need to be done now
    i++;
    if ( i >= crows ) i = 0;
    stopT = (long)table.getFloat(i,"time");
  }
}

2019-02-09_14-54-34_snap

can you tell us how you get that data?


ok, i found that in google map and had to turn the world around,
also try speed as [mph]
and added 2 more columns to integrate the way and time


what show now
?6.25 km track ?
? 201 s time ? but not fit with the “play back time”

also i added a avg loc calc so
it should zoom into any GPS data set.

// https://discourse.processing.org/t/how-to-slow-down-drawing-points/8234
// changed orientation and factor to mph   ( track: 6km, in 200sec ? )  Kohler Grand Prix / Wisconsin

Table table;
int crows;
long startT, stopT=100;
int i=0;                                     // timed global for current row
boolean debug = false; //false;
float speedf = 2.237; //3.6; //1 ;                           // if the speed is in m/s ? so time is in sec. if km/h 1/1000/3600 if m/h /0.621371
float offx=43.799, offy=-87.989, zoom=10000;

void setup() {
  size(720, 360);
  table = loadTable("test.csv", "header");          // changed back from tsv to CSV ( by excel )
  crows = table.getRowCount();
  table.addColumn("way", Table.FLOAT);
  table.addColumn("time", Table.FLOAT);

  println( crows + " total rows in table");
  for (int k = 0; k < crows; k++) {
    TableRow row = table.getRow(k);
    if ( k < crows-1) {
      row.setFloat("way", way(k));
      row.setFloat("time", speedf*row.getFloat("way")/row.getFloat("speed"));
    }
    if (debug) println("# "+k+" lat "+row.getFloat("lat") + 
      " lon "+row.getFloat("lon") + 
      " speed "+row.getFloat("speed") +
      " way "+row.getFloat("way") +
      " time "+row.getFloat("time")      );
  }
  table.addColumn("waytotal", Table.FLOAT);
  table.addColumn("timetotal", Table.FLOAT);

  //                                                 get the avg position:
  offx=0;
  offy=0;
  float wayt =0,timet=0;
  for (int k = 0; k < crows; k++) {
    TableRow row = table.getRow(k);               // This is where we compute the average
    offx +=   row.getFloat("lon");
    offy +=   row.getFloat("lat");
    wayt +=   row.getFloat("way");
    row.setFloat("waytotal", wayt);
    timet +=   row.getFloat("time");
    row.setFloat("timetotal", timet);
  }
  offx /= (float)crows; 
  offy /= (float)crows;
  println(" averagex lon "+offx+" averagey lat "+offy);
}

float way(int i) {      //https://www.movable-type.co.uk/scripts/latlong.html
  float delta = 0;
  float R = 6371e3;   // meter
  float lat1 = radians(table.getFloat(i, "lat"));
  float lat2 = radians(table.getFloat(i+1, "lat"));
  float lon1 = radians(table.getFloat(i, "lon"));
  float lon2 = radians(table.getFloat(i+1, "lon"));
  float x = (lon2-lon1)*cos((lat1+lat2)/2);
  float y = lat2 - lat1;
  delta = sqrt(sq(x) + sq(y))*R;
  return delta;
}

void draw() {
  background(0, 200, 0);
  fill(0);
  text(" pos:"+nf(table.getFloat(i, "waytotal"),4,1)+" [m]; time:"+nf(table.getFloat(i, "timetotal"),3,1)+" [s]; speed "+nf(table.getFloat(i, "speed"), 1, 1)+" [mph]", 20, height-20);
  for (int k =0; k < crows; k++ ) {
    TableRow row = table.getRow(k);
    float lat = row.getFloat("lat");
    float lon = row.getFloat("lon");
    float posx = map((lon-offx)*zoom, -180, 180, 0, width); 
    float posy = map((lat-offy)*zoom, 90, -90, 0, height); 
    if (debug) println("posx "+posx+" posy "+posy);
    if ( k < i ) { 
      stroke(200, 0, 200);
      strokeWeight(6);
    } else {   
      stroke(0, 0, 200);
      strokeWeight(3);
    }
    point(posx, posy);
  }
  mytimer();
}

void mytimer() {
  if ( millis() >= startT + stopT ) {
    startT += stopT;                         // what you need to be done now
    i++;
    if ( i >= crows ) i = 0;
    stopT = (long)table.getFloat(i, "time");
  }
}


#10

Thank you so very much! I just got this message and will begin to look at it to see if I can understand your code. I’m very new to processing and not very good at programming so it may take me a while.

If you are interested, this data is from our race car when racing at a famous track in the United States - Road America in Eklhart Lake. https://en.wikipedia.org/wiki/Road_America

Our race car has real-time telemetry including GPS tracking so we can gather data. This data is from a test day where we were practicing. Here is a link to some video from the car.

The reason I am doing this is because I am designing a “display” for inside the car that will count down the meters (in 50 meter increments) to each corner. So as you approach a corner at a high speed, the display will list 9,8,7,6,5,4,3,2,1 until the car is in the middle (apex) of the corner.

My hope is that this display will allow us to improve our race times by allowing the driver to see what number they are beginning to brake. In racing the objective is to carry as much speed into the corner as you can - often trying to brake as late as possible. However if you brake too late you will run off the race course so most inexperienced drivers have to brake a bit early and give up speed. I hope this display allows us to better know when to brake.

The actual program will be implemented in our racecar’s telemetry unit using LUA scripting - but the calculations to determine how far you are from the corner were not simple for me. In addition, because of the way the race course is designed, there are some spots on the course where you are actually closer to a different corner than the one you are driving towards. So i need some logic to determine how to display the proper number. This is very difficult to test in the real race car so I decided to try to write a Processing simulation and determine how it works. If I can get it working in Processing I will then attempt to port it over into the code for our racecar.

Thank you again so very much! You have been very kind. Now I will see if I can understand what you’ve done!.

Dave


#11

sorry, that part with the predefined points ( corners ) i did not know / understand,
so i skipped that in my version.

but if we can verify my integration ( for position meter )
and get it corrected?calibrated
you not need much calculation,
just from the list you can show
meter to corner = meter(corner) - meter(actual position)
? if that helps ?
but the incremental calculations ( meter time / meter-total time-total )
easy can accumulate errors.
but you know, when you see how far i am OFF with the total [m] and [s].

UPDATE___
i activated your
closest corner code
and think need a other way to do it,
pls test:

// https://discourse.processing.org/t/how-to-slow-down-drawing-points/8234
// changed orientation and factor to mph   ( track: 6km, in 200sec ? )  Kohler Grand Prix / Wisconsin
// v03 put in the corner again but not happy with the result
// corners near but not next on track are detected.


Table table;
int crows;
long startT, stopT=100;
int i=0;                                     // timed global for current row
boolean debug = false; //false;
float speedf = 2.237; //3.6; //1 ;                           // if the speed is in m/s ? so time is in sec. if km/h 1/1000/3600 if m/h /0.621371
float offx=43.799, offy=-87.989, zoom=10000;


//copy in the corner thing again
int num_corners = 15;                                 // A varible to set the number of corners 
int closest_corner = 0; 
int last_corner_passed = 0;
float closest_distance = 10000;
float a;
float c;
float[] corner_lat = new float[num_corners];          //Create an array for the coordinates for each corner's latitude
float[] corner_lon = new float[num_corners];
float[] dist = new float[num_corners];

float R = 6371e3;   // meter             GPS to m distance


void corner_setup() {
  corner_lat[1] =  43.792018;  //Assign latitude and lognitude for each corner   // kll delete radians
  corner_lon[1] = -87.989872; 
  corner_lat[3] =  43.791582;
  corner_lon[3] = -87.995253;
  corner_lat[5] =  43.801770;
  corner_lon[5] = -87.992557;
  corner_lat[6] =  43.801646;
  corner_lon[6] = -87.996056;
  corner_lat[7] =  43.799580;
  corner_lon[7] = -87.996246;
  corner_lat[8] =  43.797146;
  corner_lon[8] = -87.999966;
  corner_lat[11] =  43.797010;
  corner_lon[11] = -88.002596;
  corner_lat[12] =  43.804927;
  corner_lon[12] = -87.997457;
  corner_lat[14] =  43.803919;
  corner_lon[14] = -87.990119;
}

void table_setup() {
  size(720, 360);
  table = loadTable("test.csv", "header");          // changed back from tsv to CSV ( by excel )
  crows = table.getRowCount();
  table.addColumn("way", Table.FLOAT);
  table.addColumn("time", Table.FLOAT);

  println( crows + " total rows in table");
  for (int k = 0; k < crows; k++) {
    TableRow row = table.getRow(k);
    if ( k < crows-1) {
      row.setFloat("way", way(k));
      row.setFloat("time", speedf*row.getFloat("way")/row.getFloat("speed"));
    }
    if (debug) println("# "+k+" lat "+row.getFloat("lat") + 
      " lon "+row.getFloat("lon") + 
      " speed "+row.getFloat("speed") +
      " way "+row.getFloat("way") +
      " time "+row.getFloat("time")      );
  }
  table.addColumn("waytotal", Table.FLOAT);
  table.addColumn("timetotal", Table.FLOAT);

  //                                                 get the avg position:
  offx=0;
  offy=0;
  float wayt =0, timet=0;
  for (int k = 0; k < crows; k++) {
    TableRow row = table.getRow(k);               // This is where we compute the average
    offx +=   row.getFloat("lon");
    offy +=   row.getFloat("lat");
    wayt +=   row.getFloat("way");
    row.setFloat("waytotal", wayt);
    timet +=   row.getFloat("time");
    row.setFloat("timetotal", timet);
  }
  offx /= (float)crows; 
  offy /= (float)crows;
  println(" averagex lon "+offx+" averagey lat "+offy);
}

float way(int i) {      //https://www.movable-type.co.uk/scripts/latlong.html
  float delta = 0;
  float lat1 = radians(table.getFloat(i, "lat"));
  float lat2 = radians(table.getFloat(i+1, "lat"));
  float lon1 = radians(table.getFloat(i, "lon"));
  float lon2 = radians(table.getFloat(i+1, "lon"));
  float x = (lon2-lon1)*cos((lat1+lat2)/2);
  float y = lat2 - lat1;
  delta = sqrt(sq(x) + sq(y))*R;
  return delta;
}


void table_draw() {
  for (int k =0; k < crows; k++ ) {
    TableRow row = table.getRow(k);
    float lat = row.getFloat("lat");
    float lon = row.getFloat("lon");
    float posx = map((lon-offx)*zoom, -180, 180, 0, width); 
    float posy = map((lat-offy)*zoom, 90, -90, 0, height); 
    if (debug) println("posx "+posx+" posy "+posy);
    if ( k < i ) { 
      stroke(200, 0, 200);
      strokeWeight(6);
    } else {   
      stroke(0, 0, 200);
      strokeWeight(3);
    }
    point(posx, posy);
  }
}

void corner_draw() {
  for ( int ic =0; ic < num_corners; ic++) {
    if ( corner_lat[ic] > 0 || corner_lon[ic] > 0 ) {     // mask the not used corners
      noFill();
      if ( ic == closest_corner ) stroke(200, 0, 0);
      else                       stroke(200, 200, 200);
      strokeWeight(1);
      float posx = map((corner_lon[ic]-offx)*zoom, -180, 180, 0, width); 
      float posy = map((corner_lat[ic]-offy)*zoom, 90, -90, 0, height); 
      circle(posx, posy, 10);
      text(ic, posx+10, posy+5);
      if (debug) println("corner ic "+ic+" x "+posx+" y "+posy);
    }
  }
}

void setup() {
  size(720, 360);
  table_setup();
  corner_setup();
}

void draw() {
  background(0, 200, 0);
  fill(0);
  text(" pos:"+nf(table.getFloat(i, "waytotal"), 4, 1)+
    " [m]; time:"+nf(table.getFloat(i, "timetotal"), 3, 1)+
    " [s]; speed "+nf(table.getFloat(i, "speed"), 1, 1)+" [mph]"+
    " closest_corner "+closest_corner, 20, height-20);
  table_draw();
  corner_distance();
  corner_draw();
  mytimer();
}

void mytimer() {
  if ( millis() >= startT + stopT ) {
    startT += stopT;                         // what you need to be done now
    i++;
    if ( i >= crows ) i = 0;
    stopT = (long)table.getFloat(i, "time");
  }
}



void corner_distance() {
  float lat2 = radians(table.getFloat(i, "lat"));             // current pos given by i
  float lon2 = radians(table.getFloat(i, "lon"));
  closest_corner = 0;
  closest_distance = 10000;
  for (int j = 0; j < num_corners; j++) {
    float p1= radians(corner_lat[j]);
    float p2 = lat2;
    float delta_p = (lat2-radians(corner_lat[j]));
    float delta_lam = (lon2-radians(corner_lon[j]));
    a = sin(delta_p/2)*sin(delta_p/2)+cos(p1) * cos(p2)*sin(delta_lam/2)*sin(delta_lam/2);
    c = 2 * atan2(sqrt(a), sqrt(1-a));
    dist[j] = R*c;
    if (dist[j] < closest_distance) {
      closest_corner = j;
      closest_distance = dist[j];
    }
    if (dist[j] < 20) {
      last_corner_passed = j;
    }
    if (dist[j] > 10000) {
      dist[j] = 0;
    }
  }
}


my idea is to delete your corner configuration and
make a new column in the excel file
and give there the corner number at the wanted record.
( for this i show the record on screen for easy edit file )

// https://discourse.processing.org/t/how-to-slow-down-drawing-points/8234
// changed orientation and factor to mph   ( track: 6km, in 200sec ? )  Kohler Grand Prix / Wisconsin
// v03 put in the corner again but not happy with the result
// corners near but not next on track are detected.
// v04 make new column in excel file with the corner numbers at the wanted record.

Table table;
int crows;
long testT=1, startT, stopT=100;
int i=0;                                     // timed global for current row
boolean debug = false; //false;
float speedf = 2.237; //3.6; //1 ;                           // if the speed is in m/s ? so time is in sec. if km/h 1/1000/3600 if m/h /0.621371
float offx=43.799, offy=-87.989, zoom=10000;


int closest_corner_number = 0, closest_corner = 0; 
float closest_distance = 10000;

float R = 6371e3;   // meter             GPS to m distance


void table_setup() {
  size(720, 360);
  table = loadTable("test.csv", "header");          // changed back from tsv to CSV ( by excel )
  crows = table.getRowCount();
  table.addColumn("way", Table.FLOAT);
  table.addColumn("time", Table.FLOAT);

  println( crows + " total rows in table");
  for (int k = 0; k < crows; k++) {
    TableRow row = table.getRow(k);
    if ( k < crows-1) {
      row.setFloat("way", way(k));
      row.setFloat("time", speedf*row.getFloat("way")/row.getFloat("speed"));
    }
    if (debug) println("# "+k+" lat "+row.getFloat("lat") + 
      " lon "+row.getFloat("lon") + 
      " speed "+row.getFloat("speed") +
      " way "+row.getFloat("way") +
      " time "+row.getFloat("time")      );
  }
  table.addColumn("waytotal", Table.FLOAT);
  table.addColumn("timetotal", Table.FLOAT);

  //                                                 get the avg position:
  offx=0;
  offy=0;
  float wayt =0, timet=0;
  for (int k = 0; k < crows; k++) {
    TableRow row = table.getRow(k);               // This is where we compute the average
    offx +=   row.getFloat("lon");
    offy +=   row.getFloat("lat");
    wayt +=   row.getFloat("way");
    row.setFloat("waytotal", wayt);
    timet +=   row.getFloat("time");
    row.setFloat("timetotal", timet);
  }
  offx /= (float)crows; 
  offy /= (float)crows;
  println(" averagex lon "+offx+" averagey lat "+offy);
}

float way(int i) {      //https://www.movable-type.co.uk/scripts/latlong.html
  float delta = 0;
  float lat1 = radians(table.getFloat(i, "lat"));
  float lat2 = radians(table.getFloat(i+1, "lat"));
  float lon1 = radians(table.getFloat(i, "lon"));
  float lon2 = radians(table.getFloat(i+1, "lon"));
  float x = (lon2-lon1)*cos((lat1+lat2)/2);
  float y = lat2 - lat1;
  delta = sqrt(sq(x) + sq(y))*R;
  return delta;
}

void table_draw() {
  for (int k =0; k < crows; k++ ) {
    TableRow row = table.getRow(k);
    float lat = row.getFloat("lat");
    float lon = row.getFloat("lon");
    int   corner = row.getInt("corner");
    float posx = map((lon-offx)*zoom, -180, 180, 0, width); 
    float posy = map((lat-offy)*zoom, 90, -90, 0, height); 
    if (debug) println("posx "+posx+" posy "+posy);
    if ( k < i ) { 
      stroke(200, 0, 200);
      strokeWeight(6);
    } else {   
      stroke(0, 0, 200);
      strokeWeight(3);
    }
    point(posx, posy);
    if ( corner > 0 ) {               // draw corners
      stroke(200, 200, 200);
      strokeWeight(1);
      noFill();
      circle(posx, posy, 10);
      fill(200, 200, 200);
      textSize(15);
      text(corner, posx+10, posy+5);
    }
  }
}

void setup() {
  size(720, 360);
  table_setup();
  noFill();
}

void draw() {
  background(0, 200, 0);
  text_draw();
  table_draw();
  corner_distance();
  mytimer();
}

void mytimer() {
  if ( millis() >= startT + stopT ) {
    startT += stopT;                         // what you need to be done now
    i++;
    if ( i >= crows ) i = 0;
    stopT = (long)(table.getFloat(i, "time")*testT);    // slow down for test
  }
}

void corner_distance() {
  for (int j = i+1; j < crows; j++ )                     // walk up array in front to next corner
    if ( table.getInt(j, "corner") > 0 )  {
      closest_corner = j;       // remember its position j
      j = crows;                // break for loop
    }

  closest_distance = table.getFloat(closest_corner, "waytotal") - table.getFloat(i, "waytotal");
  closest_corner_number = table.getInt(closest_corner, "corner");
}

void text_draw() {
  fill(0);
  textSize(15);
  text(" rec "+i+
    " pos:"+nf(table.getFloat(i, "waytotal"), 4, 1)+
    " [m];\n time:"+nf(table.getFloat(i, "timetotal"), 3, 1)+
    " [s]; speed "+nf(table.getFloat(i, "speed"), 1, 1)+" [mph]\n"+
    " nextcorner "+closest_corner_number +
    "; dist " + (int)closest_distance+" [m] "
    , 20, height-55);
  textSize(35);
  fill(200,200,200);
  noStroke();
  rect(width-90,10,50,50);
  fill(200,0,0);
  int warning = (int)(closest_distance/50.0);
  if ( warning < 10 )  text(warning,width-80,50);  
}

void keyPressed() {
  if ( key == 's' ) {
    if ( testT == 1 ) testT = 1000;
    else testT = 1;
    println("testT "+testT);
  }
}


verify google maps


#12

latest code and csv as ZIP here


#13

Thank you so very much! I did not see this response until just now. I will take a look shortly. In the meantime, here is the code I came up with. This should work with the old CSV file.

I still do not understand how the Draw command works in Processing. In particular I don’t understand how the track is originally drawn with the small stroke blue dots all at once - but then the pink dots are drawn slowly!

I am incredibly impressed at how elegant and clear your code was! I have a lot to learn clearly.

Now that i have processing running the algorithm, I hope to make a few refinements and then begin to port this over to the race care telemetry system.

If you are curious, here is the telemetry system we use in our race car:

https://www.autosportlabs.com/product/racecapturepro-mk3/

Here is the display where I will be trying to display the distance to the track markers:

https://www.autosportlabs.com/product/shiftx3-rgb-sequential-shift-light-gear-indicator/

(it was designed with a 7-segment display intended to show what gear you are in but I hope to modify it to show the distance from each corner).

The system is programmed in LUA scripting (which I also don’t know well - I am not very good at code!)

The Racecapture system has much less memory and is slower. I don’t know if this will work but it has been fun learning about it.

Thank you so very much for your help!!!

Dave

Table table;

int crows;

long startT, stopT=100;

int num_corners = 8;   // A varible to set the number of corners 

int closest_corner = 0;   // Tracking variable determines what the closest corner is 

int last_corner_passed = 0;   // tracking varible - used to determine what the last corner we passed. 

int target_corner = 0;   // a variable showing the target we are aiming at 

/* a variable that tracks the distance (in meters) that determines if you are at a target. Since the car won't pass eaxctly through the Apex we need to look for the 
  car passing within a certain tolerance to determine if we are at a corner. This will be used to increment the target to the next corner on the race track. 
  On kent's lap he missed the apex in corner 14 by a fair amount (traffic?) so i have this set at 15 meters. In practice we'll probably want it closer
*/
int at_corner_tolerance = 15;  

int begin_marker_display = 225; // the distance from a corner in meters of when to begin displaying the markers. This might want to become a stored array allowing for differnet distances for each corner 

float closest_distance = 10000;  //Initialize a variable that will be used to determin which corner is the closest.  

float[] corner_lat = new float[num_corners]; //Create an array for the coordinates for each corner's latitude

float[] corner_lon = new float[num_corners];  //Array for coordinates of corner's longitude

int replay_speed = 10; // variable that controls replay redraw speed 

int[] corner_num = new int[num_corners];  //array storing the actual track corner number.  

int i=0;                                     // timed global for current row

boolean debug = true; //false;

float speedf = 36; //1 ;                           // if the speed is in m/s ? so time is in sec. if km/h 1/1000/3600

void setup() {

//the following section contains the latitude and lontitude of the apex of each corner - along with the acutal official Road America corner number 
  
corner_lat[0] = (43.792018);  //Corner 1
corner_lon[0] = (-87.989872); // 
corner_num[0] = 1;

corner_lat[1] = (43.791582);  //Corner 3
corner_lon[1] = (-87.995253); //
corner_num[1] = 3;

corner_lat[2] = ( 43.801770); //Corner 5
corner_lon[2] = (-87.992557); //
corner_num[2] = 5;

corner_lat[3] = (43.801646);  //Corner 6
corner_lon[3] = (-87.996056); //
corner_num[3] = 6;

//Skip corner 7 - drive full-out though this corner and don't need braking?  

//corner_lat[4] = ( 43.799580); //Corner 7
//corner_lon[4] = (-87.996246); //
//corner_num[4] = 7;

 corner_lat[4] = ( 43.797146); //Corner 8
 corner_lon[4] = (-87.999966); //
 corner_num[4] = 8;

corner_lat[5] = (43.797010); //Corner 11
corner_lon[5] = (-88.002596); //
corner_num[5] = 11;

corner_lat[6] = (43.804927); //Corner 12
corner_lon[6] = (-87.997457);  //
corner_num[6] = 12;

corner_lat[7] = ( 43.803919); //Corner 14
corner_lon[7] = (-87.990119); //
corner_num[7] = 14;
    
size(1000, 700);

table = loadTable("test.csv", "header");          // load all GPS data

crows = table.getRowCount(); // Set crows to the number of rows in the table 

// Add the following calculated columns to the table

table.addColumn("step", Table.FLOAT);  //the distance in meters to the next GPS point. 

table.addColumn("time", Table.FLOAT); //the calculated time between this point and the next point 

table.addColumn("dist to closest corner", Table.FLOAT);  //distance to closest corner from that GPS point. May or may not be the target corner since some points are closer to other corners

table.addColumn("closest corner num", Table.INT); //the numerical number of the closest corner 

table.addColumn("target corner", Table.INT); //a placeholder column for the next corner we are aiming at 
  
table.addColumn("track marker", Table.INT); //the numerical display that will be shown on the dash if we are within 300 yards of the target corner 
  
table.addColumn("dist to target corner", Table.FLOAT); //the distance to the corner we are driving tol 

if (debug){println( crows + " total rows in table");}

/*
/Main loop for reading in the GPS points from the table and adding calculated rows 
*/

for (int k = 0; k < crows; k++) { 
  
  TableRow row = table.getRow(k); // Get a row
  
  if ( k < crows-1) { // can't read the last row since we are comparing each point to the next to determine the "step"  

      row.setFloat("step",step(k));  //pass the current row to the routine "step" that will calculate the distance to the next point.  

      row.setFloat("time",speedf*row.getFloat("step")/row.getFloat("speed")); //divide distance by current speed to determine the time between points. 
      
      row.setInt("closest corner num", check_closest(k)); //pass the current row number to the routine check_closest which will determine what is the closest corner number.        
      
      //now that we've determined which corner is the closest - pass this corner number to the routine dist_calc and record the lat and lon results in the table so each GPS row also contains the distance to the closest corner
      row.setFloat("dist to closest corner",dist_calc(corner_lat[table.getInt(k,"closest corner num") ], corner_lon[table.getInt(k,"closest corner num")],+row.getFloat("lat"),+row.getFloat("lon"))); 
      
      
      /* this section of code determines if we are close enough to corner to assume we are passing it. Because the car doesn't pass exactly through the apex of the corner we need a tolerance 
      // if we are within that tolerance we can assume we are at a coner and reset the target to the next corner on the track. If you pass the last corner you need to set the target nack to the first corner (corner 0)
      */
      if (table.getFloat(k,"dist to closest corner")<at_corner_tolerance){  //are we close the the taget corner? 
        
        target_corner = table.getInt(k,"closest corner num")+1; // update the target corner to the next corner
        
        if (target_corner == num_corners){  // if we are at the last corner - reset your target to the first corner (corner 0)
          
          target_corner =0;
        }
      }
      
      row.setInt("target corner", target_corner);  // store the new target corner in the table 
      
      /*pass the current GPS position and new target cornner to the distance routine and store to new distance to the next corner in the table. So each row in the table will include the 
      / next corner we are driving to and the disance to that corner. 
      */
      row.setFloat("dist to target corner",dist_calc(corner_lat[table.getInt(k,"target corner") ], corner_lon[table.getInt(k,"target corner")],+row.getFloat("lat"),+row.getFloat("lon"))); 
      
      if (table.getFloat(k,"dist to target corner") < begin_marker_display){ // are we closer than the target distance?
      
        row.setInt("track marker", round(table.getFloat(k,"dist to target corner")/(begin_marker_display / 9)));  // if so, set the track marker - there will be 9 digits displayed over the marker display distance 
     
      }
    }

    if (debug) println("# "+k+" lat "+row.getFloat("lat") + 

                       " lon "+row.getFloat("lon") + 

                       " speed "+row.getFloat("speed") +

                       " step "+row.getFloat("step") +

                       " time "+row.getFloat("time") +
                       
                       " distance to closest corner num   "+row.getFloat("dist to closest corner") +
                       
                       "target corner "+row.getInt("target corner")
                       
                       );
     }
   }

/*
    Routine check_closest is used to determine which corner is the closest to our current location. 
    Sometimes this will be the corner we are currently driving to (target corner)
    Sometimes this will be the corner we just passed and are leaving
    Sometimes because of the layout of the track, this will be a differnt corner that may not be in our direct dirving line 
    (this happens near corner 14 of road america where you actually pass close to corner 5 when driving from corner 13 to 14.  
*/

int check_closest(int i) {
  
  int closest = 0; //initialize the varible that will hold the closest corner number
  
  float delta_min = 10000; //initialize the variable that will track the minimum distance 
  
  float lat1 = (table.getFloat(i,"lat"));  // get the lat an lon for the current location from the table.
  
  float lon1 = (table.getFloat(i,"lon"));
  
  for (int k = 0; k < num_corners; k++) { // now check the distance between this location and all the other corners 
      
      float lat2 = corner_lat[k]; // get lat and lon for corner k
      
      float lon2 = corner_lon[k];
      
      float delta = dist_calc(lat1, lon1, lat2, lon2); //pass the current GPS location and each corners lat and lon to the routine delta that calculates the distance between the two 
      
      if (delta < delta_min){ // check to see if the distance to each corner is the smallest distance so far. 
        
        delta_min=delta; //if so - reset the minimum tracker and record the closest number
        
        closest = k;
      }
    }
    
    return closest; // return an int with the number of the corner that is closest to this GPS point 

}


/* 
/  Routine step is passed the current row number and passes this point and the next one to another routine to calculate the distance to the next GPS point 
/  This is only used by the processing routine to determine the time between points for more accurate redraw - and will not be needed in the RaceCapture LUA script
*/
float step(int i) { 

    float delta = 0; //local variable to hold the step distance

    float lat1 = (table.getFloat(i,"lat"));

    float lat2 = (table.getFloat(i+1,"lat"));

    float lon1 = (table.getFloat(i,"lon"));

    float lon2 = (table.getFloat(i+1,"lon"));

    delta = dist_calc(lat1, lon1, lat2, lon2); //pass lat and lon to the dist_calc routine to determine disnace.  

    return delta;

}


/* 
/  Routine dist calc is the main computational routine to use some math to calculate the distance between two GPS points
/  https://www.movable-type.co.uk/scripts/latlong.html
  
  More accurate but slower - we are not using this method: 
     var φ1 = lat1.toRadians(), φ2 = lat2.toRadians(), Δλ = (lon2-lon1).toRadians(), R = 6371e3; // gives d in metres
     var d = Math.acos( Math.sin(φ1)*Math.sin(φ2) + Math.cos(φ1)*Math.cos(φ2) * Math.cos(Δλ) ) * R;

  A bit less acurate but much faster - use this method: 
    var x = (λ2-λ1) * Math.cos((φ1+φ2)/2);
    var y = (φ2-φ1);
    var d = Math.sqrt(x*x + y*y) * R;
    
*/

float dist_calc(float lat1, float lon1, float lat2, float lon2){   // pull in lat and lon for the two points
  
  float dist = 0; // initialize the distance 
  
  float R = 6371e3;   // Approximate earth's radius in meters  - used in calcualtions 
  
  lat1 = radians(lat1); //convert decimal degree GPS readings to radians.  Calcs assume radians

  lat2 = radians(lat2); 

  lon1 = radians(lon1);

  lon2 = radians(lon2); 
  
  float x = (lon2-lon1)*cos((lat1+lat2)/2); //do some math - per equations above
  
  float y = lat2 - lat1 ; //do some math per equations above

  dist = sqrt(sq(x) + sq(y))*R; //do some math per equations above

  return dist;  //return the distance between the two distance points to the main routine 
  
}

/*
/    Draw routine for processing 
*/

void draw() {

  background(0, 200, 0);

  fill(0);

  textSize(16);

  text("closest corner = " +nf(corner_num[ (table.getInt(i,"closest corner num"))])   , width -400, height-150);  
  
  text("next corner = " +nf(corner_num[ (table.getInt(i,"target corner"))])   , width -400, height-120);  
  
  text("distance to closest corner  " + nf(table.getFloat(i,"dist to closest corner"),1,1) + " meters", width - 400, height-90);
  
  text("distance to next corner  " + nf(table.getFloat(i,"dist to target corner"),1,1 ) + "meters", width - 400, height-60);
  
  text("speed "+nf(table.getFloat(i,"speed"),1,1)+" mph",width - 400, height-30);
  
  text("GPS Track Marker",width - 750, height -300); 
  
  textSize(64);
  
  text(nf(table.getInt(i,"track marker")), width - 700, height-250);

  translate(width/2, height/2+200);

  float offx=43.799, offy=-87.989, zoom=50000;
  
  for (int k=0; k < num_corners; k++){
    
    strokeWeight(8);
    
    point((corner_lat[k]-offx)*zoom, (corner_lon[k]-offy)*zoom*.75);
  
  }
  
  for (int k =0; k < crows; k++ ) {
    
    TableRow row = table.getRow(k);

    float lat = row.getFloat("lat");

    float lon = row.getFloat("lon");

    float posx = (lat-offx)*zoom;

    float posy = (lon-offy)*zoom;

    if ( k < i ) { 

     stroke(200, 0, 200);

     strokeWeight(3);
     
     if (row.getFloat("dist to target corner") <begin_marker_display){
       
       stroke(45, 312, 64);
       
     }
   
     } else {
       
       stroke(0, 0, 200);
       
       strokeWeight(1);
       
     }

    point(posx, posy*.75);

  }
  
  mytimer();

}


void mytimer() {

  if ( millis() >= startT + stopT ) {

    startT += stopT;                         

    i++;

    if ( i >= crows ) i = 0;

    stopT = (long)table.getFloat(i,"time")*replay_speed;    // Adjust the timer based on the actual time for each point so the simulations slows down and speed up appropriately 

  }

}

#14

that is a illusion by my animation,

  • the dots are defined by the csv file table ( centered and zoomed )
  • the draw starts with a background ( what clears the screen every time draw is executed
  • and that is default 60 FPS times per second
    but as i work on a 40euro computer with 1GB RAM for CPU and GPU
    it is much slower here.
  • in one FOR loop all records are drawn as a point
    only color and point size ( strokeWeight ) are depending on the
    animation of the CAR actual position.
  • that position progresses depending on a timer
    ( whats delay setpoint is set differently for each point ) depending on the
    from GPS calculated way, and delta time ( by your speed column )( possibly mph )
  • but all that calculation ( and 2 more total columns way and time )
    are done directly after loading the data in setup(), and not while draw().

note, but there are other concepts, like

  • not use background in draw,
  • load background in setup
  • make the track one time

and in draw
any add a position point ( over the track points )

in both cases you might not be able to see the difference
between the 2 “modes”


now i hope you understand my latest concept
with the corner column
and not using your distance calc
but using my GPS (track ) way calc.
besides it eliminates your "closest v.s. next " corner problem,
it makes also the tool very variable,

  • it should work on a new track just by give a other csv file.
  • see the rec number in replay ( slow speed [s]) and edit the csv by add column “corner”
    any corner number at that records.

save the csv and restart the sketch.


#15

I do now understand how you drew the track. Very impressive. The draw command works differntly than what I was expecting. I need more experience.

I just ran your code - amazing. You did exactly what I did - in roughly 1/3rd the number of code lines. Amazing. I have not yet studied it.

I don’t yet understand your latest concept but will study it until I do. I will be very interested to see how you solved the “closest versus next” corner problem.

Will it work on a real track? on a real track we will not have the CSV file of the entire track - but will only have a “stream” of real time GPS location data and the location of the corners.

No need to answer it. I will study it until I understand it. I need to go spend some time with my wife or she’s going to get angry with me. thanks again for your help.

Dave

(ps… where do you live? )


#16

in that case forget my code / concept.

( but i would assume that you drive each track minimum one time before a race ?? )


#17

Usually,. But not always. Perhaps we can make that a condition.


#18

I need to study your code in greater detail. I think I’m beginning to understand how you did it but I don’t fully understand. A few observations/questions:

In practice, remember that we don’t have a table file of GPS locations for driving around the track. This is a log file from a practice lap but in a race we would only be streaming real-time GPS locations as the car is driving. Because the drivers will follow different courses and not always drive over the same location, each lap will have a unique set of GPS positions.

We also have to contend with the possibility of losing the GPS signal during a race. Our GPS if fairly reliable - but not perfect. We will often drop out of GPS coverage for a few seconds.

If I understand your method, it would involve keeping track of how far you had driven (perhaps I am mistaken). However if the car lost GPS we would not be able to pick back up again until we passed the start-finish line, correct?

I am very eager to better understand your code as memory is a huge problem on the LUA script and I fear we may not have enough memory (we have previously run out of memory with a much simpler script - but the developer of the application says he has fixed some issues that have opened up more memory. We shall see if we have enough).

Your code seems much more efficient than mine. But I have to be able to account for differences between the actual lap being driven as well as the possibility of losing GPS during a lap. Does your code accommodate this?

Cheers,

Dave

fyi… here is the car:

Google Photos


#19

-a- you started here with a data set ( a csv file )
but now you say in real there will be/might be no data set.
-b- for the calculation i loaded the data set / csv file in a table
( memory ) and created more ( calculated ) columns what eats more memory
and now you say your hardware runs out on memory just by a simple sketch?
-c- if the driver follow a other track as the loaded one,

  • the calculated “track way” data
  • and also your corner list
    are useless, our both code will give wrong info
    ( like your "nearest corner " just makes a line from car position to any corner
    even there is no street under that line )

but there is no first data acquisition lap needed:
like you can read the corners from the google map,
the whole track can be read from there, question is only
if there is a semi automated way? download kmz?
and if there are only a few points for the track ( and not 6 points per meter? 6km/1000rows)
the memory and calculation requirement drops very much.
what that means for the accuracy i better not speculate.


actually that would be possible, from a last valid GPS pos. ( on track )
can calculate actual position just by track data and car speed.
actually that is what we are doing here, so sorry, the track data are essential.


so what super small LUA system/board and GPS adapter you are using?


can you load data files to it ?usb?wifi? anyhow?
is it much smaller as a
https://www.raspberrypi.org/products/raspberry-pi-zero-w/
or i am using

+++ that is the computer i am working on

  • for processing sketch test
  • or this forum work


#20

This is basically correct.

The driver will basically follow the same track - but not exactly. I would guess we would see deviations as much as 15 meters - depending on traffic.

The LUA system is this: https://www.autosportlabs.com/product/racecapturepro-mk3/

I am not certain if there is a way to download and access an entire track map. Perhaps there is. I will look.