Exemplo n.º 1
0
def test_rasterize():
    geodict = GeoDict({'xmin': 0.5, 'xmax': 3.5,
                       'ymin': 0.5, 'ymax': 3.5,
                       'dx': 1.0, 'dy': 1.0,
                       'ny': 4, 'nx': 4})
    print('Testing rasterizeFromGeometry() burning in values from a polygon sequence...')
    # Define two simple polygons and assign them to shapes
    poly1 = [(0.25, 3.75), (1.25, 3.25), (1.25, 2.25)]
    poly2 = [(2.25, 3.75), (3.25, 3.75), (3.75, 2.75),
             (3.75, 1.50), (3.25, 0.75), (2.25, 2.25)]
    shape1 = {'properties': {'value': 5}, 'geometry': mapping(Polygon(poly1))}
    shape2 = {'properties': {'value': 7}, 'geometry': mapping(Polygon(poly2))}
    shapes = [shape1, shape2]
    print('Testing burning in values where polygons need not contain pixel centers...')
    grid = Grid2D.rasterizeFromGeometry(
        shapes, geodict, fillValue=0, attribute='value', mustContainCenter=False)
    output = np.array([[5, 5, 7, 7],
                       [5, 5, 7, 7],
                       [0, 0, 7, 7],
                       [0, 0, 0, 7]])
    np.testing.assert_almost_equal(grid.getData(), output)
    print('Passed burning in values where polygons need not contain pixel centers.')

    print('Testing burning in values where polygons must contain pixel centers...')
    grid2 = Grid2D.rasterizeFromGeometry(
        shapes, geodict, fillValue=0, attribute='value', mustContainCenter=True)
    output = np.array([[5, 0, 7, 0],
                       [0, 0, 7, 7],
                       [0, 0, 0, 7],
                       [0, 0, 0, 0]])
    np.testing.assert_almost_equal(grid2.getData(), output)
    print('Passed burning in values where polygons must contain pixel centers.')
Exemplo n.º 2
0
def test_rasterize():
    geodict = GeoDict({'xmin':0.5,'xmax':3.5,
                       'ymin':0.5,'ymax':3.5,
                       'dx':1.0,'dy':1.0,
                       'ny':4,'nx':4})
    print('Testing rasterizeFromGeometry() burning in values from a polygon sequence...')
    #Define two simple polygons and assign them to shapes
    poly1 = [(0.25,3.75),(1.25,3.25),(1.25,2.25)]
    poly2 = [(2.25,3.75),(3.25,3.75),(3.75,2.75),(3.75,1.50),(3.25,0.75),(2.25,2.25)]
    shape1 = {'properties':{'value':5},'geometry':mapping(Polygon(poly1))}
    shape2 = {'properties':{'value':7},'geometry':mapping(Polygon(poly2))}
    shapes = [shape1,shape2]
    print('Testing burning in values where polygons need not contain pixel centers...')
    grid = Grid2D.rasterizeFromGeometry(shapes,geodict,fillValue=0,attribute='value',mustContainCenter=False)
    output = np.array([[5,5,7,7],
                       [5,5,7,7],
                       [0,0,7,7],
                       [0,0,0,7]])
    np.testing.assert_almost_equal(grid.getData(),output)
    print('Passed burning in values where polygons need not contain pixel centers.')

    print('Testing burning in values where polygons must contain pixel centers...')
    grid2 = Grid2D.rasterizeFromGeometry(shapes,geodict,fillValue=0,attribute='value',mustContainCenter=True)
    output = np.array([[5,0,7,0],
                       [0,0,7,7],
                       [0,0,0,7],
                       [0,0,0,0]])
    np.testing.assert_almost_equal(grid2.getData(),output)
    print('Passed burning in values where polygons must contain pixel centers.')
Exemplo n.º 3
0
def getNoDataGrid(predictors,xmin,xmax,ymin,ymax):
    txmin = xmin
    txmax = xmax
    tymin = ymin
    tymax = ymax
    mindx = 9999999999
    mindy = 9999999999
    #figure out bounds enclosing all files
    for predname,predfile in predictors.items():
        if not os.path.isfile(predfile):
            continue
        ftype = getFileType(predfile)
        if ftype == 'shapefile':
            f = fiona.open(predfile,'r')
            bxmin,bymin,bxmax,bymax = f.bounds
            f.close()
            if bxmin < txmin:
                txmin = bxmin
            if bxmax > txmax:
                txmax = bxmax
            if bymin < tymin:
                tymin = bymin
            if bymax > tymax:
                tymax = bymax
        elif ftype == 'grid':
            gridtype = getGridType(predfile)
            if gridtype is None:
                raise Exception('File "%s" does not appear to be either a GMT grid or an ESRI grid.' % gridfile)
            fdict = getFileGeoDict(predfile,gridtype)
            if fdict.dx < mindx:
                mindx = fdict.dx
            if fdict.dy < mindy:
                mindy = fdict.dy
            if fdict.xmin < txmin:
                txmin = fdict.xmin
            if fdict.xmax > txmax:
                txmax = txmax
            if fdict.ymin < tymin:
                tymin = tymin
            if fdict.ymax > tymax:
                tymax = tymax
    sdict = GeoDict.createDictFromBox(txmin,txmax,tymin,tymax,mindx,mindy)
    nanarray = np.zeros((sdict.ny,sdict.nx),dtype=np.int8)
    for predname,predfile in predictors.items():
        if not os.path.isfile(predfile):
            continue
        ftype = getFileType(predfile)
        if ftype == 'shapefile':
            shapes = list(fiona.open(predfile,'r'))
            grid = Grid2D.rasterizeFromGeometry(shapes,sdict)
        else:
            gridtype = getGridType(predfile)
            if gridtype == 'gmt':
                grid = GMTGrid.load(predfile,samplegeodict=sdict,resample=True,method='nearest',doPadding=True)
            else:
                grid = GDALGrid.load(predfile,samplegeodict=sdict,resample=True,method='nearest',doPadding=True)
        nangrid = np.isnan(grid.getData())
        nanarray = nanarray | nangrid
    nangrid = Grid2D(data=nanarray,geodict=sdict)
    return nangrid
Exemplo n.º 4
0
    def getLossByShapes(self,
                        mmidata,
                        popdata,
                        isodata,
                        shapes,
                        geodict,
                        eventyear=None,
                        gdpobj=None):
        """Divide the losses calculated per grid cell into polygons that intersect with the grid.

        :param mmidata:
          Array of MMI values, dimensions (M,N).
        :param popdata:
          Array of population values, dimensions (M,N).
        :param isodata:
          Array of numeric country code values, dimensions (M,N).
        :param shapes:
          Sequence of GeoJSON-like polygons as returned from fiona.open().
        :param eventyear:
          4 digit event year, must be not None if loss type is economic.
        :param gdpobj:
          GDP object, containing per capita GDP data from all countries.  
          Must not be None if calculating economic losses.
        :returns:
          Tuple of:
            1) modified sequence of polygons, including a new field "fatalities" or "dollars_lost".
            2) Total number of losses in all polygons.
        """
        lossgrid = self.getLossGrid(mmidata, popdata, isodata)
        polyshapes = []
        totloss = 0
        if self._loss_type == 'fatality':
            fieldname = 'fatalities'
        else:
            fieldname = 'dollars_lost'
        for polyrec in shapes:
            polygon = shapely.geometry.shape(polyrec['geometry'])
            #overlay the polygon on top of a grid, turn polygon pixels to 1, non-polygon pixels to 0.
            tgrid = Grid2D.rasterizeFromGeometry([polygon],
                                                 geodict,
                                                 fillValue=0,
                                                 burnValue=1.0,
                                                 attribute='value',
                                                 mustContainCenter=True)
            #get the indices of the polygon cells
            shapeidx = tgrid.getData() == 1.0
            #get the sum of those cells in the loss grid
            losses = np.nansum(lossgrid[shapeidx])
            polyrec['properties'][fieldname] = int(losses)
            polyshapes.append(polyrec)
            totloss += int(losses)

        return (polyshapes, totloss)
Exemplo n.º 5
0
def create_overlay_image(container, filename):
    """Create a semi-transparent PNG image of intensity.

    Args:
        container (ShakeMapOutputContainer): Results of model.conf.
        filename (str): Path to desired output PNG file.
    Returns:
        GeoDict: GeoDict object for the intensity grid.
    """
    # extract the intensity data from the container
    comp = container.getComponents('MMI')
    if len(comp) == 0:
        return None
    comp = comp[0]
    imtdict = container.getIMTGrids('MMI', comp)
    mmigrid = imtdict['mean']
    gd = GeoDict(imtdict['mean_metadata'])
    imtdata = mmigrid.copy()
    rows, cols = imtdata.shape

    # get the intensity colormap
    palette = ColorPalette.fromPreset('mmi')

    # map intensity values into
    # RGBA array
    rgba = palette.getDataColor(imtdata, color_format='array')

    # set the alpha value to 255 wherever we have MMI 0
    rgba[imtdata <= 1.5] = 0

    if 'CALLED_FROM_PYTEST' not in os.environ:
        # mask off the areas covered by ocean
        oceans = shpreader.natural_earth(category='physical',
                                         name='ocean',
                                         resolution='10m')
        bbox = (gd.xmin, gd.ymin, gd.xmax, gd.ymax)
        with fiona.open(oceans) as c:
            tshapes = list(c.items(bbox=bbox))
            shapes = []
            for tshp in tshapes:
                shapes.append(shape(tshp[1]['geometry']))
            if len(shapes):
                oceangrid = Grid2D.rasterizeFromGeometry(shapes, gd,
                                                         fillValue=0.0)
                rgba[oceangrid.getData() == 1] = 0

    # save rgba image as png
    img = Image.fromarray(rgba)
    img.save(filename)
    return gd
Exemplo n.º 6
0
def create_overlay_image(container, oceanfile, filename):
    """Create a semi-transparent PNG image of intensity.

    Args:
        container (ShakeMapOutputContainer): Results of model.conf.
        oceanfile (str): Path to shapefile containing ocean polygons.
        filename (str): Path to desired output PNG file.
    Returns:
        GeoDict: GeoDict object for the intensity grid.
    """
    # extract the intensity data from the container
    comp = container.getComponents('MMI')[0]
    imtdict = container.getIMTGrids('MMI', comp)
    mmigrid = imtdict['mean']
    gd = mmigrid.getGeoDict()
    imtdata = mmigrid.getData().copy()
    rows, cols = imtdata.shape

    # get the intensity colormap
    palette = ColorPalette.fromPreset('mmi')

    # map intensity values into
    # RGBA array
    rgba = palette.getDataColor(imtdata, color_format='array')

    # set the alpha value to 255 wherever we have MMI 0
    rgba[imtdata <= 1.5] = 0

    # mask off the areas covered by ocean
    bbox = (gd.xmin, gd.ymin, gd.xmax, gd.ymax)
    with fiona.open(oceanfile) as c:
        tshapes = list(c.items(bbox=bbox))
        shapes = []
        for tshp in tshapes:
            shapes.append(shape(tshp[1]['geometry']))
        if len(shapes):
            oceangrid = Grid2D.rasterizeFromGeometry(shapes, gd, fillValue=0.0)
            rgba[oceangrid.getData() == 1] = 0

    # save rgba image as png
    img = Image.fromarray(rgba)
    img.save(filename)
    return gd
Exemplo n.º 7
0
def computeCoverage(gdict,
                    inventory,
                    numdiv=30.,
                    method='nearest',
                    proj='moll'):
    """Fast method to produce grid of area actually affected by landsliding in each cell defined by geodict

    :param gdict: geodict, likely taken from model to compare inventory against
    :param inventory: full file path to shapefile of inventory, must be in geographic coordinates, WGS84
    :type inventory: string
    :param numdiv: Approximate amount to subdivide each cell of geodict by to compute areas (higher number slower but more accurate)
    :return inventorygrid: Grid2D object reporting proportional area of landsliding inside each cell defined by geodict
    :param method: method for resampling when projecting back to geographic coordinates, nearest recommended but not perfect. Cubic not recommended.

    :returns: Grid2D object reporting approximate areal coverage of input inventory corresponding to geodict
    """

    lat0 = np.mean((gdict.ymin, gdict.ymax))
    lon0 = np.mean((gdict.xmin, gdict.xmax))
    gdsubdiv = {
        'xmin': gdict.xmin,
        'xmax': gdict.xmax,
        'ymin': gdict.ymin,
        'ymax': gdict.ymax,
        'dx': gdict.dx / numdiv,
        'dy': gdict.dy / numdiv,
        'ny': gdict.ny * numdiv,
        'nx': gdict.nx * numdiv
    }
    subgd = GeoDict(gdsubdiv, adjust='res')

    f = fiona.open(inventory)

    invshp = list(f.items())
    f.close()
    shapes = [shape(inv[1]['geometry']) for inv in invshp]

    # Rasterize with oversampled area
    rast = Grid2D.rasterizeFromGeometry(shapes,
                                        subgd,
                                        fillValue=0.,
                                        burnValue=1.0,
                                        mustContainCenter=True)

    # Transform to equal area projection
    projs = '+proj=%s +datum=WGS84 +lat_0=%0.5f +lon_0=%0.5F +units=meters +x_0=0 +y_0=0' % (
        proj, lat0, lon0)
    equal_area = rast.project(projection=projs)
    egdict = equal_area.getGeoDict()

    gdds = {
        'xmin': egdict.xmin,
        'xmax': egdict.xmax,
        'ymin': egdict.ymin,
        'ymax': egdict.ymax,
        'dx': egdict.dx * numdiv,
        'dy': egdict.dy * numdiv,
        'ny': egdict.ny / numdiv,
        'nx': egdict.nx / numdiv
    }
    dsgd = GeoDict(gdds, adjust='res')

    # NEED METHOD THAT WILL USE BLOCK MEAN OR SUM
    eabig = equal_area.interpolateToGrid(dsgd, method='block_mean')

    # Project back
    eabigproj = eabig.project(projection=gdict.projection)

    # Resample to original grid
    inventorygrid = eabigproj.interpolateToGrid(gdict, method='linear')

    return inventorygrid
Exemplo n.º 8
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)