import processing.opengl.*; import codeanticode.glgraphics.*; import de.fhpotsdam.unfolding.*; import de.fhpotsdam.unfolding.geo.*; import de.fhpotsdam.unfolding.utils.*; // --------------DECLARATIONS-------------------- int checkpoint = 0; // fonts and map de.fhpotsdam.unfolding.Map displayMap; PFont RestName, RestAddress, RestDescrip; // content variables Restaurant[] displayArray; // **could have also done Arraylist?? ListingArea[] ListingAreas; ListingArea areaChosen; // application triggers int appPhase = 0; // 0 = user input phase, 1 = main app phase int inputSubPhase = 0; // 0 = enter zone code, 1 = use current or update, 2 = update all or just 1 boolean overPoint = false; int overRestNum = -1; // default off position String typing = ""; // Variable to store text currently being typed // --------------SETUP-------------------- void setup() { size(1000, 500, GLConstants.GLGRAPHICS); // must = image dimensions displayMap = new de.fhpotsdam.unfolding.Map(this); RestName = createFont("Arial", 16, true); RestAddress = createFont("Arial", 12, true); RestDescrip = createFont("Arial", 11, true); // **MANUALLY DECLARING LISTINGAREAS FOR NOW ListingAreas = new ListingArea[3]; ListingAreas[0] = new ListingArea("Union Square", "1", "230"); ListingAreas[1] = new ListingArea("Tribeca", "2", "229"); ListingAreas[2] = new ListingArea("Soho", "3", "223"); // Check which zone user wants to see println("Please enter code for desired zone:"); for (int i=0; i (displayArray[i].pixelCoord.x - 20) ) && ( mouseY < (displayArray[i].pixelCoord.y + 20) && mouseY > (displayArray[i].pixelCoord.y - 20) ) ) { overPoint = true; overRestNum = i; // array position! } else { overPoint = false; overRestNum = -1; // default off position } i++; } } //*************** // ????? decided to use global trigger here (overRestNum) v passing in as a parameter. // any reason one generally should do it one way v the other? should text box be an object class?? void drawLabel(Restaurant restaurant_in) { PixelCoordinate drawCoords = restaurant_in.pixelCoord; int NameOffset = 35; int AddressOffset = NameOffset + 22; int DescripOffset = AddressOffset + 15; fill(255); stroke(0); rectMode(CORNER); rect(drawCoords.x + 10, drawCoords.y + 10, 250, 140); textFont(RestName); fill(0); textAlign(LEFT); text(restaurant_in.name, drawCoords.x + 20, drawCoords.y+ NameOffset); textFont(RestAddress); fill(0, 0, 255); text(restaurant_in.address, drawCoords.x + 20, drawCoords.y+ AddressOffset); textFont(RestDescrip); fill(100, 100, 100); text(restaurant_in.info, drawCoords.x + 20, drawCoords.y+ DescripOffset, 230, 70); // thumb if (restaurant_in.pic_URL != null) { image(restaurant_in.pic, drawCoords.x + 200, drawCoords.y + 20, 45, 45); // assume images all same size on site, although if not, can scale using .width/.height of the PImage objects } } // --------------HELPER FUNCTIONS-------------------- //*************** boolean checkInput(String key_in) { for (int i=0; i"); int endTag = pageContent.indexOf("Results", resultsSectionStart); // will work backward from first appearance of "Results" after tag "class="results">" // test spots for number of digits in results # char testSpot1 = pageContent.charAt(endTag-3); char testSpot2 = pageContent.charAt(endTag-4); char testSpot3 = pageContent.charAt(endTag-5); // **should deal with error / >999 case! if (testSpot1 == ' ') { // number is single digit numResults = int(pageContent.charAt(endTag-2)); } else if (testSpot2 == ' ') { // if number is 2-digit numResults = int(pageContent.substring(endTag-3, endTag-1)); } else { // number is 3-digit numResults = int(pageContent.substring(endTag-4, endTag-1)); } listings = new Restaurant[numResults]; // declare listings array } //*************** void setRestaurants() { int numPages = ceil(float(numResults)/25); for (int i = 0; i < numPages; i++) { // cycle listings pages String listingContent = pullContent("http://nymag.com/srch?t=restaurant&N=265+336+"+queryCode+"&No="+((i*25)+1)+"&Ns=nyml_sort_name%7C0"); // pull listings page content (query string starts on 1, not 0) String searchTag = "criticsPick"; int searchStart = 0; boolean lastOnPage = false; int j = 0; while (lastOnPage == false) { int tagStart = listingContent.indexOf(searchTag, searchStart); // Find start of the first "criticsPick" tag, indicating first listing if (tagStart == - 1) { lastOnPage = true; } else { int urlStart = tagStart + searchTag.length() + 19; // Move to beginning of URL... ****TO DO**** +19 for distance between "criticsPick" and href -- FIX... USE INDEX OF HREF *AFTER* CRITICSPICK int urlEnd = listingContent.indexOf("\"", urlStart); // Find the index of the end tag listings[i*25+j] = new Restaurant(); listings[i*25+j].URL = listingContent.substring(urlStart, urlEnd); // Set the text in between to restaurant URL listings[i*25+j].setAttributes(); // Use URL to get rest of info (except pixelCoords) via Restaurant method setAttributes() searchStart = urlEnd; } j++; } } } //*************** // Store listing area content for future use void saveData() { // save info PrintWriter output = createWriter("ListingAreas/"+name+"/data.txt"); // Create a new file in the sketch directory output.println("LISTING AREA DATA"); output.println("update"); output.println(month()+"."+day()+"."+year()+" - "+hour()+":"+minute()); output.println(); output.println("name\tinputCode\tqueryCode\tbaseQueryURL\tnumResults"); output.println(name+"\t"+inputCode+"\t"+queryCode+"\t"+baseQueryURL+"\t"+numResults); output.println(); output.println("RESTAURANT DATA"); output.println(); output.println("URL\tname\taddress\tpic_URL\tlatitude\tlongitude"); output.println("info"); output.println(); for (int i=0; i < listings.length; i++) { output.println(listings[i].URL+"\t"+listings[i].name+"\t"+listings[i].address+"\t"+listings[i].pic_URL+"\t"+listings[i].latitude+"\t"+listings[i].longitude); output.println(listings[i].info); output.println(); } output.flush(); // Writes the remaining data to the file output.close(); // Finishes the file // save images for (int i=0; i < listings.length; i++) { // save thumbs if (listings[i].pic_URL != null) listings[i].pic.save("ListingAreas/"+name+"/thumbs/"+listings[i].name+".jpg"); } // set update in AreaListing object attribute and config file lastUpdate = month()+"."+day()+"."+year()+" - "+hour()+":"+minute(); updateConfig(lastUpdate); } //*************** // Helper method to update config file (assumes areaArrayPos set to get to the right line) // **should be global helper function? also, would have had to rewrite whole doc anyway if used // output writer? void updateConfig(String lastUpdate_in) { // read in full config file String[] lines = loadStrings("ListingAreas/config.txt"); String[][] configData = new String[lines.length][lines[0].split("\t").length]; for (int i = 0; i < lines.length; i++) { String[] lineData = lines[i].split("\t"); for (int j = 0; j < lineData.length; j++) { configData[i][j] = lineData[j]; } } // change relevant field configData[int(inputCode)][4] = lastUpdate_in; // currently, area listings start on line 1 (line 0 = labels), so = inputcode // output new file String[] output = new String[configData.length]; for (int i = 0; i < configData.length; i++) { output[i] = join(configData[i], "\t"); } saveStrings("ListingAreas/config.txt", output); } //*************** boolean checkUpdate() { // requires name already set (as will be in ListingArea constructor) BufferedReader reader = createReader("ListingAreas/"+name+"/data.txt"); // Create a new file in the sketch directory if (reader == null) { // no info file, ie area has never been downloaded return false; } else { getLine(reader); // "LISTING AREA DATA" getLine(reader); // "update" lastUpdate = getLine(reader); return true; } } //*************** boolean loadData() { BufferedReader reader = createReader("ListingAreas/"+name+"/data.txt"); // Create a new file in the sketch directory if (reader == null) { // no info file, ie area has never been downloaded println("no file found!"); return false; } else { // load info getLine(reader); // "LISTING AREA DATA" getLine(reader); // "update" getLine(reader); // update value -- already set in checkUpdate getLine(reader); // blank line getLine(reader); // "area param labels" String areaParamsLine = getLine(reader); String[] areaParams = areaParamsLine.split("\t"); numResults = int(areaParams[4]); getLine(reader); // blank line getLine(reader); // "restaurant data" getLine(reader); // blank line getLine(reader); // restaurant param labels getLine(reader); // info param label getLine(reader); // blank line listings = new Restaurant[numResults]; for (int i=0; i < listings.length; i++) { String restaurantParamLine = getLine(reader); String[] restaurantParams = restaurantParamLine.split("\t"); listings[i] = new Restaurant(); listings[i].URL = restaurantParams[0]; listings[i].name = restaurantParams[1]; listings[i].address = restaurantParams[2]; listings[i].pic_URL = restaurantParams[3]; if (listings[i].pic_URL.equals("null")) listings[i].pic_URL = null; listings[i].latitude = float(restaurantParams[4]); listings[i].longitude = float(restaurantParams[5]); listings[i].loc = new Location(listings[i].latitude,listings[i].longitude); // use stored latlong to create location for unfolding maps listings[i].info = getLine(reader); getLine(reader); // blank line } // load images for (int i=0; i < listings.length; i++) { // save thumbs if (listings[i].pic_URL != null) listings[i].pic = loadImage("ListingAreas/"+name+"/thumbs/"+listings[i].name+".jpg"); } } return true; } } class PixelCoordinate { int x; int y; PixelCoordinate(int x_in, int y_in) { // constructor x = x_in; y = y_in; } } class Restaurant { String URL; String name, address, info, pic_URL; float latitude, longitude; Location loc; PixelCoordinate pixelCoord; PImage pic; Restaurant() { // constructor -- **could do this like Querylist, and require at least a URL? } // --------------METHODS-------------------- void setAttributes() { //REQUIRES URL BE SET! // Get all the HTML/XML source code into an array of strings String[] lines = loadStrings(URL); // Turn array into one long String String xml = join(lines, "" ); // set attirbutes from webpage using parsing helper function name = giveMeTextBetween(xml, "headline\" content=\"", "\""); address = giveMeTextBetween(xml, "nyml_address\" content=\"", "\""); latitude = float (giveMeTextBetween(xml, "nyml_address_latitude\" content=\"", "\"")); longitude = float (giveMeTextBetween(xml, "nyml_address_longitude\" content=\"", "\"")); loc = new Location(latitude,longitude); // ****TO DO**** need to set up as switch between whichever of the tags has longest length! // ALSO, to clean up the text... extraneous characters etc info = giveMeTextBetween(xml, "nyml_main_website_blurb\" content=\"", "\""); // pic_URL -- account for fact some restaurants don't have thumbs String pic_Directory = giveMeTextBetween(xml, "nyml_photo_url\" content=\"", "\""); if (pic_Directory.length() > 0) { pic_URL = "http://nymag.com" + pic_Directory; pic = loadImage(pic_URL); }else{ pic_URL = null; } } // A helper function that returns a substring between two substrings String giveMeTextBetween(String s, String before, String after) { int start = s.indexOf(before); // Find the index of the beginning tag if (start == - 1) return""; // If we don't find anything, send back a blank String start += before.length(); // Move to the end of the beginning tag int end = s.indexOf(after, start); // Find the index of the end tag if (end == -1) return""; // If we don't find the end tag, send back a blank String return s.substring(start, end); // Return the text in between } }