U g@sdZddddddddd d d d d ddddddddgZddlZddlZddlZddlZddlZddlZddlZ ddl Z ddl Z ddl m Z ddZeejjdZGdddeZGdddeZGdddeZGdddeZGd ddeZGd!ddeZGd"ddeZd#d Zd$d Zd%d Zd&d Zd'd Zd5d(dZ d6d*dZ!d7d-dZ"d8d/dZ#d9d0dZ$d:d2dZ%Gd3dde&Z'd;d4dZ(dS)t|j|j}|S)a!A cmp comparison for StormInfo objects intended to be used with sorted(). This is intended to be used on cleaned vitals returned by clean_up_vitals. For other purposes, use vitcmp. Uses the following method: 1. Sort numerically by when.year 2. Break ties by sorting lexically by stormid3 3. Break ties by sorting by date/time 4. Break ties by retaining original order ("stable sort"). @param a,b StormInfo objects to orderr)rwhenyear longstormidrrcrrrr s cCsVt|j|j}|dkr&t|j|j }|dkrsz#clean_up_vitals..cSsg|]}|qSrrrGrrrrJscSsg|]}|qSrrrHxrrrrJs) r r r sortedrlistsetreversedr addappend) vitalsname_number_checkerbasin_center_checker vitals_cmpZ sortvitalsZ nn_ok_vitalsZ keepvitalsZ revuniqvitalsseenr9rFZ uniqvitalsrrrrs,   $@cCsJtd|}|rF|}t|d|}d|krB|ddk rB| }|SdS)a!Converts a string like "551N" to 55.1, correctly handling the sign of each hemisphere. @code floatlatlon(string="311N",fact=10.0) = float(31.1) # degrees North @endcode @returns degrees North or degrees East @param string Latitude or longitude in sum multiple of a degree, followed by N, S, E or W to specify the hemisphere. Must be a positive number @param fact The strung value is divided by this number. The default is to convert from tenths of a degree in string to a degree return value. @note This function does not accept negative numbers. That means the tcvitals "badval" -999 or -99 or -9999 will result in a None return value. z@\A(?P0*\d+)(?:(?P[NnEe ])|(?P[SsWw]))\ZnumnegativeN)research groupdictfloat)stringfactmmdictZlatlonrrrrs ?c Cs<t|dkstdddddddd g}t|}d gd}d } tdD]`} || d k rB|| d krBt|| || <|| d krd|| <qB|| |9<t| || } qB|dkrtd krd||f} | || <|| nd|d krd S||} tdD]D} d||| d| t|d d|f} || || <|| qd S)a!Internal function that parses wind or sea quadrant information. This is part of the internal implementation of StormInfo: it deals with parsing wind or sea quadrant information. @param[out] data Output quadrant information, a dict mapping from the quadrant name in qset. @param[out] qset output set of quadrants that were seen @param irad Integer radius. For example, 34 may indicate the 34 kt wind radius. @param qcode Special code used for circular (all quadrants) data @param qdata String quadrant data information for each quadrant @param what What type of data is this? Wind radii? Sea height radii? This should be an alphanumeric string. @param conversion Unit conversion. ZNNQZNEQZEEQZSEQZSSQZSWQZWWQZNWQNgrcrZAAAz %sCIRCLE%dz%s%s%d) lenAssertionErrorintranger^maxcountrQindex) dataqsetiradZqcodeZqdatawhat conversionZquadrantZfqdataZmaxdataivarZiquadrrrr s0        * Fc Cs\t}|D]L}z|td|dWq ttfk rT}z |rDW5d}~XYq Xq |S)a !Reads data from a tcvitals file. This reads line by line from the given file object fd, parsing tcvitals. @returns A list of StormInfo objects, one per line. @param fd An opened file to read. @param raise_all If raise_all=True, exceptions will be raised immediately. Otherwise, any StormInfoError or ValueError will be logged or ignored, and parsing will continue. @param logger The logger is a logging.Logger object in which to log messages, or None (the default) to disable logging.tcvitals N)rNrRrrstripr ValueError)fdlogger raise_alloutlineerrrr8s c csPt|trt|}t|tr t|dk rbtj|d}d}|dk sJt|dk rb|d|f|dk rd|f}|dk r|d|f|dk rt|d } |dk r|d|fd} |D]l} |dk r| d d |krq|dk r| d d |krq|dk r| d | krq| d 7} t d| dVq|dk rL|d| fdS)a;!Faster way of finding tcvitals data for a specific case. A fast method of finding tcvitals in a file: instead of parsing each line into a StormInfo, it simply scans the characters of the line trying to find the right storm and time. Returns a list of matching vitals as StormInfo objects. * fd - the stream-like object to read from * logger - the logging.Logger to log to, or None * raise_all - if True, exceptions are raised immediately instead of just being logged. * when - the date to look for, or None * stnum - the storm number (ie.: 09 in 09L) * basin1 - the basin letter (ie.: L in 09L) @warning This function cannot handle errors in the formatting of the tcvitals lines. It will only work if the data in fd strictly follows the tcvitals format.N %Y%m%d %H%MabcdzVITALS: search for when=%sz%02dzVITALS: search for stnum=%srzVITALS: search for basin1=%s rvrwzVITALS: yielded %d matches) isinstancestrrjritcutilnumerics to_datetimestrftimedebugupperrrx) rzr{r|r=r6r;ZstrwhenrZstrstnumZ strbasin1rmr~rrrrNs:    Tc Cst}d}t}|D]}t|dkr@|dk r|d|fqnNd|kr|dddd}|dksr||kr|}||qn |dkrqz$|td|||d t}d}Wqttfk r}z |r΂W5d}~XYqXq|S) aM!Scans an A deck file connected to stream-like object fd, reading it into a list of StormInfo objects. Returns the list. @param logger A logging.Logger to log to. @param raise_all If False, log and ignore errors instead of raising them. @param fd the file object to read @returns a list of StormInfo objects N(z#Ignoring short line (<40 chars): %sCARQr,rgcarq)linetypeinputsr{r|) rNrhwarningsplitstriprRrrry) rzr{r|r}r=storer~Zwhen2rrrrrs4   cseZdZdZd6fdd ZddZd d Zd d Zd dZd7ddZ d8ddZ d9ddZ d:ddZ d;ddZ ddd Zd?d"d#Zd@d$d%ZdAd&d'Zd(d)Zd*d+Zd,d-ZdBd.d/ZdCd0d1ZdDd2d3Zd4d5ZZS)Era!Storm vitals information from ATCF, B-deck, tcvitals or message files. Represents all information about a one storm at one time as a Python object. It can read a single line of a tcvitals file, one time from a Best Track file, or CARQ entries from an Aid Deck file at a single time. It will scan multiple lines from a Best Track or CARQ group to get the last forecast hour (up to a maximum of 72hrs) and all possible radii for one time. This class is meant for complex manipulations of a small amount of data, not for manipulations of whole databases. It can be used for manipulating the entire TCVitals database, or multiple ATCF deck files, but will generally be slower than other libraries -- operations will take on the order of ten times as long. @todo Write a separate class for simple manipulations of a whole ATCF database. This could be done efficiently using an in-memory sqlite3 database.rNTc s|dk r&t|tjs&tdt|jtt|d|_ d|_ ||_ d|_ |dkrnt ||_||jnN|dkrt ||_||jn,|dkrt||_|j|t ||t|dn|dks|d kr|dk}d d }t|tstd t|j|jD]P\}} |d dkr"q|d ddkr>|r>q|| sLq| |j|<q|r|jD]<\}} || sqj|d ddkrj| |j|dd<qjntdt|fdS)a!StormInfo constructor Constructor for the StormInfo class. You should not call this directly. Instead, use the other parsing functions in this module to generate tcvitals from file objects. @param linetype type of vitals: tcvitals, message, carq (ATCF CARQ entries), old, or copy. See below @param inputs inputs, converted to a string before processing @param carq additional CARQ data to fill in more information @param logger a logging.Logger for log messages @param raise_all if True, exceptions will be raised if parser errors happen The constructor can create StormInfo objects in several ways, specified by the @c type argument: * @c tcvitals --- Parse a line of a tcvitals file. * @c message --- Parse a tropical cyclone message file. * @c carq --- Parse A deck CARQ entries. * @c old --- Take another StormInfo whose storm id/name has been replaced with another id/name through invest renumbering. Swap the old invest id/name with the current non-invest id/name. * @c copy --- Do a deep copy of the supplied StormInfoNzQIn StormInfo constructor, logger must be a logging.Logger, but instead it is a %sFrvr.r)linestechr{r|oldcopycSs,ttttjtjfD]}t||rdSqdS)NTF)rrjr^datetime timedeltar)rutrrr checktypes   z%StormInfo.__init__..checktypez\In StormInfo constructor, when linetype=="old", inputs must be a StormInfo object, not a %s.r_reZold_zAUnknown storm info format %s: only know "tcvitals" and "message".)rloggingLogger TypeErrorr*rr-rr&_cenlo_cenlaformat has_old_stnumrr~_parse_tcvitals_line_parse_message_linerr _parse_carqbool__dict__itemsZInvalidStormInfoFormatr') r%rrrr{r|rrkrIr/rrr&sd      zStormInfo.__init__cCs td|S)zj!Returns a copy of this StormInfo, but with the last renumbering or renaming of the vitals undone.rrr(rrrrsz StormInfo.oldcCs td|S)z!Returns a copy if this object.rrr(rrrr szStormInfo.copycCs || S)z\!Same as self + (-amount) @param amount The amount of time to extrapolate backwards.r)r%amountrrr__sub__szStormInfo.__sub__c Cs<|}tj|d}td|jd}tjd}tjj }tjj |dd}|t |j |}|t |j |} t| ||dtj} t||t |j||dtj} tt|j| dd|_tt|j| dd|_tj|d|j|_|jd |_d D]} | |jkr|j| =qd |_|S) z!Returns a copy of this object, with the vitals extrapolated forward "amount" hours. Only the location is changed. @param amount the amount of time to extrapolate forwardrrXf@T)Znegokrg %Y%m%d%H)flatflonfhrF)rrr to_timedeltarl stormspeedmathpi constantsRearth to_fractionsinstormdircosr^r<roundlonto_datetime_relr=rYMDHr havefcstloc) r%rrZdtamountZvmagpi180rdtdxdydlatdlonrIrrr__add__s& * zStormInfo.__add__cCs|jdk r |jdk r |j|jfS|j}|dk s2t|j}|}|}|dk rl|d||f|d||f|||_|_||fS)ag!Decide domain center based on the storm location. Returns a tuple containing a pair of floats (cenlo, cenla) which are the domain center longitude and latitude, respectively. Results are cached internally so future calls will not have to recompute the center location. @param logger a logging.Logger for log messagesNDecided cenlo=%f cenla=%fStorm is at lon=%f lat=%f)rrrrir<info)r%r{ storm_lon storm_latcenlocenlarrrtcutil_domain_center_storm,s  z$StormInfo.tcutil_domain_center_stormc CsP|jdk r |jdk r |j|jfS|j}|dk s2t|j}|jrT|jdk sLt|j}n|d}|dk sht|}|dkrz| }t|}|dkrd}|dkrd}|dkrd }|d krd }|d krd }|dkrd}|dkr| }|dk r| d||f||}|dkr|d8}|dkr |d8}t d|d|dd}|dkrN|d8}|dkr`|d7}|} |dk r| d| |f| d||fd} t | t |dkr|d} |dk r| d| fd} t | t |dkr|d} |dk r | d| fd} |dk r(| s(| d| d | |f| ||_|_| |fS)!a!Decide domain center based on the storm location, basin, and, if available, the 72hr forecast location. Returns a tuple containing a pair of floats (cenlo, cenla) which are the domain center longitude and latitude, respectively. Results are cached internally so future calls will not have to recompute the center location. @param logger a logging.Logger for log messagesNg4@rg.@g9@#g>@rgA@,gD@r5gF@z$Averaging storm_lon=%f and avglon=%fgv@gvrXg@rgfrrFrg@z1Center is too far east of storm. Moving it to %fTz1Center is too far west of storm. Moving it to %fz2Center is within +/- 5 degrees longitude of storm.z*Final outer domain center is lon=%f lat=%f) rrrrir<rrrfloorrrj) r%r{rrZavglonrZilatdiffresultrZmovedrrrtcutil_domain_centerCs             zStormInfo.tcutil_domain_centerc Cs|j}||d<|||||\}}}} |s8td|dd} |D]@} | rf|j|| || ||dd} |j|| || ||dq@|dk r(z.NcSsg|] }|qSr)rrKrrrrJszqCARQ entries in deck files must have at least eight fields (everything through lat & lon). Cannot parse this: %scs$g|]}|d|kqS)rr)rHjrtrrrrJs)rrrgrezgBasin, storm number, YMDH and technique must match for ALL LINES when parsing CARQ data in _parse_carq.rrrH) rNrkrhranyrj_parse_atcf_timerRri) r%rrr{r|rrrZmyfhrrrrrs>   zStormInfo._split_carqc Csd}|ddkr8|dkr&t|d}nt|d|jd<t|d}tj|d|dd |d d |d |ddd d }||jd <|d |jd<d S)aV!Internal function for getting the time out of ATCF data. Do not call this. It is an internal implementation routine. Adds to this StormInfo object the "when" parameter that contains the analysis time. If available, will also add the "technum" technique sort number. The instr is a line of original input text for error messages, the "data" is an output from _split_carq, and the other parameters are inputs to the original constructor. @param data Four element array where the last two are the forecast hour and minute. @param tech technique name to grep for, usually CARQ, though BEST also works when using B deck files @param logger a logging.Logger for log messages @param raise_all raise all exceptions instead of ignoring some of themrrcZBESTtechnumrgi@Bi'rN)r>monthdayhourminutesecond microsecondtzinfor=rr)rjrrr)r%rorr{r|iminZiwhenr=rrrrs$    zStormInfo._parse_atcf_timec Cszddlm}m}m}|j}d|kr*|j} nt} | |d<t|} | dkrzBt|d} |d} t || | | |ddd|| |d | f<WnRt t t t fk r} z,|d k r|jd t| |fd d |r̂W5d } ~ XYnX| dkrvzBt|d}|d}t || |||ddd|||d|f<WnJt t t t fk rt|d k rh|jdt| |fd d |rpYnXd S)a!Internal function for parsing radii and sea information in ATCF data Do not call this. It is an internal implementation routine. Adds to this StormInfo object radii and sea height data from the given input. The instr is a line of original input text for error messages, the "data" is an output from _split_carq, and the other parameters are inputs to the original constructor. @param instr string to parse @param tech technique name to grep for, usually CARQ, though BEST also works when using B deck files @param logger a logging.Logger for log messages @param raise_all raise all exceptions instead of ignoring some of themrft2mnmi2kmkts2mpsrp rcz windcode%02dNz'could not parse wind radii: %s line: %sTr"Zseasz seascode%02dz-could not parse wave height info: %s line: %s)tcutil.constantsrrrrrprOrhrjrrryrrrr)r%instrror{r|rrrrrpnrqZwindcoderZiseasZseascoderrrr sL      z StormInfo._parse_atcf_radii_seascs0dk r&ttjs&tdtjddlm}m}m }t }|j d} d} dd<t dd<t d d <t d d <fd d} fdd} |dkr؈ddkr| dd||dkrddkr| dd|dkrddkrdd<|dkr<ddkr<| dd|dkr`ddkr`| dd||dkrddkr| dd||d krddkr| d!d||d"kr̈d dkr| d#d ||d$krd"dkrd"} |d%krd$dkrd$d&kr| d'd$||d(krBd%dkrBd%d)<|d*kr|d(dkr|d(d+krrd,d-<n | d-d(|d.krd*dkr| d/d*||d0kr̈d.dkrtd.d1<|d2krd0dkrtd0dd3<|| | |t d4d5d1d6<dS)7a !Internal function that parses most of a line of ATCF data. Do not call this. It is an internal implementation routine. Parses just about everything except the time, radii and sea height from the input ATCF data. The instr is a line of original input text for error messages, the "data" is an output from _split_carq, and the other parameters are inputs to the original constructor. @param instr string to parse @param data split-up elements of a A deck line @param tech technique name to grep for, usually CARQ, though BEST also works when using B deck files @param logger a logging.Logger for log messages @param raise_all raise all exceptions instead of ignoring some of themNzLin _parse_atcf_meat, logger must be a logging.Logger, but instead it is a %srrre techniquertaurr<rrc sz(t|}|dkr&t|||<Wnrttttfk r}zLdk rnjdt|t|fddrt d|t|fW5d}~XYnXdSNrzcould not parse %s: %s line: %sTrz%s: %s: line %s) rjr^rryrrrr'rr)srtrAixrrrorr{r|rrfic]s z'StormInfo._parse_atcf_meat..ficc sz t|}|dkr||<Wnrttttfk r}zLdk rfjdt|t|fddrtd|t|fW5d}~XYnXdSr) r^rryrrrr'rr)rrtZfxrrrrfams  z&StormInfo._parse_atcf_meat..fa rrcwmaxrpminr stormtyperpocirrocirmwgustseyediamLmaxseasrZinitialsXrrr r7r depthrTr)rrrrr*rrrrrrhrrjrrr _set_basinrenumber_stormr)r%rror{r|rrrrr#r$rrZ subregionrrrr>sh          &      zStormInfo._parse_atcf_meatcCs|j|ttjjddS)a|!Do not call this routine directly. Call StormInfo("message",instr) instead. This subroutine parses one line of a hurricane message text that is assumed to be for the current century. The format of a hurricane message is the same as for a tcvitals file, except that the century is omitted and the file is always exactly one line.r)century)rrjrutcnowr>)r%rrrrrs zStormInfo._parse_message_linec Cstd|}|s$tdt|f|td}td}td}td}td}td} td } td } |j} d | kr|| d } nt} | | d <t|}|D]\}}|d kr||krqtd t |t|f|z ||kr|| |<n||krt | | |<n|| kr$t | | |<n|| krDt | d| |<n||kr`t | | |<n|| krt | }t | | |<|dkr| |nX||krt|d}|d kr||krtdt |t |t|f||| |dd<Wqtk r@}z*tdt |t|t |t|f|W5d }~XYqXqd| kod| kod| kod| ko| ddkp| ddkp| ddkp| ddk| d<d| kod| ko| dd k o| dd k | d<d| kr| d | d<ntd|f|d| krd | krd!| kr4t | d!}n*t t}|d"ksP|d#kr^td$|fd%|t | dt | d d&f}|| d'<tj|| d(<ntd)t|f||jd*kr|jdkrd+|_d,| kr||jntd-|f||j|jd.d/| d| d0<|S)1a!Parses one line of tcvitals data Do not call this routine directly. Call StormInfo("tcvitals",instr) instead. This subroutine parses one line of a tcvitals file of a format described here: http://www.emc.ncep.noaa.gov/mmb/data_processing/tcvitals_description.htm Here is an example line with only some of the possible data: @code{.tcvitals} JTWC 31W HAIYAN 20131104 1200 061N 1483E 270 077 0989 1008 0352 23 064 0084 0074 0074 0084 M ... more stuff ... @endcode The resulting data is put in self._data. Note that, at this time, there is one new field not present in the above mentioned webpage. The "storm type parameter" is a two letter description of the type of the storm: LO=low, WV=wave, etc. (there are many possibilities). That field is at the end of the line described in the above link, after one space. The "century" argument is the first two digits of the year, so 19 for the 1900s, 20 for the 2000s and so on. If century is missing or None, and the tcvitals does not specify the century either, then InvalidVitals will be raised. If both are available, the tcvitals century is used.ab(?xi) (?P
\S+) \s+ (?P\d\d)(?P[A-Za-z]) \s+ (?P[A-Za-z_ -]+) \s+ (?P\d\d)? (?P\d\d\d\d\d\d) \s+ (?P\d\d\d\d) \s+ (?P-?0*\d+[NS ]) \s+ (?P-?0*\d+[EW ]) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) (?: \s+ (?P\S) (?: \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) (?: \s+ (?P-?0*\d+) \s+ (?P-?0*\d+[NS ]) \s+ (?P-?0*\d+[EW ]) (?: \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) \s+ (?P-?0*\d+) (?: \s+ (?P\S\S?) )? )? )? )? )?zCannot parse vitals: %s)NE50SE50SW50NW50rfstrlatfstrlonNE64SE64SW64NW64r rawcenturyr/)rawbasin rawstormnamer> rawYYMMDDrawHHMMr/)strlatr8strlonr9)r:r)r6)rrrr rr"r) NE34SE34SW34NW34r4r5r6r7r:r;r<r=)rrpNz0Mandatory variable %s had None value in line: %srXrz6Mandatory variable %s had invalid value %s in line: %srrcz4Cannot parse vitals key %s value %s: %s from line %srErFrGrHrCrrrr@r7z'No storm name detected in this line: %srArBr>r!z8Implausable tcvitals century %d. Require 16 through 20.z %02d%06d%02drrr=z.Cannot determine date and time from vitals: %sr)Qr?z$Cannot find a basin in this line: %sT) discardoldr)r[r\rr'rOrdictr]rrrr^rjrQrreplaceryrrrrrrr?r<r0r1r6r)r%rr2raZnoneokZrawsZlatlonsZstripmeZint1Zfloat1Z float1radiiZfloat10rrprbrrIvalreplrZicenturyZsdaterrrrs            (4     zStormInfo._parse_tcvitals_lineFcCsd|jkr|s|j|jd<t|tr8t|dd|_n:t|dd|_|jdksZ|jdkrbd|_n|jdd|_|jdk stdS)z!Sets the two letter storm type self.stormtype. @param discardold If discardold=False (the default), then the old value, if any, is moved to self.old_stormtype. @param stormtype the storm type informationr old_stormtyperrgXXNrc)rrrrgetattrri)r%rrKrrr set_stormtypeLs  zStormInfo.set_stormtypecCsd|jkr|s|j|jd<t|dd|_|jdks@|jdkrnd|jdd|jdd|jdd f|_|j|jd <d S) z!Sets the name of the storm. @param newname the new storm name @param discardold If discardold=False (the default) then the old storm name is moved to self.old_stormname.r7 old_stormnamerrrvr.z%s%-9s%srNr)rr7rrr~r)r%newnamerKrrr rename_storm]s  zStormInfo.rename_stormcCsd|jkrP|sP|j|jd<|j|jd<|j|jd<|j|jd<|j|jd<d|_t||_d|j|jf|_d |j |jf|_d|j|j f|_|j d kr|j j d krd |j |j|j jd f|_nd |j |j|j jf|_|jdks|jdkrd|jd d|j|jd df|_dS)aU!Changes the storm number. Changes the storm number: the 09 in 09L. That changes self.stnum, stormid3, stormid3lc, stormid4 and longstormid. @param newnumber the new storm number @param discardold If discardold=False (the default), then the old values are moved to the old_stnum, old_stormid3, etc.r6 old_stnum old_stormid3old_stormid3lc old_stormid4old_longstormidTz%02d%sz%s%02drrz %s%02d%04drrvr.%s%02d%srN)rr6rB stormid3lcrDr?rrjr; pubbasin2basin1lcr<r=rr>rr~)r%Z newnumberrKrrrr1ks,           zStormInfo.renumber_stormcs|fdd}|dd|dd|dd|d d |d d jd ksRjdkrxdjddjjddf_dS)z!Swaps the new and old stormid variables. The stnum and old_stnum are swapped, the stormid3 and old_stormid3 are swapped, and so on.cs<|jkr8|jkr8j|}j|j|<|j|<dSN)r)orkeepr(rrswapnames z(StormInfo.swap_numbers..swapnamerWr6rXrBrYr]rZrDr[r?rvr.r\rrrN)rr~r6)r%rcrr(r swap_numberss       zStormInfo.swap_numberscCs |jddS)zx!Returns a tcvitals version of this data. This is not cached, and will be recalculated every time it is called.F no_centuryas_tcvitals_or_messager(rrr as_tcvitalsszStormInfo.as_tcvitalscCs |jddS)z|!Returns a message line version of this data. This is not cached, and will be recalculated every time it is called.Trergr(rrr as_messageszStormInfo.as_messagec sN|jfdd}fdd}dd}|r0d}nd}d t|jd d tt|jd t|jd t|jd d |j |t d||j d|j d krdndt d||j d|j d krdnd|drdnt d||j |drdnt d||jd|drdnt d||j|dr"dnt d||j|dr>dnt d||j|d rZd!nt d"||j|d#rvdnt d||j|d$rdnt d||j|d%rdnt d||j|d&rdnt d||j|d'rdnt d||jf}d(koDd)koDd*koD|jd+k oD|jd+k oD|jd+k oD|jd k}d,k}d-krh|sh|sh|Sd.|tt|d-d/d f}d0kr|s|s|Sd1||d2rdnt d||j|d3rdnt d||j|d4rdnt d||j|d0rdnt d||jf}|rd5||jd kr2d!nt d"t|jt d||jd|jd krbdndt d||jd|jd krdndf}d6kr|s|S|s|d77}d1||d8rdnt d||j |d9rdnt d||j!|d:rdnt d||j"|d6rdnt d||j#f}|s4|Sd;|t|j$d d<fS)=aP!Internal function that underlies as_tcvitals() and as_message() Returns a tcvitals or message version of this data. This is not cached, and will be recalculated every time it is called. @param no_century If no_century=True, then only two digits of the year are written, and the line will be a message.cs(|kr dS|}|dkr dS|dkS)NTrrrrNrrrbads z-StormInfo.as_tcvitals_or_message..badcs(|kr dS|}|dkr dS|dkS)NTgh㈵>rrkrlrrbad0s z.StormInfo.as_tcvitals_or_message..bad0cSsttt|Sr`)rjabsr)rtrrrcintz.StormInfo.as_tcvitals_or_message..cintz %y%m%d %H%MrzX%-4s %02d%s %-9s %s %03d%s %04d%s %03d %03d %04d %04d %04d %02d %03d %04d %04d %04d %04drrerrirXNSrEWrr-ihrirrfiLrr i'ricr"rErFrGrHrrrNrr/z%s %sr,r7z%s %04d %04d %04d %04dr4r5r6z%s %02d %03d%s %04d%sr=z -9 -99N -999Wr:r;r<z%s % 2srg)%rrr:rjror6r;r7r=rminr<rrrrrr rr"rErFrGrHrrrrRr4r5r6r7r:r;r<r=r) r%rfrmrnrpZ datestringrZgotfcstZgotstrrlrrhs     z StormInfo.as_tcvitals_or_messagecCs|s@|j|jd<|j|jd<|j|jd<|j|jd<|j|jd<||||jdkrd|jdd |jd|jd d f|_d S) a=!Changes the basin of this StormInfo @param basin the primary basin (IO, L, etc.) @param subbasin the subbasin. For example, IO has the subbasins AA and BB. @param discardold If discardold=False (the default), then the old values are moved to the old_stnum, old_stormid3, etc. Zold_tcutilbasin2Z old_pubbasin2Z old_basin1Z old_basin1lcZ old_basinnamervz%s%s%srrrN) tcutilbasin2rr^r;r_ basinnamer0rr~)r%r#r$rKrrr change_basins       zStormInfo.change_basincCs\t||}|d|jd<|d|jd<|d|jd<|d|jd<|d|jd <d S) a!This is a utility function that creates the one and two letter basins from a raw one and/or two letter basin. If the input basin is invalid, InvalidBasinError is raised. @param basin the primary basin (IO, L, etc.) @param subbasin the subbasin. For example, IO has the subbasins AA and BB. @param discardold If discardold=False (the default), then the old values are moved to the old_stnum, old_stormid3, etc. rrxrr^rgr;r_rryN)rrrr)r%r#r$rKbbrrrr0s zStormInfo._set_basincCsd|_d|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_ d|_ d|_ d|_ d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_!d|_"d|_#d|_$d|_%d|_&d|_'d|_(d|_)d|_*d|_+d|_,d|_-d|_!d|_.d|_"d|_d|_/d|_0d|_1d|_2d|_3d|_4d|_5d|_/d|_6d|_7d|_8d|_9d|_:d|_;d|_d|_?dS)z_!Ensure that self.varname exists for all member variables, so that Doxygen detects themrcrN)@r:rrrrr7rrr=rrpZ windcode34Z windcode50Z windcode64rrr<rrrrr r"r$r&r*rrr/r4r5r6r7r8r9r:r;r<r=rr>r@r?rArBrCrDr6rErFrGrHrPrTrWrBrXr]rYrDrZr?r[r~r(rrrZ __doxygenszStormInfo.__doxygen)rNT)N)N)rNT)NT)rNT)NT)NT)N)F)F)F)F)NF)NF)rr r!r"r&rrrrrrrrrrrrrrSrVr1rdrirjrhrzr0Z_StormInfo__doxygenr1rrr/rrs4F  H 6 /  1 T 0    V  cCst|}|dkrdn t|}|dks4|dkr|d kr(d!}n|d"kr8d#}nd$}n|d%krNd$}n|d&kr|dkrhd}n$|dkrxd'}n|d(krd)}nd*}nj|d+kr|dkrd,}n|dkrd-}nd.}n:|d(krd)}n*|d krd!}n|d"krd#}n t|||S)/a*!Converts basin identifiers Given a one-letter or two-letter tropical basin identifier, and possibly another one-letter tropical basin identifier (subbasin), attempts to determine more information about the basin. Some information may be ambiguous if a two letter basin is specified. Returns a four-element tuple: 1. The internal (tcutil/GFDL) two-letter basin identifier. These have an unambiguous mapping to the one-letter basin. 2. The public, standard two-letter basin identifier used by JTWC and others. These are ambiguous: IO can be A or B, and SH can be S or P. 3. The one-letter basin identifier. 4. A description of the meaning of the basin. @param basin the primary basin @param subbasin Optional: the subbasin, if knownNrcALr))r|r|r)zNorth Atlantic (L/AL)SLrJ)r}r}rJSouth Atlantic (Q/SL/LS)LS)rrrJr~EPrt)rrrtzNorth East Pacific (E/EP)CPC)rrrzNorth Central Pacific (C/CP)SSrs)rSHrszSouth Pacific (S/SH)PPP)rrrzSouth Indian Ocean (P/SH/PP)AAA)rIOrz#Indian Ocean: Arabian Sea (A/IO/AA)NA)rrrz#Indian Ocean: Arabian Sea (A/IO/NA)BBB)rrrz%Indian Ocean: Bay of Bengal (B/IO/BB)WPO)ZOOrrz2North West Pacific: South China Sea Basin (O/W/WP)T)ZTTrrz+North West Pacific: East China Sea (T/W/WP))rrruzNorth West Pacific (W/WP)rur)rrrzSouth Indian Ocean (P/SH)U)ZUUrrz*South Pacific: Australian Basin (U/P/S/SH))rrrsz(South Pacific or South Indian Ocean (SH)r)rrrz Indian Ocean: Arabian Sea (A/IO))rrrz"Indian Ocean: Bay of Bengal (B/IO))rrrz#Unspecified North Indian Ocean (IO))rrr)r#r$rrr{rrrrsh                )NNN)rX)rcrd)NF)NFNNN)NT)N))r"__all__r[rrZ fractionsrrtcutil.numericsrrpdb functoolsrrrjnowr>r Exceptionrrrrrrrr r r r r rrrrrrobjectrrrrrrsr0  /  +  5 !f