def test_presets(): palettes = ColorPalette.getPresets() palettes.sort() assert palettes == ['mmi', 'pop', 'shaketopo'] pop = ColorPalette.fromPreset('pop') values = [(0, 1.0), (5, 0.749), (50, 0.623), (100, 0.498), (500, 0.372), (1000, 0.247), (5000, 0.1215), (10000, 0.0)] for value in values: zvalue = value[0] expected_red = value[1] red = pop.getDataColor(zvalue)[0] np.testing.assert_almost_equal(expected_red, red, decimal=2) assert pop.vmin == 0 assert pop.vmax == 50000 mmi = ColorPalette.fromPreset('mmi') values = [(0.5, 1.0), (1.5, 0.874)] for value in values: zvalue = value[0] expected_red = value[1] red = mmi.getDataColor(zvalue)[0] np.testing.assert_almost_equal(expected_red, red, decimal=2) assert mmi.vmin == 0 assert mmi.vmax == 10 topo = ColorPalette.fromPreset('shaketopo') assert topo.vmin == -100 assert topo.vmax == 9200
def make_overlay(adict): """ Make a transparent PNG of intensity and a world file Args: adict (dict): The usual dictionary for the mapping functions. Returns: nothing: Nothing. """ mmidict = adict['imtdict'] mmi_array = mmidict['mean'] geodict = GeoDict(mmidict['mean_metadata']) palette = ColorPalette.fromPreset('mmi') mmi_rgb = palette.getDataColor(mmi_array, color_format='array') img = Image.fromarray(mmi_rgb) pngfile = os.path.join(adict['datadir'], 'intensity_overlay.png') img.save(pngfile, "PNG") # write out a world file # https://en.wikipedia.org/wiki/World_file worldfile = os.path.join(adict['datadir'], 'intensity_overlay.pngw') with open(worldfile, 'wt') as f: f.write('%.4f\n' % geodict.dx) f.write('0.0\n') f.write('0.0\n') f.write('-%.4f\n' % geodict.dy) f.write('%.4f\n' % geodict.xmin) f.write('%.4f\n' % geodict.ymax) return
def test_cpt(): rm = 100 # number of lines to remove on black end of magma_r # how much at the zero end should be *just* white before transitioning to # meet colors ad = 50 magma_cpt = cm.get_cmap('magma_r', 512) # start with magma_r white_bit = np.array([255 / 256, 250 / 256, 250 / 256, 1]) # create array of white slip_cpt = magma_cpt(np.linspace(0, 1, 512)) # initialize slip_cpt # move beginning up to remove black end slip_cpt[rm:, :] = slip_cpt[0:-rm, :] # gradient from white to beginning of new magma r_s = np.linspace(white_bit[0], slip_cpt[rm][0], rm - ad) g_s = np.linspace(white_bit[1], slip_cpt[rm][1], rm - ad) b_s = np.linspace(white_bit[2], slip_cpt[rm][2], rm - ad) slip_cpt[ad:rm, :][:, 0] = r_s slip_cpt[ad:rm, :][:, 1] = g_s slip_cpt[ad:rm, :][:, 2] = b_s slip_cpt[:ad, :] = white_bit slipmap = ListedColormap(slip_cpt) z0 = np.arange(0, 300, 1) z1 = np.arange(1, 301, 1) ncolors = 64 resolution = (z1.max() - z0.min()) / ncolors name = 'test' cpt = ColorPalette.fromColorMap(name, z0, z1, slipmap, resolution=resolution) try: tdir = tempfile.mkdtemp() tfile = os.path.join(tdir, 'test.cpt') cpt.write(tfile) cpt2 = ColorPalette.fromFile(tfile) np.testing.assert_almost_equal( cpt.getDataColor(150)[0], cpt2.getDataColor(150)[0]) except: pass finally: if os.path.isdir(tdir): shutil.rmtree(tdir)
def test_colormap(): viridis = plt.get_cmap('viridis') cmap = ColorPalette.fromColorMap('viridis', np.arange(0, 10), np.arange(1, 11), viridis) zero_value = np.array( [0.26666666666666666, 0.00392156862745098, 0.32941176470588235, 1.0]) ten_value = np.array( [0.9921568627450981, 0.9058823529411765, 0.1450980392156863, 1.0]) np.testing.assert_almost_equal(cmap.getDataColor(0), zero_value) np.testing.assert_almost_equal(cmap.getDataColor(10), ten_value) viridis = plt.get_cmap('viridis') cmap = ColorPalette.fromColorMap('viridis', np.arange(-4, 5), np.arange(-3, 6), viridis, is_log=True) dcolor = cmap.getDataColor(np.exp(-4.0)) tcolor = np.array( (0.26666666666666666, 0.00392156862745098, 0.32941176470588235, 1.0)) np.testing.assert_almost_equal(dcolor, tcolor)
def create_line_styles(): """Create line styles for contour KML. Args: """ line_styles = {} cpalette = ColorPalette.fromPreset('mmi') for mmi in np.arange(0, 11, 0.5): pid = '%.1f' % mmi rgb = cpalette.getDataColor(mmi, color_format='hex') line_style = skml.LineStyle(color=flip_rgb(rgb), width=2.0) style = skml.Style(linestyle=line_style) line_styles[pid] = style return line_styles
def test_file(): try: tdir = tempfile.mkdtemp() tfile = os.path.join(tdir, 'test.cpt') f = open(tfile, 'wt') f.write(TEST_FILE) f.close() cp = ColorPalette.fromFile(tfile) assert cp._cdict == TEST_DICT except: pass finally: if os.path.isdir(tdir): shutil.rmtree(tdir)
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_stations(container, datadir, document): """Create a KMZ file containing station KML and necessary icons files. Args: container (ShakeMapOutputContainer): Results of model.conf. datadir (str): Path to data directory where output KMZ will be written. document (Element): LXML KML Document element. Returns: str: Path to output KMZ file. """ create_styles(document) # get a color palette object to convert intensity values to # html colors cpalette = ColorPalette.fromPreset('mmi') # get the station data from the container station_dict = container.getStationDict() # Group the MMI and instrumented stations separately mmi_folder = etree.SubElement(document, 'Folder') mmi_name = etree.SubElement(mmi_folder, 'name') mmi_name.text = 'Macroseismic Stations' mmi_vis = etree.SubElement(mmi_folder, 'visibility') mmi_vis.text = '0' ins_folder = etree.SubElement(document, 'Folder') ins_name = etree.SubElement(ins_folder, 'name') ins_name.text = 'Instrumented Stations' ins_vis = etree.SubElement(ins_folder, 'visibility') ins_vis.text = '0' for station in station_dict['features']: if station['properties']['station_type'] == 'seismic': make_placemark(ins_folder, station, cpalette) else: make_placemark(mmi_folder, station, cpalette) # we need to find the triangle and circle icons and copy them to # the output directory this_dir, _ = os.path.split(__file__) data_path = os.path.join(this_dir, '..', 'data', 'mapping') triangle_file = os.path.join(data_path, TRIANGLE) circle_file = os.path.join(data_path, CIRCLE) tridest = os.path.join(datadir, TRIANGLE) cirdest = os.path.join(datadir, CIRCLE) shutil.copyfile(triangle_file, tridest) shutil.copyfile(circle_file, cirdest) return (tridest, cirdest)
def create_stations(container, datadir, document): """Create a KMZ file containing station KML and necessary icons files. Args: container (ShakeMapOutputContainer): Results of model.conf. datadir (str): Path to data directory where output KMZ will be written. document (Element): LXML KML Document element. Returns: str: Path to output KMZ file. """ # get a color palette object to convert intensity values to # html colors cpalette = ColorPalette.fromPreset('mmi') # get the station data from the container station_dict = container.getStationDict() # Group the MMI and instrumented stations separately mmi_folder = document.newfolder(name="Macroseismic Stations", visibility=0) ins_folder = document.newfolder(name="Instrumented Stations", visibility=0) for station in station_dict['features']: intensity = get_intensity(station) rgb = cpalette.getDataColor(intensity, color_format='hex') color = flip_rgb(rgb) if station['properties']['station_type'] == 'seismic': style_map = create_styles(document, TRIANGLE, 0.6, 0.8, color) make_placemark(ins_folder, station, cpalette, style_map) else: style_map = create_styles(document, CIRCLE, 0.4, 0.6, color) make_placemark(mmi_folder, station, cpalette, style_map) # we need to find the triangle and circle icons and copy them to # the output directory this_dir, _ = os.path.split(__file__) data_path = os.path.join(this_dir, '..', 'data', 'mapping') triangle_file = os.path.join(data_path, TRIANGLE) circle_file = os.path.join(data_path, CIRCLE) tridest = os.path.join(datadir, TRIANGLE) cirdest = os.path.join(datadir, CIRCLE) shutil.copyfile(triangle_file, tridest) shutil.copyfile(circle_file, cirdest) return (tridest, cirdest)
def make_pin_thumbnail(adict): """Make the artsy-thumbnail for the pin on the USGS webpages. """ imtdict = adict['imtdict'] grid = imtdict['mean'] metadata = imtdict['mean_metadata'] num_pixels = 300 randx = np.random.rand(num_pixels) randy = np.random.rand(num_pixels) rx = (randx * metadata['nx']).astype(np.int) ry = (randy * metadata['ny']).astype(np.int) rvals = np.arange(num_pixels) x_grid = np.arange(400) y_grid = np.arange(400) mx_grid, my_grid = np.meshgrid(x_grid, y_grid) grid = griddata(np.hstack( [randx.reshape((-1, 1)) * 400, randy.reshape((-1, 1)) * 400]), grid[ry, rx], (mx_grid, my_grid), method='nearest') grid = (grid * 10 + 0.5).astype(np.int).astype(np.float) / 10.0 rgrid = griddata(np.hstack( [randx.reshape((-1, 1)) * 400, randy.reshape((-1, 1)) * 400]), rvals, (mx_grid, my_grid), method='nearest') irgrid = rgrid.astype(np.int32) mypols = [p[0]['coordinates'] for p in rasterio.features.shapes(irgrid)] mmimap = ColorPalette.fromPreset('mmi') plt.figure(figsize=(2.75, 2.75), dpi=96, frameon=False) plt.axis('off') plt.tight_layout() plt.imshow(grid, cmap=mmimap.cmap, vmin=1.5, vmax=9.5) for pol in mypols: mycoords = list(zip(*pol[0])) plt.plot(mycoords[0], mycoords[1], color='#cccccc', linewidth=0.2) plt.savefig(os.path.join(adict['datadir'], "pin-thumbnail.png"), dpi=96, bbox_inches=matplotlib.transforms.Bbox([[0.47, 0.39], [2.50, 2.50]]), pad_inches=0)
def test_simplemap(): z0 = [0, 1, 2] z1 = [1, 2, 3] rgb0 = [(0, 0, 0), (85, 85, 85), (170, 170, 170)] rgb1 = [(85, 85, 85), (170, 170, 170), (255, 255, 255)] nan_color = (0, 0, 0, 0) cp = ColorPalette('test', z0, z1, rgb0, rgb1, nan_color=nan_color) assert cp.getDataColor(0.5) == (0.16470588235294117, 0.16470588235294117, 0.16470588235294117, 1.0) cp.vmin = -1.0 cp.vmax = 4.0 assert cp.getDataColor(0.5) == (0.29803921568627451, 0.29803921568627451, 0.29803921568627451, 1.0) assert cp.getDataColor(0.5, '255') == (76, 76, 76, 255) assert cp.getDataColor(0.5, 'hex') == ('#4C4C4C') assert cp._cdict == TEST_DICT
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 _create_palette(imtype, levels): """Create a ColorPalette object from given levels and IMT type. Args: imtype (str): One of 'PGV','PGA','SA(0.3)',etc. levels (sequence): Sequence of contour levels. Returns: ColorPalette: ColorPalette using range of input data and IMT_CMAP. """ # this method assumes that levels are in logspace if len(levels) > 1: if len(levels) % 2: levels.append(levels[-1]) nsteps = 256 z0 = np.linspace(np.log(levels[0]), np.log(levels[-2]), nsteps) z1 = np.linspace(np.log(levels[1]), np.log(levels[-1]), nsteps) else: z0 = np.array([levels[0], levels[0]*10]) z1 = np.array([levels[0], levels[0]*10]) cmap = plt.get_cmap(IMT_CMAP) palette = ColorPalette.fromColorMap(imtype, z0, z1, cmap, is_log=True) return palette
def create_line_styles(document): """Create line styles for contour KML. Args: document (Element): LXML KML Document element. """ gxns = 'http://www.google.com/kml/ext/2.2' nsmap = {'gx': gxns} cpalette = ColorPalette.fromPreset('mmi') for mmi in np.arange(0, 11, 0.5): pid = 'style_mi_%.1f' % mmi style = etree.SubElement(document, 'Style', id=pid) linestyle = etree.SubElement(style, 'LineStyle') color = etree.SubElement(linestyle, 'color') rgb = cpalette.getDataColor(mmi, color_format='hex') color.text = flip_rgb(rgb) width = etree.SubElement(linestyle, 'width') width.text = '2.0' # TODO: this doesn't work! vis = etree.SubElement(linestyle, '{%s}labelVisibility' % gxns, nsmap=nsmap) vis.text = '1'
def contour(container, imtype, component, filter_size): """ Generate contours of a specific IMT and return as a Shapely MultiLineString object. Args: container (ShakeMapOutputContainer): ShakeMapOutputContainer with ShakeMap output data. imtype (str): String containing the name of an Intensity Measure Type found in container. component (str): Intensity Measure component found in container. filter_size (int): Integer filter (see https://docs.scipy.org/doc/scipy-0.16.1/reference/generated/scipy.ndimage.filters.median_filter.html) Returns: list: List of dictionaries containing two fields - geometry: GeoJSON-like representation of one of the objects in https://toblerity.org/fiona/manual.html#geometry-types - properties: Dictionary of properties describing that feature. Raises: NotImplementedError -- if the user attempts to contour a data file with sets of points rather than grids. """ # noqa intensity_colormap = ColorPalette.fromPreset('mmi') imtdict = container.getIMTGrids(imtype, component) gridobj = imtdict['mean'] grid = gridobj.getData() metadata = gridobj.getGeoDict().asDict() if imtype == 'MMI': sgrid = grid units = 'mmi' elif imtype == 'PGV': sgrid = np.exp(grid) units = 'cms' else: sgrid = np.exp(grid) * 100.0 units = 'pctg' if filter_size > 0: fgrid = median_filter(sgrid, size=filter_size) else: fgrid = sgrid interval_type = 'log' if imtype == 'MMI': interval_type = 'linear' intervals = getContourLevels(np.min(fgrid), np.max(fgrid), itype=interval_type) lonstart = metadata['xmin'] latstart = metadata['ymin'] lonspan = np.abs(metadata['xmax'] - lonstart) latspan = np.abs(metadata['ymax'] - latstart) nlon = metadata['nx'] nlat = metadata['ny'] line_strings = [] # dictionary of MultiLineStrings and props for cval in intervals: contours = measure.find_contours(fgrid, cval) # # Convert coords to geographic coordinates; the coordinates # are returned in row, column order (i.e., (y, x)) # new_contours = [] plot_contours = [] for ic, coords in enumerate(contours): # coords is a line segment if len(coords) <= 20: # skipping little contour islands? continue mylons = coords[:, 1] * lonspan / nlon + lonstart mylats = (nlat - coords[:, 0]) * latspan / nlat + latstart contours[ic][:, 0] = mylons[:] contours[ic][:, 1] = mylats[:] plot_contours.append(contours[ic]) new_contours.append(contours[ic].tolist()) if len(new_contours): mls = MultiLineString(new_contours) props = {'value': cval, 'units': units} if imtype == 'MMI': color_array = np.array(intensity_colormap.getDataColor(cval)) color_rgb = np.array(color_array[0:3] * 255, dtype=int).tolist() props['color'] = '#%02x%02x%02x' % tuple(color_rgb) if (cval * 2) % 2 == 1: props['weight'] = 4 else: props['weight'] = 2 line_strings.append({ 'geometry': mapping(mls), 'properties': props }) return line_strings
import matplotlib.cm as cm import matplotlib.colors as colors import os # third party imports from impactutils.colors.cpalette import ColorPalette import numpy as np from openquake.hazardlib.geo.geodetic import point_at from openquake.hazardlib.geo.utils import OrthographicProjection # local imports from fault.io.timeseries import read_from_directory from fault.io.fsp import read_from_file homedir = os.path.dirname(os.path.abspath(__file__)) COLORS = ColorPalette.fromFile(os.path.join(homedir, 'fault2.cpt')) class Fault(object): """Class for analyzing a fault and associated information.""" def __init__(self): self._event = None self._segments = None self._timeseries_dict = None def autocorrelateSums(self, rows, columns): """Return slips summed along each axis and autocorrelated. Args: rows (nd.array): Array of slips summed along the rows. columns (nd.array): Array of slips summed along the columns.
def contour(imtdict, imtype, filter_size, gmice): """ Generate contours of a specific IMT and return as a Shapely MultiLineString object. Args: container (ShakeMapOutputContainer): ShakeMapOutputContainer with ShakeMap output data. imtype (str): String containing the name of an Intensity Measure Type found in container. filter_size (int): Integer filter (see https://docs.scipy.org/doc/scipy-0.16.1/reference/generated/scipy.ndimage.filters.median_filter.html) Returns: list: List of dictionaries containing two fields - geometry: GeoJSON-like representation of one of the objects in https://toblerity.org/fiona/manual.html#geometry-types - properties: Dictionary of properties describing that feature. Raises: NotImplementedError -- if the user attempts to contour a data file with sets of points rather than grids. """ # noqa oqimt = imt.from_string(imtype) intensity_colormap = ColorPalette.fromPreset('mmi') grid = imtdict['mean'] metadata = imtdict['mean_metadata'] if imtype == 'MMI': sgrid = grid units = 'mmi' elif imtype == 'PGV': sgrid = np.exp(grid) units = 'cms' else: sgrid = np.exp(grid) * 100.0 units = 'pctg' if filter_size > 0: fgrid = median_filter(sgrid, size=int(filter_size)) else: fgrid = sgrid interval_type = 'log' if imtype == 'MMI': interval_type = 'linear' grid_min = np.nanmin(fgrid) grid_max = np.nanmax(fgrid) if grid_max - grid_min: intervals = getContourLevels(grid_min, grid_max, itype=interval_type) else: intervals = np.array([]) lonstart = metadata['xmin'] latstart = metadata['ymin'] lonend = metadata['xmax'] if lonend < lonstart: lonstart -= 360 lonspan = np.abs(lonend - lonstart) latspan = np.abs(metadata['ymax'] - latstart) nlon = metadata['nx'] nlat = metadata['ny'] line_strings = [] # dictionary of MultiLineStrings and props for cval in intervals: contours = measure.find_contours(fgrid, cval) # # Convert coords to geographic coordinates; the coordinates # are returned in row, column order (i.e., (y, x)) # new_contours = [] plot_contours = [] for ic, coords in enumerate(contours): # coords is a line segment # # This greatly reduces the number of points in the contours # without changing their shape too much # coords = measure.approximate_polygon(coords, filter_size / 20) mylons = np.around(coords[:, 1] * lonspan / nlon + lonstart, decimals=6) mylats = np.around( (nlat - coords[:, 0]) * latspan / nlat + latstart, decimals=6) contours[ic] = np.hstack((mylons[:].reshape( (-1, 1)), mylats[:].reshape((-1, 1)))) plot_contours.append(contours[ic]) new_contours.append(contours[ic].tolist()) if len(new_contours): mls = MultiLineString(new_contours) props = {'value': cval, 'units': units} if imtype == 'MMI': pass elif imtype == 'PGV': lcval = np.log(cval) else: lcval = np.log(cval / 100) if gmice: mmival = gmice.getMIfromGM(np.array([lcval]), oqimt)[0][0] elif imtype == 'MMI': mmival = cval else: mmival = 1 color_array = np.array(intensity_colormap.getDataColor(mmival)) color_rgb = np.array(color_array[0:3] * 255, dtype=int).tolist() props['color'] = '#%02x%02x%02x' % tuple(color_rgb) if imtype == 'MMI': if (cval * 2) % 2 == 1: props['weight'] = 4 else: props['weight'] = 2 else: props['weight'] = 4 line_strings.append({ 'geometry': mapping(mls), 'properties': props }) return line_strings
def __init__(self, container, topofile, layerdict, cities_file, logger): """Initialize MapMaker object. Args: container (ShakeMapOutputContainer): ShakeMapOutputContainer object containing model results. topofile (str): Path to file containing global topography grid. layerdict (dict): Dictionary containing fields: - coast: Global coastline shapefile. - ocean: Global ocean shapefile. - lake: Global lakes shapefile. - country: Global country boundaries shapefile. - state: Global state (or equivalent) boundaries shapefile. - roads: Global roads directory containing directories with regional shapefiles. cities_file (str): Path to geonames cities1000.txt file. logger (Logger): Python logging instance. Raises: KeyError: When any of layerdict keys are missing. """ req_keys = set(['coast', 'ocean', 'lake', 'country', 'state']) if len(set(layerdict.keys()).intersection(req_keys)) != len(req_keys): raise KeyError( 'layerdict input must have all keys from %s' % str(req_keys)) self.container = container self.topofile = topofile self.layerdict = layerdict cities = BasemapCities.loadFromGeoNames(cities_file) self.cities = cities self.city_cols = CITY_COLS self.city_rows = CITY_ROWS self.cities_per_grid = CITIES_PER_GRID self.intensity_colormap = ColorPalette.fromPreset('mmi') self.contour_colormap = ColorPalette.fromPreset('shaketopo') station_dict = container.getStationDict() self.stations = station_dict rupture_dict = container.getRuptureDict() info_dict = json.loads( container.getString('info.json'))['input']['event_information'] event_dict = { 'eventsourcecode': info_dict['event_id'], 'lat': float(info_dict['latitude']), 'lon': float(info_dict['longitude']), 'depth': float(info_dict['depth']), 'mag': float(info_dict['magnitude']) } origin = Origin(event_dict) if rupture_dict['features'][0]['geometry']['type'] == 'Point': rupture = PointRupture(origin) else: rupture = rupture_from_dict_and_origin(rupture_dict, origin) self.fault = rupture self.fig_width = FIG_WIDTH self.fig_height = FIG_HEIGHT self.logger = logger # clip all the vector data now so that map rendering will be fast t1 = time.time() self._clipBounds() t2 = time.time() self.logger.debug('%.1f seconds to clip vectors.' % (t2 - t1))
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 plot_regression(event_table, imc, imc_table, imt, filename, distance_metric='EpicentralDistance', colormap='viridis'): """Make summary "regression" plot. TODO: * Add GMPE curve and compute mean/sd for all the observations and then also report the standardized residuals. * Better definitions of column names and units. """ fig = plt.figure(figsize=(10, 5)) # ax = plt.subplot(1, 1, 1) ax = fig.add_axes([BOTTOM, AX1_LEFT, AX1_WIDTH, AX1_HEIGHT]) if distance_metric not in imc_table.columns: raise KeyError('Distance metric "%s" not found in table' % distance_metric) imt = imt.upper() # Stupid hack to get units for now. Need a better, more systematic # approach if imt.startswith("SA") | (imt == "PGA"): units = "%g" elif imt.startswith('FAS') or imt in ['ARIAS', 'PGV']: units = "cm/s" else: units = 'Unknown units for IMT %s' % imt if imt not in imc_table.columns: raise KeyError('IMT "%s" not found in table' % imt) # get the event information # group imt data by event id # plot imts by event using colors banded by magnitude eventids = event_table['id'] # set up the color bands minmag = event_table['magnitude'].min() min_mag = min(np.floor(minmag / DELTA_MAG) * DELTA_MAG, MIN_MAG) maxmag = event_table['magnitude'].max() max_mag = max(np.ceil(maxmag / DELTA_MAG) * DELTA_MAG, MAX_MAG) z0 = np.arange(min_mag, max_mag, 0.5) z1 = np.arange(min_mag + DELTA_MAG, max_mag + DELTA_MAG, DELTA_MAG) cmap = plt.get_cmap(colormap) palette = ColorPalette.fromColorMap('mag', z0, z1, cmap) colors = [] for zval in np.arange(min_mag, max_mag + 0.5, 0.5): tcolor = palette.getDataColor(zval, 'hex') colors.append(tcolor) cmap2 = mpl.colors.ListedColormap(colors) for eventid in eventids: emag = event_table[event_table['id'] == eventid].magnitude.to_numpy()[0] norm_mag = (emag - min_mag) / (max_mag - min_mag) color = cmap2(norm_mag) erows = imc_table[imc_table['EarthquakeId'] == eventid] distance = erows[distance_metric] imtdata = erows[imt] ax.loglog(distance, imtdata, mfc=color, mec='k', marker='o', linestyle='None') ax.set_xlabel('%s (km)' % distance_metric) ax.set_ylabel('%s (%s)' % (imt, units)) bounds = np.arange(min_mag, max_mag + 1.0, 0.5) norm = mpl.colors.BoundaryNorm(bounds, cmap2.N) divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="3%", pad=0.05) mpl.colorbar.ColorbarBase( cax, cmap=cmap2, norm=norm, ticks=bounds, # optional spacing='proportional', orientation='vertical') plt.sca(ax) plt.suptitle('%s vs %s (#eqks=%i)' % (distance_metric, imt, len(eventids))) plt.title('for component %s' % (imc)) plt.savefig(filename)
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)
def _test_intensity(): datadir = os.path.abspath(os.path.join(homedir, "..", "data", "eventdata", "northridge")) shakefile = os.path.join(datadir, "northridge_grid.xml") topofile = os.path.join(datadir, "northridge_topo.grd") rupturefile = os.path.join(datadir, "northridge_fault.txt") cityfile = os.path.join(datadir, "northridge_cities.txt") coastfile = os.path.join(datadir, "northridge_coastline.json") countryfile = os.path.join(datadir, "northridge_countries.json") statefile = os.path.join(datadir, "northridge_states.json") lakefile = os.path.join(datadir, "northridge_lakes.json") oceanfile = os.path.join(datadir, "northridge_ocean.json") stationfile = os.path.join(datadir, "northridge_stations.db") roadfile = os.path.join(datadir, "northridge_roads.json") tancptfile = os.path.join(shakedir, "shakemap", "mapping", "tan.cpt") shakecptfile = os.path.join(shakedir, "shakemap", "mapping", "shakecpt.cpt") layerdict = { "coast": coastfile, "ocean": oceanfile, "lake": lakefile, "country": countryfile, "roads": roadfile, "state": statefile, } tancolormap = ColorPalette.fromPreset("shaketopo") shakecolormap = ColorPalette.fromPreset("mmi") cities = BasemapCities.loadFromCSV(cityfile) shakemap = ShakeGrid.load(shakefile, adjust="res") stations = StationList(stationfile) rupture = QuadRupture.readRuptureFile(rupturefile) edict = shakemap.getEventDict() eventdict = { "lat": edict["lat"], "lon": edict["lon"], "depth": edict["depth"], "mag": edict["magnitude"], "time": edict["event_timestamp"], } source = Source(eventdict, rupture) maker = MapMaker(shakemap, topofile, stations, rupture, layerdict, source, cities) # draw intensity map outfolder = os.path.expanduser("~") maker.setIntensityLayer("mmi") maker.setIntensityGMTColorMap(shakecolormap) intensity_map = maker.drawIntensityMap(outfolder) print("Intensity map saved as: %s" % intensity_map) # draw contour maps maker.setContourGMTColorMap(tancolormap) # Draw pgv contours maker.setContourLayer("pgv") contour_pgv_map = maker.drawContourMap(outfolder) print("PGV contour map saved as: %s" % contour_pgv_map) # Draw pga contours maker.setContourLayer("pga") contour_pga_map = maker.drawContourMap(outfolder) print("PGA contour map saved as: %s" % contour_pga_map) # Draw psa0.3 contours maker.setContourLayer("psa03") contour_psa03_map = maker.drawContourMap(outfolder) print("PSA0.3 contour map saved as: %s" % contour_psa03_map) # Draw psa1.0 contours maker.setContourLayer("psa10") contour_psa10_map = maker.drawContourMap(outfolder) print("PSA1.0 contour map saved as: %s" % contour_psa10_map) # Draw psa3.0 contours maker.setContourLayer("psa30") contour_psa30_map = maker.drawContourMap(outfolder) print("PSA3.0 contour map saved as: %s" % contour_psa30_map)
def _test_intensity(): datadir = os.path.abspath(os.path.join( homedir, '..', 'data', 'eventdata', 'northridge')) shakefile = os.path.join(datadir, 'northridge_grid.xml') topofile = os.path.join(datadir, 'northridge_topo.grd') faultfile = os.path.join(datadir, 'northridge_fault.txt') cityfile = os.path.join(datadir, 'northridge_cities.txt') coastfile = os.path.join(datadir, 'northridge_coastline.json') countryfile = os.path.join(datadir, 'northridge_countries.json') statefile = os.path.join(datadir, 'northridge_states.json') lakefile = os.path.join(datadir, 'northridge_lakes.json') oceanfile = os.path.join(datadir, 'northridge_ocean.json') stationfile = os.path.join(datadir, 'northridge_stations.db') roadfile = os.path.join(datadir, 'northridge_roads.json') tancptfile = os.path.join(shakedir, 'shakemap', 'mapping', 'tan.cpt') shakecptfile = os.path.join( shakedir, 'shakemap', 'mapping', 'shakecpt.cpt') layerdict = {'coast': coastfile, 'ocean': oceanfile, 'lake': lakefile, 'country': countryfile, 'roads': roadfile, 'state': statefile} tancolormap = ColorPalette.fromPreset('shaketopo') shakecolormap = ColorPalette.fromPreset('mmi') cities = BasemapCities.loadFromCSV(cityfile) shakemap = ShakeGrid.load(shakefile, adjust='res') stations = StationList(stationfile) fault = Fault.readFaultFile(faultfile) edict = shakemap.getEventDict() eventdict = {'lat': edict['lat'], 'lon': edict['lon'], 'depth': edict['depth'], 'mag': edict['magnitude'], 'time': edict['event_timestamp']} source = Source(eventdict, fault) maker = MapMaker(shakemap, topofile, stations, fault, layerdict, source, cities) # draw intensity map outfolder = os.path.expanduser('~') maker.setIntensityLayer('mmi') maker.setIntensityGMTColorMap(shakecolormap) intensity_map = maker.drawIntensityMap(outfolder) print('Intensity map saved as: %s' % intensity_map) # draw contour maps maker.setContourGMTColorMap(tancolormap) # Draw pgv contours maker.setContourLayer('pgv') contour_pgv_map = maker.drawContourMap(outfolder) print('PGV contour map saved as: %s' % contour_pgv_map) # Draw pga contours maker.setContourLayer('pga') contour_pga_map = maker.drawContourMap(outfolder) print('PGA contour map saved as: %s' % contour_pga_map) # Draw psa0.3 contours maker.setContourLayer('psa03') contour_psa03_map = maker.drawContourMap(outfolder) print('PSA0.3 contour map saved as: %s' % contour_psa03_map) # Draw psa1.0 contours maker.setContourLayer('psa10') contour_psa10_map = maker.drawContourMap(outfolder) print('PSA1.0 contour map saved as: %s' % contour_psa10_map) # Draw psa3.0 contours maker.setContourLayer('psa30') contour_psa30_map = maker.drawContourMap(outfolder) print('PSA3.0 contour map saved as: %s' % contour_psa30_map)
def getHistoricalEvents(self, maxmmi, nmmi, ndeaths, clat, clon): """Select three earthquakes from internal list that are "representative" and similar to input event. First event should be the event "most similar" in exposure, and with the fewest fatalities. Second event should also be "similar" in exposure, and with the most fatalities. Third event should be the deadliest and/or highest population exposure event. :param maxmmi: MMI level of maximum exposure. :param nmmi: Number of people exposure to maxmmi. :param ndeaths: Number of estimated people killed from shaking. :param clat: Origin latitude. :param clon: Origin latitude. :returns: List of three dictionaries (or three None values), containing fields: - EventID 14 character event ID based on time: (YYYYMMDDHHMMSS). - Time Pandas Timestamp object. - Lat Event latitude. - Lon Event longitude. - Depth Event depth. - Magnitude Event magnitude. - CountryCode Two letter country code in which epicenter is located. - ShakingDeaths Number of fatalities due to shaking. - TotalDeaths Number of total fatalities. - Injured Number of injured. - Fire Integer (0 or 1) indicating if any fires occurred as a result of this earthquake. - Liquefaction Integer (0 or 1) indicating if any liquefaction occurred as a result of this earthquake. - Tsunami Integer (0 or 1) indicating if any tsunamis occurred as a result of this earthquake. - Landslide Integer (0 or 1) indicating if any landslides occurred as a result of this earthquake. - MMI1 - Number of people exposed to Mercalli intensity 1. - MMI2 - Number of people exposed to Mercalli intensity 2. - MMI3 - Number of people exposed to Mercalli intensity 3. - MMI4 - Number of people exposed to Mercalli intensity 4. - MMI5 - Number of people exposed to Mercalli intensity 5. - MMI6 - Number of people exposed to Mercalli intensity 6. - MMI7 - Number of people exposed to Mercalli intensity 7. - MMI8 - Number of people exposed to Mercalli intensity 8. - MMI9+ Number of people exposed to Mercalli intensity 9 and above. - MaxMMI Highest intensity level with at least 1000 people exposed. - NumMaxMMI Number of people exposed at MaxMMI. - Distance Distance of this event from input event, in km. - Color The hex color that should be used for row color in historical events table. """ # get the worst event first newdf = self._dataframe.sort_values( ['ShakingDeaths', 'MaxMMI', 'NumMaxMMI'], ascending=False) if not len(newdf): return [None, None, None] worst = newdf.iloc[0] newdf = newdf.drop(newdf.index[[ 0 ]]) # get rid of that first row, so we don't re-include the same event if not len(newdf): less_bad = None else: # get the similar but less bad event less_bad, newdf = self.getSimilarEvent(newdf, maxmmi, nmmi, ndeaths, go_down=True) if not len(newdf): more_bad = None else: # get the similar but worse event more_bad, newdf = self.getSimilarEvent(newdf, maxmmi, nmmi, ndeaths, go_down=False) events = [] colormap = ColorPalette.fromPreset('mmi') if less_bad is not None: lessdict = to_ordered_dict(less_bad) rgbval = colormap.getDataColor(lessdict['MaxMMI']) rgb255 = tuple([int(c * 255) for c in rgbval])[0:3] lessdict['Color'] = '#%02x%02x%02x' % rgb255 events.append(lessdict) if more_bad is not None: moredict = to_ordered_dict(more_bad) rgbval = colormap.getDataColor(moredict['MaxMMI']) rgb255 = tuple([int(c * 255) for c in rgbval])[0:3] moredict['Color'] = '#%02x%02x%02x' % rgb255 events.append(moredict) worstdict = to_ordered_dict(worst) rgbval = colormap.getDataColor(worstdict['MaxMMI']) rgb255 = tuple([int(c * 255) for c in rgbval])[0:3] worstdict['Color'] = '#%02x%02x%02x' % rgb255 events.append(worstdict) return events
def plot_regression( event_table, imc, imc_table, imt, filename, distance_metric="EpicentralDistance", colormap="viridis", ): """Make summary "regression" plot. TODO: * Add GMPE curve and compute mean/sd for all the observations and then also report the standardized residuals. * Better definitions of column names and units. """ fig = plt.figure(figsize=(10, 5)) ax = fig.add_axes([BOTTOM, AX1_LEFT, AX1_WIDTH, AX1_HEIGHT]) if distance_metric not in imc_table.columns: raise KeyError( f'Distance metric "{distance_metric}" not found in table') imt = imt.upper() # Stupid hack to get units for now. Need a better, more systematic # approach if imt.startswith("SA") | (imt == "PGA"): units = "%g" elif imt.startswith("FAS") or imt in ["ARIAS", "PGV"]: units = "cm/s" else: units = f"Unknown units for IMT {imt}" if imt not in imc_table.columns: raise KeyError(f'IMT "{imt}" not found in table') # get the event information # group imt data by event id # plot imts by event using colors banded by magnitude eventids = event_table["id"] # set up the color bands minmag = event_table["magnitude"].min() min_mag = min(np.floor(minmag / DELTA_MAG) * DELTA_MAG, MIN_MAG) maxmag = event_table["magnitude"].max() max_mag = max(np.ceil(maxmag / DELTA_MAG) * DELTA_MAG, MAX_MAG) z0 = np.arange(min_mag, max_mag, 0.5) z1 = np.arange(min_mag + DELTA_MAG, max_mag + DELTA_MAG, DELTA_MAG) cmap = plt.get_cmap(colormap) palette = ColorPalette.fromColorMap("mag", z0, z1, cmap) colors = [] for zval in np.arange(min_mag, max_mag + 0.5, 0.5): tcolor = palette.getDataColor(zval, "hex") colors.append(tcolor) cmap2 = mpl.colors.ListedColormap(colors) for eventid in eventids: emag = event_table[event_table["id"] == eventid].magnitude.to_numpy()[0] norm_mag = (emag - min_mag) / (max_mag - min_mag) color = cmap2(norm_mag) erows = imc_table[imc_table["EarthquakeId"] == eventid] distance = erows[distance_metric] imtdata = erows[imt] ax.loglog(distance, imtdata, mfc=color, mec="k", marker="o", linestyle="None") ax.set_xlabel(f"{distance_metric} (km)") ax.set_ylabel(f"{imt} ({units})") bounds = np.arange(min_mag, max_mag + 1.0, 0.5) norm = mpl.colors.BoundaryNorm(bounds, cmap2.N) divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="3%", pad=0.05) mpl.colorbar.ColorbarBase( cax, cmap=cmap2, norm=norm, ticks=bounds, # optional spacing="proportional", orientation="vertical", ) plt.sca(ax) plt.suptitle("%s vs %s (#eqks=%i)" % (distance_metric, imt, len(eventids))) plt.title(f"for component {imc}") plt.savefig(filename)
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)
def create_twopager(pdata, hazinfo, version_dir): """ :param pdata: PagerData object. :param hazinfo: HazusInfo object. :param version_dir: Path of event version directory. """ # --------------------------------------------------------------------------- # Sort out some paths # --------------------------------------------------------------------------- # Location of this module mod_dir, dummy = os.path.split(__file__) # losspager package direcotry losspager_dir = os.path.join(mod_dir, '..') # Repository root directory root_dir = os.path.join(losspager_dir, '..') # Logos directory logos_dir = os.path.join(losspager_dir, 'logos') # twopager latex template file template_file = os.path.join(logos_dir, 'twopager.tex') # --------------------------------------------------------------------------- # Read in pager data, Hazus data, and latex template # --------------------------------------------------------------------------- json_dir = os.path.join(version_dir, 'json') pdict = pdata._pagerdict edict = pdata.getEventInfo() with open(template_file, 'r') as f: template = f.read() # --------------------------------------------------------------------------- # Fill in template values # --------------------------------------------------------------------------- # Sort out origin time olat = edict['lat'] olon = edict['lon'] otime_utc = edict['time'] date_utc = datetime.strptime(otime_utc, "%Y-%m-%d %H:%M:%S") date_local = pdata.local_time DoW = date_local.strftime('%a') otime_local = date_local.strftime('%H:%M:%S') otime_local = DoW + ' ' + otime_local template = template.replace("[ORIGTIME]", otime_utc) template = template.replace("[LOCALTIME]", otime_local) # Some paths template = template.replace("[VERSIONFOLDER]", version_dir) template = template.replace("[HOMEDIR]", root_dir) # Magnitude location string under USGS logo magloc = 'M %.1f, %s' % (edict['mag'], texify(edict['location'])) template = template.replace("[MAGLOC]", magloc) # Pager version ver = "Version " + str(pdict['pager']['version_number']) template = template.replace("[VERSION]", ver) # Epicenter location lat = edict['lat'] lon = edict['lon'] dep = edict['depth'] if lat > 0: hlat = "N" else: hlat = "S" if lon > 0: hlon = "E" else: hlon = "W" template = template.replace("[LAT]", '%.4f' % abs(lat)) template = template.replace("[LON]", '%.4f' % abs(lon)) template = template.replace("[HEMILAT]", hlat) template = template.replace("[HEMILON]", hlon) template = template.replace("[DEPTH]", '%.1f' % dep) # Tsunami warning? --- need to fix to be a function of tsunamic flag if edict['tsunami']: template = template.replace( "[TSUNAMI]", "FOR TSUNAMI INFORMATION, SEE: tsunami.gov") else: template = template.replace("[TSUNAMI]", "") # Elapsed time if pdata.isScenario(): elapse = '' else: elapse = "Created: " + \ pdict['pager']['elapsed_time'] + " after earthquake" template = template.replace("[ELAPSED]", elapse) # Summary alert color template = template.replace("[SUMMARYCOLOR]", pdata.summary_alert.capitalize()) template = template.replace("[ALERTFILL]", pdata.summary_alert) # Summary comment template = template.replace("[IMPACT1]", texify(pdict['comments']['impact1'])) template = template.replace("[IMPACT2]", texify(pdict['comments']['impact2'])) # Hazus arrow color and relative position hazdel = (hazinfo.hazloss) / LOSS_CONV if hazdel < 0.1: hazdelval = 0.1 elif hazdel > 1000000: hazdelval = 1000000 else: hazdelval = hazdel arrowloc = (((6 - log10(hazdelval)) * 0.83) - 0.07) # distance (in cm) to the left from right end of the econ histogram template = template.replace("[ARROWSHIFT]", '%.2f' % arrowloc) shift = arrowloc + 1.75 # value is ARROWSHIFT plus 1.75 # white box around the arrow and text to "open" the lines between values template = template.replace("[BOXSHIFT]", '%.2f' % shift) # color of the Hazus econ loss value using PAGER color scale template = template.replace("[HAZUS_SUMMARY]", hazinfo.summary_color) # MMI color pal pal = ColorPalette.fromPreset('mmi') # get all of the tag tables (green_tag_table, yellow_tag_table, red_tag_table) = hazinfo.createTaggingTables() # Building Tags by occupancy template = template.replace('[GREEN_TAG_TABLE]', green_tag_table) template = template.replace('[YELLOW_TAG_TABLE]', yellow_tag_table) template = template.replace('[RED_TAG_TABLE]', red_tag_table) # Direct economic losses table econ_losses_table = hazinfo.createEconTable() template = template.replace('[DEL_TABLE]', econ_losses_table) # Non-fatal injuries table injuries_table = hazinfo.createInjuryTable() template = template.replace('[NFI_TABLE]', injuries_table) # Shelter needs table shelter_table = hazinfo.createShelterTable() template = template.replace('[SHELTER_TABLE]', shelter_table) # Earthquake Debris table debris_table = hazinfo.createDebrisTable() template = template.replace('[DEBRIS_TABLE]', debris_table) eventid = edict['eventid'] # query ComCat for information about this event # fill in the url, if we can find it try: ccinfo = ComCatInfo(eventid) eventid, allids = ccinfo.getAssociatedIds() event_url = ccinfo.getURL() + '#pager' except: event_url = DEFAULT_PAGER_URL eventid = "Event ID: " + eventid template = template.replace("[EVENTID]", texify(eventid)) template = template.replace("[EVENTURL]", texify(event_url)) template = template.replace("[HAZUSURL]", texify(DEFAULT_FEMA_URL)) # Write latex file tex_output = os.path.join(version_dir, 'twopager.tex') with open(tex_output, 'w') as f: f.write(template) pdf_output = os.path.join(version_dir, 'twopager.pdf') stderr = '' try: cwd = os.getcwd() os.chdir(version_dir) cmd = '%s -interaction nonstopmode --output-directory %s %s' % ( LATEX_TO_PDF_BIN, version_dir, tex_output) logging.info('Running %s...' % cmd) res, stdout, stderr = get_command_output(cmd) os.chdir(cwd) if not res: if os.path.isfile(pdf_output): msg = 'pdflatex created output file with non-zero exit code.' return (pdf_output, msg) return (None, stderr) else: if os.path.isfile(pdf_output): return (pdf_output, stderr) else: pass except Exception as e: pass finally: os.chdir(cwd) return (None, stderr)
def create_onepager(pdata, version_dir, debug=False): """ :param pdata: PagerData object. :param version_dir: Path of event version directory. :param debug: bool for whether or not to add textpos boxes to onepager. """ #--------------------------------------------------------------------------- # Sort out some paths #--------------------------------------------------------------------------- # Locaiton of this module mod_dir, dummy = os.path.split(__file__) # losspager package direcotry losspager_dir = os.path.join(mod_dir, '..') # Repository root directory root_dir = os.path.join(losspager_dir, '..') # Data directory data_dir = os.path.join(losspager_dir, 'data') # Onepager latex template file template_file = os.path.join(data_dir, 'onepager2.tex') #--------------------------------------------------------------------------- # Read in pager data and latex template #--------------------------------------------------------------------------- json_dir = os.path.join(version_dir, 'json') pdict = pdata._pagerdict edict = pdata.getEventInfo() with open(template_file, 'r') as f: template = f.read() #--------------------------------------------------------------------------- # Fill in template values #--------------------------------------------------------------------------- # Sort out origin time olat = edict['lat'] olon = edict['lon'] otime_utc = edict['time'] date_utc = datetime.strptime(otime_utc, "%Y-%m-%d %H:%M:%S") date_local = pdata.local_time DoW = date_local.strftime('%a') otime_local = date_local.strftime('%H:%M:%S') otime_local = DoW + ' ' + otime_local template = template.replace("[ORIGTIME]", otime_utc) template = template.replace("[LOCALTIME]", otime_local) # Some paths template = template.replace("[VERSIONFOLDER]", version_dir) template = template.replace("[HOMEDIR]", root_dir) # Magnitude location string under USGS logo magloc = 'M %.1f, %s' % (edict['mag'], texify(edict['location'])) template = template.replace("[MAGLOC]", magloc) # Pager version ver = "Version " + str(pdict['pager']['version_number']) template = template.replace("[VERSION]", ver) template = template.replace("[VERSIONX]", "2.5") # Epicenter location lat = edict['lat'] lon = edict['lon'] dep = edict['depth'] if lat > 0: hlat = "N" else: hlat = "S" if lon > 0: hlon = "E" else: hlon = "W" template = template.replace("[LAT]", '%.4f' % abs(lat)) template = template.replace("[LON]", '%.4f' % abs(lon)) template = template.replace("[HEMILAT]", hlat) template = template.replace("[HEMILON]", hlon) template = template.replace("[DEPTH]", '%.1f' % dep) # Tsunami warning? --- need to fix to be a function of tsunamic flag if edict['tsunami']: template = template.replace( "[TSUNAMI]", "FOR TSUNAMI INFORMATION, SEE: tsunami.gov") else: template = template.replace("[TSUNAMI]", "") if pdata.isScenario(): elapse = '' else: elapse = "Created: " + pdict['pager'][ 'elapsed_time'] + " after earthquake" template = template.replace("[ELAPSED]", elapse) template = template.replace("[IMPACT1]", texify(pdict['comments']['impact1'])) template = template.replace("[IMPACT2]", texify(pdict['comments']['impact2'])) template = template.replace("[STRUCTCOMMENT]", texify(pdict['comments']['struct_comment'])) # Summary alert color template = template.replace("[SUMMARYCOLOR]", pdata.summary_alert.capitalize()) template = template.replace("[ALERTFILL]", pdata.summary_alert) # fill in exposure values max_border_mmi = pdata._pagerdict['population_exposure'][ 'maximum_border_mmi'] explist = pdata.getTotalExposure() pophold = 0 for mmi in range(1, 11): iexp = mmi - 1 if mmi == 2: pophold += explist[iexp] continue elif mmi == 3: pop = explist[iexp] + pophold macro = '[MMI2-3]' else: pop = explist[iexp] macro = '[MMI%i]' % mmi if pop < 1000: pop = round_to_nearest(pop, round_value=1000) if max_border_mmi > mmi and mmi <= 4: if pop == 0: popstr = '--*' else: if pop < 1000: pop = round_to_nearest(pop, round_value=1000) popstr = pop_round_short(pop) + '*' else: popstr = pop_round_short(pop) template = template.replace(macro, popstr) # MMI color pal pal = ColorPalette.fromPreset('mmi') # Historical table htab = pdata.getHistoricalTable() if htab[0] is None: # use pdata.getHistoricalComment() htex = pdata.getHistoricalComment() else: # build latex table htex = """ \\begin{tabularx}{7.25cm}{lrc*{1}{>{\\centering\\arraybackslash}X}*{1}{>{\\raggedleft\\arraybackslash}X}} \hline \\textbf{Date} &\\textbf{Dist.}&\\textbf{Mag.}&\\textbf{Max} &\\textbf{Shaking}\\\\ \\textbf{(UTC)}&\\textbf{(km)} & &\\textbf{MMI(\#)}&\\textbf{Deaths} \\\\ \hline [TABLEDATA] \hline \multicolumn{5}{p{7.2cm}}{\\small [COMMENT]} \end{tabularx}""" comment = pdata._pagerdict['comments']['secondary_comment'] htex = htex.replace("[COMMENT]", texify(comment)) tabledata = "" nrows = len(htab) for i in range(nrows): date = htab[i]['Time'].split()[0] dist = str(int(htab[i]['Distance'])) mag = str(htab[i]['Magnitude']) mmi = dec_to_roman(np.round(htab[i]['MaxMMI'], 0)) col = pal.getDataColor(htab[i]['MaxMMI']) texcol = "%s,%s,%s" % (col[0], col[1], col[2]) nmmi = pop_round_short(htab[i]['NumMaxMMI']) mmicell = '%s(%s)' % (mmi, nmmi) shakedeath = htab[i]['ShakingDeaths'] if np.isnan(shakedeath): death = "--" else: death = pop_round_short(shakedeath) row = '%s & %s & %s & \cellcolor[rgb]{%s} %s & %s \\\\ '\ '\n' %(date, dist, mag, texcol, mmicell, death) tabledata = tabledata + row htex = htex.replace("[TABLEDATA]", tabledata) template = template.replace("[HISTORICAL_BLOCK]", htex) # City table ctex = """ \\begin{tabularx}{7.25cm}{lXr} \hline \\textbf{MMI} & \\textbf{City} & \\textbf{Population} \\\\ \hline [TABLEDATA] \hline \end{tabularx}""" ctab = pdata.getCityTable() nrows = len(ctab.index) tabledata = "" for i in range(nrows): mmi = dec_to_roman(np.round(ctab['mmi'].iloc[i], 0)) city = ctab['name'].iloc[i] if ctab['pop'].iloc[i] == 0: pop = '$<$1k' else: if ctab['pop'].iloc[i] < 1000: popnum = round_to_nearest(ctab['pop'].iloc[i], round_value=1000) else: popnum = ctab['pop'].iloc[i] pop = pop_round_short(popnum) col = pal.getDataColor(ctab['mmi'].iloc[i]) texcol = "%s,%s,%s" % (col[0], col[1], col[2]) if ctab['on_map'].iloc[i] == 1: if ctab['pop'].iloc[i] == 0: pop = '\\boldmath$<$\\textbf{1k}' row = '\\rowcolor[rgb]{%s}\\textbf{%s} & \\textbf{%s} & '\ '%s\\\\ \n' %(texcol, mmi, city, pop) else: row = '\\rowcolor[rgb]{%s}\\textbf{%s} & \\textbf{%s} & '\ '\\textbf{%s}\\\\ \n' %(texcol, mmi, city, pop) else: row = '\\rowcolor[rgb]{%s}%s & %s & '\ '%s\\\\ \n' %(texcol, mmi, city, pop) tabledata = tabledata + row ctex = ctex.replace("[TABLEDATA]", tabledata) template = template.replace("[CITYTABLE]", ctex) eventid = edict['eventid'] # query ComCat for information about this event # fill in the url, if we can find it try: ccinfo = ComCatInfo(eventid) eventid, allids = ccinfo.getAssociatedIds() event_url = ccinfo.getURL() + '#pager' except: event_url = DEFAULT_PAGER_URL eventid = "Event ID: " + eventid template = template.replace("[EVENTID]", texify(eventid)) template = template.replace("[EVENTURL]", texify(event_url)) # Write latex file tex_output = os.path.join(version_dir, 'onepager.tex') with open(tex_output, 'w') as f: f.write(template) pdf_output = os.path.join(version_dir, 'onepager.pdf') stderr = '' try: cwd = os.getcwd() os.chdir(version_dir) cmd = '%s -interaction nonstopmode --output-directory %s %s' % ( LATEX_TO_PDF_BIN, version_dir, tex_output) print('Running %s...' % cmd) res, stdout, stderr = get_command_output(cmd) os.chdir(cwd) if not res: return (None, stderr) else: if os.path.isfile(pdf_output): return (pdf_output, stderr) else: pass except Exception as e: pass finally: os.chdir(cwd) return (None, stderr)
def execute(self): """ Write raster.zip file containing ESRI Raster files of all the IMTs in shake_result.hdf. Raises: NotADirectoryError: When the event data directory does not exist. FileNotFoundError: When the the shake_result HDF file does not exist. """ install_path, data_path = get_config_paths() datadir = os.path.join(data_path, self._eventid, 'current', 'products') if not os.path.isdir(datadir): raise NotADirectoryError('%s is not a valid directory.' % datadir) datafile = os.path.join(datadir, 'shake_result.hdf') if not os.path.isfile(datafile): raise FileNotFoundError('%s does not exist.' % datafile) # Open the ShakeMapOutputContainer and extract the data container = ShakeMapOutputContainer.load(datafile) if container.getDataType() != 'grid': raise NotImplementedError('raster module can only operate on ' 'gridded data, not sets of points') # create GIS-readable .flt files of imt and uncertainty self.logger.debug('Creating GIS grids...') layers = container.getIMTs() # Package up all of these files into one zip file. zfilename = os.path.join(datadir, 'raster.zip') zfile = zipfile.ZipFile(zfilename, mode='w', compression=zipfile.ZIP_DEFLATED) files_written = [] for layer in layers: _, layer = layer.split('/') fileimt = oq_to_file(layer) # This is a bit hacky -- we only produce the raster for the # first IMC returned. It should work as long as we only have # one IMC produced per ShakeMap run. imclist = container.getComponents(layer) imtdict = container.getIMTGrids(layer, imclist[0]) mean_grid = Grid2D(imtdict['mean'], GeoDict(imtdict['mean_metadata'])) std_grid = Grid2D(imtdict['std'], GeoDict(imtdict['std_metadata'])) mean_gdal = GDALGrid.copyFromGrid(mean_grid) std_gdal = GDALGrid.copyFromGrid(std_grid) mean_fname = os.path.join(datadir, '%s_mean.flt' % fileimt) mean_hdr = os.path.join(datadir, '%s_mean.hdr' % fileimt) std_fname = os.path.join(datadir, '%s_std.flt' % fileimt) std_hdr = os.path.join(datadir, '%s_std.hdr' % fileimt) self.logger.debug('Saving %s...' % mean_fname) mean_gdal.save(mean_fname) files_written.append(mean_fname) files_written.append(mean_hdr) self.logger.debug('Saving %s...' % std_fname) std_gdal.save(std_fname) files_written.append(std_fname) files_written.append(std_hdr) zfile.write(mean_fname, '%s_mean.flt' % fileimt) zfile.write(mean_hdr, '%s_mean.hdr' % fileimt) zfile.write(std_fname, '%s_std.flt' % fileimt) zfile.write(std_hdr, '%s_std.hdr' % fileimt) zfile.close() # nuke all of the copies of the files we just put in the zipfile for file_written in files_written: os.remove(file_written) # make a transparent PNG of intensity and a world file imclist = container.getComponents('MMI') mmidict = container.getIMTGrids('MMI', imclist[0]) mmi_array = mmidict['mean'] geodict = GeoDict(mmidict['mean_metadata']) palette = ColorPalette.fromPreset('mmi') mmi_rgb = palette.getDataColor(mmi_array, color_format='array') img = Image.fromarray(mmi_rgb) pngfile = os.path.join(datadir, 'intensity_overlay.png') img.save(pngfile, "PNG") # write out a world file # https://en.wikipedia.org/wiki/World_file worldfile = os.path.join(datadir, 'intensity_overlay.pngw') with open(worldfile, 'wt') as f: f.write('%.4f\n' % geodict.dx) f.write('0.0\n') f.write('0.0\n') f.write('-%.4f\n' % geodict.dy) f.write('%.4f\n' % geodict.xmin) f.write('%.4f\n' % geodict.ymax) container.close()