예제 #1
0
 def createEconTable(self):
     table_lines = [
         "\\begin{tabularx}{\\barwidth}{lc*{1}{>{\\raggedleft\\arraybackslash}X}}"
     ]
     table_lines.append("\\hline")
     table_lines.append(
         "\\textbf{County} & \\textbf{State} & \\textbf{Total (\\textdollar M)} \\\\"
     )
     table_lines.append("\\hline")
     ntotal = len(self._dataframe)
     for i in range(0, self._ncounties):
         row = self._dataframe.iloc[i]
         fips = int(row["CountyFips"])
         county_name, state_abbrev = self._county_dict[fips]
         # econ losses are in thousands of dollars
         lossvalue = round_to_nearest(row["EconLoss"] * 1e3, 1000000)
         loss_str = "{:,}".format(int(lossvalue / 1e6))
         line = "\\truncate{4cm}{%s} & %s & %s \\\\" % (
             county_name,
             state_abbrev,
             loss_str,
         )
         table_lines.append(line)
     fmt = "\\multicolumn{2}{l}{\\textbf{Total (%i counties)}} & \\multicolumn{1}{>{\\raggedleft}X}{\\textbf{%s}} \\\\"
     total_dollars = self._dataframe["EconLoss"].sum() * 1e3
     total_rounded = round_to_nearest(total_dollars, 1000000)
     total_str = "{:,}".format(int(total_rounded / 1e6))
     line = fmt % (ntotal, total_str)
     table_lines.append(line)
     table_lines.append("\\hline")
     table_lines.append("\\end{tabularx}")
     table_text = "\n".join(table_lines)
     return table_text
예제 #2
0
def test():
    print('Testing decimal to roman number conversion...')
    assert text.dec_to_roman(10) == 'X'
    print('Passed decimal to roman number conversion...')

    print('Testing setting number precision...')
    assert text.set_num_precision(7642, 2) == 7600
    assert text.set_num_precision(321, 2) == 320
    print('Passed setting number precision...')

    print('Testing rounding population value...')
    assert text.pop_round(7642) == '8,000'
    print('Passed rounding population value...')

    print('Testing rounding dollar value...')
    assert text.dollar_round(1.234e9, digits=2, mode='short') == '$1.2B'
    assert text.dollar_round(1.234e9, digits=2, mode='long') == '$1.2 billion'
    print('Passed rounding population value...')

    print('Testing abbreviating population value...')
    assert text.pop_round_short(1024125) == '1,024k'
    print('Passed abbreviating population value...')

    print('Testing rounding to nearest integer value...')
    assert text.round_to_nearest(998, round_value=1000) == 1000
    assert text.round_to_nearest(78, round_value=100) == 100
    print('Passed rounding population value...')

    print('Testing flooring to nearest integer value...')
    assert text.floor_to_nearest(1501, floor_value=1000) == 1000
    assert text.floor_to_nearest(51, floor_value=100) == 0
    print('Passed flooring population value...')

    print('Testing ceiling to nearest integer value...')
    assert text.ceil_to_nearest(1001, ceil_value=1000) == 2000
    assert text.ceil_to_nearest(49, ceil_value=100) == 100
    print('Passed ceiling population value...')

    print('Testing commify...')
    assert text.commify(1234567) == '1,234,567'
    print('Passed commify...')

    assert text.floor_to_nearest(0.56, floor_value=0.1) == 0.5
    assert text.ceil_to_nearest(0.44, ceil_value=0.1) == 0.5
    assert text.round_to_nearest(0.48, round_value=0.1) == 0.5
    assert text.pop_round_short(125) == '125'
    assert text.pop_round_short(1.23e6, usemillion=True) == '1m'
    assert text.pop_round_short(0) == '0'
    assert text.dollar_round(1.23, digits=2, mode='short') == '$1'
    assert text.dollar_round(1.23e3, digits=2, mode='short') == '$1.2K'
    assert text.dollar_round(1.23e3, digits=2, mode='long') == '$1.2 thousand'
    assert text.dollar_round(1.23e6, digits=2, mode='short') == '$1.2M'
    assert text.dollar_round(1.23e6, digits=2, mode='long') == '$1.2 million'
    assert text.dec_to_roman(10) == 'X'
예제 #3
0
def format_exposure(exposures, format, max_border_mmi):
    expstr_hold = 'Estimated Population Exposure\n'
    if format == 'short':
        # get the three highest exposures with 1,000 people or greater
        # format them as:
        # I6=19,000
        # I5=1,034,000
        expstr = 'No population exposure'
        if len(exposures):
            expstr = ''
            expohold = 0
            for mmi in range(10, 0, -1):
                pop = 0
                expo = exposures[mmi - 1]
                if mmi == 10:
                    expohold = expo
                elif mmi == 9:
                    pop = expo + expohold
                else:
                    pop = expo
                pop = round_to_nearest(pop, round_value=1000)
                if pop >= MIN_POP:
                    popstr = pop_round(pop)
                    expstr += 'I%i=%s\n' % (mmi, popstr)
    else:
        # get the all  highest exposures with 1,000 people or greater
        # format them as:
        # MMI6 19,000
        # MMI5 1,034,000
        expstr = expstr_hold + '\tIntensity Population\n'
        if len(exposures):
            expohold = 0
            for mmi in range(10, 0, -1):
                pop = 0
                expo = exposures[mmi - 1]
                if mmi == 10:
                    expohold = expo
                elif mmi == 9:
                    pop = expo + expohold
                else:
                    pop = expo
                pop = round_to_nearest(pop, round_value=1000)
                if pop >= MIN_POP:
                    popstr = pop_round(pop)
                    flag = ''
                    if mmi < max_border_mmi:
                        flag = '*'
                    expstr += 'MMI%i\t%-8s%s\n' % (mmi, popstr, flag)
    if expstr == expstr_hold:
        expstr = 'No population exposure.'
    else:
        if format == 'long':
            expstr += '\n* - MMI level extends beyond map boundary, actual population exposure may be larger.\n'
    return expstr
예제 #4
0
def get_quake_desc(event,lat,lon,isMainEvent):
    ndeaths = event['TotalDeaths']
    #summarize the exposure values
    exposures = np.array([event['MMI1'],event['MMI2'],event['MMI3'],event['MMI4'],event['MMI5'],
                         event['MMI6'],event['MMI7'],event['MMI8'],event['MMI9+']])
    exposures = np.array([round_to_nearest(exp,1000) for exp in exposures])
    #get the highest two exposures greater than zero
    iexp = np.where(exposures > 0)[0][::-1]

    romans = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX or greater']
    if len(iexp) >= 2:
        exposures = [exposures[iexp[1]],exposures[iexp[0]]]
        ilevels = [romans[iexp[1]],romans[iexp[0]]]
        expfmt = ', with estimated population exposures of %s at intensity'
        expfmt = expfmt + ' %s and %s at intensity %s'
        exptxt = expfmt % (commify(int(exposures[0])),ilevels[0],commify(int(exposures[1])),ilevels[1])
    else:
        exptxt = ''

    #create string describing this most impactful event
    dfmt = 'A magnitude %.1f earthquake %i km %s of this event struck %s on %s (UTC)%s'

    mag = event['Magnitude']
    etime = event['Time'].strftime('%B %d, %Y')
    etime = re.sub(' 0',' ',etime)
    country = Country()
    if not event['Name']:
        if event['CountryCode'] == 'UM' and event['Latitude'] > 40: #hack for persistent error in expocat
            cdict = country.getCountryCode('US')
        else:
            cdict = country.getCountryCode(event['CountryCode'])
        if cdict:
            cname = cdict['Name']
        else:
            cname = 'in the open ocean'
    else:
        cname = event['Name'].replace('"','')
        
    cdist = round(geodetic_distance(event['Lat'],event['Lon'],lat,lon))
    cdir = get_compass_dir(lat,lon,event['Lat'],event['Lon'],format='long').lower()
    if ndeaths and str(ndeaths) != "nan":
        dfmt = dfmt + ', resulting in a reported %s %s.'
        
        if ndeaths > 1:
            dstr = 'fatalities'
        else:
            dstr = 'fatality'

        ndeathstr = commify(int(ndeaths))
        eqdesc = dfmt % (mag,cdist,cdir,cname,etime,exptxt,ndeathstr,dstr)
    else:
        dfmt = dfmt + ', with no reported fatalities.'
        eqdesc = dfmt % (mag,cdist,cdir,cname,etime,exptxt)

    return eqdesc
예제 #5
0
def create_onepager(pdata, version_dir, debug=False):
    """
    :param pdata:
      PagerData object.
    :param version_dir: 
      Path of event version directory.
    :param debug:
      bool for whether or not to add textpos boxes to onepager.
    """

    #---------------------------------------------------------------------------
    # Sort out some paths
    #---------------------------------------------------------------------------

    # Locaiton of this module
    mod_dir, dummy = os.path.split(__file__)

    # losspager package direcotry
    losspager_dir = os.path.join(mod_dir, '..')

    # Repository root directory
    root_dir = os.path.join(losspager_dir, '..')

    # Data directory
    data_dir = os.path.join(losspager_dir, 'data')

    # Onepager latex template file
    template_file = os.path.join(data_dir, 'onepager2.tex')

    #---------------------------------------------------------------------------
    # Read in pager data and latex template
    #---------------------------------------------------------------------------

    json_dir = os.path.join(version_dir, 'json')
    pdict = pdata._pagerdict
    edict = pdata.getEventInfo()

    with open(template_file, 'r') as f:
        template = f.read()

    #---------------------------------------------------------------------------
    # Fill in template values
    #---------------------------------------------------------------------------

    # Sort out origin time
    olat = edict['lat']
    olon = edict['lon']
    otime_utc = edict['time']
    date_utc = datetime.strptime(otime_utc, "%Y-%m-%d %H:%M:%S")

    date_local = pdata.local_time
    DoW = date_local.strftime('%a')
    otime_local = date_local.strftime('%H:%M:%S')
    otime_local = DoW + ' ' + otime_local
    template = template.replace("[ORIGTIME]", otime_utc)
    template = template.replace("[LOCALTIME]", otime_local)

    # Some paths
    template = template.replace("[VERSIONFOLDER]", version_dir)
    template = template.replace("[HOMEDIR]", root_dir)

    # Magnitude location string under USGS logo
    magloc = 'M %.1f, %s' % (edict['mag'], texify(edict['location']))
    template = template.replace("[MAGLOC]", magloc)

    # Pager version
    ver = "Version " + str(pdict['pager']['version_number'])
    template = template.replace("[VERSION]", ver)
    template = template.replace("[VERSIONX]", "2.5")

    # Epicenter location
    lat = edict['lat']
    lon = edict['lon']
    dep = edict['depth']
    if lat > 0:
        hlat = "N"
    else:
        hlat = "S"
    if lon > 0:
        hlon = "E"
    else:
        hlon = "W"
    template = template.replace("[LAT]", '%.4f' % abs(lat))
    template = template.replace("[LON]", '%.4f' % abs(lon))
    template = template.replace("[HEMILAT]", hlat)
    template = template.replace("[HEMILON]", hlon)
    template = template.replace("[DEPTH]", '%.1f' % dep)

    # Tsunami warning? --- need to fix to be a function of tsunamic flag
    if edict['tsunami']:
        template = template.replace(
            "[TSUNAMI]", "FOR TSUNAMI INFORMATION, SEE: tsunami.gov")
    else:
        template = template.replace("[TSUNAMI]", "")

    if pdata.isScenario():
        elapse = ''
    else:
        elapse = "Created: " + pdict['pager'][
            'elapsed_time'] + " after earthquake"
    template = template.replace("[ELAPSED]", elapse)
    template = template.replace("[IMPACT1]",
                                texify(pdict['comments']['impact1']))
    template = template.replace("[IMPACT2]",
                                texify(pdict['comments']['impact2']))
    template = template.replace("[STRUCTCOMMENT]",
                                texify(pdict['comments']['struct_comment']))

    # Summary alert color
    template = template.replace("[SUMMARYCOLOR]",
                                pdata.summary_alert.capitalize())
    template = template.replace("[ALERTFILL]", pdata.summary_alert)

    # fill in exposure values
    max_border_mmi = pdata._pagerdict['population_exposure'][
        'maximum_border_mmi']
    explist = pdata.getTotalExposure()
    pophold = 0
    for mmi in range(1, 11):
        iexp = mmi - 1
        if mmi == 2:
            pophold += explist[iexp]
            continue
        elif mmi == 3:
            pop = explist[iexp] + pophold
            macro = '[MMI2-3]'
        else:
            pop = explist[iexp]
            macro = '[MMI%i]' % mmi
        if pop < 1000:
            pop = round_to_nearest(pop, round_value=1000)
        if max_border_mmi > mmi and mmi <= 4:
            if pop == 0:
                popstr = '--*'
            else:
                if pop < 1000:
                    pop = round_to_nearest(pop, round_value=1000)
                popstr = pop_round_short(pop) + '*'
        else:
            popstr = pop_round_short(pop)
        template = template.replace(macro, popstr)

    # MMI color pal
    pal = ColorPalette.fromPreset('mmi')

    # Historical table
    htab = pdata.getHistoricalTable()
    if htab[0] is None:
        # use pdata.getHistoricalComment()
        htex = pdata.getHistoricalComment()
    else:
        # build latex table
        htex = """
\\begin{tabularx}{7.25cm}{lrc*{1}{>{\\centering\\arraybackslash}X}*{1}{>{\\raggedleft\\arraybackslash}X}}
\hline
\\textbf{Date} &\\textbf{Dist.}&\\textbf{Mag.}&\\textbf{Max}    &\\textbf{Shaking}\\\\
\\textbf{(UTC)}&\\textbf{(km)} &              &\\textbf{MMI(\#)}&\\textbf{Deaths} \\\\
\hline
[TABLEDATA]
\hline
\multicolumn{5}{p{7.2cm}}{\\small [COMMENT]}
\end{tabularx}"""
        comment = pdata._pagerdict['comments']['secondary_comment']
        htex = htex.replace("[COMMENT]", texify(comment))
        tabledata = ""
        nrows = len(htab)
        for i in range(nrows):
            date = htab[i]['Time'].split()[0]
            dist = str(int(htab[i]['Distance']))
            mag = str(htab[i]['Magnitude'])
            mmi = dec_to_roman(np.round(htab[i]['MaxMMI'], 0))
            col = pal.getDataColor(htab[i]['MaxMMI'])
            texcol = "%s,%s,%s" % (col[0], col[1], col[2])
            nmmi = pop_round_short(htab[i]['NumMaxMMI'])
            mmicell = '%s(%s)' % (mmi, nmmi)
            shakedeath = htab[i]['ShakingDeaths']
            if np.isnan(shakedeath):
                death = "--"
            else:
                death = pop_round_short(shakedeath)
            row = '%s & %s & %s & \cellcolor[rgb]{%s} %s & %s \\\\ '\
                  '\n' %(date, dist, mag, texcol, mmicell, death)
            tabledata = tabledata + row
        htex = htex.replace("[TABLEDATA]", tabledata)
    template = template.replace("[HISTORICAL_BLOCK]", htex)

    # City table
    ctex = """
\\begin{tabularx}{7.25cm}{lXr}
\hline
\\textbf{MMI} & \\textbf{City} & \\textbf{Population}  \\\\
\hline
[TABLEDATA]
\hline
\end{tabularx}"""
    ctab = pdata.getCityTable()
    nrows = len(ctab.index)
    tabledata = ""
    for i in range(nrows):
        mmi = dec_to_roman(np.round(ctab['mmi'].iloc[i], 0))
        city = ctab['name'].iloc[i]
        if ctab['pop'].iloc[i] == 0:
            pop = '$<$1k'
        else:
            if ctab['pop'].iloc[i] < 1000:
                popnum = round_to_nearest(ctab['pop'].iloc[i],
                                          round_value=1000)
            else:
                popnum = ctab['pop'].iloc[i]
            pop = pop_round_short(popnum)
        col = pal.getDataColor(ctab['mmi'].iloc[i])
        texcol = "%s,%s,%s" % (col[0], col[1], col[2])
        if ctab['on_map'].iloc[i] == 1:
            if ctab['pop'].iloc[i] == 0:
                pop = '\\boldmath$<$\\textbf{1k}'
                row = '\\rowcolor[rgb]{%s}\\textbf{%s} & \\textbf{%s} & '\
                      '%s\\\\ \n' %(texcol, mmi, city, pop)
            else:
                row = '\\rowcolor[rgb]{%s}\\textbf{%s} & \\textbf{%s} & '\
                      '\\textbf{%s}\\\\ \n' %(texcol, mmi, city, pop)
        else:
            row = '\\rowcolor[rgb]{%s}%s & %s & '\
                  '%s\\\\ \n' %(texcol, mmi, city, pop)
        tabledata = tabledata + row
    ctex = ctex.replace("[TABLEDATA]", tabledata)
    template = template.replace("[CITYTABLE]", ctex)

    eventid = edict['eventid']

    # query ComCat for information about this event
    # fill in the url, if we can find it
    try:
        ccinfo = ComCatInfo(eventid)
        eventid, allids = ccinfo.getAssociatedIds()
        event_url = ccinfo.getURL() + '#pager'
    except:
        event_url = DEFAULT_PAGER_URL

    eventid = "Event ID: " + eventid
    template = template.replace("[EVENTID]", texify(eventid))
    template = template.replace("[EVENTURL]", texify(event_url))

    # Write latex file
    tex_output = os.path.join(version_dir, 'onepager.tex')
    with open(tex_output, 'w') as f:
        f.write(template)

    pdf_output = os.path.join(version_dir, 'onepager.pdf')
    stderr = ''
    try:
        cwd = os.getcwd()
        os.chdir(version_dir)
        cmd = '%s -interaction nonstopmode --output-directory %s %s' % (
            LATEX_TO_PDF_BIN, version_dir, tex_output)
        print('Running %s...' % cmd)
        res, stdout, stderr = get_command_output(cmd)
        os.chdir(cwd)
        if not res:
            return (None, stderr)
        else:
            if os.path.isfile(pdf_output):
                return (pdf_output, stderr)
            else:
                pass
    except Exception as e:
        pass
    finally:
        os.chdir(cwd)
    return (None, stderr)
예제 #6
0
def draw_contour(shakegrid,
                 popgrid,
                 oceanfile,
                 oceangridfile,
                 cityfile,
                 basename,
                 borderfile=None,
                 is_scenario=False):
    """Create a contour map showing MMI contours over greyscale population.

    :param shakegrid:
      ShakeGrid object.
    :param popgrid:
      Grid2D object containing population data.
    :param oceanfile:
      String path to file containing ocean vector data in a format compatible
      with fiona.
    :param oceangridfile:
      String path to file containing ocean grid data .
    :param cityfile:
      String path to file containing GeoNames cities data.
    :param basename:
      String path containing desired output PDF base name, i.e.,
      /home/pager/exposure.  ".pdf" and ".png" files will
      be made.
    :param make_png:
      Boolean indicating whether a PNG version of the file should also be
      created in the same output folder as the PDF.
    :returns:
      Tuple containing:
        - Name of PNG file created, or None if PNG output not specified.
        - Cities object containing the cities that were rendered on the
          contour map.
    """
    gd = shakegrid.getGeoDict()

    # Retrieve the epicenter - this will get used on the map
    center_lat = shakegrid.getEventDict()['lat']
    center_lon = shakegrid.getEventDict()['lon']

    # load the ocean grid file (has 1s in ocean, 0s over land)
    # having this file saves us almost 30 seconds!
    oceangrid = read(oceangridfile,
                     samplegeodict=gd,
                     resample=True,
                     doPadding=True)

    # load the cities data, limit to cities within shakemap bounds
    allcities = Cities.fromDefault()
    cities = allcities.limitByBounds((gd.xmin, gd.xmax, gd.ymin, gd.ymax))

    # define the map
    # first cope with stupid 180 meridian
    height = (gd.ymax - gd.ymin) * DEG2KM
    if gd.xmin < gd.xmax:
        width = (gd.xmax - gd.xmin) * np.cos(np.radians(center_lat)) * DEG2KM
        xmin, xmax, ymin, ymax = (gd.xmin, gd.xmax, gd.ymin, gd.ymax)
    else:
        xmin, xmax, ymin, ymax = (gd.xmin, gd.xmax, gd.ymin, gd.ymax)
        xmax += 360
        width = ((gd.xmax + 360) - gd.xmin) * \
            np.cos(np.radians(center_lat)) * DEG2KM

    aspect = width / height

    # if the aspect is not 1, then trim bounds in x or y direction
    # as appropriate
    if width > height:
        dw = (width - height) / 2.0  # this is width in km
        xmin = xmin + dw / (np.cos(np.radians(center_lat)) * DEG2KM)
        xmax = xmax - dw / (np.cos(np.radians(center_lat)) * DEG2KM)
        width = (xmax - xmin) * np.cos(np.radians(center_lat)) * DEG2KM
    if height > width:
        dh = (height - width) / 2.0  # this is width in km
        ymin = ymin + dh / DEG2KM
        ymax = ymax - dh / DEG2KM
        height = (ymax - ymin) * DEG2KM

    aspect = width / height
    figheight = FIGWIDTH / aspect
    bbox = (xmin, ymin, xmax, ymax)
    bounds = (xmin, xmax, ymin, ymax)
    figsize = (FIGWIDTH, figheight)

    # Create the MercatorMap object, which holds a separate but identical
    # axes object used to determine collisions between city labels.
    mmap = MercatorMap(bounds, figsize, cities, padding=0.5)
    fig = mmap.figure
    ax = mmap.axes
    # this needs to be done here so that city label collision
    # detection will work
    fig.canvas.draw()

    geoproj = mmap.geoproj
    proj = mmap.proj

    # project our population grid to the map projection
    projstr = proj.proj4_init
    popgrid_proj = popgrid.project(projstr)
    popdata = popgrid_proj.getData()
    newgd = popgrid_proj.getGeoDict()

    # Use our GMT-inspired palette class to create population and MMI colormaps
    popmap = ColorPalette.fromPreset('pop')
    mmimap = ColorPalette.fromPreset('mmi')

    # set the image extent to that of the data
    img_extent = (newgd.xmin, newgd.xmax, newgd.ymin, newgd.ymax)
    plt.imshow(popdata,
               origin='upper',
               extent=img_extent,
               cmap=popmap.cmap,
               vmin=popmap.vmin,
               vmax=popmap.vmax,
               zorder=POP_ZORDER,
               interpolation='nearest')

    # draw 10m res coastlines
    ax.coastlines(resolution="10m", zorder=COAST_ZORDER)

    states_provinces = cfeature.NaturalEarthFeature(
        category='cultural',
        name='admin_1_states_provinces_lines',
        scale='50m',
        facecolor='none')

    ax.add_feature(states_provinces, edgecolor='black', zorder=COAST_ZORDER)

    # draw country borders using natural earth data set
    if borderfile is not None:
        borders = ShapelyFeature(
            Reader(borderfile).geometries(), ccrs.PlateCarree())
        ax.add_feature(borders,
                       zorder=COAST_ZORDER,
                       edgecolor='black',
                       linewidth=2,
                       facecolor='none')

    # clip the ocean data to the shakemap
    bbox = (gd.xmin, gd.ymin, gd.xmax, gd.ymax)
    oceanshapes = _clip_bounds(bbox, oceanfile)

    ax.add_feature(ShapelyFeature(oceanshapes, crs=geoproj),
                   facecolor=WATERCOLOR,
                   zorder=OCEAN_ZORDER)

    # So here we're going to project the MMI data to
    # our mercator map, then smooth and contour that
    # projected grid.

    # smooth the MMI data for contouring, themn project
    mmi = shakegrid.getLayer('mmi').getData()
    smoothed_mmi = gaussian_filter(mmi, FILTER_SMOOTH)
    newgd = shakegrid.getGeoDict().copy()
    smooth_grid = Grid2D(data=smoothed_mmi, geodict=newgd)
    smooth_grid_merc = smooth_grid.project(projstr)
    newgd2 = smooth_grid_merc.getGeoDict()

    # project the ocean grid
    oceangrid_merc = oceangrid.project(projstr)

    # create masked arrays using the ocean grid
    data_xmin, data_xmax = newgd2.xmin, newgd2.xmax
    data_ymin, data_ymax = newgd2.ymin, newgd2.ymax
    smooth_data = smooth_grid_merc.getData()
    landmask = np.ma.masked_where(oceangrid_merc._data == 0.0, smooth_data)
    oceanmask = np.ma.masked_where(oceangrid_merc._data == 1.0, smooth_data)

    # contour the data
    contourx = np.linspace(data_xmin, data_xmax, newgd2.nx)
    contoury = np.linspace(data_ymin, data_ymax, newgd2.ny)
    ax.contour(
        contourx,
        contoury,
        np.flipud(oceanmask),
        linewidths=3.0,
        linestyles='solid',
        zorder=1000,
        cmap=mmimap.cmap,
        vmin=mmimap.vmin,
        vmax=mmimap.vmax,
        levels=np.arange(0.5, 10.5, 1.0),
    )

    ax.contour(
        contourx,
        contoury,
        np.flipud(landmask),
        linewidths=2.0,
        linestyles='dashed',
        zorder=OCEANC_ZORDER,
        cmap=mmimap.cmap,
        vmin=mmimap.vmin,
        vmax=mmimap.vmax,
        levels=np.arange(0.5, 10.5, 1.0),
    )

    # the idea here is to plot invisible MMI contours at integer levels
    # and then label them. clabel method won't allow text to appear,
    # which is this case is kind of ok, because it allows us an
    # easy way to draw MMI labels as roman numerals.
    cs_land = plt.contour(
        contourx,
        contoury,
        np.flipud(oceanmask),
        linewidths=0.0,
        levels=np.arange(0, 11),
        alpha=0.0,
        zorder=CLABEL_ZORDER,
    )

    clabel_text = ax.clabel(cs_land,
                            cs_land.cvalues,
                            colors='k',
                            fmt='%.0f',
                            fontsize=40)
    for clabel in clabel_text:
        x, y = clabel.get_position()
        label_str = clabel.get_text()
        roman_label = MMI_LABELS[label_str]
        th = plt.text(x,
                      y,
                      roman_label,
                      zorder=CLABEL_ZORDER,
                      ha='center',
                      va='center',
                      color='black',
                      weight='normal',
                      size=16)
        th.set_path_effects([
            path_effects.Stroke(linewidth=2.0, foreground='white'),
            path_effects.Normal()
        ])

    cs_ocean = plt.contour(
        contourx,
        contoury,
        np.flipud(landmask),
        linewidths=0.0,
        levels=np.arange(0, 11),
        zorder=CLABEL_ZORDER,
    )

    clabel_text = ax.clabel(cs_ocean,
                            cs_ocean.cvalues,
                            colors='k',
                            fmt='%.0f',
                            fontsize=40)
    for clabel in clabel_text:
        x, y = clabel.get_position()
        label_str = clabel.get_text()
        roman_label = MMI_LABELS[label_str]
        th = plt.text(x,
                      y,
                      roman_label,
                      ha='center',
                      va='center',
                      color='black',
                      weight='normal',
                      size=16)
        th.set_path_effects([
            path_effects.Stroke(linewidth=2.0, foreground='white'),
            path_effects.Normal()
        ])

    # draw meridians and parallels using Cartopy's functions for that
    gl = ax.gridlines(draw_labels=True,
                      linewidth=2,
                      color=(0.9, 0.9, 0.9),
                      alpha=0.5,
                      linestyle='-',
                      zorder=GRID_ZORDER)
    gl.xlabels_top = False
    gl.xlabels_bottom = False
    gl.ylabels_left = False
    gl.ylabels_right = False
    gl.xlines = True

    # let's floor/ceil the edges to nearest half a degree
    gxmin = np.floor(xmin * 2) / 2
    gxmax = np.ceil(xmax * 2) / 2
    gymin = np.floor(ymin * 2) / 2
    gymax = np.ceil(ymax * 2) / 2

    xlocs = np.linspace(gxmin, gxmax + 0.5, num=5)
    ylocs = np.linspace(gymin, gymax + 0.5, num=5)

    gl.xlocator = mticker.FixedLocator(xlocs)
    gl.ylocator = mticker.FixedLocator(ylocs)
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER
    gl.xlabel_style = {'size': 15, 'color': 'black'}
    gl.ylabel_style = {'size': 15, 'color': 'black'}

    # TODO - figure out x/y axes data coordinates
    # corresponding to 10% from left and 10% from top
    # use geoproj and proj
    dleft = 0.01
    dtop = 0.97
    proj_str = proj.proj4_init
    merc_to_dd = pyproj.Proj(proj_str)

    # use built-in transforms to get from axes units to data units
    display_to_data = ax.transData.inverted()
    axes_to_display = ax.transAxes

    # these are x,y coordinates in projected space
    yleft, t1 = display_to_data.transform(
        axes_to_display.transform((dleft, 0.5)))
    t2, xtop = display_to_data.transform(axes_to_display.transform(
        (0.5, dtop)))

    # these are coordinates in lon,lat space
    yleft_dd, t1_dd = merc_to_dd(yleft, t1, inverse=True)
    t2_dd, xtop_dd = merc_to_dd(t2, xtop, inverse=True)

    # drawing our own tick labels INSIDE the plot, as
    # Cartopy doesn't seem to support this.
    yrange = ymax - ymin
    xrange = xmax - xmin
    ddlabelsize = 12
    for xloc in gl.xlocator.locs:
        outside = xloc < xmin or xloc > xmax
        # don't draw labels when we're too close to either edge
        near_edge = (xloc - xmin) < (xrange * 0.1) or (xmax - xloc) < (xrange *
                                                                       0.1)
        if outside or near_edge:
            continue
        xtext = r'$%.1f^\circ$W' % (abs(xloc))
        ax.text(xloc,
                xtop_dd,
                xtext,
                fontsize=ddlabelsize,
                zorder=GRID_ZORDER,
                ha='center',
                fontname=DEFAULT_FONT,
                transform=ccrs.Geodetic())

    for yloc in gl.ylocator.locs:
        outside = yloc < gd.ymin or yloc > gd.ymax
        # don't draw labels when we're too close to either edge
        near_edge = (yloc - gd.ymin) < (yrange * 0.1) or (gd.ymax - yloc) < (
            yrange * 0.1)
        if outside or near_edge:
            continue
        if yloc < 0:
            ytext = r'$%.1f^\circ$S' % (abs(yloc))
        else:
            ytext = r'$%.1f^\circ$N' % (abs(yloc))
        ax.text(yleft_dd,
                yloc,
                ytext,
                fontsize=ddlabelsize,
                zorder=GRID_ZORDER,
                va='center',
                fontname=DEFAULT_FONT,
                transform=ccrs.Geodetic())

    # draw cities
    mapcities = mmap.drawCities(shadow=True, zorder=CITIES_ZORDER)

    # draw the figure border thickly
    # TODO - figure out how to draw map border
    # bwidth = 3
    # ax.spines['top'].set_visible(True)
    # ax.spines['left'].set_visible(True)
    # ax.spines['bottom'].set_visible(True)
    # ax.spines['right'].set_visible(True)
    # ax.spines['top'].set_linewidth(bwidth)
    # ax.spines['right'].set_linewidth(bwidth)
    # ax.spines['bottom'].set_linewidth(bwidth)
    # ax.spines['left'].set_linewidth(bwidth)

    # Get the corner of the map with the lowest population
    corner_rect, filled_corner = _get_open_corner(popgrid, ax)
    clat2 = round_to_nearest(center_lat, 1.0)
    clon2 = round_to_nearest(center_lon, 1.0)

    # draw a little globe in the corner showing in small-scale
    # where the earthquake is located.
    proj = ccrs.Orthographic(central_latitude=clat2, central_longitude=clon2)
    ax2 = fig.add_axes(corner_rect, projection=proj)
    ax2.add_feature(cfeature.OCEAN,
                    zorder=0,
                    facecolor=WATERCOLOR,
                    edgecolor=WATERCOLOR)
    ax2.add_feature(cfeature.LAND, zorder=0, edgecolor='black')
    ax2.plot([clon2], [clat2],
             'w*',
             linewidth=1,
             markersize=16,
             markeredgecolor='k',
             markerfacecolor='r')
    ax2.gridlines()
    ax2.set_global()
    ax2.outline_patch.set_edgecolor('black')
    ax2.outline_patch.set_linewidth(2)

    # Draw the map scale in the unoccupied lower corner.
    corner = 'lr'
    if filled_corner == 'lr':
        corner = 'll'
    draw_scale(ax, corner, pady=0.05, padx=0.05)

    # Draw the epicenter as a black star
    plt.sca(ax)
    plt.plot(center_lon,
             center_lat,
             'k*',
             markersize=16,
             zorder=EPICENTER_ZORDER,
             transform=geoproj)

    if is_scenario:
        plt.text(center_lon,
                 center_lat,
                 'SCENARIO',
                 fontsize=64,
                 zorder=WATERMARK_ZORDER,
                 transform=geoproj,
                 alpha=0.2,
                 color='red',
                 horizontalalignment='center')

    # create pdf and png output file names
    pdf_file = basename + '.pdf'
    png_file = basename + '.png'

    # save to pdf
    plt.savefig(pdf_file)
    plt.savefig(png_file)

    return (pdf_file, png_file, mapcities)
예제 #7
0
def draw_contour(shakefile,
                 popfile,
                 oceanfile,
                 cityfile,
                 outfilename,
                 make_png=False):
    """Create a contour map showing population (greyscale) underneath contoured MMI.

    :param shakefile:
      String path to ShakeMap grid.xml file.
    :param popfile:
      String path to GDALGrid-compliant file containing population data.
    :param oceanfile:
      String path to file containing ocean vector data in a format compatible with fiona.
    :param cityfile:
      String path to file containing GeoNames cities data.
    :param outfilename:
      String path containing desired output PDF filename.
    :param make_png:
      Boolean indicating whether a PNG version of the file should also be created in the
      same output folder as the PDF.
    :returns:
      Tuple containing: 
        - Name of PNG file created, or None if PNG output not specified.
        - CartopyCities object containing the cities that were rendered on the contour map.
    """
    #load the shakemap - for the time being, we're interpolating the
    #population data to the shakemap, which would be important
    #if we were doing math with the pop values.  We're not, so I think it's ok.
    shakegrid = ShakeGrid.load(shakefile, adjust='res')
    gd = shakegrid.getGeoDict()

    #retrieve the epicenter - this will get used on the map
    clat = shakegrid.getEventDict()['lat']
    clon = shakegrid.getEventDict()['lon']

    #load the population data, sample to shakemap
    popgrid = GDALGrid.load(popfile, samplegeodict=gd, resample=True)
    popdata = popgrid.getData()

    #smooth the MMI data for contouring
    mmi = shakegrid.getLayer('mmi').getData()
    smoothed_mmi = gaussian_filter(mmi, FILTER_SMOOTH)

    #clip the ocean data to the shakemap
    bbox = (gd.xmin, gd.ymin, gd.xmax, gd.ymax)
    oceanshapes = _clip_bounds(bbox, oceanfile)

    #load the cities data, limit to cities within shakemap bounds
    allcities = CartopyCities.fromDefault()
    cities = allcities.limitByBounds((gd.xmin, gd.xmax, gd.ymin, gd.ymax))

    # Define ocean/land masks to do the contours, since we want different contour line styles over land and water.
    oceangrid = Grid2D.rasterizeFromGeometry(oceanshapes,
                                             gd,
                                             burnValue=1.0,
                                             fillValue=0.0,
                                             mustContainCenter=False,
                                             attribute=None)
    oceanmask = np.ma.masked_where(oceangrid == 1.0, smoothed_mmi)
    landmask = np.ma.masked_where(oceangrid == 0.0, smoothed_mmi)

    # Use our GMT-inspired palette class to create population and MMI colormaps
    popmap = ColorPalette.fromPreset('pop')
    mmimap = ColorPalette.fromPreset('mmi')

    #use the ShakeMap to determine the aspect ratio of the map
    aspect = (gd.xmax - gd.xmin) / (gd.ymax - gd.ymin)
    figheight = FIGWIDTH / aspect
    fig = plt.figure(figsize=(FIGWIDTH, figheight))

    # set up axes object with PlateCaree (non) projection.
    ax = plt.axes([0.02, 0.02, 0.95, 0.95], projection=ccrs.PlateCarree())

    #set the image extent to that of the data
    img_extent = (gd.xmin, gd.xmax, gd.ymin, gd.ymax)
    plt.imshow(popdata,
               origin='upper',
               extent=img_extent,
               cmap=popmap.cmap,
               vmin=popmap.vmin,
               vmax=popmap.vmax,
               zorder=9,
               interpolation='none')

    #define arrays of latitude and longitude we will use to plot MMI contours
    lat = np.linspace(gd.ymin, gd.ymax, gd.ny)
    lon = np.linspace(gd.xmin, gd.xmax, gd.nx)

    #contour the masked land/ocean MMI data at half-integer levels
    plt.contour(lon,
                lat,
                landmask,
                linewidths=3.0,
                linestyles='solid',
                zorder=10,
                cmap=mmimap.cmap,
                vmin=mmimap.vmin,
                vmax=mmimap.vmax,
                levels=np.arange(0.5, 10.5, 1.0))

    plt.contour(lon,
                lat,
                oceanmask,
                linewidths=2.0,
                linestyles='dashed',
                zorder=13,
                cmap=mmimap.cmap,
                vmin=mmimap.vmin,
                vmax=mmimap.vmax,
                levels=np.arange(0.5, 10.5, 1.0))

    #the idea here is to plot invisible MMI contours at integer levels and then label them.
    #labeling part does not currently work.
    cs = plt.contour(lon,
                     lat,
                     landmask,
                     linewidths=0.0,
                     levels=np.arange(0, 11),
                     zorder=10)
    #clabel is not actually drawing anything, but it is blotting out a portion of the contour line.  ??
    ax.clabel(cs, np.arange(0, 11), colors='k', zorder=25)

    #set the extent of the map to our data
    ax.set_extent([lon.min(), lon.max(), lat.min(), lat.max()])

    #draw the ocean data
    if isinstance(oceanshapes[0], mPolygon):
        for shape in oceanshapes[0]:
            ocean_patch = PolygonPatch(shape,
                                       zorder=10,
                                       facecolor=WATERCOLOR,
                                       edgecolor=WATERCOLOR)
            ax.add_patch(ocean_patch)
    else:
        ocean_patch = PolygonPatch(oceanshapes[0],
                                   zorder=10,
                                   facecolor=WATERCOLOR,
                                   edgecolor=WATERCOLOR)
        ax.add_patch(ocean_patch)

    # add coastlines with desired scale of resolution
    ax.coastlines('10m', zorder=11)

    #draw meridians and parallels using Cartopy's functions for that
    gl = ax.gridlines(crs=ccrs.PlateCarree(),
                      draw_labels=True,
                      linewidth=2,
                      color=(0.9, 0.9, 0.9),
                      alpha=0.5,
                      linestyle='-',
                      zorder=20)
    gl.xlabels_top = False
    gl.xlabels_bottom = False
    gl.ylabels_left = False
    gl.ylabels_right = False
    gl.xlines = True
    xlocs = np.arange(np.floor(gd.xmin - 1), np.ceil(gd.xmax + 1))
    ylocs = np.arange(np.floor(gd.ymin - 1), np.ceil(gd.ymax + 1))
    gl.xlocator = mticker.FixedLocator(xlocs)
    gl.ylocator = mticker.FixedLocator(ylocs)
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER
    gl.xlabel_style = {'size': 15, 'color': 'black'}
    gl.ylabel_style = {'size': 15, 'color': 'black'}

    #drawing our own tick labels INSIDE the plot, as Cartopy doesn't seem to support this.
    yrange = gd.ymax - gd.ymin
    xrange = gd.xmax - gd.xmin
    for xloc in gl.xlocator.locs:
        outside = xloc < gd.xmin or xloc > gd.xmax
        #don't draw labels when we're too close to either edge
        near_edge = (xloc - gd.xmin) < (xrange * 0.1) or (gd.xmax - xloc) < (
            xrange * 0.1)
        if outside or near_edge:
            continue
        if xloc < 0:
            xtext = r'$%s^\circ$W' % str(abs(int(xloc)))
        else:
            xtext = r'$%s^\circ$E' % str(int(xloc))
        ax.text(xloc,
                gd.ymax - (yrange / 35),
                xtext,
                fontsize=14,
                zorder=20,
                ha='center',
                fontname='Bitstream Vera Sans')

    for yloc in gl.ylocator.locs:
        outside = yloc < gd.ymin or yloc > gd.ymax
        #don't draw labels when we're too close to either edge
        near_edge = (yloc - gd.ymin) < (yrange * 0.1) or (gd.ymax - yloc) < (
            yrange * 0.1)
        if outside or near_edge:
            continue
        if yloc < 0:
            ytext = r'$%s^\circ$S' % str(abs(int(yloc)))
        else:
            ytext = r'$%s^\circ$N' % str(int(yloc))
        thing = ax.text(gd.xmin + (xrange / 100),
                        yloc,
                        ytext,
                        fontsize=14,
                        zorder=20,
                        va='center',
                        fontname='Bitstream Vera Sans')

    #Limit the number of cities we show - we may not want to use the population size
    #filter in the global case, but the map collision filter is a little sketchy right now.
    mapcities = cities.limitByPopulation(25000)
    mapcities = mapcities.limitByGrid()
    mapcities = mapcities.limitByMapCollision(ax, shadow=True)
    mapcities.renderToMap(ax, shadow=True, fontsize=12, zorder=11)

    #Get the corner of the map with the lowest population
    corner_rect, filled_corner = _get_open_corner(popgrid, ax)
    clat = round_to_nearest(clat, 1.0)
    clon = round_to_nearest(clon, 1.0)

    #draw a little globe in the corner showing in small-scale where the earthquake is located.
    proj = ccrs.Orthographic(central_latitude=clat, central_longitude=clon)
    ax2 = fig.add_axes(corner_rect, projection=proj)
    ax2.add_feature(cartopy.feature.OCEAN,
                    zorder=0,
                    facecolor=WATERCOLOR,
                    edgecolor=WATERCOLOR)
    ax2.add_feature(cartopy.feature.LAND, zorder=0, edgecolor='black')
    ax2.plot([clon], [clat],
             'w*',
             linewidth=1,
             markersize=16,
             markeredgecolor='k',
             markerfacecolor='r')
    gh = ax2.gridlines()
    ax2.set_global()
    ax2.outline_patch.set_edgecolor('black')
    ax2.outline_patch.set_linewidth(2)

    #Draw the map scale in the unoccupied lower corner.
    corner = 'lr'
    if filled_corner == 'lr':
        corner = 'll'
    draw_scale(ax, corner, pady=0.05, padx=0.05)

    plt.savefig(outfilename)

    pngfile = None
    if make_png:
        fpath, fname = os.path.split(outfilename)
        fbase, t = os.path.splitext(fname)
        pngfile = os.path.join(fpath, fbase + '.png')
        plt.savefig(pngfile)

    return (pngfile, mapcities)
예제 #8
0
def get_quake_desc(event, lat, lon, isMainEvent):
    ndeaths = event["TotalDeaths"]
    # summarize the exposure values
    exposures = np.array([
        event["MMI1"],
        event["MMI2"],
        event["MMI3"],
        event["MMI4"],
        event["MMI5"],
        event["MMI6"],
        event["MMI7"],
        event["MMI8"],
        event["MMI9+"],
    ])
    exposures = np.array([round_to_nearest(exp, 1000) for exp in exposures])
    # get the highest two exposures greater than zero
    iexp = np.where(exposures > 0)[0][::-1]

    romans = [
        "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX or greater"
    ]
    if len(iexp) >= 2:
        exposures = [exposures[iexp[1]], exposures[iexp[0]]]
        ilevels = [romans[iexp[1]], romans[iexp[0]]]
        expfmt = ", with estimated population exposures of %s at intensity"
        expfmt = expfmt + " %s and %s at intensity %s"
        exptxt = expfmt % (
            commify(int(exposures[0])),
            ilevels[0],
            commify(int(exposures[1])),
            ilevels[1],
        )
    else:
        exptxt = ""

    # create string describing this most impactful event
    dfmt = "A magnitude %.1f earthquake %i km %s of this event struck %s on %s (UTC)%s"

    mag = event["Magnitude"]
    etime = pd.Timestamp(event["Time"])
    etime = etime.strftime("%B %d, %Y")
    etime = re.sub(" 0", " ", etime)
    country = Country()
    if pd.isnull(event["Name"]):
        # hack for persistent error in expocat
        if event["CountryCode"] == "UM" and event["Lat"] > 40:
            cdict = country.getCountry("US")
        else:
            cdict = country.getCountry(event["CountryCode"])
        if cdict:
            cname = cdict["Name"]
        else:
            cname = "in the open ocean"
    else:
        cname = event["Name"].replace('"', "")

    cdist = round(geodetic_distance(event["Lat"], event["Lon"], lat, lon))
    cdir = get_compass_dir(lat, lon, event["Lat"], event["Lon"],
                           format="long").lower()
    if ndeaths and str(ndeaths) != "nan":
        dfmt = dfmt + ", resulting in a reported %s %s."

        if ndeaths > 1:
            dstr = "fatalities"
        else:
            dstr = "fatality"

        ndeathstr = commify(int(ndeaths))
        eqdesc = dfmt % (mag, cdist, cdir, cname, etime, exptxt, ndeathstr,
                         dstr)
    else:
        dfmt = dfmt + ", with no reported fatalities."
        eqdesc = dfmt % (mag, cdist, cdir, cname, etime, exptxt)

    return eqdesc