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.')
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.')
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
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)
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
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
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
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)