class GBIFQuery(GBIFQueryLayout): # Global variables to store queried information __obs__ = [] __selectedTaxon__ = set() __description__ = "" masterKeys = set() def __init__(self, parent=None): MaxLon, MinLon, MaxLat, MinLat = 180, -180, 90, -90 self.GBIFSpecific = GBIFSpecific() self.GBIFGeneric = GBIFGeneric() GBIFQueryLayout.__init__(self, parent) self.SetIcon( wx.Icon(GenGIS.mainWindow.GetExeDir() + "images/CrazyEye.ico", wx.BITMAP_TYPE_ICO)) self.m_Compass.SetIcon( wx.Icon( GenGIS.mainWindow.GetExeDir() + "images/GBIF_compass_small.png", wx.BITMAP_TYPE_PNG)) self.m_Add.SetBitmapLabel( wx.Image( GenGIS.mainWindow.GetExeDir() + "images/green_arrow_down.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()) self.m_Add.SetBitmapHover( wx.Image( GenGIS.mainWindow.GetExeDir() + "images/green_arrow_down_hover.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()) self.m_Remove.SetBitmapLabel( wx.Image(GenGIS.mainWindow.GetExeDir() + "images/red_arrow_up.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()) self.m_Remove.SetBitmapHover( wx.Image( GenGIS.mainWindow.GetExeDir() + "images/red_arrow_up_hover.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()) self.graphicalElementIds = [] self.__selectedTaxon__ = set() self.__obs__ = [] self.m_IDList.Clear() #fix to expand summary box enough to print two lines of text properly self.m_Summary.SetLabel("\n\n") #No Map Data self.m_AddData.Disable() #Map Data if GenGIS.layerTree.GetNumMapLayers() > 0: self.m_AddData.Enable() borders = GenGIS.layerTree.GetMapLayer( 0).GetController().GetMapBorders() #check if geographic coordinates are used or some other measure; only geographic are compatible geographic = GenGIS.StudyController.IsUsingGeographic( GenGIS.study.GetController()) projected = GenGIS.StudyController.IsUsingProjection( GenGIS.study.GetController()) if (not (geographic or projected)): wx.MessageBox( "Geographic coordinates are not being used in the current map file. Only geographic coordinates are compatible with MG-RAST. Geographic range will need to be manually set, and any returned data will not display correctly.", "Warning") self.m_AddData.Disable() self.m_MinLat.SetValue(str(MinLat)) self.m_MaxLat.SetValue(str(MaxLat)) self.m_MinLon.SetValue(str(MinLon)) self.m_MaxLon.SetValue(str(MaxLon)) else: #Text boxes hate non String types. use int to round, and string to make them fit the container # if projected coordinates but not geographic, a conversion may be possible if (projected and not geographic): convTop = self.GBIFGeneric.SpecialUTMConversion( borders.x1, borders.y1) convBottom = self.GBIFGeneric.SpecialUTMConversion( borders.dx, borders.dy) borders.x1 = convTop.easting borders.y1 = convTop.northing borders.dx = convBottom.easting borders.dy = convBottom.northing #limiting borders self.m_MinLat.SetValue( str("%.1f" % self.GBIFGeneric.BorderTest(MinLat, borders.y1, max))) self.m_MaxLat.SetValue( str("%.1f" % self.GBIFGeneric.BorderTest(MaxLat, borders.dy, min))) self.m_MinLon.SetValue( str("%.1f" % self.GBIFGeneric.BorderTest(MinLon, borders.x1, max))) self.m_MaxLon.SetValue( str("%.1f" % self.GBIFGeneric.BorderTest(MaxLon, borders.dx, min))) # Query GBIF for Taxa in Lat/Lon Boundary def OnSearch(self, event): wx.BeginBusyCursor() # Clear the results list self.m_Result.Clear() taxon = self.m_TaxonName.GetLineText(0) taxon = taxon.split() if (len(taxon) == 0): wx.MessageBox("You did not enter a taxon name.") else: minLatitude = float(self.m_MinLat.GetValue()) maxLatitude = float(self.m_MaxLat.GetValue()) minLongitude = float(self.m_MinLon.GetValue()) maxLongitude = float(self.m_MaxLon.GetValue()) result = self.GBIFSpecific.GETTAXRESULT(taxon, self.m_Result) self.m_Result.InsertItems(result, 0) wx.EndBusyCursor() # Create Sequence and Location files for selected Taxa def OnCalculate(self, event): self.m_Summary.SetLabel("\n") records, distLocations = 0, 0 self.__obs__ = [] self.__conversions__ = [] self.__description__ = "" wx.BeginBusyCursor() if (self.__selectedTaxon__): minLatitude = float(self.m_MinLat.GetValue()) maxLatitude = float(self.m_MaxLat.GetValue()) minLongitude = float(self.m_MinLon.GetValue()) maxLongitude = float(self.m_MaxLon.GetValue()) self.m_Progress.WriteText("Starting...\n") for tax in self.__selectedTaxon__: obs, self.masterKeys, records = self.GBIFSpecific.GETOBSENTIRERANGE( tax[0], minLatitude, maxLatitude, minLongitude, maxLongitude, self.m_Progress) self.__obs__.append(obs) distLocations += len(obs) self.m_Progress.WriteText("Done.\n") else: wx.MessageBox("Please select some Taxa.") summaryText = ("%d records retrieved.\n%d distinct locations." % (records, distLocations)) f = self.m_Summary.GetFont() dc = wx.WindowDC(self.m_Summary) dc.SetFont(f) aSize = dc.GetMultiLineTextExtent(summaryText) aSize = wx.Size(aSize[0], aSize[1]) self.m_Summary.SetSize(aSize) self.m_Summary.SetLabel(summaryText) wx.EndBusyCursor() # Present the number of locations a user is about to query # Used as a check by the user to know they aren't going to produce way too much data. def OnPreCalculate(self, event): wx.BeginBusyCursor() self.m_Progress.WriteText("Retrieving record counts.\n") self.m_Summary.SetLabel("\n") if (self.__selectedTaxon__): count = 0 for tax in self.__selectedTaxon__: count += self.GBIFSpecific.GETCOUNT(tax[1].split(), tax[0], self.m_Progress) self.m_Summary.SetLabel( "There were %d records for the given location." % count) else: wx.MessageBox("Please select some Taxa.") wx.EndBusyCursor() # Redirects User to Wiki page for this plugin def OnHelp(self, event): wx.LaunchDefaultBrowser( 'http://kiwi.cs.dal.ca/GenGIS/index.php/Description_of_GenGIS_plugins#GBIF_Query' ) # Adds Data to GenGIS def OnAddData(self, event): if (len(self.__obs__) > 0): OUTLText, OUTSText = self.GBIFGeneric.GETTEXT( self.__obs__, self.masterKeys) OUTLArray = self.GBIFGeneric.CPPOUT(OUTLText) OUTSArray = self.GBIFGeneric.CPPOUT(OUTSText) OUTLArray.pop() OUTSArray.pop() layerName = "GBIFLayer_%d" % GenGIS.layerTree.GetNumLocationSetLayers( ) GenGIS.mainWindow.OpenLocationsCSVFile(OUTLArray, layerName) GenGIS.mainWindow.OpenSequenceCSVFile(OUTSArray, layerName) #Get the number of last location layer added (the gbif one) numLocationLayers = GenGIS.layerTree.GetNumLocationSetLayers() locationSetLayer = GenGIS.layerTree.GetLocationSetLayer( numLocationLayers - 1) else: wx.MessageBox("Please make a successful GBIF Query first.") # Exports Location and Sequence Data to a location of the users choice def OnExportData(self, event): if (len(self.__obs__) > 0): fileTypes = 'Loc and Seq Files (*.csv)|*.csv' dlg = wx.FileDialog(self, "Save plot", "", "", fileTypes, wx.SAVE) if dlg.ShowModal() == wx.ID_OK: filename = dlg.GetFilename() dir = dlg.GetDirectory() file_split = filename.split(".", 1) #creates the directories OUTLfile = os.path.join(dir, file_split[0] + "_locs.csv") OUTSfile = os.path.join(dir, file_split[0] + "_seqs.csv") OUTLText, OUTSText = self.GBIFGeneric.GETTEXT( self.__obs__, self.masterKeys) self.GBIFGeneric.WRITEEXPORT(OUTLfile, OUTLText) self.GBIFGeneric.WRITEEXPORT(OUTSfile, OUTSText) dlg.Destroy() else: wx.MessageBox("Please make a successful GBIF Query first.") # Add Data from Results Table to ID List def OnAdd(self, event): i = 0 IDCount = self.m_IDList.GetCount() for index in self.m_Result.GetSelections(): selected = self.m_Result.GetString(index) split = selected.split(" | ") if (int(split[0]), split[1]) not in self.__selectedTaxon__: self.m_IDList.InsertItems(["%s" % selected], IDCount + i) self.__selectedTaxon__.add((int(split[0]), split[1])) i += 1 # Remove Data from ID List def OnRemove(self, event): candidates = sorted(self.m_IDList.GetSelections(), reverse=True) for index in candidates: selected = self.m_IDList.GetString(index) split = selected.split(" | ") self.__selectedTaxon__.remove((int(split[0]), split[1])) self.m_IDList.Delete(index) # Close the Plugin def OnClose(self, event): # remove plotted lines for id in self.graphicalElementIds: GenGIS.graphics.RemoveLine(id) GenGIS.viewport.Refresh() event.Skip() # Close the Plugin def OnOK(self, event): self.Close() def OnLatEnter(self, event): str = event.GetString() str2 = re.sub('[^\d\.\-]', '', str) if str2 and str != "-": str3 = float(str2) if str3 > 90: str2 = str2[:-1] elif str3 < -90: str2 = str2[:-1] event.GetClientData().SetValue(str2) event.GetClientData().SetInsertionPoint(len(str2)) def OnLonEnter(self, event): str = event.GetString() str2 = re.sub('[^\d\.\-]', '', str) if str2 and str != "-": str3 = float(str2) if str3 > 180: str2 = str[:-1] elif str3 < -180: str2 = str[:-1] event.GetClientData().SetValue(str2) event.GetClientData().SetInsertionPoint(len(str2))
class MGRastQuery(MGRASTQueryLayout): # Global variables to store queried information __obs__ = [] __selectedTaxon__ = set() __description__ = "" __options__ = "" __metaVals__ = {} __TAXON__ = [ "Domain", "Phylum", "Class", "Order", "Family", "Genus", "Species", "Strain" ] def __init__(self, parent=None): self.__options__ = Options() self.MGRastSpecific = MGRastSpecific() MGRASTQueryLayout.__init__(self, parent) self.GBIFGeneric = GBIFGeneric() self.SetIcon( wx.Icon(GenGIS.mainWindow.GetExeDir() + "images/CrazyEye.ico", wx.BITMAP_TYPE_ICO)) self.m_Add.SetBitmapLabel( wx.Image( GenGIS.mainWindow.GetExeDir() + "images/green_arrow_down.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()) self.m_Add.SetBitmapHover( wx.Image( GenGIS.mainWindow.GetExeDir() + "images/green_arrow_down_hover.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()) self.m_Remove.SetBitmapLabel( wx.Image(GenGIS.mainWindow.GetExeDir() + "images/red_arrow_up.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()) self.m_Remove.SetBitmapHover( wx.Image( GenGIS.mainWindow.GetExeDir() + "images/red_arrow_up_hover.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()) self.m_Compass.SetIcon( wx.Icon( GenGIS.mainWindow.GetExeDir() + "images/GBIF_compass_small.png", wx.BITMAP_TYPE_PNG)) MaxLon, MinLon, MaxLat, MinLat = 180, -180, 90, -90 self.m_IDList.Clear() #fix to expand summary box enough to print two lines of text properly self.m_Summary.SetLabel("\n\n") #Map Data if GenGIS.layerTree.GetNumMapLayers() > 0: self.m_AddData.Enable() borders = GenGIS.layerTree.GetMapLayer( 0).GetController().GetMapBorders() #check if geographic coordinates are used or some other measure; only geographic are compatible geographic = GenGIS.StudyController.IsUsingGeographic( GenGIS.study.GetController()) projected = GenGIS.StudyController.IsUsingProjection( GenGIS.study.GetController()) if (not (geographic or projected)): wx.MessageBox( "Geographic coordinates are not being used in the current map file. Only geographic coordinates are compatible with MG-RAST. Geographic range will need to be manually set, and any returned data will not display correctly.", "Warning") self.m_AddData.Disable() self.m_MinLat.SetValue(str(MinLat)) self.m_MaxLat.SetValue(str(MaxLat)) self.m_MinLon.SetValue(str(MinLon)) self.m_MaxLon.SetValue(str(MaxLon)) else: #Text boxes hate non String types. use int to round, and string to make them fit the container # if projected coordinates but not geographic, a conversion may be possible if (projected and not geographic): convTop = self.GBIFGeneric.SpecialUTMConversion( borders.x1, borders.y1) convBottom = self.GBIFGeneric.SpecialUTMConversion( borders.dx, borders.dy) borders.x1 = convTop.easting borders.y1 = convTop.northing borders.dx = convBottom.easting borders.dy = convBottom.northing #limiting borders self.m_MinLat.SetValue( str("%.1f" % self.GBIFGeneric.BorderTest(MinLat, borders.y1, max))) self.m_MaxLat.SetValue( str("%.1f" % self.GBIFGeneric.BorderTest(MaxLat, borders.dy, min))) self.m_MinLon.SetValue( str("%.1f" % self.GBIFGeneric.BorderTest(MinLon, borders.x1, max))) self.m_MaxLon.SetValue( str("%.1f" % self.GBIFGeneric.BorderTest(MaxLon, borders.dx, min))) # Query GBIF for Taxa in Lat/Lon Boundary def OnSearch(self, event): wx.BeginBusyCursor() # Clear the results list self.m_Result.Clear() searchType = self.__options__.GetSearchType() taxon = self.m_TaxonName.GetLineText(0) taxon = taxon.split() if (len(taxon) == 0): wx.MessageBox("You did not enter a taxon name.") else: minLatitude = float(self.m_MinLat.GetValue()) maxLatitude = float(self.m_MaxLat.GetValue()) minLongitude = float(self.m_MinLon.GetValue()) maxLongitude = float(self.m_MaxLon.GetValue()) if searchType == "study": self.__selectedTaxon__.clear() # test data for multiple study search mgm4440037.3 mgm4440055.3 mgm4440064.3 for tax in taxon: self.__selectedTaxon__.add(("dummy value", tax)) self.OnCalculate(wx.EVT_BUTTON) else: matches = self.MGRastSpecific.GETTAXRESULT( taxon, searchType, minLatitude, maxLatitude, minLongitude, maxLongitude, self.m_Summary) if matches: self.m_Result.InsertItems(matches, 0) wx.EndBusyCursor() # Create Sequence and Location files for selected Taxa ###################### # # CAN NOT PERFORM MORE THAN ONE QUERY PER SECOND # ###################### def OnCalculate(self, event): self.MGRastSpecific.RESETSEQUENCE() self.__obs__ = [] self.__metaVals__ = {} sequence = {} self.m_Summary.SetLabel("\n") records, distLocations = 0, 0 searchType = self.__options__.GetSearchType() additFields = "%s%s%s%s%s%s%s" % ( self.__options__.GetFilterLevel(), self.__options__.GetFilterSource(), self.__options__.GetGroupLevel(), self.__options__.GetHitType(), self.__options__.GetIdentity(), self.__options__.GetLength(), self.__options__.GetSource()) if (self.__selectedTaxon__): self.m_Progress.WriteText("Starting...\n") #index of which taxon is being used index = 0 for tax in self.__selectedTaxon__: startTime = time.time() obs, metaVals, taxonLength = self.MGRastSpecific.GETOBS( tax[1], searchType, additFields, self.m_Progress) # Some sort of error occured trying to get that object, skip to next one if not obs: self.SetFocus() continue taxonomy = [] for i in range(0, taxonLength): if i < len(self.__TAXON__): taxonomy.append(self.__TAXON__[i]) else: taxonomy[i].append("Level_%s" % i) self.__TAXON__ = taxonomy if obs: self.__obs__.append(obs) if sequence: self.__sequences__.append(sequence) # add all unique key/val pairs from json files for key, value in metaVals.iteritems(): if key in self.__metaVals__: self.__metaVals__[key][index] = value else: list = [''] * len(self.__selectedTaxon__) list[index] = value self.__metaVals__[key] = list index += 1 if (time.time() - startTime) < 1: sleep.time(1) # Now query again for the Sequence to that study if self.__options__.GetSequence(): fileTypes = 'Sequence data files (*.fasta)|*.fasta' dlg = wx.FileDialog(self, "Save plot", "", "", fileTypes, wx.SAVE) if dlg.ShowModal() == wx.ID_OK: filename = dlg.GetFilename() dir = dlg.GetDirectory() file_split = filename.split(".", 1) outFile = ("%s/%s_sequenceData.fasta.gz" % (dir, file_split[0])) self.MGRastSpecific.GETSEQUENCES(tax[1], self.m_Progress, outFile) else: wx.MessageBox("Please select some Taxa.") self.m_Progress.WriteText("Done\n") # Redirects User to Wiki page for this plugin def OnHelp(self, event): wx.LaunchDefaultBrowser( 'http://kiwi.cs.dal.ca/GenGIS/index.php/Description_of_GenGIS_plugins#MG-RAST_Query' ) # Adds Data to GenGIS def OnAddData(self, event): # check required data has been loaded if GenGIS.layerTree.GetNumMapLayers() == 0: wx.MessageBox( "This action requires a Map layer. Please load a Map.") self.Close() return taxonLevels = ','.join(str(i) for i in self.__TAXON__) if (len(self.__obs__) > 0): OUTLText, OUTSText = self.MGRastSpecific.GETTEXT( self.__obs__, self.__metaVals__) OUTLArray = self.GBIFGeneric.CPPOUT(OUTLText) OUTSArray = self.GBIFGeneric.CPPOUT(OUTSText) metKey = ','.join(sorted(self.__metaVals__.keys())) OUTLArray.insert(0, "Site ID,Latitude,Longitude,Cell ID,%s" % metKey) OUTSArray.insert( 0, "Sequence ID,Site ID,CellLat,CellLong,Richness,%s,Taxonomy" % taxonLevels) OUTLArray.pop() OUTSArray.pop() layerName = "MGRASTLayer_%d" % GenGIS.layerTree.GetNumLocationLayers( ) GenGIS.mainWindow.OpenLocationsCSVFile(OUTLArray, layerName) GenGIS.mainWindow.OpenSequenceCSVFile(OUTSArray, layerName) #Get the number of last location layer added (the gbif one) numLocationLayers = GenGIS.layerTree.GetNumLocationSetLayers() locationSetLayer = GenGIS.layerTree.GetLocationSetLayer( numLocationLayers - 1) # locationSetLayer.SetDescription(self.__description__) else: wx.MessageBox("Please make a successful MG-RAST Query first.") # Exports Location and Sequence Data to a location of the users choice def OnExportData(self, event): if (len(self.__obs__) > 0): taxonLevels = ','.join(str(i) for i in self.__TAXON__) fileTypes = 'Loc and Seq Files (*.csv)|*.csv' dlg = wx.FileDialog(self, "Save plot", "", "", fileTypes, wx.SAVE) if dlg.ShowModal() == wx.ID_OK: filename = dlg.GetFilename() dir = dlg.GetDirectory() file_split = filename.split(".", 1) #creates the directories OUTLfile = os.path.join(dir, file_split[0] + "_locs.csv") OUTSfile = os.path.join(dir, file_split[0] + "_seqs.csv") OUTLText, OUTSText = self.MGRastSpecific.GETTEXT( self.__obs__, self.__metaVals__) # metKey = ','.join(str(x) for x in self.__metaKeys__) metKey = ','.join(sorted(self.__metaVals__.keys())) # print metKey self.GBIFGeneric.WRITEEXPORT( OUTLfile, OUTLText, "Site ID,Latitude,Longitude,Cell ID,%s\n" % metKey) self.GBIFGeneric.WRITEEXPORT( OUTSfile, OUTSText, "Sequence ID,Site ID,CellLat,CellLong,Richness,%s,Taxonomy\n" % taxonLevels) # if self.__options__.GetSequence(): # self.MGRastSpecific.WRITESEQUENCES(dir,file_split[0],self.__sequences__) dlg.Destroy() else: wx.MessageBox("Please make a successful MG-RAST Query first.") # Add Data from Results Table to ID List def OnAdd(self, event): i = 0 IDCount = self.m_IDList.GetCount() for index in self.m_Result.GetSelections(): selected = self.m_Result.GetString(index) split = selected.split(" | ") if (split[0], split[1]) not in self.__selectedTaxon__: self.m_IDList.InsertItems(["%s" % selected], IDCount + i) #add study ID and study location self.__selectedTaxon__.add((split[0], split[1])) i += 1 # Remove Data from ID List def OnRemove(self, event): candidates = sorted(self.m_IDList.GetSelections(), reverse=True) for index in candidates: selected = self.m_IDList.GetString(index) split = selected.split(" | ") self.__selectedTaxon__.remove((split[0], split[1])) self.m_IDList.Delete(index) # Close the Plugin def OnClose(self, event): self.__options__.Close() event.Skip() # Close the Plugin def OnOK(self, event): self.Close() self.__options__.Close() def OnLatEnter(self, event): str = event.GetString() str2 = re.sub('[^\d\.\-]', '', str) if str2 and str != "-": str3 = float(str2) if str3 > 90: str2 = str2[:-1] elif str3 < -90: str2 = str2[:-1] event.GetClientData().SetValue(str2) event.GetClientData().SetInsertionPoint(len(str2)) def OnLonEnter(self, event): str = event.GetString() str2 = re.sub('[^\d\.\-]', '', str) if str2 and str != "-": str3 = float(str2) if str3 > 180: str2 = str[:-1] elif str3 < -180: str2 = str[:-1] event.GetClientData().SetValue(str2) event.GetClientData().SetInsertionPoint(len(str2)) def OnOptions(self, event): self.__options__.Show()