/* BASIC DATA VISUALIZATION Jeff Thompson Fall 2011 An interactive program visualizing recent earthquake data, automatically downloaded from the USGS weekly data. BASIC STEPS: 1. Download file 2. Get number of lines in the file (this is the # of earthquakes) 3. Read data points (lat, long, magnitude) into arrays, scaling values as we do so 4. Re-format the location strings 5. Visualize! LATITUDE/LONGITUDE DETAILS: Latitude: 90 (north pole) > 0 (equator) > -90 (south pole) -- this is the Y direction Longitude: 180 east > 0 (Prime Meridian) > -180 west -- this is the X direction COLUMNS ARE: Source, Eqid, Version, Datetime (as a full string), Latitude, Longitude, Magnitude (strength), Depth, NST, Region DATA SOURCE: http://earthquake.usgs.gov/earthquakes/catalogs In this case, we're automatically downloading the 2.5+ earthquakes from the last 7 days MAP IMAGE: Note that the lat/long grid is square and the landforms are distorted - using a normal projection would mean fancy math to get the locations correct, this is a kludge Image via Wikipedia user strebe (licenced under a CreativeCommons license - thanks!) http://en.wikipedia.org/wiki/File:Equirectangular_projection_SW.jpg www.jeffreythompson.org */ // DATA READING VARIABLES String filename = "earthquakes.csv"; BufferedReader reader; String tempLine; int numLines = 0; // VISUALIZATION VARIABLES int rectSize = 5; float[] latitude; // latitude and longitude - set length later when we have number of lines float[] longitude; float[] magnitude; // ditto for strength of the quake String[] location; // store text labels PFont titleFont; // larger text for label PFont detailFont; // smaller for descriptions boolean alreadyDisplaying = false; PImage worldMap; void setup() { // DOWNLOAD DATA // see downloadFile.pde for details downloadFile(filename); // LOAD IMAGE FILE // load first to get image size > sketch dimensions worldMap = loadImage("EquirectangularProjection-1200pxDimmed.png"); // BASIC SETUP size(worldMap.width, worldMap.height); background(255); noStroke(); smooth(); rectMode(CENTER); // FONT SETUP titleFont = createFont("HelveticaNeue-60.vlw", 60); detailFont = createFont("HelveticaNeue-12.vlw", 12); // FIRST, GET NUMBER OF LINES IN FILE reader = createReader(filename); // load the file specified earlier println("Getting number of lines (may take a bit)..."); try { while ( (tempLine = reader.readLine ()) != null) { // run through until the end of the file numLines++; // counting lines } } // CATCH ANY ERRORS READING THE FILE catch (IOException e) { println("ERROR READING FILE!"); } // WHEN DONE, PRINT NUMBER OF LINES numLines -= 1; // subtract 1 since the first line isn't data println("Number of lines: " + numLines); // RE-0PEN THE FILE // since we've run through the file already, we re-open it to start again reader = createReader(filename); // READ DATA INTO ARRAYS // store in a simpler format that we can read in draw() println("\nReading data into arrays (also may take a while)..."); latitude = new float[numLines]; // -1 since first line isn't data longitude = new float[numLines]; magnitude = new float[numLines]; location = new String[numLines]; try { // read and skip the first line tempLine = reader.readLine(); // read the rest of the data! for (int i=0; i= longitude[i]-rectSize/2 && mouseX <= longitude[i]+rectSize/2 && mouseY >= latitude[i]-rectSize/2 && mouseY <= latitude[i]+rectSize/2 && alreadyDisplaying == false) { // if so, display info... // magnitude textAlign(LEFT); textFont(titleFont); fill(255, 0, 0); text(nf(magnitude[i]/25.5, 0, 1), 20, height-20); // nf trims the float value to 1 decimal place textFont(detailFont); fill(0); text("/ 9 MAGNITUDE", 120, height-20); // location textAlign(RIGHT); text(location[i], width-20, height-40); // while it may seems redundant to change these values back to lat/long range, it is // quicker to do this for one set performance the scaling for all the values each frame float tempLong = map(longitude[i], 0, width, -180, 180); float tempLat = map(latitude[i], 0, height, 90, -90); // test hemispheres and print coordinates (with + signs as well) if (tempLong <= 0 && tempLat <= 0) { // SW text(tempLat + "S, " + tempLong + "W", width-20, height-20); } else if (tempLong > 0 && tempLat <= 0) { // SE text(tempLat + "S, " + "+" + tempLong + "E", width-20, height-20); } else if (tempLong > 0 && tempLat > 0) { // NE text("+" + tempLat + "N, " + "+" + tempLong + "E", width-20, height-20); } else if (tempLong <= 0 && tempLat > 0) { // NW text("+" + tempLat + "N, " + tempLong + "W", width-20, height-20); } // TO PREVENT DISPLAYING MULTIPLE DATA POINTS FOR NEARBY EARTHQUAKES // set the 'alreadyDiplaying' flag alreadyDisplaying = true; // set fill color for this rectangle to red and draw fill(255, 0, 0, 100); noStroke(); // scale exponentially: e^x = 2.7... the function for an exponential curve // 0 > 8103 is a hardcoded range for 0-9 Richter // 30 > 200 is the desired output range float tempMag = map(pow(2.718281828,(magnitude[i]/25.5)), 0,8103, 30,200); ellipse(longitude[i], latitude[i], tempMag,tempMag); } // otherwise, draw rectangles/lines else { // draw rectangles with fill color showing magnitude fill(magnitude[i],0,0, 200); stroke(255); rect(longitude[i], latitude[i], rectSize, rectSize); } } // AFTER ALL DATA IS READ, RESET THE 'alreadyDisplaying' FLAG and BRING BACK THE CURSOR alreadyDisplaying = false; }