def fromColorMap(cls, name, z0, z1, cmap, resolution=None, nan_color=0, is_log=False): """Construct a ColorPalette from ranges of Z values and a Matplotlib Colormap. Args: name (str): Name of Colormap. z0 (sequence): Sequence of z0 values. z1 (sequence): Sequence of z1 values. cmap (Colormap): Matplotlib Colormap object. resolution (float): Desired Resolution of the data values in data units. For example, the preset population color map has a resolution of 1.0, meaning that we want to be able to distinguish between color values associated with a difference of 1 person. This sets the number of colors to be: `max(256,int((max(z1)-min(z0))/resolution))` nan_color (0 or 4 sequence): Either 0 or RGBA quadruplet (A is for Alpha, where 0 is transparent, and 255 is opaque.) """ # use the whole dynamic range of the colormap if len(z0) != len(z1): raise ConsistentLengthError('Lengths of input sequences to ' 'ColorPalette.fromColorMap() must be identical.') zmin = np.min(z0) zmax = np.max(z1) rgb0 = [] rgb1 = [] for zbottom, ztop in zip(z0, z1): znorm0 = (zbottom - zmin) / (zmax - zmin) rgb_bottom = np.round(np.array(cmap(znorm0)[0:3]) * 255) rgb0.append(rgb_bottom.tolist()) znorm1 = (ztop - zmin) / (zmax - zmin) rgb_top = np.round(np.array(cmap(znorm1)[0:3]) * 255) rgb1.append(rgb_top.tolist()) return cls(name, z0, z1, rgb0, rgb1, resolution=resolution, nan_color=nan_color, is_log=is_log)
def __init__(self, name, z0, z1, rgb0, rgb1, resolution=None, nan_color=0, is_log=False): """Construct a DataColorMap from input Z values and RGB specs. Args: name: Name of colormap. z0: Sequence of z0 values. z1: Sequence of z1 values. rgb0: Sequence of RGB triplets (values between 0-255). rgb1: Sequence of RGB triplets (values between 0-255). resolution: Desired Resolution of the data values in data units. For example, the preset population color map has a resolution of 1.0, meaning that we want to be able to distinguish between color values associated with a difference of 1 person. This sets the number of colors to be: `max(256,int((max(z1)-min(z0))/resolution))` nan_color: Either 0 or RGBA quadruplet (A is for Alpha, where 0 is transparent, and 255 is opaque.) """ # validate that lengths are all identical if len(z0) != len(z1) != len(rgb0) != len(rgb1): raise ConsistentLengthError('Lengths of input sequences to ColorPalette() ' 'must be identical.') self._is_log = is_log z0 = np.array(z0) z1 = np.array(z1) self._vmin = z0.min() self._vmax = z1.max() if isinstance(nan_color, int): nan_color = [nan_color] * 4 self.nan_color = np.array(nan_color) / 255.0 # Change the z values to be between 0 and 1 adj_z0 = (z0 - self._vmin) / (self._vmax - self._vmin) # should this be z0 - vmin? adj_z1 = (z1 - self._vmin) / (self._vmax - self._vmin) # loop over the sequences, and construct a dictionary of red, green, # blue tuples B = -.999 * 255 # this will mark the y0 value in the first row (isn't used) E = .999 * 255 # this will mark the y1 value in the last row (isn't used) # if we add dummy rows to our rgb sequences, we can do one simple loop # through. rgb0_t = rgb0.copy() rgb1_t = rgb1.copy() # append a dummy row to the end of RGB0 rgb0_t.append((E, E, E)) # prepend a dummy row to the beginning of RGB1 rgb1_t.insert(0, (B, B, B)) # Make the column of x values have the same length as the rgb sequences x = np.append(adj_z0, adj_z1[-1]) cdict = {'red': [], 'green': [], 'blue': [] } for i in range(0, len(x)): red0 = rgb1_t[i][0] / 255.0 red1 = rgb0_t[i][0] / 255.0 green0 = rgb1_t[i][1] / 255.0 green1 = rgb0_t[i][1] / 255.0 blue0 = rgb1_t[i][2] / 255.0 blue1 = rgb0_t[i][2] / 255.0 cdict['red'].append((x[i], red0, red1)) cdict['green'].append((x[i], green0, green1)) cdict['blue'].append((x[i], blue0, blue1)) self._cdict = cdict.copy() # choose the number of colors to store the colormap # if we choose too low, then there may not be enough colors to # accurately capture the resolution of our data. # this isn't perfect numcolors = DEFAULT_NCOLORS if resolution is not None: ncolors_tmp = np.ceil((self._vmax - self._vmin) / resolution) numcolors = max(DEFAULT_NCOLORS, ncolors_tmp) self._cmap = LinearSegmentedColormap(name, cdict, N=numcolors) self._cmap.set_bad(self.nan_color)
def get_distance(methods, lat, lon, dep, rupture, dx=0.5): """ Calculate distance using any one of a number of distance measures. One of quadlist OR hypo must be specified. The following table gives the allowed distance strings and a description of each. +--------+----------------------------------------------------------+ | String | Description | +========+==========================================================+ | repi | Distance to epicenter. | +--------+----------------------------------------------------------+ | rhypo | Distance to hypocenter. | +--------+----------------------------------------------------------+ | rjb | Joyner-Boore distance; this is closest distance to the | | | surface projection of the rupture plane. | +--------+----------------------------------------------------------+ | rrup | Rupture distance; closest distance to the rupture plane. | +--------+----------------------------------------------------------+ | rx | Strike-normal distance; same as GC2 coordiante T. | +--------+----------------------------------------------------------+ | ry | Strike-parallel distance; same as GC2 coordiante U, but | | | with a shift in origin definition. See Spudich and Chiou | | | (2015) http://dx.doi.org/10.3133/ofr20151028. | +--------+----------------------------------------------------------+ | ry0 | Horizontal distance off the end of the rupture measured | | | parallel to strike. Can only be zero or positive. We | | | compute this as a function of GC2 coordinate U. | +--------+----------------------------------------------------------+ | U | GC2 coordinate U. | +--------+----------------------------------------------------------+ | T | GC2 coordinate T. | +--------+----------------------------------------------------------+ | rvolc | Part of the rupture distance spent in volcanic region. | | | Currently set to 0 for all points. | +--------+----------------------------------------------------------+ Args: methods (list): List of strings (or just a string) of distances to compute. lat (array): A numpy array of latitudes. lon (array): A numpy array of longidues. dep (array): A numpy array of depths (km). rupture (Rupture): A ShakeMap Rupture instance. dx (float): Mesh spacing for rupture; only used if rupture is an EdgeRupture subclass. Returns: dict: dictionary of numpy arrays of distances, size of lon.shape. """ rupture._mesh_dx = dx # Dictionary for holding/returning the requested distances distdict = dict() # Coerce methods into list if it isn't if not isinstance(methods, list): methods = [methods] # Check that all requested distances are available methods_available = set(get_distance_measures()) if not set(methods).issubset(methods_available): raise NotImplementedError( 'One or more requested distance method is not ' 'valid or is not implemented yet') # Check dimensions of site coordinates if (lat.shape != lon.shape) or (lat.shape != dep.shape): raise ConsistentLengthError( 'lat, lon, and dep must have the same shape.') # ------------------------------------------------------------------------- # Point distances # ------------------------------------------------------------------------- if 'rhypo' in methods: distdict['rhypo'] = rupture.computeRhyp(lon, lat, dep) if 'repi' in methods: distdict['repi'] = rupture.computeRepi(lon, lat, dep) # ------------------------------------------------------------------------- # Rupture distances # ------------------------------------------------------------------------- gc2_distances = set(['rx', 'ry', 'ry0', 'U', 'T']) if 'rrup' in methods: distdict['rrup'], distdict['rrup_var'] = rupture.computeRrup( lon, lat, dep) if 'rjb' in methods: distdict['rjb'], distdict['rjb_var'] = rupture.computeRjb( lon, lat, dep) # If any of the GC2-related distances are requested, may as well do all if len(set(methods).intersection(gc2_distances)) > 0: distdict.update(rupture.computeGC2(lon, lat, dep)) if 'rvolc' in methods: distdict['rvolc'] = np.zeros_like(lon) return distdict
def fromVertices(cls, xp0, yp0, zp0, xp1, yp1, zp1, xp2, yp2, zp2, xp3, yp3, zp3, origin, group_index=None, reference=None): """ Create a QuadDrupture instance from the vector of vertices that fully define the quadrilaterals. The points p0, ..., p3 are labeled below for a trapezoid: :: p0--------p1 / | / | p3-----------p2 All of the following vector arguments must have the same length. Args: xp0 (array): Array or list of longitudes (floats) of p0. yp0 (array): Array or list of latitudes (floats) of p0. zp0 (array): Array or list of depths (floats) of p0. xp1 (array): Array or list of longitudes (floats) of p1. yp1 (array): Array or list of latitudes (floats) of p1. zp1 (array): Array or list of depths (floats) of p1. xp2 (array): Array or list of longitudes (floats) of p2. yp2 (array): Array or list of latitudes (floats) of p2. zp2 (array): Array or list of depths (floats) of p2. xp3 (array): Array or list of longitudes (floats) of p3. yp3 (array): Array or list of latitudes (floats) of p3. zp3 (array): Array or list of depths (floats) of p3. origin (Origin): Reference to a ShakeMap Origin object. group_index (list): List of integers to indicate group index. If None then each quadrilateral is assumed to be in a different group since there is no guarantee that any of them are continuous. reference (str): String explaining where the rupture definition came from (publication style reference, etc.) Returns: QuadRupture object, where the rupture is modeled as a series of trapezoids. Raises: Exception: if the lengths of the inptu arrays are not all equal, or if the length of the group_index is not the same as the arrays (if group_index is supplied).. """ if len(xp0) == len(yp0) == len(zp0) == len(xp1) == len(yp1) == \ len(zp1) == len(xp2) == len(yp2) == len(zp2) == len(xp3) == \ len(yp3) == len(zp3): pass else: raise ConsistentLengthError('All vectors specifying quadrilateral ' 'vertices must have the same length.') nq = len(xp0) if group_index is not None: if len(group_index) != nq: raise ConsistentLengthError( "group_index must have same length as vertices.") else: group_index = np.array(range(nq)) xp0 = np.array(xp0, dtype='d') yp0 = np.array(yp0, dtype='d') zp0 = np.array(zp0, dtype='d') xp1 = np.array(xp1, dtype='d') yp1 = np.array(yp1, dtype='d') zp1 = np.array(zp1, dtype='d') xp2 = np.array(xp2, dtype='d') yp2 = np.array(yp2, dtype='d') zp2 = np.array(zp2, dtype='d') xp3 = np.array(xp3, dtype='d') yp3 = np.array(yp3, dtype='d') zp3 = np.array(zp3, dtype='d') # --------------------------------------------------------------------- # Create GeoJSON object # --------------------------------------------------------------------- coords = [] u_groups = np.unique(group_index) n_groups = len(u_groups) for i in range(n_groups): ind = np.where(u_groups[i] == group_index)[0] lons = np.concatenate([ xp0[ind[0]].reshape((1, )), xp1[ind], xp2[ind][::-1], xp3[ind][::-1][-1].reshape((1, )), xp0[ind[0]].reshape((1, )) ]) lats = np.concatenate([ yp0[ind[0]].reshape((1, )), yp1[ind], yp2[ind][::-1], yp3[ind][::-1][-1].reshape((1, )), yp0[ind[0]].reshape((1, )) ]) deps = np.concatenate([ zp0[ind[0]].reshape((1, )), zp1[ind], zp2[ind][::-1], zp3[ind][::-1][-1].reshape((1, )), zp0[ind[0]].reshape((1, )) ]) poly = [] for lon, lat, dep in zip(lons, lats, deps): poly.append([lon, lat, dep]) coords.append(poly) d = { "type": "FeatureCollection", "metadata": { "reference": reference }, "features": [{ "type": "Feature", "properties": { "rupture type": "rupture extent" }, "geometry": { "type": "MultiPolygon", "coordinates": [coords] } }] } # Add origin information to metadata odict = origin.__dict__ for k, v in odict.items(): if isinstance(v, HistoricTime): d['metadata'][k] = v.strftime(constants.TIMEFMT) else: d['metadata'][k] = v if hasattr(origin, 'id'): d['metadata']['eventid'] = origin.id return cls(d, origin)
def fromOrientation(cls, px, py, pz, dx, dy, length, width, strike, dip, origin): """ Create a QuadRupture instance from a known point, shape, and orientation. A point is defined as a set of latitude, longitude, and depth, which is located in the corner between the tail of the vector pointing in the strike direction and the dip direction (nearest to the surface). The shape is defined by length, width, dx, and dy. The length is the measurement of the quadrilateral in the direction of strike, and width is the measurement of quadrilateral in the direction of dip. Dx is the measurement on the plane in the strike direction between the known point and the corner between the tail of the vector pointing in the strike direction and the dip direction (nearest to the surface). Dy is the measurement on the plane in the dip direction between the known point and the corner between the tail of the vector pointing in the strike direction and the dip direction (nearest to the surface). The orientation is defined by azimuth and angle from horizontal, strike and dip respectively. For example in plane view: :: strike direction p1*------------------->>p2 * | dy | dip |--------o | direction | dx known point | Width V | V | p4----------------------p3 Length Args: px (array): Array or list of longitudes (floats) of the known point. py (array): Array or list of latitudes (floats) of the known point. pz (array): Array or list of depths (floats) of the known point. dx (array): Array or list of distances (floats), in the strike direction, between the known point and P1. dx must be less than length. dy (array): Array or list of distances (floats), in the dip direction, between the known point and P1. dy must be less than width. length (array): Array or list of widths (floats) of the plane in the strike direction. width (array): Array or list of widths (floats) of the plane in the dip direction. strike (array): Array or list of strike angles (floats). dip (array): Array or list of dip angles (floats). origin (Origin): Reference to a ShakeMap Origin object. Returns: QuadRupture instance. Raises: Exception: if the lengths of the points arrays are not all equal. """ # Verify that arrays are of equal length if len(px) == len(py) == len(pz) == len(dx) == len(dy) == len( length) == len(width) == len(strike) == len(dip): pass else: raise ConsistentLengthError( 'Number of px, py, pz, dx, dy, length, width, ' 'strike, dip points must be ' 'equal.') # Verify that all are numpy arrays px = np.array(px, dtype='d') py = np.array(py, dtype='d') pz = np.array(pz, dtype='d') dx = np.array(dx, dtype='d') dy = np.array(dy, dtype='d') length = np.array(length, dtype='d') width = np.array(width, dtype='d') strike = np.array(strike, dtype='d') dip = np.array(dip, dtype='d') # Get P1 and P2 (top horizontal points) theta = np.rad2deg(np.arctan2(dy * np.cos(np.deg2rad(dip)), dx)) P1_direction = strike + 180 + theta P1_distance = np.sqrt(dx**2 + (dy * np.cos(np.deg2rad(dip)))**2) P2_direction = strike P2_distance = length P1_lon = [] P1_lat = [] P2_lon = [] P2_lat = [] for idx, value in enumerate(px): P1_points = point_at(px[idx], py[idx], P1_direction[idx], P1_distance[idx]) P1_lon += [P1_points[0]] P1_lat += [P1_points[1]] P2_points = point_at(P1_points[0], P1_points[1], P2_direction[idx], P2_distance[idx]) P2_lon += [P2_points[0]] P2_lat += [P2_points[1]] # Get top depth top_horizontal_depth = pz - np.abs(dy * np.sin(np.deg2rad(dip))) # Get QuadRupture object quad = QuadRupture.fromTrace(P1_lon, P1_lat, P2_lon, P2_lat, top_horizontal_depth, width, dip, origin, strike=strike) return quad
def fromTrace(cls, xp0, yp0, xp1, yp1, zp, widths, dips, origin, strike=None, group_index=None, reference=""): """ Create a QuadRupture instance from a set of vertices that define the top of the rupture, and an array of widths/dips. Each rupture quadrilaterial is defined by specifying the latitude, longitude, and depth of the two vertices on the top edges, which must have the dame depths. The other verticies are then constructed from the top edges and the width and dip of the quadrilateral. Args: xp0 (array): Array or list of longitudes (floats) of p0. yp0 (array): Array or list of latitudes (floats) of p0. xp1 (array): Array or list of longitudes (floats) of p1. yp1 (array): Array or list of latitudes (floats) of p1. zp (array): Array or list of depths for each of the top of rupture rectangles (km). widths (array): Array of widths for each of rectangle (km). dips (array): Array of dips for each of rectangle (degrees). origin (Origin): Reference to a ShakeMap origin object. strike (array): If None then strike is computed from verticies of top edge of each quadrilateral. If the array has only a single value, then all quadrilaterals are constructed assuming this strike direction. If an array with the same length as the trace coordinates then it specifies the strike for each quadrilateral. group_index (list): List of integers to indicate group index. If None then each quadrilateral is assumed to be in a different group since there is no guarantee that any of them are continuous. reference (str): String explaining where the rupture definition came from (publication style reference, etc.). Returns: QuadRupture instance. Raises: Exception: if the input arrays are not all the same length, or if the strike array is not length 1 or the same length as the input arrays. """ if not (len(xp0) == len(yp0) == len(xp1) == len(yp1) == len(zp) == len(dips) == len(widths)): raise ConsistentLengthError( 'Number of xp0,yp0,xp1,yp1,zp,widths,dips points must be ' 'equal.') if strike is not None and len(xp0) != len(strike) and len(strike) != 1: raise TypeError( 'Strike must be None or an array of one value or the ' 'same length as trace coordinates.') if group_index is None: group_index = np.array(range(len(xp0))) # Convert dips to radians dips = np.radians(dips) # Ensure that all input sequences are numpy arrays xp0 = np.array(xp0, dtype='d') xp1 = np.array(xp1, dtype='d') yp0 = np.array(yp0, dtype='d') yp1 = np.array(yp1, dtype='d') zp = np.array(zp, dtype='d') widths = np.array(widths, dtype='d') dips = np.array(dips, dtype='d') # Get a projection object west = np.min((xp0.min(), xp1.min())) east = np.max((xp0.max(), xp1.max())) south = np.min((yp0.min(), yp1.min())) north = np.max((yp0.max(), yp1.max())) # Projected coordinates are in km proj = OrthographicProjection(west, east, north, south) xp2 = np.zeros_like(xp0) xp3 = np.zeros_like(xp0) yp2 = np.zeros_like(xp0) yp3 = np.zeros_like(xp0) zpdown = np.zeros_like(zp) for i in range(0, len(xp0)): # Project the top edge coordinates p0x, p0y = proj(xp0[i], yp0[i]) p1x, p1y = proj(xp1[i], yp1[i]) # Get the rotation angle defined by these two points if strike is None: dx = p1x - p0x dy = p1y - p0y theta = np.arctan2(dx, dy) # theta is angle from north elif len(strike) == 1: theta = np.radians(strike[0]) else: theta = np.radians(strike[i]) R = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) # Rotate the top edge points into a new coordinate system (vertical # line) p0 = np.array([p0x, p0y]) p1 = np.array([p1x, p1y]) p0p = np.dot(R, p0) p1p = np.dot(R, p1) # Get right side coordinates in project, rotated system dz = np.sin(dips[i]) * widths[i] dx = np.cos(dips[i]) * widths[i] p3xp = p0p[0] + dx p3yp = p0p[1] p2xp = p1p[0] + dx p2yp = p1p[1] # Get right side coordinates in un-rotated projected system p3p = np.array([p3xp, p3yp]) p2p = np.array([p2xp, p2yp]) Rback = np.array([[np.cos(-theta), -np.sin(-theta)], [np.sin(-theta), np.cos(-theta)]]) p3 = np.dot(Rback, p3p) p2 = np.dot(Rback, p2p) p3x = np.array([p3[0]]) p3y = np.array([p3[1]]) p2x = np.array([p2[0]]) p2y = np.array([p2[1]]) # project lower edge points back to lat/lon coordinates lon3, lat3 = proj(p3x, p3y, reverse=True) lon2, lat2 = proj(p2x, p2y, reverse=True) xp2[i] = lon2 xp3[i] = lon3 yp2[i] = lat2 yp3[i] = lat3 zpdown[i] = zp[i] + dz # --------------------------------------------------------------------- # Create GeoJSON object # --------------------------------------------------------------------- coords = [] u_groups = np.unique(group_index) n_groups = len(u_groups) for i in range(n_groups): ind = np.where(u_groups[i] == group_index)[0] lons = np.concatenate([ xp0[ind[0]].reshape((1, )), xp1[ind], xp2[ind][::-1], xp3[ind][::-1][-1].reshape((1, )), xp0[ind[0]].reshape((1, )) ]) lats = np.concatenate([ yp0[ind[0]].reshape((1, )), yp1[ind], yp2[ind][::-1], yp3[ind][::-1][-1].reshape((1, )), yp0[ind[0]].reshape((1, )) ]) deps = np.concatenate([ zp[ind[0]].reshape((1, )), zp[ind], zpdown[ind][::-1], zpdown[ind][::-1][-1].reshape((1, )), zp[ind[0]].reshape((1, )) ]) poly = [] for lon, lat, dep in zip(lons, lats, deps): poly.append([lon, lat, dep]) coords.append(poly) d = { "type": "FeatureCollection", "metadata": { "reference": reference }, "features": [{ "type": "Feature", "properties": { "rupture type": "rupture extent" }, "geometry": { "type": "MultiPolygon", "coordinates": [coords] } }] } # Add origin information to metadata odict = origin.__dict__ for k, v in odict.items(): if isinstance(v, HistoricTime): d['metadata'][k] = v.strftime(constants.TIMEFMT) else: d['metadata'][k] = v return cls(d, origin)