How to add values to array based on condition? (read two files)

I have data from two csv files that I import as Tables. The first file contains country names and their population. The second file contains migration flows between countries over 4 time periods. The files would look like this in table form:

File 1:

+----------+------------+
| country  | population |
+----------+------------+
| countryA | 100000     |
+----------+------------+
| countryB | 200000     |
+----------+------------+
| countryC | 300000     |
+----------+------------+
...

File 2:

+----------------+--------------+---------+---------+---------+---------+
| country_origin | country_dest | mig1990 | mig1995 | mig2000 | mig2005 |
+----------------+--------------+---------+---------+---------+---------+
| countryA       | countryB     | 100     | 100     | 100     | 100     |
+----------------+--------------+---------+---------+---------+---------+
| countryA       | countryC     | 200     | 200     | 200     | 200     |
+----------------+--------------+---------+---------+---------+---------+
| countryB       | countryA     | 300     | 300     | 300     | 300     |
+----------------+--------------+---------+---------+---------+---------+
| countryB       | countryC     | 400     | 400     | 400     | 400     |
+----------------+--------------+---------+---------+---------+---------+
...

I would like to visualize the countries as spheres (with size corresponding to population size) and connect them with lines (with line thickness corresponding to migration flow size). The spheres are located randomly in space. The spheres are colored with sequential colors using lerpColor() and initial values start = color(204, 102, 0); and end = color(0, 102, 153); The lines have a color gradient between the starting sphere and the end sphere.

I’ve created a class “Country”, which takes the following parameters:

  // Instance Variables
  int population;
  PVector coordinates;
  float posx, posy, posz;
  color sphereColor;
  int[] flowNumArray;
  String origin;
  String destination;
  int mig1990, mig1995, mig2000, mig2005;

  // Constructor Declaration of Class <-- this is a function, only call when we want a new item
  // To create a new instance
  Country(int population, float posx, float posy, float posz, color sphereColor, String origin, String destination, int mig1990, int mig1995, int mig2000, int mig2005) {
    this.population = population;
    this.coordinates = new PVector(posx, posy, posz);
    this.sphereColor = sphereColor;
    this.origin = origin;
    this.destination = destination;
    this.flowNumArray = new int[] {
    mig1990, mig1995, mig2000, mig2005
  };
  }
}

Now I’m struggling to add the values from both tables into my class so that each instance contains all the values that are associated with a country. The challenge is that the population table is much shorter than the migration table, so I can’t just loop over the values. When I call this in my for-loop for the population table:

countries.add(new Country(countryName, posx, posy, posz, sphereColor));

I get the error The constructor "Country(String, float, float, float, int)" does not exist (I understand why but how do I fix it?)

I suppose I need a (or two?) for loop that takes all the values from either table and adds them to a Country instance. Or I create the instance in one for-loop and then append it in a second for-loop based on the condition that the country==country_origin? I would appreciate if you could show me some skeleton code on how to do this. Thanks!

No. Better Make a 2nd class Migration and arraylist of it. Each contains reference to two cities and year etc

Do you mean that every migration class would should it just contain one country pair and the size of the migration flow? Or should it be one origin country and all of its destination countries?

Yes, this is what I mean. Every object in the Migration ArrayList has one country pair.
The size of the migration flow must be an array over time within an object I guess.

But there are several ways of doing this.

Remark

By the way: when you need to check whether a country is already registered you can look at StringList - but I don’t think you need this

so the idea is:

  • read File 1: store countries in ArrayList 1 (no duplicates in countries)
  • read File 2: store migration data in ArrayList 2 - (no new countries compared to File 1)

Chrisir

I see how I’d do this to create spheres and lines. But how about creating a gradient in the line that changes color from the starting sphere to the end sphere? Wouldn’t I need to have all the sphere data in the same class as the migration flow data so I could do something like

for (int i = 0; i < class.size(); i++){
beginShape();
stroke(class.get(i).get.color()); //starting color of gradient
line(class.get(i).coordinates.get(posx), class.get(i).coordinates.get(posy), class.get(i).coordinates.get(posz));
stroke(class.get(i-1).get.color());  //end color of gradient
endShape;
}
1 Like

to be honest, apart from the data model you use, I wouldn’t know how to make a line with a color gradient in the first place. Even if I had just line(100,200, 300, 500);

In my point of view: No.

Instead the migration object would have the indexes of the two cities and in the Migration class you
would say:

void display() {
    City city1 = cities.get(indexFromCity); 
    City city2 = cities.get(indexToCity); 
    
    strokeWeight(map(migNumber, 0, 400000, 1, 9) ); 
    
    line(city1.posx, city1.posy, 
         city2.posx, city2.posy);  
}
//
1 Like

any progress here…?

just make a test Sketch

I can help you

1 Like

Thank you so much. So, I did get it to work but I ended up pursuing my initial idea a bit further:

In my class, I created an ArrayList of destinations and an ArrayList of int-arrays.

  // Instance Variables
  int population;
  PVector coordinates;
  float posx, posy, posz;
  color sphereColor;
  ArrayList<ArrayList<Integer>> flowNumArray = new ArrayList<ArrayList<Integer>>();
  String origin;
  ArrayList<String> destination = new ArrayList<String>();
  int[] mig1990, mig1995, mig2000, mig2005;

  Country(int population, float posx, float posy, float posz, color sphereColor, String origin, String destination, int mig1990, int mig1995, int mig2000, int mig2005) {
    this.population = population;
    this.coordinates = new PVector(posx, posy, posz);
    this.sphereColor = sphereColor;
    this.origin = origin;
    this.destination = destination;
    this.flowNumArray = new int[] {
    mig1990, mig1995, mig2000, mig2005
  };
  }
}

Then when I instantiated the class, I simply called 2 nested for loops. One for origin etc. and another for-loop for the destinations and their associated values.

here is my version with two classes

// https://discourse.processing.org/t/how-to-add-values-to-array-based-on-condition-read-two-files/28340



ArrayList<City> cities = new ArrayList(); 
ArrayList<Migration> migrations = new ArrayList();

String[] countryData = 
  {
  "country,population", 
  "countryA,100000", 
  "countryB,200000", 
  "countryC,300000" 
};

String[] migData = 
  {
  "country_origin,country_dest,mig1990,mig1995,mig2000,mig2005", 
  "countryA,countryB,100,100,100,100", 
  "countryA,countryC,200,200,200,200", 
  "countryB,countryA,300,300,300,300", 
  "countryB,countryC,400,400,400,400"
};

void setup() {
  size(1660, 860);
  background(111); 

  // read data 
  for (String stringOneLineCity : countryData) {
    String[] sLineParts=split(stringOneLineCity, ",");

    if (!sLineParts[1].equals("population")) {
      City newCity = new City (sLineParts[0], sLineParts[1]);
      cities.add(newCity);
    }//if
  }//for

  // read data 
  for (String stringOneLineMigration : migData) {
    String[] sLineParts=split(stringOneLineMigration, ",");

    int from = getCity(sLineParts[0]);
    int to =   getCity(sLineParts[1]); 
    if ( ! (from == -1 || to == -1) ) {
      Migration newMigration = new Migration (from, to, 
        sLineParts[2], sLineParts[3], 
        sLineParts[4], sLineParts[5] );
      migrations.add(newMigration);
    }//if
  }//for
}//func

void draw() {
  background(111); 
  // display 1
  for (City c : cities) {
    c.display();
  }

  // display 2
  for (Migration m : migrations) {
    m.display();
  }
}//func

int getCity(String c_) {
  // returns the index of a city in the ArrayList cities
  // based on the name of the city 
  int i=0; 
  for (City c : cities) {
    if (c.name.equals(c_))
      return i;
    i++;
  }
  println("getCity failed with " 
    +c_);
  return -1;
}

// ==================================================================================

class City {

  float posx=random(100, width-100), 
    posy=random(100, height-100);
  String name="";
  int pop=0; 
  color col=color(random(255), random(255), random(255) ); 

  //constr 
  City(String name_, String pop_) {
    name=name_;
    pop=int(pop_);
  }//constr 

  void display() {
    //
    strokeWeight(1); 
    fill(col); 
    ellipse(posx, posy, 
      pop/1000, pop/1000);
  }//
  //
}//class

// ==================================================================================

class Migration {

  int indexFromCity; 
  int indexToCity;

  String[] migData; 

  //constr 
  Migration(int from, int to, 
    String... vals) {
    //
    indexFromCity=from; 
    indexToCity=to;
    migData = vals;
  }//constr 

  void display() {
    City city1 = cities.get(indexFromCity); 
    City city2 = cities.get(indexToCity); 

    stroke(0); 

    strokeWeight(map(int(migData[0]), 
      0, 400, 1, 15) ); 

    line(city1.posx, city1.posy, 
      city2.posx, city2.posy);

    //reset 
    strokeWeight(1);
  }
  //
}//class
//

1 Like

Very elegant, although I’m still working through it. I’m new to OOP and I have to wrap my head around how each class does more than just store data.

1 Like

I haven’t done the development of migration over time though

I didn’t have time to read your post, I was very busy

I don’t know how to draw a line with gradient

Maybe shader, see tutorial

Of course you can make a line pf pixels yourself

Look at for-loop, lerp() and lerpColor

I’ve managed to draw a line with a gradient. Essentially, you create a beginshape, endshape environment. Inside, you first set the color for one side, specify two vertices. Then you set the color for the other side and draw two vertices like so

beginShape();
stroke(0,0,0);
vertex(0,0,0);
vertex(5,5,5);
stroke(255,255,255);
vertex(10,10,10);
vertex(15,15,15);
endShape();
1 Like