Java tutorial
/* * The Shepherd Project - A Mark-Recapture Framework * Copyright (C) 2011 Jason Holmberg * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.ecocean; import org.apache.commons.codec.digest.DigestUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Calendar; import java.util.StringTokenizer; import java.text.SimpleDateFormat; import java.util.TreeMap; import java.util.Vector; import java.util.HashMap; import java.util.GregorianCalendar; import java.lang.Math; import java.io.*; import java.lang.reflect.Field; import javax.jdo.Query; import org.apache.commons.lang3.builder.ToStringBuilder; import javax.servlet.http.HttpServletRequest; import org.ecocean.genetics.*; import org.ecocean.tag.AcousticTag; import org.ecocean.tag.MetalTag; import org.ecocean.tag.SatelliteTag; import org.ecocean.Util; import org.ecocean.servlet.ServletUtilities; import org.ecocean.identity.IBEISIA; import org.ecocean.media.*; import javax.servlet.http.HttpServletRequest; import org.joda.time.DateTime; import org.joda.time.LocalDateTime; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; import org.ecocean.security.Collaboration; import org.ecocean.servlet.ServletUtilities; import javax.servlet.http.HttpServletRequest; //note these are different. so be explicit if you need the org.json.JSONObject flavor //import org.json.JSONObject; import org.datanucleus.api.rest.orgjson.JSONObject; import org.datanucleus.api.rest.orgjson.JSONArray; import org.datanucleus.api.rest.orgjson.JSONException; /** * An <code>encounter</code> object stores the complete data for a single sighting/capture report. * <code>Encounters</code> are added to MarkedIndividual objects as multiple encounters are associated with * known individuals. * <p/> * * @author Jason Holmberg * @version 2.0 */ public class Encounter implements java.io.Serializable { static final long serialVersionUID = -146404246317385604L; public static final String STATE_MATCHING_ONLY = "matching_only"; /** * The following attributes are described in the Darwin Core quick reference at: * http://rs.tdwg.org/dwc/terms/#dcterms:type * <p/> * Wherever possible, this class will be extended with Darwin Core attributes for greater adoption of the standard. */ private String sex = "unknown"; private String locationID = "None"; private Double maximumDepthInMeters; private Double maximumElevationInMeters; private String catalogNumber = ""; private String individualID; private int day = 0; private int month = -1; private int year = 0; private Double decimalLatitude; private Double decimalLongitude; private String verbatimLocality; private String occurrenceRemarks = ""; private String modified; private String occurrenceID; private String recordedBy; private String otherCatalogNumbers; private String behavior; private String eventID; private String measurementUnit; private String verbatimEventDate; private String dynamicProperties; public String identificationRemarks = ""; public String genus = ""; public String specificEpithet; public String lifeStage; public String country; private static HashMap<String, ArrayList<Encounter>> _matchEncounterCache = new HashMap<String, ArrayList<Encounter>>(); /* * The following fields are specific to this mark-recapture project and do not have an easy to map Darwin Core equivalent. */ //An URL to a thumbnail image representing the encounter. //This is private String dwcImageURL; //Defines whether the sighting represents a living or deceased individual. //Currently supported values are: "alive" and "dead". private String livingStatus; //observed age (if any) via IBEIS zebra projects private Double age; //Date the encounter was added to the library. private String dwcDateAdded; private Long dwcDateAddedLong; // If Encounter spanned more than one day, date of release private Date releaseDate; private Long releaseDateLong; //Size of the individual in meters private Double size; //Additional comments added by library users private String researcherComments = "None"; //username of the logged in researcher assigned to the encounter //this STring is matched to an org.ecocean.User object to obtain more information private String submitterID; //name, email, phone, address of the encounter reporter private String submitterEmail, submitterPhone, submitterAddress; private String hashedSubmitterEmail; private String hashedPhotographerEmail; private String hashedInformOthers; private String informothers; //name, email, phone, address of the encounter photographer private String photographerName, photographerEmail, photographerPhone, photographerAddress; //a Vector of Strings defining the relative path to each photo. The path is relative to the servlet base directory public Vector additionalImageNames = new Vector(); //a Vector of Strings of email addresses to notify when this encounter is modified private Vector interestedResearchers = new Vector(); //time metrics of the report private int hour = 0; private String minutes = "00"; private String state = ""; //the globally unique identifier (GUID) for this Encounter private String guid; private Long dateInMilliseconds; //describes how the shark was measured private String size_guess = "none provided"; //String reported GPS values for lat and long of the encounter private String gpsLongitude = "", gpsLatitude = ""; //whether this encounter has been rejected and should be hidden from public display //unidentifiable encounters generally contain some data worth saving but not enough for accurate photo-identification //private boolean unidentifiable = false; //whether this encounter has a left-side spot image extracted //public boolean hasSpotImage = false; //whether this encounter has a right-side spot image extracted //public boolean hasRightSpotImage = false; //Indicates whether this record can be exposed via TapirLink private boolean okExposeViaTapirLink = false; //whether this encounter has been approved for public display //private boolean approved = true; //integers of the latitude and longitude degrees //private int lat=-1000, longitude=-1000; //name of the stored file from which the left-side spots were extracted public String spotImageFileName = ""; //name of the stored file from which the right-side spots were extracted public String rightSpotImageFileName = ""; //string descriptor of the most obvious scar (if any) as reported by the original submitter //we also use keywords to be more specific public String distinguishingScar = "None"; //describes how this encounter was matched to an existing shark - by eye, by pattern recognition algorithm etc. //DEPRECATING OLD DATA CONSTRUCT //private int numSpotsLeft = 0; //private int numSpotsRight = 0; //SPOTS //an array of the extracted left-side superSpots //private superSpot[] spots; private ArrayList<SuperSpot> spots; //an array of the extracted right-side superSpots //private superSpot[] rightSpots; private ArrayList<SuperSpot> rightSpots; //an array of the three extracted left-side superSpots used for the affine transform of the I3S algorithm //private superSpot[] leftReferenceSpots; private ArrayList<SuperSpot> leftReferenceSpots; //an array of the three extracted right-side superSpots used for the affine transform of the I3S algorithm //private superSpot[] rightReferenceSpots; private ArrayList<SuperSpot> rightReferenceSpots; //an open ended string that allows a type of patterning to be identified. //as an example, see the use of color codes at splashcatalog.org, allowing pre-defined fluke patterning types //to be used to help narrow the search for a marked individual private String patterningCode; //submitting organization and project further detail the scope of who submitted this project private String submitterOrganization; private String submitterProject; //hold submittedData //private List<DataCollectionEvent> collectedData; private List<TissueSample> tissueSamples; private List<SinglePhotoVideo> images; //private ArrayList<MediaAsset> media; private ArrayList<Annotation> annotations; private List<Measurement> measurements; private List<MetalTag> metalTags; private AcousticTag acousticTag; private SatelliteTag satelliteTag; private Boolean mmaCompatible = false; //start constructors /** * empty constructor required by the JDO Enhancer */ public Encounter() { } /** * Use this constructor to add the minimum level of information for a new encounter * The Vector <code>additionalImages</code> must be a Vector of Blob objects * * NOTE: technically this is DEPRECATED cuz, SinglePhotoVideos? really? */ public Encounter(int day, int month, int year, int hour, String minutes, String size_guess, String location, String submitterName, String submitterEmail, List<SinglePhotoVideo> images) { if (images != null) System.out.println("WARNING: danger! deprecated SinglePhotoVideo-based Encounter constructor used!"); this.verbatimLocality = location; this.recordedBy = submitterName; this.submitterEmail = submitterEmail; //now we need to set the hashed form of the email addresses this.hashedSubmitterEmail = Encounter.getHashOfEmailString(submitterEmail); this.images = images; this.day = day; this.month = month; this.year = year; this.hour = hour; this.minutes = minutes; this.size_guess = size_guess; this.setDWCDateAdded(); this.setDWCDateLastModified(); this.resetDateInMilliseconds(); } public Encounter(Annotation ann) { this(new ArrayList<Annotation>(Arrays.asList(ann))); } public Encounter(ArrayList<Annotation> anns) { this.catalogNumber = Util.generateUUID(); this.annotations = anns; this.setDateFromAssets(); this.setSpeciesFromAssets(); this.setLatLonFromAssets(); this.setDWCDateAdded(); this.setDWCDateLastModified(); this.resetDateInMilliseconds(); } /** * Returns an array of all of the superSpots for this encounter. * * @return the array of superSpots, taken from the croppedImage, that make up the digital fingerprint for this encounter */ public ArrayList<SuperSpot> getSpots() { //return HACKgetSpots(); return spots; } public ArrayList<SuperSpot> getRightSpots() { //return HACKgetRightSpots(); return rightSpots; } /** * Returns an array of all of the superSpots for this encounter. * * @return the array of superSpots, taken from the croppedImage, that make up the digital fingerprint for this encounter */ /* these have gone away! dont be setting spots on Encounter any more .... NOT SO FAST... we regress for whaleshark.org... */ public void setSpots(ArrayList<SuperSpot> newSpots) { spots = newSpots; } public void setRightSpots(ArrayList<SuperSpot> newSpots) { rightSpots = newSpots; } /** * Removes any spot data */ public void removeSpots() { spots = null; } public void removeRightSpots() { rightSpots = null; } //yes, there "should" be only one of each of these, but we be thorough! public void removeLeftSpotMediaAssets(Shepherd myShepherd) { ArrayList<MediaAsset> spotMAs = this.findAllMediaByLabel(myShepherd, "_spot"); for (MediaAsset ma : spotMAs) { System.out.println( "INFO: removeLeftSpotMediaAsset() detaching " + ma + " from parent id=" + ma.getParentId()); ma.setParentId(null); } } public void removeRightSpotMediaAssets(Shepherd myShepherd) { ArrayList<MediaAsset> spotMAs = this.findAllMediaByLabel(myShepherd, "_spotRight"); for (MediaAsset ma : spotMAs) { System.out.println( "INFO: removeRightSpotMediaAsset() detaching " + ma + " from parent id=" + ma.getParentId()); ma.setParentId(null); } } public void nukeAllSpots() { leftReferenceSpots = null; rightReferenceSpots = null; spots = null; rightSpots = null; } /** * Returns the number of spots in the cropped image stored for this encounter. * * @return the number of superSpots that make up the digital fingerprint for this encounter */ public int getNumSpots() { return (spots == null) ? 0 : spots.size(); /* ArrayList<SuperSpot> fakeSpots = HACKgetSpots(); if(fakeSpots!=null){return fakeSpots.size();} else{return 0;} */ } public int getNumRightSpots() { return (rightSpots == null) ? 0 : rightSpots.size(); /* ArrayList<SuperSpot> fakeRightSpots = HACKgetRightSpots(); if(fakeRightSpots!=null){return fakeRightSpots.size();} else{return 0;} */ } public boolean hasLeftSpotImage() { return (this.getNumSpots() > 0); } public boolean hasRightSpotImage() { return (this.getNumRightSpots() > 0); } /** * Sets the recorded length of the shark for this encounter. */ public void setSize(Double mysize) { if (mysize != null) { size = mysize; } else { size = null; } } /** * Returns the recorded length of the shark for this encounter. * * @return the length of the shark */ public double getSize() { return size.doubleValue(); } public Double getSizeAsDouble() { return size; } /** * Sets the units of the recorded size and depth of the shark for this encounter. * Acceptable entries are either "Feet" or "Meters" */ public void setMeasureUnits(String measure) { measurementUnit = measure; } /** * Returns the units of the recorded size and depth of the shark for this encounter. * * @return the units of measure used by the recorded of this encounter, either "feet" or "meters" */ public String getMeasureUnits() { return measurementUnit; } public String getMeasurementUnit() { return measurementUnit; } /** * Returns the recorded location of this encounter. * * @return the location of this encounter */ public String getLocation() { return verbatimLocality; } public void setLocation(String location) { this.verbatimLocality = location; } /** * Sets the recorded sex of the shark in this encounter. * Acceptable values are "Male" or "Female" */ public void setSex(String thesex) { if (thesex != null) { sex = thesex; } else { sex = null; } } /** * Returns the recorded sex of the shark in this encounter. * * @return the sex of the shark, either "male" or "female" */ public String getSex() { return sex; } /** * Returns any submitted comments about scarring on the shark. * * @return any comments regarding observed scarring on the shark's body */ public boolean getMmaCompatible() { return mmaCompatible; } public void setMmaCompatible(boolean b) { mmaCompatible = b; } public String getComments() { return occurrenceRemarks; } /** * Sets the initially submitted comments about markings and additional details on the shark. */ public void setComments(String newComments) { occurrenceRemarks = newComments; } /** * Returns any comments added by researchers * * @return any comments added by authroized researchers */ public String getRComments() { return researcherComments; } /** * Adds additional comments about the encounter * * @param newComments any additional comments to be added to the encounter */ public void addComments(String newComments) { if ((researcherComments != null) && (!(researcherComments.equals("None")))) { researcherComments += newComments; } else { researcherComments = newComments; } } /** * Returns the name of the person who submitted this encounter data. * * @return the name of the person who submitted this encounter to the database */ public String getSubmitterName() { return recordedBy; } public void setSubmitterName(String newname) { recordedBy = newname; } /** * Returns the e-mail address of the person who submitted this encounter data * * @return the e-mail address of the person who submitted this encounter data */ public String getSubmitterEmail() { return submitterEmail; } public void setSubmitterEmail(String newemail) { submitterEmail = newemail; this.hashedSubmitterEmail = Encounter.getHashOfEmailString(newemail); } /** * Returns the phone number of the person who submitted this encounter data. * * @return the phone number of the person who submitted this encounter data */ public String getSubmitterPhone() { return submitterPhone; } /** * Sets the phone number of the person who submitted this encounter data. */ public void setSubmitterPhone(String newphone) { submitterPhone = newphone; } /** * Returns the mailing address of the person who submitted this encounter data. * * @return the mailing address of the person who submitted this encounter data */ public String getSubmitterAddress() { return submitterAddress; } /** * Sets the mailing address of the person who submitted this encounter data. */ public void setSubmitterAddress(String address) { submitterAddress = address; } /** * Returns the name of the person who took the primaryImage this encounter. * * @return the name of the photographer who took the primary image for this encounter */ public String getPhotographerName() { return photographerName; } /** * Sets the name of the person who took the primaryImage this encounter. */ public void setPhotographerName(String name) { photographerName = name; } /** * Returns the e-mail address of the person who took the primaryImage this encounter. * * @return @return the e-mail address of the photographer who took the primary image for this encounter */ public String getPhotographerEmail() { return photographerEmail; } /** * Sets the e-mail address of the person who took the primaryImage this encounter. */ public void setPhotographerEmail(String email) { photographerEmail = email; this.hashedPhotographerEmail = Encounter.getHashOfEmailString(email); } /** * Returns the phone number of the person who took the primaryImage this encounter. * * @return the phone number of the photographer who took the primary image for this encounter */ public String getPhotographerPhone() { return photographerPhone; } /** * Sets the phone number of the person who took the primaryImage this encounter. */ public void setPhotographerPhone(String phone) { photographerPhone = phone; } /** * Returns the mailing address of the person who took the primaryImage this encounter. * * @return the mailing address of the photographer who took the primary image for this encounter */ public String getPhotographerAddress() { return photographerAddress; } /** * Sets the mailing address of the person who took the primaryImage this encounter. */ public void setPhotographerAddress(String address) { photographerAddress = address; } /** * Sets the recorded depth of this encounter. */ public void setDepth(Double myDepth) { if (myDepth != null) { maximumDepthInMeters = myDepth; } else { maximumDepthInMeters = null; } } /** * Returns the recorded depth of this encounter. * * @return the recorded depth for this encounter */ public double getDepth() { return maximumDepthInMeters.doubleValue(); } public Double getDepthAsDouble() { return maximumDepthInMeters; } //public Vector getAdditionalImages() {return additionalImages;} /** * Returns the file names of all images taken for this encounter. * * @return a vector of image name Strings */ public Vector getAdditionalImageNames() { Vector imageNamesOnly = new Vector(); //List<SinglePhotoVideo> images=getCollectedDataOfClass(SinglePhotoVideo.class); if ((images != null) && (images.size() > 0)) { int imagesSize = images.size(); for (int i = 0; i < imagesSize; i++) { SinglePhotoVideo dce = (SinglePhotoVideo) images.get(i); imageNamesOnly.add(dce.getFilename()); } } return imageNamesOnly; } /** * Adds another image to the collection of images for this encounter. * These images should be the additional or non-side shots. * public void addAdditionalImageName(SinglePhotoVideo file) { images.add(file); } */ /* public void approve() { approved = true; okExposeViaTapirLink = true; } */ /** public void resetAdditionalImageName(int position, String fileName) { additionalImageNames.set(position, fileName); //additionalImageNames.add(fileName); } */ /** * Removes the specified additional image from this encounter. * * @param imageFile the image to be removed from the additional images stored for this encounter */ /* public void removeAdditionalImageName(String imageFile) { for (int i = 0; i < collectedData.size(); i++) { String thisName = images.get(i).getFilename(); if ((thisName.equals(imageFile)) || (thisName.indexOf("#") != -1)) { images.remove(i); i--; } } } */ /* public void removeDataCollectionEvent(DataCollectionEvent dce) { collectedData.remove(dce); } */ /** * Returns the unique encounter identifier number for this encounter. * * @return a unique integer String used to identify this encounter in the database */ public String getEncounterNumber() { return catalogNumber; } public String generateEncounterNumber() { return Util.generateUUID(); } public String dir(String baseDir) { return baseDir + File.separator + "encounters" + File.separator + this.subdir(); } //like above, but class method so you pass the encID public static String dir(String baseDir, String id) { return baseDir + File.separator + "encounters" + File.separator + subdir(id); } //like above, but can pass a File in for base public static String dir(File baseDir, String id) { return baseDir.getAbsolutePath() + File.separator + "encounters" + File.separator + subdir(id); } //subdir() is kind of a utility function, which can be called as enc.subdir() or Encounter.subdir(IDSTRING) as needed public String subdir() { return subdir(this.getEncounterNumber()); } public static String subdir(String id) { String d = id; //old-world if (Util.isUUID(id)) { //new-world d = id.charAt(0) + File.separator + id.charAt(1) + File.separator + id; } return d; } /** * Returns the date of this encounter. * * @return a Date object * @see java.util.Date */ public String getDate() { String date = ""; String time = ""; if (year <= 0) { return "Unknown"; } else if (month == -1) { return Integer.toString(year); } if (hour != -1) { String localMinutes = minutes; if (localMinutes.length() == 1) { localMinutes = "0" + localMinutes; } time = String.format("%02d:%s", hour, localMinutes); } if (day > 0) { date = String.format("%04d-%02d-%02d %s", year, month, day, time); } else if (month > -1) { date = String.format("%04d-%02d %s", year, month, time); } else { date = String.format("%04d %s", year, month, time); } return date; } public String getShortDate() { String date = ""; if (year <= 0) { return "Unknown"; } else if (month == -1) { return Integer.toString(year); } if (day > 0) { date = String.format("%02d/%02d/%04d", day, month, year); } else { date = String.format("%02d/%04d", month, year); } return date; } /** * Returns the String discussing how the size of this animal was approximated. * * @return a String with text about how the size of this animal was estimated/measured */ public String getSizeGuess() { return size_guess; } public void setDay(int day) { this.day = day; resetDateInMilliseconds(); } public void setHour(int hour) { this.hour = hour; resetDateInMilliseconds(); } public void setMinutes(String minutes) { this.minutes = minutes; resetDateInMilliseconds(); } public String getMinutes() { return minutes; } public int getHour() { return hour; } public void setMonth(int month) { this.month = month; resetDateInMilliseconds(); } public void setYear(int year) { this.year = year; resetDateInMilliseconds(); } public int getDay() { return day; } public int getMonth() { return month; } public int getYear() { return year; } /** * Returns the String holding specific location data used for searching * * @return the String holding specific location data used for searching */ public String getLocationCode() { return locationID; } /** * A legacy method replaced by setLocationID(...). * * */ public void setLocationCode(String newLoc) { setLocationID(newLoc); } /** * Returns the String holding specific location data used for searching * * @return the String holding specific location data used for searching */ public String getDistinguishingScar() { return distinguishingScar; } /** * Sets the String holding scarring information for the encounter */ public void setDistinguishingScar(String scar) { distinguishingScar = scar; } /** * Sets the String documenting how the size of this animal was approximated. */ public void setSizeGuess(String newGuess) { size_guess = newGuess; } public String getMatchedBy() { if ((identificationRemarks == null) || (identificationRemarks.equals(""))) { return "Unknown"; } return identificationRemarks; } public void setMatchedBy(String matchType) { identificationRemarks = matchType; } public void setIdentificationRemarks(String matchType) { identificationRemarks = matchType; } /** * Sets the unique encounter identifier to be usd with this encounter. * Once this is set, it cannot be changed without possible impact to the * database structure. * * @param num the unique integer to be used to uniquely identify this encoun ter in the database */ public void setEncounterNumber(String num) { catalogNumber = num; } //this is probably what you wanted above to do. :/ public boolean hasMarkedIndividual() { if ((individualID == null) || individualID.toLowerCase().equals("unassigned")) return false; return true; } public void assignToMarkedIndividual(String sharky) { individualID = sharky; } /* public boolean wasRejected() { return unidentifiable; } public void reject() { unidentifiable = true; //okExposeViaTapirLink=false; } public void reaccept() { unidentifiable = false; //okExposeViaTapirLink=true; } */ public String getGPSLongitude() { if (gpsLongitude == null) { return ""; } else { return gpsLongitude; } } public void setGPSLongitude(String newLong) { gpsLongitude = newLong; } public String getGPSLatitude() { if (gpsLatitude == null) { return ""; } else { return gpsLatitude; } } public void setGPSLatitude(String newLat) { gpsLatitude = newLat; } public Encounter getClone() { Encounter tempEnc = new Encounter(); try { tempEnc = (Encounter) this.clone(); } catch (java.lang.CloneNotSupportedException e) { e.printStackTrace(); } return tempEnc; } public String getSpotImageFileName() { return spotImageFileName; } public void setSpotImageFileName(String name) { spotImageFileName = name; } //------------- //for the right side spot image public String getRightSpotImageFileName() { return rightSpotImageFileName; } public void setRightSpotImageFileName(String name) { rightSpotImageFileName = name; } //---------------- //really only intended to convert legacy SinglePhotoVideo to MediaAsset/Annotation world public ArrayList<Annotation> generateAnnotations(String baseDir, Shepherd myShepherd) { if ((annotations != null) && (annotations.size() > 0)) return annotations; if ((images == null) || (images.size() < 1)) return null; //probably pointless, so... if (annotations == null) annotations = new ArrayList<Annotation>(); boolean thumbDone = false; ArrayList<MediaAsset> haveMedia = new ArrayList<MediaAsset>(); //so we dont add duplicates! for (SinglePhotoVideo spv : images) { MediaAsset ma = spv.toMediaAsset(myShepherd); if (ma == null) { System.out.println( "WARNING: Encounter.generateAnnotations() could not create MediaAsset from SinglePhotoVideo " + spv.getDataCollectionEventID() + "; skipping"); continue; } if (haveMedia.contains(ma)) { System.out.println( "WARNING: Encounter.generateAnnotations() found a duplicate MediaAsset in the SinglePhotoVideo images; skipping -- " + ma); continue; } //note: we need at least minimal metadata (w,h) in order to make annotation, so if this fails, we are no-go try { ma.updateMetadata(); } catch (IOException ioe) { System.out.println( "WARNING: Encounter.generateAnnotations() failed to updateMetadata() on original MediaAsset " + ma + " (skipping): " + ioe.toString()); continue; } ma.addLabel("_original"); haveMedia.add(ma); annotations.add(new Annotation(getTaxonomyString(), ma)); //if (!media.contains(ma)) media.add(ma); //File idir = new File(this.dir(baseDir)); File idir = new File(spv.getFullFileSystemPath()).getParentFile(); //now we iterate through flavors that could be derived //TODO is it bad to assume ".jpg" ? i forget! addMediaIfNeeded(myShepherd, new File(idir, spv.getDataCollectionEventID() + ".jpg"), "spv/" + spv.getDataCollectionEventID() + "/" + spv.getDataCollectionEventID() + ".jpg", ma, "_watermark"); addMediaIfNeeded(myShepherd, new File(idir, spv.getDataCollectionEventID() + "-mid.jpg"), "spv/" + spv.getDataCollectionEventID() + "/" + spv.getDataCollectionEventID() + "-mid.jpg", ma, "_mid"); // note: we "assume" thumb was created from 0th spv, cuz we simply dont know but want it living somewhere if (!thumbDone) addMediaIfNeeded(myShepherd, new File(idir, "/thumb.jpg"), "spv/" + spv.getDataCollectionEventID() + "/thumb.jpg", ma, "_thumb"); thumbDone = true; } //we need to have the spot image as a child under *some* MediaAsset from above, but unfortunately we do not know its lineage. so we just pick one. :/ MediaAsset sma = spotImageAsMediaAsset( ((annotations.size() < 1) ? null : annotations.get(0).getMediaAsset()), baseDir, myShepherd); return annotations; } //utility method for created MediaAssets // note: also will check for existence of mpath and fail silently if doesnt exist private MediaAsset addMediaIfNeeded(Shepherd myShepherd, File mpath, String key, MediaAsset parentMA, String label) { if ((mpath == null) || !mpath.exists()) return null; AssetStore astore = AssetStore.getDefault(myShepherd); org.json.JSONObject sp = astore.createParameters(mpath); if (key != null) sp.put("key", key); //will use default from createParameters() (if there was one even) MediaAsset ma = astore.find(sp, myShepherd); if (ma != null) { ma.addLabel(label); if (parentMA != null) ma.setParentId(parentMA.getId()); return ma; } System.out.println("creating new MediaAsset for key=" + key); try { ma = astore.copyIn(mpath, sp); } catch (IOException ioe) { System.out.println("Could not create MediaAsset for key=" + key + ": " + ioe.toString()); return null; } if (parentMA != null) { ma.setParentId(parentMA.getId()); ma.updateMinimalMetadata(); //for children (ostensibly derived?) MediaAssets, really only need minimal metadata or so i claim } else { try { ma.updateMetadata(); //root images get the whole deal (guess this sh/could key off label=_original ?) } catch (IOException ioe) { //we dont care (well sorta) ... since IOException usually means we couldnt open file or some nonsense that we cant recover from } } ma.addLabel(label); MediaAssetFactory.save(ma, myShepherd); return ma; } //this makes assumption (for flukes) that both right and left image files are identical // TODO handle that they are different // TODO also maybe should reuse addMediaIfNeeded() for some of this where redundant public MediaAsset spotImageAsMediaAsset(MediaAsset parent, String baseDir, Shepherd myShepherd) { if ((spotImageFileName == null) || spotImageFileName.equals("")) return null; File fullPath = new File(this.dir(baseDir) + "/" + spotImageFileName); //System.out.println("**** * ***** looking for spot file " + fullPath.toString()); if (!fullPath.exists()) return null; //note: this only technically matters if we are *creating* the MediaAsset if (parent == null) { System.out.println("seems like we do not have a parent MediaAsset on enc " + this.getCatalogNumber() + ", so cannot add spot MediaAsset for " + fullPath.toString()); return null; } AssetStore astore = AssetStore.getDefault(myShepherd); if (astore == null) { System.out.println("No AssetStore in Encounter.spotImageAsMediaAsset()"); return null; } System.out.println("trying spotImageAsMediaAsset with file=" + fullPath.toString()); org.json.JSONObject sp = astore.createParameters(fullPath); sp.put("key", this.subdir() + "/spotImage-" + spotImageFileName); //note: this really only applies to S3 AssetStores, but shouldnt hurt others? MediaAsset ma = astore.find(sp, myShepherd); if (ma == null) { System.out.println("did not find MediaAsset for params=" + sp + "; creating one?"); try { ma = astore.copyIn(fullPath, sp); ma.addDerivationMethod("historicSpotImageConversion", true); ma.updateMinimalMetadata(); //System.out.println("params? " + ma.getParameters()); ma.addLabel("_spot"); ma.addLabel("_annotation"); MediaAssetFactory.save(ma, myShepherd); //System.out.println("params? " + ma.getParameters()); } catch (java.io.IOException ex) { System.out.println("spotImageAsMediaAsset threw IOException " + ex.toString()); } } ma.setParentId(parent.getId()); return ma; } public void setSubmitterID(String username) { if (username != null) { submitterID = username; } else { submitterID = null; } } //old method. use getAssignedUser() instead public String getSubmitterID() { return getAssignedUsername(); } public String getAssignedUsername() { return submitterID; } public Vector getInterestedResearchers() { return interestedResearchers; } public void addInterestedResearcher(String email) { interestedResearchers.add(email); } /* public boolean isApproved() { return approved; } */ public void removeInterestedResearcher(String email) { for (int i = 0; i < interestedResearchers.size(); i++) { String rName = (String) interestedResearchers.get(i); if (rName.equals(email)) { interestedResearchers.remove(i); } } } public double getRightmostSpot() { double rightest = 0; ArrayList<SuperSpot> spots = getSpots(); for (int iter = 0; iter < spots.size(); iter++) { if (spots.get(iter).getTheSpot().getCentroidX() > rightest) { rightest = spots.get(iter).getTheSpot().getCentroidX(); } } return rightest; } public double getLeftmostSpot() { double leftest = getRightmostSpot(); ArrayList<SuperSpot> spots = getSpots(); for (int iter = 0; iter < spots.size(); iter++) { if (spots.get(iter).getTheSpot().getCentroidX() < leftest) { leftest = spots.get(iter).getTheSpot().getCentroidX(); } } return leftest; } public double getHighestSpot() { double highest = getLowestSpot(); ArrayList<SuperSpot> spots = getSpots(); for (int iter = 0; iter < spots.size(); iter++) { if (spots.get(iter).getTheSpot().getCentroidY() < highest) { highest = spots.get(iter).getTheSpot().getCentroidY(); } } return highest; } public double getLowestSpot() { double lowest = 0; ArrayList<SuperSpot> spots = getSpots(); for (int iter = 0; iter < spots.size(); iter++) { if (spots.get(iter).getTheSpot().getCentroidY() > lowest) { lowest = spots.get(iter).getTheSpot().getCentroidY(); } } return lowest; } public com.reijns.I3S.Point2D[] getThreeLeftFiducialPoints() { com.reijns.I3S.Point2D[] Rray = new com.reijns.I3S.Point2D[3]; if (getLeftReferenceSpots() != null) { ArrayList<SuperSpot> refsLeft = getLeftReferenceSpots(); Rray[0] = new com.reijns.I3S.Point2D(refsLeft.get(0).getTheSpot().getCentroidX(), refsLeft.get(0).getTheSpot().getCentroidY()); Rray[1] = new com.reijns.I3S.Point2D(refsLeft.get(1).getTheSpot().getCentroidX(), refsLeft.get(1).getTheSpot().getCentroidY()); Rray[2] = new com.reijns.I3S.Point2D(refsLeft.get(2).getTheSpot().getCentroidX(), refsLeft.get(2).getTheSpot().getCentroidY()); System.out.println(" I found three left reference points!"); } else { com.reijns.I3S.Point2D topLeft = new com.reijns.I3S.Point2D(getLeftmostSpot(), getHighestSpot()); com.reijns.I3S.Point2D bottomLeft = new com.reijns.I3S.Point2D(getLeftmostSpot(), getLowestSpot()); com.reijns.I3S.Point2D bottomRight = new com.reijns.I3S.Point2D(getRightmostSpot(), getLowestSpot()); Rray[0] = topLeft; Rray[1] = bottomLeft; Rray[2] = bottomRight; } return Rray; } public com.reijns.I3S.Point2D[] getThreeRightFiducialPoints() { com.reijns.I3S.Point2D[] Rray = new com.reijns.I3S.Point2D[3]; if (getRightReferenceSpots() != null) { ArrayList<SuperSpot> refsRight = getRightReferenceSpots(); Rray[0] = new com.reijns.I3S.Point2D(refsRight.get(0).getTheSpot().getCentroidX(), refsRight.get(0).getTheSpot().getCentroidY()); Rray[1] = new com.reijns.I3S.Point2D(refsRight.get(1).getTheSpot().getCentroidX(), refsRight.get(1).getTheSpot().getCentroidY()); Rray[2] = new com.reijns.I3S.Point2D(refsRight.get(2).getTheSpot().getCentroidX(), refsRight.get(2).getTheSpot().getCentroidY()); } else { com.reijns.I3S.Point2D topRight = new com.reijns.I3S.Point2D(getRightmostRightSpot(), getHighestRightSpot()); com.reijns.I3S.Point2D bottomRight = new com.reijns.I3S.Point2D(getRightmostRightSpot(), getLowestRightSpot()); com.reijns.I3S.Point2D bottomLeft = new com.reijns.I3S.Point2D(getLeftmostRightSpot(), getLowestRightSpot()); Rray[0] = topRight; Rray[1] = bottomRight; Rray[2] = bottomLeft; } return Rray; } public double getRightmostRightSpot() { double rightest = 0; ArrayList<SuperSpot> rightSpots = getRightSpots(); for (int iter = 0; iter < rightSpots.size(); iter++) { if (rightSpots.get(iter).getTheSpot().getCentroidX() > rightest) { rightest = rightSpots.get(iter).getTheSpot().getCentroidX(); } } return rightest; } public double getLeftmostRightSpot() { double leftest = getRightmostRightSpot(); ArrayList<SuperSpot> rightSpots = getRightSpots(); for (int iter = 0; iter < rightSpots.size(); iter++) { if (rightSpots.get(iter).getTheSpot().getCentroidX() < leftest) { leftest = rightSpots.get(iter).getTheSpot().getCentroidX(); } } return leftest; } public double getHighestRightSpot() { double highest = getLowestRightSpot(); ArrayList<SuperSpot> rightSpots = getRightSpots(); for (int iter = 0; iter < rightSpots.size(); iter++) { if (rightSpots.get(iter).getTheSpot().getCentroidY() < highest) { highest = rightSpots.get(iter).getTheSpot().getCentroidY(); } } return highest; } public double getLowestRightSpot() { double lowest = 0; ArrayList<SuperSpot> rightSpots = getRightSpots(); for (int iter = 0; iter < rightSpots.size(); iter++) { if (rightSpots.get(iter).getTheSpot().getCentroidY() > lowest) { lowest = rightSpots.get(iter).getTheSpot().getCentroidY(); } } return lowest; } public ArrayList<SuperSpot> getLeftReferenceSpots() { return HACKgetAnyReferenceSpots(); } public ArrayList<SuperSpot> getRightReferenceSpots() { return HACKgetAnyReferenceSpots(); } /* gone! no more setting spots on encounters! ... whoa there, yes there is for whaleshark.org */ public void setLeftReferenceSpots(ArrayList<SuperSpot> leftReferenceSpots) { this.leftReferenceSpots = leftReferenceSpots; } public void setRightReferenceSpots(ArrayList<SuperSpot> rightReferenceSpots) { this.rightReferenceSpots = rightReferenceSpots; } /** * @param population array values to get the variance for * @return the variance */ public double variance(double[] population) { long n = 0; double mean = 0; double s = 0.0; for (double x : population) { n++; double delta = x - mean; mean += delta / n; s += delta * (x - mean); } // if you want to calculate std deviation // of a sample change this to (s/(n-1)) //return (s / n); return (s / (n - 1)); } /** * @param population array values to get the standard deviation for * @return the standard deviation */ public double standard_deviation(double[] population) { return Math.sqrt(variance(population)); } /* GONE! no more spots on encounters public void setNumLeftSpots(int numspots) { numSpotsLeft = numspots; } public void setNumRightSpots(int numspots) { numSpotsRight = numspots; } */ public void setDWCGlobalUniqueIdentifier(String guid) { this.guid = guid; } public String getDWCGlobalUniqueIdentifier() { return guid; } public void setDWCImageURL(String link) { dwcImageURL = link; } public String getDWCDateLastModified() { return modified; } public void setDWCDateLastModified(String lastModified) { modified = lastModified; } public void setDWCDateLastModified() { modified = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); } public String getDWCDateAdded() { return dwcDateAdded; } public Long getDWCDateAddedLong() { return dwcDateAddedLong; } public void setDWCDateAdded(String m_dateAdded) { dwcDateAdded = m_dateAdded; } public void setDWCDateAdded() { dwcDateAdded = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); } public void setDWCDateAdded(Long m_dateAdded) { dwcDateAddedLong = m_dateAdded; //org.joda.time.DateTime dt=new org.joda.time.DateTime(dwcDateAddedLong.longValue()); //DateTimeFormatter parser1 = ISODateTimeFormat.dateOptionalTimeParser(); //setDWCDateAdded(dt.toString(parser1)); //System.out.println(" Encounter.detDWCDateAded(Long): "+dt.toString(parser1)+" which is also "+m_dateAdded.longValue()); } //public void setDateAdded(long date){dateAdded=date;} //public long getDateAdded(){return dateAdded;} public Date getReleaseDateDONOTUSE() { return releaseDate; } public Date getReleaseDate() { if ((releaseDateLong != null) && (releaseDateLong > 0)) { Date mDate = new Date(releaseDateLong); return mDate; } return null; } public Long getReleaseDateLong() { return releaseDateLong; } public void setReleaseDate(Long releaseDate) { this.releaseDateLong = releaseDate; } public void setDWCDecimalLatitude(double lat) { if (lat == -9999.0) { decimalLatitude = null; } else { decimalLatitude = (new Double(lat)); } } public void setDWCDecimalLatitude(Double lat) { if ((lat != null) && (lat <= 90) && (lat >= -90)) { this.decimalLatitude = lat; } else { this.decimalLatitude = null; } } public String getDWCDecimalLatitude() { if (decimalLatitude != null) { return Double.toString(decimalLatitude); } return null; } public void setDWCDecimalLongitude(double longit) { if ((longit >= -180) && (longit <= 180)) { this.decimalLongitude = longit; } } public String getDWCDecimalLongitude() { if (decimalLongitude != null) { return Double.toString(decimalLongitude); } return null; } public boolean getOKExposeViaTapirLink() { return okExposeViaTapirLink; } public void setOKExposeViaTapirLink(boolean ok) { okExposeViaTapirLink = ok; } public void setAlternateID(String newID) { this.otherCatalogNumbers = newID; } public String getAlternateID() { if (otherCatalogNumbers == null) { return null; } return otherCatalogNumbers; } public String getInformOthers() { if (informothers == null) { return ""; } return informothers; } public void setInformOthers(String others) { this.informothers = others; this.hashedInformOthers = Encounter.getHashOfEmailString(others); } public String getLocationID() { return locationID; } public void setLocationID(String newLocationID) { this.locationID = newLocationID.trim(); } public Double getMaximumDepthInMeters() { return maximumDepthInMeters; } public void setMaximumDepthInMeters(Double newDepth) { this.maximumDepthInMeters = newDepth; } public Double getMaximumElevationInMeters() { return maximumElevationInMeters; } public void setMaximumElevationInMeters(Double newElev) { this.maximumElevationInMeters = newElev; } public String getCatalogNumber() { return catalogNumber; } public void setCatalogNumber(String newNumber) { this.catalogNumber = newNumber; } public String getVerbatimLocality() { return verbatimLocality; } public void setVerbatimLocality(String vlcl) { this.verbatimLocality = vlcl; } public String getIndividualID() { return individualID; } public void setIndividualID(String indy) { if (indy == null) { individualID = null; return; } this.individualID = indy; } /* i cant for the life of me figure out why/how gps stuff is stored on encounters, cuz we have some strings and decimal (double, er Double?) values -- so i am doing my best to standardize on the decimal one (Double) .. half tempted to break out a class for this: lat/lon/alt/bearing etc */ public double getDecimalLatitudeAsDouble() { return decimalLatitude.doubleValue(); } public void setDecimalLatitude(Double lat) { this.decimalLatitude = lat; gpsLatitude = Util.decimalLatLonToString(lat); } public double getDecimalLongitudeAsDouble() { return decimalLongitude.doubleValue(); } public void setDecimalLongitude(Double lon) { this.decimalLongitude = lon; gpsLongitude = Util.decimalLatLonToString(lon); } public String getOccurrenceRemarks() { return occurrenceRemarks; } public void setOccurrenceRemarks(String remarks) { this.occurrenceRemarks = remarks; } public String getRecordedBy() { return recordedBy; } public void setRecordedBy(String submitterName) { this.recordedBy = submitterName; } public String getOtherCatalogNumbers() { return otherCatalogNumbers; } public void setOtherCatalogNumbers(String otherNums) { this.otherCatalogNumbers = otherNums; } public String getLivingStatus() { return livingStatus; } public void setLivingStatus(String status) { this.livingStatus = status; } public void setAge(Double a) { age = a; } public Double getAge() { return age; } public String getBehavior() { return behavior; } public void setBehavior(String beh) { this.behavior = beh; } public String getEventID() { return eventID; } public void setEventID(String id) { this.eventID = id; } public String getVerbatimEventDate() { return verbatimEventDate; } public void setVerbatimEventDate(String vet) { if (vet != null) { this.verbatimEventDate = vet; } else { this.verbatimEventDate = null; } } public String getDynamicProperties() { return dynamicProperties; } public void setDynamicProperty(String name, String value) { name = name.replaceAll(";", "_").trim().replaceAll("%20", " "); value = value.replaceAll(";", "_").trim(); if (dynamicProperties == null) { dynamicProperties = name + "=" + value + ";"; } else { //let's create a TreeMap of the properties TreeMap<String, String> tm = new TreeMap<String, String>(); StringTokenizer st = new StringTokenizer(dynamicProperties, ";"); while (st.hasMoreTokens()) { String token = st.nextToken(); int equalPlace = token.indexOf("="); try { tm.put(token.substring(0, equalPlace), token.substring(equalPlace + 1)); } catch (java.lang.StringIndexOutOfBoundsException soe) { //this is a badly formatted pair that should be ignored } } if (tm.containsKey(name)) { tm.remove(name); tm.put(name, value); //now let's recreate the dynamicProperties String String newProps = tm.toString(); int stringSize = newProps.length(); dynamicProperties = newProps.substring(1, (stringSize - 1)).replaceAll(", ", ";") + ";"; } else { dynamicProperties = dynamicProperties + name + "=" + value + ";"; } } } public String getDynamicPropertyValue(String name) { if (dynamicProperties != null) { name = name.replaceAll("%20", " "); //let's create a TreeMap of the properties TreeMap<String, String> tm = new TreeMap<String, String>(); StringTokenizer st = new StringTokenizer(dynamicProperties, ";"); while (st.hasMoreTokens()) { String token = st.nextToken(); int equalPlace = token.indexOf("="); tm.put(token.substring(0, equalPlace), token.substring(equalPlace + 1)); } if (tm.containsKey(name)) { return tm.get(name); } } return null; } public void removeDynamicProperty(String name) { name = name.replaceAll(";", "_").trim().replaceAll("%20", " "); if (dynamicProperties != null) { //let's create a TreeMap of the properties TreeMap<String, String> tm = new TreeMap<String, String>(); StringTokenizer st = new StringTokenizer(dynamicProperties, ";"); while (st.hasMoreTokens()) { String token = st.nextToken(); int equalPlace = token.indexOf("="); tm.put(token.substring(0, (equalPlace)), token.substring(equalPlace + 1)); } if (tm.containsKey(name)) { tm.remove(name); //now let's recreate the dynamicProperties String String newProps = tm.toString(); int stringSize = newProps.length(); dynamicProperties = newProps.substring(1, (stringSize - 1)).replaceAll(", ", ";") + ";"; } } } public String getIdentificationRemarks() { return identificationRemarks; } public String getHashedSubmitterEmail() { return hashedSubmitterEmail; } public String getHashedPhotographerEmail() { return hashedPhotographerEmail; } public String getHashedInformOthers() { return hashedInformOthers; } public static String getHashOfEmailString(String hashMe) { String returnString = ""; StringTokenizer tokenizer = new StringTokenizer(hashMe, ","); while (tokenizer.hasMoreTokens()) { String emailAddress = tokenizer.nextToken().trim().toLowerCase(); if (!emailAddress.equals("")) { String md5 = DigestUtils.md5Hex(emailAddress); if (returnString.equals("")) { returnString += md5; } else { returnString += "," + md5; } } } return returnString; } public String getGenus() { return genus; } public void setGenus(String newGenus) { if (newGenus != null) { genus = newGenus; } else { genus = null; } updateAnnotationTaxonomy(); } public String getSpecificEpithet() { return specificEpithet; } public void setSpecificEpithet(String newEpithet) { if (newEpithet != null) { specificEpithet = newEpithet; } else { specificEpithet = null; } updateAnnotationTaxonomy(); } private void updateAnnotationTaxonomy() { //TODO make this, duh } public String getTaxonomyString() { return Util.taxonomyString(getGenus(), getSpecificEpithet()); } public String getPatterningCode() { return patterningCode; } public void setPatterningCode(String newCode) { this.patterningCode = newCode; } //crawls thru assets and sets date.. in an ideal world would do some kinda avg or whatever if more than one TODO? public void setDateFromAssets() { //FIXME if you dare. i can *promise you* there are some timezone problems here. ymmv. if ((annotations == null) || (annotations.size() < 1)) return; DateTime dt = null; for (Annotation ann : annotations) { MediaAsset ma = ann.getMediaAsset(); if (ma == null) continue; dt = ma.getDateTime(); if (dt != null) break; //we just take the first one } if (dt != null) setDateInMilliseconds(dt.getMillis()); } public void setSpeciesFromAssets() { if ((annotations == null) || (annotations.size() < 1)) return; String[] sp = IBEISIA.convertSpecies(annotations.get(0).getSpecies()); if (sp.length > 0) this.setGenus(sp[0]); if (sp.length > 1) this.setSpecificEpithet(sp[1]); } //find the first one(s) we can public void setLatLonFromAssets() { if ((annotations == null) || (annotations.size() < 1)) return; Double lat = null; Double lon = null; for (Annotation ann : annotations) { MediaAsset ma = ann.getMediaAsset(); if (ma == null) continue; if (lat == null) lat = ma.getLatitude(); if (lon == null) lon = ma.getLongitude(); if ((lat != null) && (lon != null)) break; } if (lat != null) this.setDecimalLatitude(lat); if (lon != null) this.setDecimalLongitude(lon); } public void resetDateInMilliseconds() { if (year > 0) { int localMonth = 0; if (month > 0) { localMonth = month - 1; } int localDay = 1; if (day > 0) { localDay = day; } int localHour = 0; if (hour > -1) { localHour = hour; } int myMinutes = 0; try { myMinutes = Integer.parseInt(minutes); } catch (Exception e) { } GregorianCalendar gc = new GregorianCalendar(year, localMonth, localDay, localHour, myMinutes); dateInMilliseconds = new Long(gc.getTimeInMillis()); } else { dateInMilliseconds = null; } } public java.lang.Long getDateInMilliseconds() { return dateInMilliseconds; } // this will set all date stuff based on ms since epoch public void setDateInMilliseconds(long ms) { Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(ms); this.year = cal.get(Calendar.YEAR); this.month = cal.get(Calendar.MONTH) + 1; this.day = cal.get(Calendar.DAY_OF_MONTH); this.hour = cal.get(Calendar.HOUR); this.minutes = Integer.toString(cal.get(Calendar.MINUTE)); if (this.minutes.length() == 1) this.minutes = "0" + this.minutes; this.dateInMilliseconds = ms; } public String getDecimalLatitude() { if (decimalLatitude != null) { return Double.toString(decimalLatitude); } return null; } //public void setDecimalLatitude(String lat){this.decimalLatitude=Double.parseDouble(lat);} public String getDecimalLongitude() { if (decimalLongitude != null) { return Double.toString(decimalLongitude); } return null; } public String getSubmitterProject() { return submitterProject; } public void setSubmitterProject(String newProject) { if (newProject != null) { submitterProject = newProject; } else { submitterProject = null; } } public String getSubmitterOrganization() { return submitterOrganization; } public void setSubmitterOrganization(String newOrg) { if (newOrg != null) { submitterOrganization = newOrg; } else { submitterOrganization = null; } } // public List<DataCollectionEvent> getCollectedData(){return collectedData;} /* public ArrayList<DataCollectionEvent> getCollectedDataOfType(String type){ ArrayList<DataCollectionEvent> filteredList=new ArrayList<DataCollectionEvent>(); int cdSize=collectedData.size(); System.out.println("cdSize="+cdSize); for(int i=0;i<cdSize;i++){ System.out.println("i="+i); DataCollectionEvent tempDCE=collectedData.get(i); if(tempDCE.getType().equals(type)){filteredList.add(tempDCE);} } return filteredList; } */ /* public <T extends DataCollectionEvent> List<T> getCollectedDataOfClass(Class<T> clazz) { List<DataCollectionEvent> collectedData = getCollectedData(); List<T> result = new ArrayList<T>(); for (DataCollectionEvent dataCollectionEvent : collectedData) { if (dataCollectionEvent.getClass().isAssignableFrom(clazz)) { result.add((T) dataCollectionEvent); } } return result; } public <T extends DataCollectionEvent> List<T> getCollectedDataOfClassAndType(Class<T> clazz, String type) { List<T> collectedDataOfClass = getCollectedDataOfClass(clazz); List<T> result = new ArrayList<T>(); for (T t : collectedDataOfClass) { if (type.equals(t.getType())) { result.add(t); } } return result; } public void addCollectedDataPoint(DataCollectionEvent dce){ if(collectedData==null){collectedData=new ArrayList<DataCollectionEvent>();} if(!collectedData.contains(dce)){collectedData.add(dce);} } public void removeCollectedDataPoint(int num){collectedData.remove(num);} */ public void addTissueSample(TissueSample dce) { if (tissueSamples == null) { tissueSamples = new ArrayList<TissueSample>(); } if (!tissueSamples.contains(dce)) { tissueSamples.add(dce); } } public void removeTissueSample(int num) { tissueSamples.remove(num); } public List<TissueSample> getTissueSamples() { return tissueSamples; } public void removeTissueSample(TissueSample num) { tissueSamples.remove(num); } public void addSinglePhotoVideo(SinglePhotoVideo dce) { if (images == null) { images = new ArrayList<SinglePhotoVideo>(); } if (!images.contains(dce)) { images.add(dce); } } public void removeSinglePhotoVideo(int num) { images.remove(num); } public List<SinglePhotoVideo> getSinglePhotoVideo() { return images; } public void removeSinglePhotoVideo(SinglePhotoVideo num) { images.remove(num); } public void setMeasurement(Measurement measurement, Shepherd myShepherd) { //if measurements are null, set the empty list if (measurements == null) { measurements = new ArrayList<Measurement>(); } //now start checking for existence of a previous measurement //if we have it but the new value is null, remove the measurement if ((this.hasMeasurement(measurement.getType())) && (measurement.getValue() == null)) { Measurement m = this.getMeasurement(measurement.getType()); measurements.remove(m); myShepherd.getPM().deletePersistent(m); myShepherd.commitDBTransaction(); myShepherd.beginDBTransaction(); } //just add the measurement it if we did not have it before else if (!this.hasMeasurement(measurement.getType())) { measurements.add(measurement); myShepherd.commitDBTransaction(); myShepherd.beginDBTransaction(); } //if we had it before then just update the value else if ((this.hasMeasurement(measurement.getType())) && (measurement != null)) { Measurement m = this.getMeasurement(measurement.getType()); m.setValue(measurement.getValue()); m.setSamplingProtocol(measurement.getSamplingProtocol()); myShepherd.commitDBTransaction(); myShepherd.beginDBTransaction(); } } public void removeMeasurement(int num) { measurements.remove(num); } public List<Measurement> getMeasurements() { return measurements; } public void removeMeasurement(Measurement num) { measurements.remove(num); } public Measurement findMeasurementOfType(String type) { List<Measurement> measurements = getMeasurements(); if (measurements != null) { for (Measurement measurement : measurements) { if (type.equals(measurement.getType())) { return measurement; } } } return null; } public void addMetalTag(MetalTag metalTag) { if (metalTags == null) { metalTags = new ArrayList<MetalTag>(); } metalTags.add(metalTag); } public void removeMetalTag(MetalTag metalTag) { metalTags.remove(metalTag); } public List<MetalTag> getMetalTags() { return metalTags; } public MetalTag findMetalTagForLocation(String location) { List<MetalTag> metalTags = getMetalTags(); if (metalTags != null) { for (MetalTag metalTag : metalTags) { if (location.equals(metalTag.getLocation())) { return metalTag; } } } return null; } public AcousticTag getAcousticTag() { return acousticTag; } public void setAcousticTag(AcousticTag acousticTag) { this.acousticTag = acousticTag; } public SatelliteTag getSatelliteTag() { return satelliteTag; } public void setSatelliteTag(SatelliteTag satelliteTag) { this.satelliteTag = satelliteTag; } public String getLifeStage() { return lifeStage; } public void setLifeStage(String newStage) { if (newStage != null) { lifeStage = newStage; } else { lifeStage = null; } } /** * A convenience method that returns the first haplotype found in the TissueSamples for this Encounter. * *@return a String if found or null if no haplotype is found */ public String getHaplotype() { //List<TissueSample> tissueSamples=getCollectedDataOfClass(TissueSample.class); int numTissueSamples = tissueSamples.size(); if (numTissueSamples > 0) { for (int j = 0; j < numTissueSamples; j++) { TissueSample thisSample = tissueSamples.get(j); int numAnalyses = thisSample.getNumAnalyses(); if (numAnalyses > 0) { List<GeneticAnalysis> gAnalyses = thisSample.getGeneticAnalyses(); for (int g = 0; g < numAnalyses; g++) { GeneticAnalysis ga = gAnalyses.get(g); if (ga.getAnalysisType().equals("MitochondrialDNA")) { MitochondrialDNAAnalysis mito = (MitochondrialDNAAnalysis) ga; if (mito.getHaplotype() != null) { return mito.getHaplotype(); } } } } } } return null; } /** * A convenience method that returns the first genetic sex found in the TissueSamples for this Encounter. * *@return a String if found or null if no genetic sex is found */ public String getGeneticSex() { if (tissueSamples != null) { int numTissueSamples = tissueSamples.size(); if (numTissueSamples > 0) { for (int j = 0; j < numTissueSamples; j++) { TissueSample thisSample = tissueSamples.get(j); int numAnalyses = thisSample.getNumAnalyses(); if (numAnalyses > 0) { List<GeneticAnalysis> gAnalyses = thisSample.getGeneticAnalyses(); for (int g = 0; g < numAnalyses; g++) { GeneticAnalysis ga = gAnalyses.get(g); if (ga.getAnalysisType().equals("SexAnalysis")) { SexAnalysis mito = (SexAnalysis) ga; if (mito.getSex() != null) { return mito.getSex(); } } } } } } } return null; } public List<SinglePhotoVideo> getImages() { return images; } public ArrayList<Annotation> getAnnotations() { return annotations; } public void setAnnotations(ArrayList<Annotation> anns) { annotations = anns; } public void addAnnotation(Annotation ann) { if (annotations == null) annotations = new ArrayList<Annotation>(); annotations.add(ann); } //convenience method public ArrayList<MediaAsset> getMedia() { ArrayList<MediaAsset> m = new ArrayList<MediaAsset>(); if ((annotations == null) || (annotations.size() < 1)) return m; for (Annotation ann : annotations) { MediaAsset ma = ann.getMediaAsset(); if (ma != null) m.add(ma); } return m; } // only checks top-level MediaAssets, not children or resized images public boolean hasTopLevelMediaAsset(int id) { return (indexOfMediaAsset(id) >= 0); } // finds the index of the MA we're looking for public int indexOfMediaAsset(int id) { if (annotations == null) return -1; for (int i = 0; i < annotations.size(); i++) { MediaAsset ma = annotations.get(i).getMediaAsset(); if (ma == null) continue; if (ma.getId() == id) return i; } return -1; } // creates a new annotation and attaches the asset public void addMediaAsset(MediaAsset ma) { Annotation ann = new Annotation(getTaxonomyString(), ma); annotations.add(ann); } public void removeAnnotation(int index) { annotations.remove(index); } public void removeMediaAsset(MediaAsset ma) { removeAnnotation(indexOfMediaAsset(ma.getId())); } //this is a kinda hacky way to find media ... really used by encounter.jsp now but likely should go away? public ArrayList<MediaAsset> findAllMediaByFeatureId(Shepherd myShepherd, String[] featureIds) { ArrayList<MediaAsset> mas = new ArrayList<MediaAsset>(); for (MediaAsset ma : getMedia()) { if (ma.hasFeatures(featureIds)) mas.add(ma); ArrayList<MediaAsset> kids = ma.findChildren(myShepherd); //note: does not recurse, but... meh? if ((kids == null) || (kids.size() < 1)) continue; for (MediaAsset kma : kids) { if (kma.hasFeatures(featureIds)) mas.add(kma); } } return mas; } //down-n-dirty with no myShepherd passed! :/ public ArrayList<MediaAsset> findAllMediaByFeatureId(String[] featureIds) { Shepherd myShepherd = new Shepherd("context0"); myShepherd.setAction("Encounter.class.findAllMediaByFeatureID"); myShepherd.beginDBTransaction(); ArrayList<MediaAsset> all = findAllMediaByFeatureId(myShepherd, featureIds); myShepherd.rollbackDBTransaction(); myShepherd.closeDBTransaction(); return all; } public ArrayList<MediaAsset> findAllMediaByLabel(Shepherd myShepherd, String label) { return MediaAsset.findAllByLabel(getMedia(), myShepherd, label); } /* public MediaAsset findOneMediaByLabel(Shepherd myShepherd, String label) { return MediaAsset.findOneByLabel(media, myShepherd, label); } */ public boolean hasKeyword(Keyword word) { int imagesSize = images.size(); for (int i = 0; i < imagesSize; i++) { SinglePhotoVideo image = images.get(i); if (image.getKeywords().contains(word)) { return true; } } return false; } public String getState() { return state; } public void setState(String newState) { this.state = newState; } //DO NOT USE - LEGACY MIGRATION ONLY /* public boolean getApproved(){return approved;} public boolean getUnidentifiable(){return unidentifiable;} */ public Vector getOldAdditionalImageNames() { return additionalImageNames; } public Double getLatitudeAsDouble() { return decimalLatitude; } public Double getLongitudeAsDouble() { return decimalLongitude; } public boolean hasMeasurements() { if ((measurements != null) && (measurements.size() > 0)) { int numMeasurements = measurements.size(); for (int i = 0; i < numMeasurements; i++) { Measurement m = measurements.get(i); if (m.getValue() != null) { return true; } } } return false; } public boolean hasMeasurement(String type) { if ((measurements != null) && (measurements.size() > 0)) { int numMeasurements = measurements.size(); for (int i = 0; i < numMeasurements; i++) { Measurement m = measurements.get(i); if ((m.getValue() != null) && (m.getType().equals(type))) { return true; } } } return false; } public boolean hasBiologicalMeasurement(String type) { if ((tissueSamples != null) && (tissueSamples.size() > 0)) { int numTissueSamples = tissueSamples.size(); for (int i = 0; i < numTissueSamples; i++) { TissueSample ts = tissueSamples.get(i); if (ts.getBiologicalMeasurement(type) != null) { BiologicalMeasurement bm = ts.getBiologicalMeasurement(type); if (bm.getValue() != null) { return true; } } } } return false; } /** * Returns the first measurement of the specified type * @param type * @return */ public Measurement getMeasurement(String type) { if ((measurements != null) && (measurements.size() > 0)) { int numMeasurements = measurements.size(); for (int i = 0; i < numMeasurements; i++) { Measurement m = measurements.get(i); if ((m.getValue() != null) && (m.getType().equals(type))) { return m; } } } return null; } public BiologicalMeasurement getBiologicalMeasurement(String type) { if (tissueSamples != null) { int numTissueSamples = tissueSamples.size(); for (int y = 0; y < numTissueSamples; y++) { TissueSample ts = tissueSamples.get(y); if ((ts.getGeneticAnalyses() != null) && (ts.getGeneticAnalyses().size() > 0)) { int numMeasurements = ts.getGeneticAnalyses().size(); for (int i = 0; i < numMeasurements; i++) { GeneticAnalysis m = ts.getGeneticAnalyses().get(i); if (m.getAnalysisType().equals("BiologicalMeasurement")) { BiologicalMeasurement f = (BiologicalMeasurement) m; if ((f.getMeasurementType().equals(type)) && (f.getValue() != null)) { return f; } } } } } } return null; } public String getCountry() { return country; } public void setCountry(String newCountry) { if (newCountry != null) { country = newCountry; } else { country = null; } } public void setOccurrenceID(String vet) { if (vet != null) { this.occurrenceID = vet; } else { this.occurrenceID = null; } } public String getOccurrenceID() { return occurrenceID; } public boolean hasSinglePhotoVideoByFileName(String filename) { int numImages = images.size(); for (int i = 0; i < numImages; i++) { SinglePhotoVideo single = images.get(i); if (single.getFilename().trim().toLowerCase().equals(filename.trim().toLowerCase())) { return true; } } return false; } //convenience function to Collaboration permissions public boolean canUserAccess(HttpServletRequest request) { return Collaboration.canUserAccessEncounter(this, request); } public JSONObject sanitizeJson(HttpServletRequest request, JSONObject jobj) throws JSONException { jobj.put("location", this.getLocation()); boolean fullAccess = this.canUserAccess(request); //these are for convenience, like .hasImages above (for use in table building e.g.) if ((this.getTissueSamples() != null) && (this.getTissueSamples().size() > 0)) jobj.put("hasTissueSamples", true); if (this.hasMeasurements()) jobj.put("hasMeasurements", true); /* String context="context0"; context = ServletUtilities.getContext(request); Shepherd myShepherd = new Shepherd(context); if ((myShepherd.getAllTissueSamplesForEncounter(this.getCatalogNumber())!=null) && (myShepherd.getAllTissueSamplesForEncounter(this.getCatalogNumber()).size()>0)) jobj.put("hasTissueSamples", true); if ((myShepherd.getMeasurementsForEncounter(this.getCatalogNumber())!=null) && (myShepherd.getMeasurementsForEncounter(this.getCatalogNumber()).size()>0)) jobj.put("hasMeasurements", true); */ jobj.put("_imagesNote", ".images have been deprecated! long live MediaAssets! (see: .annotations)"); //jobj.remove("images"); //TODO uncomment after debugging /* if ((this.getImages() != null) && (this.getImages().size() > 0)) { jobj.put("hasImages", true); JSONArray jarr = new JSONArray(); for (SinglePhotoVideo spv : this.getImages()) { jarr.put(spv.sanitizeJson(request, fullAccess)); } jobj.put("images", jarr); } */ if ((this.getAnnotations() != null) && (this.getAnnotations().size() > 0)) { jobj.put("hasAnnotations", true); JSONArray jarr = new JSONArray(); for (Annotation ann : this.getAnnotations()) { jarr.put(ann.sanitizeJson(request, fullAccess)); } jobj.put("annotations", jarr); } if (fullAccess) return jobj; jobj.remove("gpsLatitude"); jobj.remove("location"); jobj.remove("gpsLongitude"); jobj.remove("verbatimLocality"); jobj.remove("locationID"); jobj.remove("gpsLongitude"); jobj.put("_sanitized", true); return jobj; } public JSONObject uiJson(HttpServletRequest request) throws JSONException { JSONObject jobj = new JSONObject(); jobj.put("individualID", this.getIndividualID()); jobj.put("url", this.getUrl(request)); jobj.put("year", this.getYear()); jobj.put("month", this.getMonth()); jobj.put("day", this.getDay()); jobj.put("gpsLatitude", this.getGPSLatitude()); jobj.put("gpsLongitude", this.getGPSLongitude()); jobj.put("location", this.getLocation()); jobj.put("locationID", this.getLocationID()); jobj = sanitizeJson(request, jobj); // we don't want annotations, which are added by sanitizeJson jobj.remove("annotations"); return jobj; } public String getUrl(HttpServletRequest request) { return request.getScheme() + "://" + CommonConfiguration.getURLLocation(request) + "/encounters/encounter.jsp?number=" + this.getCatalogNumber(); } /** * returns an array of the MediaAsset sanitized JSON, because whenever UI queries our DB (regardless of class query), * all they want in return are MediaAssets * TODO: decorate with metadata **/ public org.datanucleus.api.rest.orgjson.JSONArray sanitizeMedia(HttpServletRequest request) throws org.datanucleus.api.rest.orgjson.JSONException { org.datanucleus.api.rest.orgjson.JSONArray jarr = new org.datanucleus.api.rest.orgjson.JSONArray(); boolean fullAccess = this.canUserAccess(request); if ((this.getAnnotations() != null) && (this.getAnnotations().size() > 0)) { for (Annotation ann : this.getAnnotations()) { jarr.put(ann.sanitizeMedia(request, fullAccess)); } } return jarr; } //this simple version makes some assumptions: you already have list of collabs, and it is not visible public String collaborationLockHtml(List<Collaboration> collabs) { Collaboration c = Collaboration.findCollaborationWithUser(this.getAssignedUsername(), collabs); String collabClass = "pending"; if ((c == null) || (c.getState() == null)) { collabClass = "new"; } else if (c.getState().equals(Collaboration.STATE_REJECTED)) { collabClass = "blocked"; } return "<div class=\"row-lock " + collabClass + " collaboration-button\" data-collabowner=\"" + this.getAssignedUsername() + "\" data-collabownername=\"" + this.getSubmitterName() + "\"> </div>"; } //pass in a Vector of Encounters, get out a list that the user can NOT see public static Vector blocked(Vector encs, HttpServletRequest request) { Vector blk = new Vector(); for (int i = 0; i < encs.size(); i++) { Encounter e = (Encounter) encs.get(i); if (!e.canUserAccess(request)) blk.add(e); } return blk; } /* in short, this rebuilds (or builds for the first time) ALL *derived* images (etc?) for this encounter. it is a baby step into the future of MediaAssets that hopefully will provide a smooth(er) transition to that. right now its primary purpose is to create derived formats upon encounter creation; but that is obviously subject to change. it should be considered an asyncronous action that happens in the background magickally */ /////other possiblity: only pass basedir??? do we need context if we do that? public boolean refreshAssetFormats(Shepherd myShepherd) { ArrayList<MediaAsset> mas = this.getMedia(); if ((mas == null) || (mas.size() < 1)) return true; for (MediaAsset ma : mas) { ma.updateStandardChildren(myShepherd); } return true; } /* NOTE on "thumb.jpg" ... we only get one of these per encounter; and we do not have stored (i dont think?) which SPV it came from! this is a problem, as we cant make a thumb in refreshAssetFormats(req, spv) since we dont know if that is the "right" spv. thus, we have to treat it as a special case. */ /* public boolean refreshAssetFormats(String context, String baseDir) { boolean ok = true; //List<SinglePhotoVideo> allSPV = this.getImages(); boolean thumb = true; for (SinglePhotoVideo spv : this.getImages()) { ok &= this.refreshAssetFormats(context, baseDir, spv, thumb); thumb = false; } return ok; } //as above, but for specific SinglePhotoVideo public boolean refreshAssetFormats(String context, String baseDir, SinglePhotoVideo spv, boolean doThumb) { if (spv == null) return false; String encDir = this.dir(baseDir); boolean ok = true; if (doThumb) ok &= spv.scaleTo(context, 100, 75, encDir + File.separator + "thumb.jpg"); //TODO some day this will be a structure/definition that lives in a config file or on MediaAsset, etc. for now, ya get hard-coded //this will first try watermark version, then regular ok &= (spv.scaleToWatermark(context, 250, 200, encDir + File.separator + spv.getDataCollectionEventID() + ".jpg", "") || spv.scaleTo(context, 250, 200, encDir + File.separator + spv.getDataCollectionEventID() + ".jpg")); ok &= spv.scaleTo(context, 1024, 768, encDir + File.separator + spv.getDataCollectionEventID() + "-mid.jpg"); //for use in VM tool etc. (bandwidth friendly?) return ok; } */ //see also: future, MediaAssets public String getThumbnailUrl(String context) { MediaAsset ma = getPrimaryMediaAsset(); if (ma == null) return null; String url = null; Shepherd myShepherd = new Shepherd(context); myShepherd.setAction("Encounter.class.getThumbnailUrl"); myShepherd.beginDBTransaction(); ArrayList<MediaAsset> kids = ma.findChildrenByLabel(myShepherd, "_thumb"); if ((kids == null) || (kids.size() <= 0)) { myShepherd.rollbackDBTransaction(); myShepherd.closeDBTransaction(); return null; } ma = kids.get(0); if (ma.webURL() == null) { myShepherd.rollbackDBTransaction(); myShepherd.closeDBTransaction(); return null; } url = ma.webURL().toString(); myShepherd.rollbackDBTransaction(); myShepherd.closeDBTransaction(); return url; } //this probably needs a better name and should allow for something more like an ordered list; that said, // knowing we can always try to get THE ONE is probably useful too public MediaAsset getPrimaryMediaAsset() { ArrayList<MediaAsset> mas = getMedia(); if (mas.size() < 1) return null; //here we could walk thru and find keywords, for example return mas.get(0); } public boolean restAccess(HttpServletRequest request, org.json.JSONObject jsonobj) throws Exception { ApiAccess access = new ApiAccess(); System.out.println("hello i am in restAccess() on Encounter"); String fail = access.checkRequest(this, request, jsonobj); System.out.println("fail -----> " + fail); if (fail != null) throw new Exception(fail); //HashMap<String, String> perm = access.permissions(this, request); //System.out.println(perm); /* System.out.println("!!!----------------------------------------"); System.out.println(request.getMethod()); throw new Exception(); */ return true; } ///////// these are bunk now - dont use Features TODO fix these - perhaps by crawlng thru ma.getAnnotations() ? public static Encounter findByMediaAsset(MediaAsset ma, Shepherd myShepherd) { String queryString = "SELECT FROM org.ecocean.Encounter WHERE annotations.contains(ann) && ann.mediaAsset.id ==" + ma.getId(); Encounter returnEnc = null; Query query = myShepherd.getPM().newQuery(queryString); List results = (List) query.execute(); if ((results != null) && (results.size() >= 1)) { returnEnc = (Encounter) results.get(0); } query.closeAll(); return returnEnc; } public static List<Encounter> findAllByMediaAsset(MediaAsset ma, Shepherd myShepherd) { List<Encounter> returnEncs = new ArrayList<Encounter>(); try { String queryString = "SELECT FROM org.ecocean.Encounter WHERE annotations.contains(ann) && ann.mediaAsset.id ==" + ma.getId(); //String queryString = "SELECT FROM org.ecocean.Encounter WHERE annotations.contains(ann) && ann.features.contains(mAsset) && mAsset.id ==" + ma.getId(); Query query = myShepherd.getPM().newQuery(queryString); Collection results = (Collection) query.execute(); returnEncs = new ArrayList<Encounter>(results); query.closeAll(); } catch (Exception e) { } return returnEncs; } public static Encounter findByAnnotation(Annotation annot, Shepherd myShepherd) { String queryString = "SELECT FROM org.ecocean.Encounter WHERE annotations.contains(ann) && ann.id =='" + annot.getId() + "'"; Encounter returnEnc = null; Query query = myShepherd.getPM().newQuery(queryString); List results = (List) query.execute(); if ((results != null) && (results.size() >= 1)) { if (results.size() > 1) System.out.println("WARNING: Encounter.findByAnnotation() found " + results.size() + " Encounters that contain Annotation " + annot.getId()); returnEnc = (Encounter) results.get(0); } query.closeAll(); return returnEnc; } public static Encounter findByAnnotationId(String annid, Shepherd myShepherd) { Annotation ann = ((Annotation) (myShepherd.getPM() .getObjectById(myShepherd.getPM().newObjectIdInstance(Annotation.class, annid), true))); if (ann == null) return null; return findByAnnotation(ann, myShepherd); } /* not really sure we need this now/yet public void refreshDependentProperties() { this.resetDateInMilliseconds(); //TODO could possibly do integrity check, re: individuals/occurrences linking? } */ public static ArrayList<Encounter> getEncountersForMatching(String taxonomyString, Shepherd myShepherd) { if (_matchEncounterCache.get(taxonomyString) != null) return _matchEncounterCache.get(taxonomyString); ArrayList<Encounter> encs = new ArrayList<Encounter>(); String queryString = "SELECT FROM org.ecocean.media.MediaAsset WHERE !features.isEmpty()"; Query query = myShepherd.getPM().newQuery(queryString); List results = (List) query.execute(); for (int i = 0; i < results.size(); i++) { MediaAsset ma = (MediaAsset) results.get(i); MediaAsset top = ma.getParentRoot(myShepherd); if (top == null) continue; Encounter enc = Encounter.findByMediaAsset(top, myShepherd); if (enc == null) System.out.println("could not find enc for ma " + ma); if (enc == null) continue; if (!enc.getTaxonomyString().equals(taxonomyString)) continue; if (!encs.contains(enc)) encs.add(enc); } query.closeAll(); _matchEncounterCache.put(taxonomyString, encs); return encs; } /* this section are intentionally hacky backwards-compatible ways to get spots on an encounter in the new world of Features/Annotations/MediaAssets ... do not use these, of course... and SOON we must weed out all the encounter-based-spot calls from everywhere and clean all this mess up! */ public ArrayList<SuperSpot> HACKgetSpots() { return HACKgetAnySpots("spotsLeft"); } public ArrayList<SuperSpot> HACKgetRightSpots() { return HACKgetAnySpots("spotsRight"); } public ArrayList<SuperSpot> HACKgetAnySpots(String which) { /* RuntimeException ex = new RuntimeException(" ===== DEPRECATED ENCOUNTER SPOT BEHAVIOR! PLEASE FIX ====="); System.out.println(ex.toString()); ex.printStackTrace(); */ ArrayList<MediaAsset> mas = findAllMediaByFeatureId( new String[] { "org.ecocean.flukeEdge.edgeSpots", "org.ecocean.dorsalEdge.edgeSpots" }); if ((mas == null) || (mas.size() < 1)) return new ArrayList<SuperSpot>(); for (Feature f : mas.get(0).getFeatures()) { if (f.isType("org.ecocean.flukeEdge.edgeSpots") || f.isType("org.ecocean.dorsalEdge.edgeSpots")) { if (f.getParameters() != null) return SuperSpot.listFromJSONArray(f.getParameters().optJSONArray(which)); } } return new ArrayList<SuperSpot>(); } //err, i think ref spots are the same right or left.... at least for flukes/dorsals. :/ good luck with mantas and whalesharks! public ArrayList<SuperSpot> HACKgetAnyReferenceSpots() { /* RuntimeException ex = new RuntimeException(" ===== DEPRECATED ENCOUNTER SPOT BEHAVIOR! PLEASE FIX ====="); System.out.println(ex.toString()); ex.printStackTrace(); */ ArrayList<MediaAsset> mas = findAllMediaByFeatureId( new String[] { "org.ecocean.flukeEdge.referenceSpots", "org.ecocean.referenceEdge.edgeSpots" }); if ((mas == null) || (mas.size() < 1)) return new ArrayList<SuperSpot>(); for (Feature f : mas.get(0).getFeatures()) { if (f.isType("org.ecocean.flukeEdge.referenceSpots") || f.isType("org.ecocean.dorsalEdge.referenceSpots")) { if (f.getParameters() != null) return SuperSpot.listFromJSONArray(f.getParameters().optJSONArray("spots")); } } return new ArrayList<SuperSpot>(); } public Encounter cloneWithoutAnnotations() { Encounter enc = new Encounter(this.day, this.month, this.year, this.hour, this.minutes, this.size_guess, this.verbatimLocality, this.recordedBy, this.submitterEmail, null); enc.setCatalogNumber(Util.generateUUID()); return enc; } public String toString() { return new ToStringBuilder(this).append("catalogNumber", catalogNumber) .append("individualID", (hasMarkedIndividual() ? individualID : null)) .append("species", getTaxonomyString()).append("sex", getSex()).append("shortDate", getShortDate()) .append("numAnnotations", ((annotations == null) ? 0 : annotations.size())).toString(); } }