Using a switch statement with a for loop change data retrieved

旧街凉风 提交于 2020-06-17 15:49:55

问题


I am using processing with a file that has data in different columns. I am wanting to change the data being pulled for each column with each switch. I have a value toggle set as my variable so the value changes when clicked. 0-2. Currently I am able to run toggle, but the values do not change. Below is the current switch statement I have. For full code and associated files see Link

     PImage mapImage;
Table locationTable;
int rowCount;
Table dataTable;
float dataMin = MAX_FLOAT;
float dataMax = MIN_FLOAT;
int toggle = 0;
int x1;
int y1;
int x2;
int y2;

void setup() {
  size(750, 600);
  smooth();
  noStroke();

  mapImage = loadImage("map.png");
  x1 = 40;
  y1 = 40;

  x2 = width - 200;
  y2 = height - 80;
  locationTable = new Table("locations.tsv");
  rowCount = locationTable.getRowCount( );
  dataTable = new Table("povertynoheader.tsv");
  switch ( toggle ) {
    case 0:
    for (int row = 0; row < rowCount; row++) {
      float value = dataTable.getFloat(row, 1);
      if (value > dataMax) {
        dataMax = value;
    }
      if (value < dataMin) {
        dataMin = value;
      }
      break;
    }
        case 1:
    for (int row = 1; row < rowCount; row++) {
      float value = dataTable.getFloat(row, 0);
      if (value > dataMax) {
        dataMax = value;
    }
      if (value < dataMin) {
        dataMin = value;
      }

    }
            case 2:
    for (int row = 1; row < rowCount; row++) {
      float value = dataTable.getFloat(row, 3);
      if (value > dataMax) {
        dataMax = value;
    }
      if (value < dataMin) {
        dataMin = value;
      }  
    }
  }
  stateData = GetStateData();
}

void draw() {
  background(255);
  image(mapImage, 0, 0);
  surface.setResizable(true);

  DrawStats();
  AddTitle();
  drawLegend();
  drawsize();
}
void AddTitle() {
  fill(0);
  textSize(20);
  textAlign(CENTER);
  if ( toggle == 0 ) {
  text("2017 Poverty Rates (%) by State", width/2, 30);
  } else if (toggle == 1) {
    text("People in poverty by Household income in thousands ", width/2, 15); 
  }  else if (toggle == 2) {
      text("2014 Poverty Rates (%) by State ", width/2, 30);
  }
}

void drawLegend(){
  fill(0);
  textSize(10);
  textAlign(CENTER);
  if ( toggle == 0 ) {
  text("2017 Poverty Rates (Lowest to Highest)", 630, 300);
   int colorWidth = 15;
   int step = 1;
    noFill();  // make sure the rectangle and the points are not filled in  
    rectMode(CORNERS);
    rect(x1,y1,x2,y2);

   // Have 5 ranges of color values 
   noStroke();
   for ( int i = 1 ; i < 10 ; i = i + 2) {

        int legendX1 = x2 + step * colorWidth;
        int legendX2 = x2 + step * colorWidth + colorWidth;
        int legendY1 = (y1 + y2) / 2 - colorWidth / 2;
        int legendY2 = (y1 + y2) / 2 + colorWidth / 2;

        step = step + 1;
        float percent = norm(i, 1,10);
        color integratedColor = lerpColor(#03fc03,#fc0303,percent);
        fill(integratedColor);        
        rect(legendX1,legendY1,legendX2,legendY2);
     }   
   }
}

void DrawStats() {
  // draw circles
  for (StateData s : stateData) {
    fill(s.fill);
    ellipseMode(RADIUS);
    ellipse(s.location.x, s.location.y, s.radius, s.radius);
  }
  // draw text above circles
  for (StateData s : stateData) {
    if (dist(s.location.x, s.location.y, mouseX, mouseY) < s.radius+2) {
      fill(0);
      textAlign(CENTER);
      textSize(10);
      text(s.povertyRate + " (" + s.name + ")", s.location.x, s.location.y-s.radius-4);
    }
  }
}

void drawsize() {


void mousePressed() {

  if (toggle == 0 ) {
     toggle = 1; 
  } else if (toggle == 1) {
     toggle = 2;
  } else {
    toggle = 0;
  }

}
void keyPressed() {
  if ( key == ' ') {
    if (toggle == 0 ) {
      toggle = 1;
  } else if (toggle == 1) {
     toggle = 2;
  } else {
    toggle = 0;
  }
  }
}

回答1:


I would like to start by saying that user @Dakshesh Garambha was 100% right. The missing break was probably the least of your problems, so it didn't show much on your work, but he answered your question successfully and deserve some thanks. You can see some examples of how to write a switch statement in my code. Take a good look.


Ok, I just opened your work, and there's a lot of refactoring to work on. But, courage! All of this is very feasible.

First, As I understand this, you want to toggle between several sets of data. No problemo. You just have to load these accordingly. You still have to store them, so you'll have to modify the StateData class. As I'm very lazy, I always try to code stuff the right way, so instead of adding all sorts of details to the StateData class, I suggest to add an overload to the GetStateData() method so we can have several sets of StateData and switch from one to the other on the fly.

Honestly, coding complicated stuff is a mistake most of the time, so let's avoir that.

GetStateData signature would now look like this:

ArrayList<StateData> GetStateData(int valueColumnNumber)

The number here is just the column in the povertynoheader file where to get the value you want to show. Thing is, we can't calculate the radius with poverty2017 for the number of households or stuff like that. The global variables dataMin and dataMax are becoming useless. Erase them. We'll calculate them for every data set separately.

If you follow me so far, your StateData.pde file should look like like this:

class StateData {
  public String name;
  public PVector location;
  public float value;
  public float radius;
  public color fill;

  StateData(String name, PVector location, float value, float dataMin, float dataMax) {
    this.name = name;
    this.location = location;
    this.value = value;
    this.radius = map(value, 0, dataMax, 1.5, 15);

    float colorOffset = 255 * ((value - dataMin) / (dataMax - dataMin));
    this.fill = color(colorOffset, 255-colorOffset, 0);
  }
}

// Notice how we'll just use more StateData instead of making StateData more complex:
ArrayList<StateData> poverty2017;
ArrayList<StateData> povertyHouseholds;
ArrayList<StateData> poverty2014;

ArrayList<StateData> GetStateData(int valueColumnNumber) {
  ArrayList<StateData> data = new ArrayList<StateData>();

  // Since we need to know dataMin and dataMax, we'll just calculate one pair of these per data set
  float dataMin = MAX_FLOAT;
  float dataMax = MIN_FLOAT;
  for (int row = 0; row < rowCount; row++) {
    float value = dataTable.getFloat(row, valueColumnNumber);
    if (value > dataMax) {
      dataMax = value;
    }
    if (value < dataMin) {
      dataMin = value;
    }
  }

  for (int row = 0; row < rowCount; row++) {
    String abbrev = dataTable.getRowName(row);
    float value = dataTable.getFloat(abbrev, valueColumnNumber);
    float x = locationTable.getFloat(abbrev, 1);
    float y = locationTable.getFloat(abbrev, 2);
    data.add(new StateData(abbrev, new PVector(x, y), value, dataMin, dataMax));
  }

  return data;
}

To fill those arrays, we'll call GetStateData in the setup() method. The idea here is to calculate all this stuff only once, before anything else run, so we don't bother with it later:

void setup() {
  size(750, 600);
  smooth();
  noStroke();

  mapImage = loadImage("map.png");
  x1 = 40;
  y1 = 40;
  x2 = width - 200;
  y2 = height - 80;
  locationTable = new Table("locations.tsv");
  rowCount = locationTable.getRowCount( );
  dataTable = new Table("povertynoheader.tsv");

  // notice that we're not calculating dataMin and dataMax here anymore, as we erased these globals

  poverty2017 = GetStateData(1); // column #1
  povertyHouseholds = GetStateData(2); // column #2
  poverty2014 = GetStateData(3); // column #3... I guess?
}

And, of course, you have to modify the DrawStats() method so it draws the right data. I suggest you create a local empty arraylist and fill it from the right data set using a `switch; statement:

void DrawStats() {
  ArrayList<StateData> stateData = null;

  switch (toggle) {
    case 0:
      stateData = poverty2017;
      break;
    case 1:
      stateData = povertyHouseholds;
      break;
    case 2:
      stateData = poverty2014;
      break;
  }

  // nothing changed here: we're just drawing from a different source
  for (StateData s : stateData) {
    fill(s.fill);
    ellipseMode(RADIUS);
    ellipse(s.location.x, s.location.y, s.radius, s.radius);
  }
  // draw text (here so it's over the circles)
  for (StateData s : stateData) {
    if (dist(s.location.x, s.location.y, mouseX, mouseY) < s.radius+2) {
      fill(0);
      textAlign(CENTER);
      textSize(10);
      text(s.value + " (" + s.name + ")", s.location.x, s.location.y-s.radius-4);
    }
  }
}

And here you go! Different data every time you click or press the spacebar!

Now, some nitpicking:

The AddTitle() triggers me. I suggest you rewrite it using a similar technique to the DrawStats() method:

void AddTitle() {
  fill(0);
  textSize(20);
  textAlign(CENTER);

  String title = "";
  switch (toggle) {
    case 0:
      title = "2017 Poverty Rates (%) by State";
      break;
    case 1:
      title = "People in poverty by Household income in thousands";
      break;
    case 2:
      title = "2014 Poverty Rates (%) by State";
      break;
  }
  text(title, width/2, 15);
}

What did I change? The important part is not the switch, it's that I'm using a variable title to avoid writing the text(title, width/2, 15); several times. This way, if you change the height of the title, you don't have to hunt down and change every place in the code where you use this height. There's only one place to change. I wont re-write the drawLegend() method, but you should keep what I just said in mind when you do; it'll be easier to fix, improve or change your code later. (If you want to know, this is called writing DRY code - for Dont Repeat Yourself. Every time you copy and paste a line, you have to hunt it down if you ever want to make a change to one of it's copies, so each is a potential future bug.)

Same goes for mouseClicked() - and not mousePressed(), they are not the same - and keyPressed: they are basically a copy and paste. Instead, create a method which will centralize the code in only one place:

void mouseClicked() {
  IncreaseToggle();
}

void keyPressed() {
  if ( key == ' ') { IncreaseToggle(); }
}

void IncreaseToggle() {
  toggle++;
  if (toggle>2) {toggle=0;}
}

Also: I've seen comma in some of your data in the povertynoheader file. It'll probably cause you problems, as it won't translate to float numbers easily. If you notice missing values, check first if they correspond to these lines.

Hope all this helped. Have fun!




回答2:


You must use break; at the end of each case.(if you want to run only that particular case). What's happening in your code is whatever the toggle value is; case 2 will always run and overwrite the values of case0/case1.



来源:https://stackoverflow.com/questions/62238572/using-a-switch-statement-with-a-for-loop-change-data-retrieved

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!