-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMain.java
454 lines (433 loc) · 18.3 KB
/
Main.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
/* The Main class runs the simulation, first loading data for districts (by reading in from an Excel file), then
* generating semi-randomized data for individuals. It then allows users to produce floods of specific magnitudes in
* any of the three rivers, simulates the decision of whether or not to move for each individual, and returns
* updated population data.
*/
import java.util.ArrayList;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Main {
static ArrayList<District> districts = new ArrayList<District>(); //Will contain all 64 districts
static ArrayList<Person> people = new ArrayList<Person>(); //Will contain all 144,043,697 individuals
static int num_districts = 0;
static int total_pop = 0;
static double max_flood_depth = 500; //Source: Data on 1998 flood from CCAFS Comprehensive Disaster Management Program, https://www.slideshare.net/cgiarclimate/flood-management-in-bangladesh-pd-cdmpii-upd-28-nov13
static double average_dist = 0;
// Returns a random integer within specified range (including start and end points)
public static int randomWithRange(int min, int max)
{
int range = (max - min) + 1;
return (int)(Math.random() * range) + min;
}
// Returns Pythagorean distance between two districts
public static double distBetween(District t1, District t2) {
return Math.sqrt((Math.pow(Math.abs(t1.lat - t2.lat), 2) + Math.pow(Math.abs(t1.lon - t2.lon), 2)));
}
// Returns the average distance between all the districts in the districts list
public static double avgDist() {
double sum_distances = 0;
int num_distances = 0;
for (int i = 0; i < num_districts-1; i++) {
for (int j = i+1; j < num_districts; j++) {
sum_distances += distBetween(districts.get(i), districts.get(j));
num_distances++;
}
}
return sum_distances / num_distances;
}
/* Prints out population of each district */
public static void printpop() {
System.out.println("Population Data: ");
for (int i = 0; i < num_districts; i++) {
System.out.println(districts.get(i).name + " has population " + districts.get(i).population);
}
}
/* Generates starter data for districts and individuals. Data for districts are read in from an excel file. Data
* for individuals are then produced based on the population in each district. Characteristics for individuals--
* age, marital status, wealth, property-owning status, and employment status--are produced semi-randomly based on
* Bangladesh census data.
*/
public static void getdata() {
//Get districts data by reading in from CSV file
System.out.println("Loading geographic data...");
String csvFile = "/Users/emily/desktop/district_data.csv";
BufferedReader br = null;
String line = "";
String cvsSplitBy = ",";
try {
br = new BufferedReader(new FileReader(csvFile));
while ((line = br.readLine()) != null) {
String[] district = line.split(cvsSplitBy);
String name = district[0];
int pop = Integer.parseInt(district[1]);
double lat = Double.parseDouble(district[2]);
double lon = Double.parseDouble(district[3]);
int altitude = Integer.parseInt(district[4]);
int jamuna = Integer.parseInt(district[5]);
int ganges = Integer.parseInt(district[6]);
int meghna = Integer.parseInt(district[7]);
District new_district = new District(name, altitude, pop, lat, lon, jamuna, ganges, meghna);
districts.add(new_district);
}
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
num_districts = districts.size();
average_dist = avgDist();
//Calculate the total population for global variable
for (int i = 0; i < num_districts; i++) {
total_pop += districts.get(i).population;
}
//Get people data by semi-random production
System.out.println("Loading population data...");
//Variables that will determine the distribution of characteristics
int min_age_employment = 14; //The legal minimum age of employment in Bangladesh according to UNICEF: https://www.unicef.org/bangladesh/children_4863.html
int min_age_marriage = 18; //The legal minimum age of marraige in Bangladesh: http://www.girlsnotbrides.org/child-marriage/bangladesh/
int min_age_property = 18; //Taken as the leagl age to vote: http://chartsbin.com/view/re6
int labor_force_size = 72027270; //Data from http://data.worldbank.org/indicator/SL.TLF.TOTL.IN
double prob_employed = labor_force_size/total_pop;
double prob_married = 0.7; //An educated guess, based on worldwide marraige rates and https://www.hrw.org/news/2015/06/09/bangladesh-girls-damaged-child-marriage
double prob_property = 0.8; //No data available on Bangldesh, but took home ownership rate for India: https://en.wikipedia.org/wiki/List_of_countries_by_home_ownership_rate
//Calculate 5 income "buckets"; 1/5 of the people will be allocated to each of the buckets
double gdp_per_cap = 957.82; // Data from World Bank
double[] income_inequality = {.0522, .0910, .1333, .2056, .5179}; //Data from paper by professor at University of Dhaka, http://bea-bd.org/site/images/pdf/063.pdf
double[] incomes_by_quintile = new double[5];
for(int i = 0; i < 5; i++) {
incomes_by_quintile[i] = (gdp_per_cap*total_pop*income_inequality[i]) / (total_pop/5);
}
int wealth_variance = (int) (gdp_per_cap/10); // The amount wealth is allowed to vary within each quintile--should be no more than 200
for (int j = 0; j < num_districts; j++) {
for (int k=0; k < districts.get(j).population; k++) {
int age;
int age_indicator = randomWithRange(0, 100);
if (age_indicator < 10) {
age = randomWithRange(0, 4);//Age is distributed into 5-year buckets according to data at https://en.wikipedia.org/wiki/Demographics_of_Bangladesh. Within each bucket, age is uniformly distributed.
}
else if (age_indicator < 23) {
age = randomWithRange(5, 9);
}
else if (age_indicator < 35) {
age = randomWithRange(10, 14);
}
else if (age_indicator < 44) {
age = randomWithRange(15, 19);
}
else if (age_indicator < 53) {
age = randomWithRange(20, 24);
}
else if (age_indicator < 62) {
age = randomWithRange(25, 29);
}
else if (age_indicator < 69) {
age = randomWithRange(30, 34);
}
else if (age_indicator < 76) {
age = randomWithRange(35, 39);
}
else if (age_indicator < 82) {
age = randomWithRange(40, 44);
}
else if (age_indicator < 90) {
age = randomWithRange(45, 54);
}
else if (age_indicator < 98) {
age = randomWithRange(55, 74);
}
else {
age = randomWithRange(75, 100);
}
int wealth_quintile = randomWithRange(0, 4);
double wealth = incomes_by_quintile[wealth_quintile] + randomWithRange(-wealth_variance, wealth_variance);
boolean property = false;
boolean married = false;
boolean employed = false;
//Property-owning status is based on probability of owning property (unless under min age)
double property_indicator = randomWithRange(0, 100);
if (age < min_age_property) {
property = false;
}
else if (property_indicator <= prob_property * 100.0) {
property = true;
}
else {
property = false;
}
//Marriage status is calculated based on probability of being married (unless under min age)
double married_indicator = randomWithRange(0, 100);
if (age < min_age_marriage) {
property = false;
}
else if (married_indicator <= prob_married * 100.0) {
married = true;
}
else {
married = false;
}
//Employment status is calculated based on probability of being employed (unless under min age)
double employed_indicator = randomWithRange(0, 100);
if (age < min_age_employment) {
employed = false;
}
if (employed_indicator <= prob_employed * 100.0) {
employed = true;
}
else {
employed = false;
}
people.add(new Person(districts.get(j), age, wealth, property, married, employed));
}
System.out.println(" Loaded data for " + districts.get(j).name);
}
System.out.println("Data loaded");
}
/* The flood function accepts a river, which can be any of the three major rivers in Bangladesh, and a severity
* in cm, which must be less than the max severity specified in the global variables. It then calculates,
* for each individual, whether or not they will move and where they will move to. It then updates population
* and individual information accordingly.
*/
public static void flood(String river, double severity) {
if (river != "jamuna" && river != "ganges" && river != "meghna") {
System.out.println("invalid river");
return;
}
else if (severity > max_flood_depth) {
System.out.println("flood depth too high, not possible");
return;
}
System.out.println("FLOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOD");
ArrayList<District> affected_districts = new ArrayList<District>();
ArrayList<District> secondarily_affected = new ArrayList<District>();
ArrayList<Person> will_move = new ArrayList<Person>();
ArrayList<Integer> moving_indexes = new ArrayList<Integer>();
// Go through list of towns, finding which towns are directly affected by flood (that is, adjacent to the river)
if (river == "jamuna") {
for (int i = 0; i < num_districts; i++) {
if (districts.get(i).jamuna == 1) {
affected_districts.add(districts.get(i));
System.out.println(districts.get(i).name + " is directly affected");
}
}
}
else if (river == "ganges") {
for (int i = 0; i < num_districts; i++) {
if (districts.get(i).ganges == 1) {
affected_districts.add(districts.get(i));
System.out.println(districts.get(i).name + " is directly affected");
}
}
}
else if (river == "meghna") {
for (int i = 0; i < num_districts; i++) {
if (districts.get(i).meghna == 1) {
affected_districts.add(districts.get(i));
System.out.println(districts.get(i).name + " is directly affected");
}
}
}
//run through districts, find all districts close enough to also be affected
int num_directly_affected = affected_districts.size();
System.out.println("num directly affected: " + num_directly_affected);
for (int i = 0; i < num_districts; i++) {
if (!affected_districts.contains(districts.get(i))) {
for (int j = 0; j < num_directly_affected; j++) {
if (distBetween(districts.get(i), districts.get(j)) < avgDist()/6 && districts.get(i).elevation < 33) {
affected_districts.add(districts.get(i));
secondarily_affected.add(districts.get(i));
System.out.println(districts.get(i).name + " is secondarily affected");
break;
}
}
}
}
System.out.println("num secondarily affected: " + secondarily_affected.size());
System.out.println("num affected total: " + affected_districts.size());
/* run through people, find all people who are in affected districts. Calculate the probability they will move
using a point system. Create a list of all those who are moving. */
System.out.println("Finding people who are moving...");
for (int j = 0; j < total_pop; j++) {
Person affected_person = people.get(j);
if (affected_districts.contains(affected_person.loc)) {
int prob_move = 0; //To begin with, people are as likely to move as they are not to
//Being married makes people less likely to move than if they are single
if (affected_person.married) {
prob_move = prob_move + 3;
}
else {
prob_move = prob_move - 3;
}
//Employed people are less likely to move than unemployed ones, because moving will mean losing their job
if (affected_person.employed) {
prob_move = prob_move - 6;
}
else {
prob_move = prob_move + 6;
}
//Owning property makes a person less likely to move, because moving will mean losing their property
if (affected_person.property) {
prob_move = prob_move - 6;
}
else {
prob_move = prob_move + 6;
}
//If a person is between 40 and 50, they are slightly less likely to move because they have family, etc.
if (affected_person.age > 40 && affected_person.age < 50) {
prob_move = prob_move - 3;
}
//If a person is between 50 and 60, they are much less likely to move
else if (affected_person.age > 50 || affected_person.age < 10) {
prob_move = prob_move - 5;
}
else {
prob_move = prob_move + 3;
}
//Wealthy people are more able to move than poor ones. However, extremely wealthy people are less
//likely to move because they can just fix everything and go on with their lives.
if (affected_person.wealth < 200) {
prob_move = prob_move - 10;
}
else if (affected_person.wealth < 400) {
prob_move = prob_move - 5;
}
else if (affected_person.wealth < 600) {
prob_move = prob_move - 1;
}
else if (affected_person.wealth > 2000) {
prob_move = prob_move - 2;
}
else if (affected_person.wealth < 1000) {
prob_move = prob_move + 3;
}
else {
prob_move = prob_move + 1;
}
//People in secondarily affected regions are less likely to move than those in directly affected regions
if ((affected_person.loc.ganges == 0 && river == "ganges") || (affected_person.loc.meghna == 0 && river == "meghna") || (affected_person.loc.jamuna == 0 && river == "jamuna")) {
prob_move = prob_move - 7;
}
else {
prob_move = prob_move + 2;
}
//If the flood isn't too severe, people are less likely to move
if (severity < 250) {
prob_move = prob_move - 3;
}
else {
prob_move = prob_move + 3;
}
//Add some randomness to represent other factors
prob_move = prob_move + randomWithRange(-30, 10);
//If the probability is now greater than 0, the person will move
if (prob_move >= 0) {
//When the person moves, they lose $500 (or all their money if they have less than that in total)
affected_person.wealth -= 500;
people.get(j).wealth -= 500;
if (affected_person.wealth < 0) {
affected_person.wealth = 0;
people.get(j).wealth = 0;
}
//When a person moves, they lose their employment
affected_person.employed = false;
//When a person moves, they lose their property
affected_person.property = false;
will_move.add(affected_person);
moving_indexes.add(j);
}
}
}
int num_moving = will_move.size();
System.out.println("percent moving:" + ((double) (num_moving)) / ((double) (total_pop)));
//Calculate where each person will move to
System.out.println("Calculating where they will move to...");
int[] movements = new int[num_districts]; //Keeps track of how many people are moving to each place
for (int d = 0; d < num_districts; d++) {
movements[d] = 0;
}
for (int k = 0; k < num_moving; k++) {
Person moving_person = will_move.get(k);
//For each person, score each district based on altitude, distance from original district, and population.
//Pick the highest scoring district to move to.
int[] scores = new int[num_districts];
for (int a = 0; a < scores.length; a++) {
scores[a] = 0;
}
for (int b = 0; b < num_districts; b++) {
//If the district is affected by a flood, it is less likely to be chosen
if (affected_districts.contains(districts.get(b)) && !(secondarily_affected.contains(districts.get(b)))) {
scores[b] = -18;
}
//If the district is at high elevation, it is more likely to be chosen
if (districts.get(b).elevation > 20) {
scores[b] += 5;
}
//If the district is has a large population, it is more likely to be chosen (urbanization)
if (districts.get(b).population > 1000000) {
scores[b] += 5;
}
//If the district has an even larger population, it is more likely to be chosen (urbanization)
if (districts.get(b).population > 10000000) {
scores[b] += 10;
}
//Districts that are close by are more likely to be chosen
if (distBetween(districts.get(b), moving_person.loc) < average_dist / 2) {
scores[b] += 7;
}
if (distBetween(districts.get(b), moving_person.loc) < average_dist / 4) {
scores[b] += 7;
}
//Add in some randomness to represent variance
scores[b] += randomWithRange(-15, 15);
}
int max_score = -18;
int max_index = -1;
for (int c = 0; c < scores.length; c++) {
if (scores[c] >= max_score) {
max_score = scores[c];
max_index = c;
}
}
movements[max_index]++;
//remove 1 person from current locations population
for (int f = 0; f < num_districts; f++) {
if (districts.get(f).equals(will_move.get(k).loc)) {
districts.get(f).population--;
}
}
//update person's location
people.get(moving_indexes.get(k)).loc = districts.get(max_index);
//add 1 person to their new locations population
for (int f = 0; f < num_districts; f++) {
if (districts.get(f).equals(will_move.get(k).loc)) {
districts.get(f).population++;
}
}
}
//print out movements
for (int e = 0; e < movements.length; e++) {
System.out.println(movements[e] + " moved to " + districts.get(e).name);
}
//Print new population data
printpop();
}
//Runs the simulation, first getting the data, then running a flood
public static void main(String [] args) {
getdata();
//printpop();
//Induce flood
flood("meghna", 400);
}
}