def draw_stations_map(pstreams, event, event_dir):
    # draw map of stations and cities and stuff
    lats = np.array(
        [stream[0].stats.coordinates['latitude'] for stream in pstreams])
    lons = np.array(
        [stream[0].stats.coordinates['longitude'] for stream in pstreams])
    map_width = event.magnitude
    cy = event.latitude
    cx = event.longitude
    xmin = lons.min()
    xmax = lons.max()
    ymin = lats.min()
    ymax = lats.max()
    if xmax - xmin < map_width:
        xmin = cx - map_width / 2
        xmax = cx + map_width / 2
    if ymax - ymin < map_width:
        ymin = cy - map_width / 2
        ymax = cy + map_width / 2
    bounds = (xmin, xmax, ymin, ymax)
    figsize = (10, 10)
    cities = Cities.fromDefault()
    mmap = MercatorMap(bounds, figsize, cities)
    mmap.drawCities(draw_dots=True)
    ax = mmap.axes
    draw_scale(ax)
    ax.plot(cx, cy, 'r*', markersize=16, transform=mmap.geoproj, zorder=8)
    status = [
        FAILED_COLOR
        if np.any([trace.hasParameter("failure")
                   for trace in stream]) else PASSED_COLOR
        for stream in pstreams
    ]
    ax.scatter(lons,
               lats,
               c=status,
               marker='^',
               edgecolors='k',
               transform=mmap.geoproj,
               zorder=100,
               s=48)
    scale = '50m'
    land = cfeature.NaturalEarthFeature(category='physical',
                                        name='land',
                                        scale=scale,
                                        facecolor=LAND_COLOR)
    ocean = cfeature.NaturalEarthFeature(category='physical',
                                         name='ocean',
                                         scale=scale,
                                         facecolor=OCEAN_COLOR)
    ax.add_feature(land)
    ax.add_feature(ocean)
    ax.coastlines(resolution=scale, zorder=10, linewidth=1)
    mapfile = os.path.join(event_dir, 'stations_map.png')
    plt.savefig(mapfile)
    return mapfile
Example #2
0
    def mapSequence(self, sequence):
        sequence_frame = self.getSequenceEvents(sequence['name'])
        fig = plt.figure(figsize=[7, 7])
        clon = sequence['center_lon']
        clat = sequence['center_lat']
        xmin = sequence['xmin'] - 1
        xmax = sequence['xmax'] + 1
        ymin = sequence['ymin'] - 1
        ymax = sequence['ymax'] + 1
        bounds = [xmin, xmax, ymin, ymax]
        figsize = (7, 7)
        cities = Cities.fromDefault()
        dims = [0.1, 0.1, 0.8, 0.8]
        mmap = MercatorMap(bounds, figsize, cities, dimensions=dims)
        fig = mmap.figure
        ax = mmap.axes
        proj = mmap.proj
        markersizes = list(MSIZES.values())
        for idx, row in sequence_frame.iterrows():
            elat = row['latitude']
            elon = row['longitude']
            emag = row['magnitude']
            mdiff = np.abs(emag - np.array(list(MSIZES.keys())))
            imin = mdiff.argmin()
            markersize = markersizes[imin]
            zorder = 1 / markersize
            ax.plot([elon], [elat],
                    'g',
                    marker='o',
                    mec='k',
                    markersize=markersize,
                    zorder=zorder,
                    transform=ccrs.PlateCarree())
        mmap.drawCities(draw_dots=True)
        _draw_graticules(ax, xmin, xmax, ymin, ymax)
        corner = 'll'
        ax.coastlines(resolution='50m')
        draw_scale(ax, corner, pady=0.05, padx=0.05, zorder=SCALE_ZORDER)

        states_provinces = cfeature.NaturalEarthFeature(
            category='cultural',
            name='admin_1_states_provinces_lines',
            scale='50m',
            facecolor='none')
        states_provinces = cfeature.NaturalEarthFeature(
            category='cultural',
            name='admin_1_states_provinces_lines',
            scale='50m',
            facecolor='none')
        plt.title('%s (N=%i)' % (sequence['name'], sequence['n_earthquakes']))
        return ax, fig
Example #3
0
def test_mmap(outfile=None, bounds=None):
    if bounds is None:
        bounds = xmin, ymin, xmax, ymax = \
            -121.046000, -116.046000, 32.143500, 36.278500
    else:
        xmin, ymin, xmax, ymax = bounds
    figsize = (7, 7)
    cities = Cities.fromDefault()
    mmap = MercatorMap(bounds, figsize, cities, padding=0.5)
    fig = mmap.figure
    ax = mmap.axes
def test_mmap(outfile=None, bounds=None):
    if bounds is None:
        bounds = xmin, ymin, xmax, ymax = \
            -121.046000, -116.046000, 32.143500, 36.278500
    else:
        xmin, ymin, xmax, ymax = bounds
    figsize = (7, 7)
    cities = Cities.fromDefault()
    mmap = MercatorMap(bounds, figsize, cities, padding=0.5)
    ax = mmap.axes

    # TODO -- Travis hangs here so commenting out stuff so it doesn't hang.
    # Should sort out issue to fully test this module.

    # fig.canvas.draw()
    ax.coastlines(resolution="10m", zorder=10)
    # plt.show()
    mmap.drawCities(shadow=True)
    if outfile:
        plt.savefig(outfile)
        print(f"Figure saved to {outfile}")
Example #5
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)
Example #6
0
def draw_map(adict, override_scenario=False):
    """If adict['imtype'] is MMI, draw a map of intensity draped over
    topography, otherwise Draw IMT contour lines over hill-shaded topography.

    Args:
        adict (dictionary): A dictionary containing the following keys:
            'imtype' (str): The intensity measure type
            'topogrid' (Grid2d): A topography grid
            'allcities' (Cities): A list of global cities,
            'states_provinces' (Cartopy Feature): States/province boundaries.
            'countries' (Cartopy Feature): Country boundaries.
            'oceans' (Cartopy Feature): Oceans.
            'lakes' (Cartopy Feature): Lakes.
            'roads' (Shapely Feature): Roads.
            'faults' (Shapely Feature): Fault traces
            'datadir' (str): The path into which to deposit products
            'operator' (str): The producer of this shakemap
            'filter_size' (int): The size of the filter used before contouring
            'info' (dictionary): The shakemap info structure
            'component' (str): The intensity measure component being plotted
            'imtdict' (dictionary): Dict containing the IMT grids
            'rupdict' (dictionary): Dict containing the rupture data
            'stationdict' (dictionary): Dict of station data
            'config' (dictionary): The configuration data for this shakemap
            'tdict' (dictionary): The text strings to be printed on the map
                in the user's choice of language.
            'license_text' (str): License text to display at bottom of map
            'license_logo' (str): Path to license logo image to display
                next to license text
        override_scenario (bool): Turn off scenario watermark.

    Returns:
        Tuple of (Matplotlib figure, Matplotlib figure): Objects containing
        the map generated by this function, and the intensity legend,
        respectively. If the imtype of this map is not 'MMI', the second
        element of the tuple will be None.
    """
    imtype = adict['imtype']
    imtdict = adict['imtdict']      # mmidict
    imtdata = np.nan_to_num(imtdict['mean'], nan=0.0) # mmidata
    gd = GeoDict(imtdict['mean_metadata'])
    imtgrid = Grid2D(imtdata, gd)   # mmigrid

    gd = imtgrid.getGeoDict()

    # Retrieve the epicenter - this will get used on the map
    rupture = rupture_from_dict(adict['ruptdict'])
    origin = rupture.getOrigin()
    center_lat = origin.lat
    center_lon = origin.lon

    # load the cities data, limit to cities within shakemap bounds
    cities = adict['allcities'].limitByBounds((gd.xmin, gd.xmax,
                                               gd.ymin, gd.ymax))

    # get the map boundaries and figure size
    bounds, figsize, aspect = _get_map_info(gd)

    # Note: dimensions are: [left, bottom, width, height]
    dim_left = 0.1
    dim_bottom = 0.19
    dim_width = 0.8
    dim_height = dim_width/aspect
    if dim_height > 0.8:
        dim_height = 0.8
        dim_width = 0.8 * aspect
        dim_left = (1.0 - dim_width) / 2

    # 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,
        dimensions=[dim_left, dim_bottom, dim_width, dim_height])
    fig = mmap.figure
    ax = mmap.axes
    # this needs to be done here so that city label collision
    # detection will work
    fig.canvas.draw()

    # get the geographic projection object
    geoproj = mmap.geoproj
    # get the mercator projection object
    proj = mmap.proj
    # get the proj4 string - used by Grid2D project() method
    projstr = proj.proj4_init

    # get the projected IMT and topo grids
    pimtgrid, ptopogrid = _get_projected_grids(imtgrid, adict['topogrid'],
                                               projstr)

    # get the projected geodict
    proj_gd = pimtgrid.getGeoDict()

    pimtdata = pimtgrid.getData()
    ptopo_data = ptopogrid.getData()

    mmimap = ColorPalette.fromPreset('mmi')

    if imtype == 'MMI':
        draped_hsv = _get_draped(pimtdata, ptopo_data, mmimap)
    else:
        # get the draped topo data
        topo_colormap = ColorPalette.fromPreset('shaketopo')
        draped_hsv = _get_shaded(ptopo_data, topo_colormap)
        # convert units
        if imtype == 'PGV':
            pimtdata = np.exp(pimtdata)
        else:
            pimtdata = np.exp(pimtdata) * 100

    plt.sca(ax)
    ax.set_xlim(proj_gd.xmin, proj_gd.xmax)
    ax.set_ylim(proj_gd.ymin, proj_gd.ymax)
    img_extent = (proj_gd.xmin, proj_gd.xmax, proj_gd.ymin, proj_gd.ymax)

    plt.imshow(draped_hsv, origin='upper', extent=img_extent,
               zorder=IMG_ZORDER, interpolation='none')

    config = adict['config']
    gmice = get_object_from_config('gmice', 'modeling', config)
    gmice_imts = gmice.DEFINED_FOR_INTENSITY_MEASURE_TYPES
    gmice_pers = gmice.DEFINED_FOR_SA_PERIODS

    oqimt = imt.from_string(imtype)

    if imtype != 'MMI' and (not isinstance(oqimt, tuple(gmice_imts)) or
                            (isinstance(oqimt, imt.SA) and
                             oqimt.period not in gmice_pers)):
        my_gmice = None
    else:
        my_gmice = gmice

    if imtype != 'MMI':
        # call the contour module in plotting to get the vertices of the
        # contour lines
        contour_objects = contour(imtdict, imtype, adict['filter_size'],
                                  my_gmice)

        # get a color palette for the levels we have
        # levels = [c['properties']['value'] for c in contour_objects]

        # cartopy shapely feature has some weird behaviors, so I had to go
        # rogue and draw contour lines/labels myself.

        # To choose which contours to label, we will keep track of the lengths
        # of contours, grouped by isovalue
        contour_lens = defaultdict(lambda: [])
        def arclen(path):
            """
            Compute the arclength of *path*, which should be a list of pairs
            of numbers.
            """
            x0, y0 = [np.array(c) for c in zip(*path)]
            x1, y1 = [np.roll(c, -1) for c in (x0, y0)] # offset by 1
            # don't include first-last vertices as an edge:
            x0, y0, x1, y1 = [c[:-1] for c in (x0, y0, x1, y1)]
            return np.sum(np.sqrt((x0 - x1)**2 + (y0 - y1)**2))

        # draw dashed contours first, the ones over land will be overridden by
        # solid contours
        for contour_object in contour_objects:
            props = contour_object['properties']
            multi_lines = sShape(contour_object['geometry'])
            pmulti_lines = proj.project_geometry(multi_lines, src_crs=geoproj)
            for multi_line in pmulti_lines:
                pmulti_line = mapping(multi_line)['coordinates']
                x, y = zip(*pmulti_line)
                contour_lens[props['value']].append(arclen(pmulti_line))
                # color = imt_cmap.getDataColor(props['value'])
                ax.plot(x, y, color=props['color'], linestyle='dashed',
                        zorder=DASHED_CONTOUR_ZORDER)

        white_box = dict(
            boxstyle="round",
            ec=(0, 0, 0),
            fc=(1., 1, 1),
            color='k'
        )

        # draw solid contours next - the ones over water will be covered by
        # ocean polygon
        for contour_object in contour_objects:
            props = contour_object['properties']
            multi_lines = sShape(contour_object['geometry'])
            pmulti_lines = proj.project_geometry(multi_lines, src_crs=geoproj)

            # only label long contours (relative to others with the same
            # isovalue)
            min_len = np.array(contour_lens[props['value']]).mean()

            for multi_line in pmulti_lines:
                pmulti_line = mapping(multi_line)['coordinates']
                x, y = zip(*pmulti_line)
                # color = imt_cmap.getDataColor(props['value'])
                ax.plot(x, y, color=props['color'], linestyle='solid',
                        zorder=CONTOUR_ZORDER)
                if arclen(pmulti_line) >= min_len:
                    # try to label each segment with black text in a white box
                    xc = x[int(len(x)/3)]
                    yc = y[int(len(y)/3)]
                    if _label_close_to_edge(
                            xc, yc, proj_gd.xmin, proj_gd.xmax,
                            proj_gd.ymin, proj_gd.ymax):
                        continue
                    # TODO: figure out if box is going to go outside the map,
                    # if so choose a different point on the line.

                    # For small values, use scientific notation with 1 sig fig
                    # to avoid multiple contours labelled 0.0:
                    value = props['value']
                    fmt = '%.1g' if abs(value) < 0.1 else '%.1f'
                    ax.text(xc, yc, fmt % value, size=8,
                            ha="center", va="center",
                            bbox=white_box, zorder=AXES_ZORDER-1)

    # make the border thicker
    lw = 2.0
    ax.outline_patch.set_zorder(BORDER_ZORDER)
    ax.outline_patch.set_linewidth(lw)
    ax.outline_patch.set_joinstyle('round')
    ax.outline_patch.set_capstyle('round')

    # Coastlines will get drawn when we draw the ocean edges
    # ax.coastlines(resolution="10m", zorder=COAST_ZORDER, linewidth=3)

    if adict['states_provinces']:
        ax.add_feature(adict['states_provinces'], edgecolor='0.5',
                       zorder=COAST_ZORDER)

    if adict['countries']:
        ax.add_feature(adict['countries'], edgecolor='black',
                       zorder=BORDER_ZORDER)

    if adict['oceans']:
        ax.add_feature(adict['oceans'], edgecolor='black',
                       zorder=OCEAN_ZORDER)

    if adict['lakes']:
        ax.add_feature(adict['lakes'], edgecolor='black',
                       zorder=OCEAN_ZORDER)

    if adict['faults'] is not None:
        ax.add_feature(adict['faults'], edgecolor='firebrick',
                       zorder=ROAD_ZORDER)

    if adict['roads'] is not None:
        ax.add_feature(adict['roads'], edgecolor='dimgray',
                       zorder=ROAD_ZORDER)

    # draw graticules, ticks, tick labels
    _draw_graticules(ax, *bounds)

    # is this event a scenario?
    info = adict['info']
    etype = info['input']['event_information']['event_type']
    is_scenario = etype == 'SCENARIO'

    if is_scenario and not override_scenario:
        plt.text(
            center_lon, center_lat,
            adict['tdict']['title_parts']['scenario'],
            fontsize=72,
            zorder=SCENARIO_ZORDER, transform=geoproj,
            alpha=WATERMARK_ALPHA, color=WATERMARK_COLOR,
            horizontalalignment='center',
            verticalalignment='center',
            rotation=45,
            path_effects=[
                path_effects.Stroke(linewidth=1, foreground='black')]
        )

    # Draw the map scale in the unoccupied lower corner.
    corner = 'll'
    draw_scale(ax, corner, pady=0.05, padx=0.05, zorder=SCALE_ZORDER)

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

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

    # draw the rupture polygon(s) in black, if not point rupture
    point_source = True
    if not isinstance(rupture, PointRupture):
        point_source = False
        json_dict = rupture._geojson
        for feature in json_dict['features']:
            for coords in feature['geometry']['coordinates']:
                for pcoords in coords:
                    poly2d = sLineString([xy[0:2] for xy in pcoords])
                    ppoly = proj.project_geometry(poly2d)
                    mppoly = mapping(ppoly)['coordinates']
                    for spoly in mppoly:
                        x, y = zip(*spoly)
                        ax.plot(x, y, 'k', lw=1, zorder=FAULT_ZORDER)

    # draw the station data on the map
    stations = adict['stationdict']
    _draw_stations(ax, stations, imtype, mmimap, geoproj)

    _draw_title(imtype, adict)

    process_time = info['processing']['shakemap_versions']['process_time']
    map_version = int(info['processing']['shakemap_versions']['map_version'])
    if imtype == 'MMI':
        _draw_mmi_legend(fig, mmimap, gmice, process_time,
                         map_version, point_source, adict['tdict'])
        # make a separate MMI legend
        fig2 = plt.figure(figsize=figsize)
        _draw_mmi_legend(fig2, mmimap, gmice, process_time,
                         map_version, point_source, adict['tdict'])

    else:
        _draw_imt_legend(fig, mmimap, imtype, gmice, process_time, map_version,
                         point_source, adict['tdict'])
        plt.draw()
        fig2 = None

    _draw_license(fig, adict)

    return (fig, fig2)
def draw_stations_map(pstreams, event, event_dir):
    # draw map of stations and cities and stuff
    lats = np.array(
        [stream[0].stats.coordinates['latitude'] for stream in pstreams])
    lons = np.array(
        [stream[0].stats.coordinates['longitude'] for stream in pstreams])
    cy = event.latitude
    cx = event.longitude
    xmin = lons.min()
    xmax = lons.max()
    ymin = lats.min()
    ymax = lats.max()

    diff_x = max(abs(cx - xmin), abs(cx - xmax))
    diff_y = max(abs(cy - ymin), abs(cy - ymax))

    xmax = cx + MAP_PADDING * diff_x
    xmin = cx - MAP_PADDING * diff_x
    ymax = cy + MAP_PADDING * diff_y
    ymin = cy - MAP_PADDING * diff_y

    bounds = (xmin, xmax, ymin, ymax)
    figsize = (10, 10)
    cities = Cities.fromDefault()
    mmap = MercatorMap(bounds, figsize, cities)
    mmap.drawCities(draw_dots=True)
    ax = mmap.axes
    draw_scale(ax)
    ax.plot(cx, cy, 'r*', markersize=16, transform=mmap.geoproj, zorder=8)
    status = [
        FAILED_COLOR
        if np.any([trace.hasParameter("failure")
                   for trace in stream]) else PASSED_COLOR
        for stream in pstreams
    ]
    ax.scatter(lons,
               lats,
               c=status,
               marker='^',
               edgecolors='k',
               transform=mmap.geoproj,
               zorder=100,
               s=48)

    passed_marker = mlines.Line2D([], [],
                                  color=PASSED_COLOR,
                                  marker='^',
                                  markeredgecolor='k',
                                  markersize=12,
                                  label='Passed station',
                                  linestyle='None')
    failed_marker = mlines.Line2D([], [],
                                  color=FAILED_COLOR,
                                  marker='^',
                                  markeredgecolor='k',
                                  markersize=12,
                                  label='Failed station',
                                  linestyle='None')
    earthquake_marker = mlines.Line2D([], [],
                                      color='red',
                                      marker='*',
                                      markersize=12,
                                      label='Earthquake Epicenter',
                                      linestyle='None')
    ax.legend(handles=[passed_marker, failed_marker, earthquake_marker],
              fontsize=12)

    scale = '50m'
    land = cfeature.NaturalEarthFeature(category='physical',
                                        name='land',
                                        scale=scale,
                                        facecolor=LAND_COLOR)
    ocean = cfeature.NaturalEarthFeature(category='physical',
                                         name='ocean',
                                         scale=scale,
                                         facecolor=OCEAN_COLOR)
    ax.add_feature(land)
    ax.add_feature(ocean)
    ax.coastlines(resolution=scale, zorder=10, linewidth=1)
    mapfile = os.path.join(event_dir, 'stations_map.png')
    plt.savefig(mapfile)
    return mapfile
Example #8
0
def draw_stations_map(pstreams, event, event_dir):

    # interactive html map is created first
    lats = np.array(
        [stream[0].stats.coordinates["latitude"] for stream in pstreams])
    lons = np.array(
        [stream[0].stats.coordinates["longitude"] for stream in pstreams])
    stnames = np.array([stream[0].stats.station for stream in pstreams])
    networks = np.array([stream[0].stats.network for stream in pstreams])

    failed = np.array([
        np.any([trace.hasParameter("failure") for trace in stream])
        for stream in pstreams
    ])

    failure_reasons = list(
        pd.Series(
            [
                next(tr for tr in st if tr.hasParameter(
                    "failure")).getParameter("failure")["reason"]
                for st in pstreams if not st.passed
            ],
            dtype=str,
        ))

    station_map = folium.Map(location=[event.latitude, event.longitude],
                             zoom_start=7,
                             control_scale=True)

    failed_coords = zip(lats[failed], lons[failed])
    failed_stations = stnames[failed]
    failed_networks = networks[failed]
    failed_station_df = pd.DataFrame({
        "stnames": failed_stations,
        "network": failed_networks,
        "coords": failed_coords,
        "reason": failure_reasons,
    })

    passed_coords = zip(lats[~failed], lons[~failed])
    passed_stations = stnames[~failed]
    passed_networks = networks[~failed]
    passed_station_df = pd.DataFrame({
        "stnames": passed_stations,
        "network": passed_networks,
        "coords": passed_coords,
    })

    # Plot the failed first
    for i, r in failed_station_df.iterrows():
        station_info = "NET: {} LAT: {:.2f} LON: {:.2f} REASON: {}".format(
            r["network"], r["coords"][0], r["coords"][1], r["reason"])
        folium.CircleMarker(
            location=r["coords"],
            tooltip=r["stnames"],
            popup=station_info,
            color=FAILED_COLOR,
            fill=True,
            radius=6,
        ).add_to(station_map)

    for i, r in passed_station_df.iterrows():
        station_info = "NET: {}\n LAT: {:.2f} LON: {:.2f}".format(
            r["network"], r["coords"][0], r["coords"][1])
        folium.CircleMarker(
            location=r["coords"],
            tooltip=r["stnames"],
            popup=station_info,
            color=PASSED_COLOR,
            fill=True,
            radius=10,
        ).add_to(station_map)

    event_info = "MAG: {} LAT: {:.2f} LON: {:.2f} DEPTH: {:.2f}".format(
        event.magnitude, event.latitude, event.longitude, event.depth)
    folium.CircleMarker(
        [event.latitude, event.longitude],
        popup=event_info,
        color="yellow",
        fill=True,
        radius=15,
    ).add_to(station_map)

    html_mapfile = os.path.join(event_dir, "stations_map.html")
    station_map.save(html_mapfile)

    # now the static map for the report is created
    # draw map of stations and cities and stuff
    cy = event.latitude
    cx = event.longitude
    xmin = lons.min()
    xmax = lons.max()
    ymin = lats.min()
    ymax = lats.max()

    diff_x = max(abs(cx - xmin), abs(cx - xmax), 1)
    diff_y = max(abs(cy - ymin), abs(cy - ymax), 1)

    xmax = cx + MAP_PADDING * diff_x
    xmin = cx - MAP_PADDING * diff_x
    ymax = cy + MAP_PADDING * diff_y
    ymin = cy - MAP_PADDING * diff_y

    bounds = (xmin, xmax, ymin, ymax)
    figsize = (10, 10)
    cities = Cities.fromDefault()
    mmap = MercatorMap(bounds, figsize, cities)
    mmap.drawCities(draw_dots=True)
    ax = mmap.axes
    draw_scale(ax)
    ax.plot(cx, cy, "r*", markersize=16, transform=mmap.geoproj, zorder=8)

    failed = np.array([
        np.any([trace.hasParameter("failure") for trace in stream])
        for stream in pstreams
    ])

    # Plot the failed first
    ax.scatter(
        lons[failed],
        lats[failed],
        c=FAILED_COLOR,
        marker="v",
        edgecolors="k",
        transform=mmap.geoproj,
        zorder=100,
        s=48,
    )

    # Plot the successes above the failures
    ax.scatter(
        lons[~failed],
        lats[~failed],
        c=PASSED_COLOR,
        marker="^",
        edgecolors="k",
        transform=mmap.geoproj,
        zorder=101,
        s=48,
    )

    passed_marker = mlines.Line2D(
        [],
        [],
        color=PASSED_COLOR,
        marker="^",
        markeredgecolor="k",
        markersize=12,
        label="Passed station",
        linestyle="None",
    )
    failed_marker = mlines.Line2D(
        [],
        [],
        color=FAILED_COLOR,
        marker="v",
        markeredgecolor="k",
        markersize=12,
        label="Failed station",
        linestyle="None",
    )
    earthquake_marker = mlines.Line2D(
        [],
        [],
        color="red",
        marker="*",
        markersize=12,
        label="Earthquake Epicenter",
        linestyle="None",
    )
    ax.legend(handles=[passed_marker, failed_marker, earthquake_marker],
              fontsize=12)

    scale = "50m"
    land = cfeature.NaturalEarthFeature(category="physical",
                                        name="land",
                                        scale=scale,
                                        facecolor=LAND_COLOR)
    ocean = cfeature.NaturalEarthFeature(category="physical",
                                         name="ocean",
                                         scale=scale,
                                         facecolor=OCEAN_COLOR)
    ax.add_feature(land)
    ax.add_feature(ocean)
    ax.coastlines(resolution=scale, zorder=10, linewidth=1)
    png_mapfile = os.path.join(event_dir, "stations_map.png")
    plt.savefig(png_mapfile)
    return (png_mapfile, html_mapfile)
Example #9
0
    def drawHazusMap(self, shakegrid, filename, model_config):
        gd = shakegrid.getGeoDict()

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

        # define the map
        # first cope with stupid 180 meridian
        height = (gd.ymax - gd.ymin) * 111.191
        if gd.xmin < gd.xmax:
            width = (gd.xmax - gd.xmin) * \
                np.cos(np.radians(center_lat)) * 111.191
            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)) * 111.191

        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)) * 111.191)
            xmax = xmax - dw / (np.cos(np.radians(center_lat)) * 111.191)
            width = (xmax - xmin) * np.cos(np.radians(center_lat)) * 111.191
        if height > width:
            dh = (height - width) / 2.0  # this is width in km
            ymin = ymin + dh / 111.191
            ymax = ymax - dh / 111.191
            height = (ymax - ymin) * 111.191

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

        # load the counties here so we can grab the county names to
        # draw on the map
        counties_file = model_config['counties']
        counties_shapes = fiona.open(counties_file, 'r')
        counties = counties_shapes.items(bbox=(xmin, ymin, xmax, ymax))
        county_shapes = []

        county_columns = {
            'name': [],
            'lat': [],
            'lon': [],
            'pop': [],
        }

        for cid, county in counties:
            # county is a dictionary
            county_shape = sShape(county['geometry'])
            state_fips = county['properties']['STATEFP10']
            county_fips = county['properties']['COUNTYFP10']
            fips = int(state_fips + county_fips)
            df = self._dataframe
            weight = 1
            if (df['CountyFips'] == fips).any():
                loss_row = df[df['CountyFips'] == fips].iloc[0]
                weight = loss_row['EconLoss']
            center_point = county_shape.centroid
            county_name = county['properties']['NAMELSAD10'].replace(
                'County', '').strip()
            # feature = ShapelyFeature([county_shape], ccrs.PlateCarree(),
            #                          zorder=COUNTY_ZORDER)
            county_shapes.append(county_shape)
            county_columns['name'].append(county_name)
            county_columns['pop'].append(county_shape.area * weight)
            county_columns['lat'].append(center_point.y)
            county_columns['lon'].append(center_point.x)
            # ax.add_feature(feature, facecolor=GREY,
            #                edgecolor='grey', linewidth=0.5)
            # tx, ty = mmap.proj.transform_point(
            #     center_point.x, center_point.y, ccrs.PlateCarree())
            # plt.text(tx, ty, county_name,
            #          zorder=NAME_ZORDER,
            #          horizontalalignment='center',
            #          verticalalignment='center')

        # Create the MercatorMap object, which holds a separate but identical
        # axes object used to determine collisions between city labels.
        # here we're pretending that county names are city names.
        county_df = pd.DataFrame(county_columns)
        cities = Cities(county_df)
        mmap = MercatorMap(bounds, figsize, cities, padding=0.5)
        fig = mmap.figure
        ax = mmap.axes
        geoproj = mmap.geoproj
        proj = mmap.proj

        # this is a workaround to an occasional problem where some vector layers
        # are not rendered. See
        # https://github.com/SciTools/cartopy/issues/1155#issuecomment-432941088
        proj._threshold /= 6

        # this needs to be done here so that city label collision
        # detection will work
        fig.canvas.draw()

        # draw county names
        mmap.drawCities(zorder=NAME_ZORDER)

        # now draw the counties in grey
        for county_shape in county_shapes:
            feature = ShapelyFeature([county_shape],
                                     ccrs.PlateCarree(),
                                     zorder=COUNTY_ZORDER)
            ax.add_feature(feature,
                           facecolor=GREY,
                           edgecolor='grey',
                           linewidth=0.5,
                           zorder=COUNTY_ZORDER)

        # now draw the county boundaries only so that we can see
        # them on top of the colored tracts.
        for county_shape in county_shapes:
            feature = ShapelyFeature([county_shape],
                                     ccrs.PlateCarree(),
                                     zorder=COUNTY_ZORDER)
            ax.add_feature(feature,
                           facecolor=(0, 0, 0, 0),
                           edgecolor='grey',
                           linewidth=0.5,
                           zorder=NAME_ZORDER)

        # define bounding box we'll use to clip vector data
        bbox = (xmin, ymin, xmax, ymax)

        # load and clip ocean vectors to match map boundaries
        oceanfile = model_config['ocean_vectors']
        oceanshapes = _clip_bounds(bbox, oceanfile)
        ax.add_feature(ShapelyFeature(oceanshapes, crs=geoproj),
                       facecolor=WATERCOLOR,
                       zorder=OCEAN_ZORDER)

        # draw states with black border - TODO: Look into
        states_file = model_config['states']
        transparent = '#00000000'
        states = _clip_bounds(bbox, states_file)
        ax.add_feature(ShapelyFeature(states, crs=geoproj),
                       facecolor=transparent,
                       edgecolor='k',
                       zorder=STATE_ZORDER)

        # draw census tracts, colored by loss level
        tracts_file = model_config['tracts']
        tract_shapes = fiona.open(tracts_file, 'r')
        tracts = tract_shapes.items(bbox=(xmin, ymin, xmax, ymax))
        ntracts = 0
        for tid, tract in tracts:
            # tract is a dictionary
            ntracts += 1
            tract_shape = sShape(tract['geometry'])
            state_fips = str(int(tract['properties']['STATEFP10']))
            county_fips = state_fips + tract['properties']['COUNTYFP10']
            fips_column = self._dataframe['CountyFips']
            if not fips_column.isin([county_fips]).any():
                continue
            tract_fips = int(county_fips + tract['properties']['TRACTCE10'])
            econloss = 0.0
            if tract_fips in self._tract_loss:
                econloss = self._tract_loss[tract_fips]
                # print('Tract %i: Economic loss: %.3f' % (tract_fips, econloss))
            else:
                x = 1

            if econloss < 1e3:
                color = GREEN
            elif econloss >= 1e3 and econloss < 1e5:
                color = YELLOW
            elif econloss >= 1e5 and econloss < 1e6:
                color = ORANGE
            else:
                color = RED
            feature = ShapelyFeature([tract_shape],
                                     ccrs.PlateCarree(),
                                     zorder=TRACT_ZORDER)
            ax.add_feature(feature, facecolor=color)

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

        # save our map out to a file
        logging.info('Saving to %s' % filename)
        t0 = time.time()
        plt.savefig(filename, dpi=300)
        t1 = time.time()
        logging.info('Done saving map - %.2f seconds' % (t1 - t0))