Ejemplo n.º 1
0
def make_shape_files(adict, method='pcontour'):
    fgrid = adict['fgrid']
    dx = adict['dx']
    dy = adict['dy']
    xmin = adict['xmin']
    ymax = adict['ymax']
    contour_levels = adict['contour_levels']
    tdir = adict['tdir']
    fname = adict['fname']
    schema = adict['schema']
    gdict = adict['gdict']
    imt = adict['imt']
    gmice = adict['gmice']

    if method == 'pcontour':
        gjson = pcontour(fgrid,
                         dx,
                         dy,
                         xmin,
                         ymax,
                         contour_levels,
                         4,
                         0,
                         fmt=1)
        features = gjson['features']
    elif method == 'skimage':
        features = contour(gdict, imt, 10, gmice)
    else:
        raise ValueError('Unknown contour method.')
    with fiona.open(os.path.join(tdir, fname + '.shp'), 'w', 'ESRI Shapefile',
                    schema) as c:
        for jobj in features:
            c.write(jobj)
Ejemplo n.º 2
0
def create_contours(container, document):
    """Create a KML file containing MMI contour lines.

    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.
    """
    # TODO - label contours? gx:labelVisibility doesn't seem to be working...

    folder = document.newfolder(name='Contours', visibility=0)
    mmi_line_styles = create_line_styles()
    pgm_line_style = skml.Style(linestyle=skml.LineStyle(width=3))
    ic = skml.IconStyle(scale=0)

    component = list(container.getComponents())[0]
    imts = container.getIMTs(component)
    for imt in imts:
        line_strings = contour(container.getIMTGrids(imt, component), imt,
                               DEFAULT_FILTER_SIZE, None)
        # make a folder for the contours
        imt_folder = folder.newfolder(name='%s Contours' % imt,
                                      visibility=0)

        for line_string in line_strings:
            if imt == 'MMI':
                val = '%.1f' % line_string['properties']['value']
            else:
                val = '%g' % line_string['properties']['value']
            line_list = []
            for segment in line_string['geometry']['coordinates']:
                ctext = []
                for vertex in segment:
                    ctext.append((vertex[0], vertex[1]))
                ls = skml.LineString(coords=ctext)
                line_list.append(ls)
                lc = len(ctext)
                if lc < 10:
                    dopts = []
                elif (ctext[0][0] == ctext[-1][0] and
                      ctext[0][1] == ctext[-1][1]):
                    if lc < 30:
                        dopts = [0, int(lc/2)]
                    elif lc < 60:
                        dopts = [0, int(lc/3), int(2*lc/3)]
                    else:
                        dopts = [0, int(lc/4), int(lc/2), int(3*lc/4)]
                else:
                    dopts = [int(lc/2)]
                for i in dopts:
                    p = imt_folder.newpoint(name=val, coords=[ctext[i]],
                                            visibility=0)
                    p.style.iconstyle = ic
            mg = imt_folder.newmultigeometry(geometries=line_list,
                                             visibility=0,
                                             name="%s %s" % (imt, val))
            if imt == 'MMI':
                mg.style = mmi_line_styles[val]
            else:
                mg.style = pgm_line_style
Ejemplo n.º 3
0
def create_contours(container, document):
    """Create a KML file containing MMI contour lines.

    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.
    """
    # TODO - label contours? gx:labelVisibility doesn't seem to be working...
    imts = container.getIMTs()
    if 'MMI' not in imts:
        return
    component = container.getComponents('MMI')[0]
    line_strings = contour(container, 'MMI', component, DEFAULT_FILTER_SIZE)

    # make a folder for the contours
    folder = etree.SubElement(document, 'Folder')
    name = etree.SubElement(folder, 'name')
    name.text = 'Contours'
    visibility = etree.SubElement(folder, 'visibility')
    visibility.text = '0'

    create_line_styles(folder)
    for line_string in line_strings:
        placemark = etree.SubElement(folder, 'Placemark')
        visibility = etree.SubElement(placemark, 'visibility')
        visibility.text = '0'
        styleurl = etree.SubElement(placemark, 'styleUrl')
        mmi = line_string['properties']['value']
        styleurl.text = '#style_mi_%.1f' % mmi
        name = etree.SubElement(placemark, 'name')
        name.text = 'MMI %.1f' % (mmi)
        geometry = etree.SubElement(placemark, 'MultiGeometry')
        for segment in line_string['geometry']['coordinates']:
            linestring = etree.SubElement(geometry, 'LineString')
            coordinates = etree.SubElement(linestring, 'coordinates')
            ctext = ''
            for vertex in segment:
                lon = vertex[0]
                lat = vertex[1]
                ctext += '%.4f,%.4f,0\n' % (lon, lat)
            coordinates.text = ctext
Ejemplo n.º 4
0
def contour_to_files(container,
                     output_dir,
                     logger,
                     filter_size=DEFAULT_FILTER_SIZE):
    """
    Generate contours of all IMT values.

    Args:
      container (ShakeMapOutputContainer): ShakeMapOutputContainer with
          ShakeMap output data.
      output_dir (str): Path to directory where output files will be written.
      logger (logging.Logger): Python logging Logger instance.

    Raises:
        LookupError: When configured file format is not supported
    """

    # Right now geojson is all we support; if that changes, we'll have
    # to add a configuration or command-line option
    file_format = 'geojson'
    # open a file for writing
    driver, extension = FORMATS[file_format]
    sa_schema = {
        'geometry': 'MultiLineString',
        'properties': {
            'value': 'float',
            'units': 'str',
            'color': 'str',
            'weight': 'int'
        }
    }
    mmi_schema = {
        'geometry': 'MultiLineString',
        'properties': {
            'value': 'float',
            'units': 'str',
            'color': 'str',
            'weight': 'int'
        }
    }
    crs = {
        'no_defs': True,
        'ellps': 'WGS84',
        'datum': 'WGS84',
        'proj': 'longlat'
    }

    config = container.getConfig()
    gmice = get_object_from_config('gmice', 'modeling', config)
    gmice_imts = gmice.DEFINED_FOR_INTENSITY_MEASURE_TYPES
    gmice_pers = gmice.DEFINED_FOR_SA_PERIODS

    imtlist = container.getIMTs()
    for imtype in imtlist:
        component, imtype = imtype.split('/')
        fileimt = oq_to_file(imtype)
        oqimt = imt.from_string(imtype)
        if component == 'GREATER_OF_TWO_HORIZONTAL':
            fname = 'cont_%s.%s' % (fileimt, extension)
        else:
            fname = 'cont_%s_%s.%s' % (fileimt, component, extension)
        filename = os.path.join(output_dir, fname)
        if os.path.isfile(filename):
            fpath, fext = os.path.splitext(filename)
            flist = glob.glob(fpath + '.*')
            for fname in flist:
                os.remove(fname)

        if imtype == 'MMI' or 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

        # fiona spews a warning here when driver is geojson
        # this warning appears to be un-catchable using
        # with warnings.catch_warnings()
        # or
        # logging.captureWarning()
        # or
        # even redirecting stderr/stdout to IO streams
        # not sure where the warning is coming from,
        # but there appears to be no way to stop it...
        with fiona.drivers():
            if imtype == 'MMI':
                selected_schema = mmi_schema
            else:
                selected_schema = sa_schema
            vector_file = fiona.open(filename,
                                     'w',
                                     driver=driver,
                                     schema=selected_schema,
                                     crs=crs)

            line_strings = contour(container, imtype, component, filter_size,
                                   my_gmice)

            for feature in line_strings:
                vector_file.write(feature)

                # Grab some metadata
            meta = container.getMetadata()
            event_info = meta['input']['event_information']
            mdict = {
                'eventid': event_info['event_id'],
                'longitude': float(event_info['longitude']),
                'latitude': float(event_info['latitude'])
            }

            logger.debug('Writing contour file %s' % filename)
            vector_file.close()

            # Get bounds
            tmp = fiona.open(filename)
            bounds = tmp.bounds
            tmp.close()

            # Read back in to add metadata/bounds
            data = json.load(open(filename))
            data['metadata'] = mdict
            data['bbox'] = bounds
            with open(filename, 'w') as outfile:
                json.dump(data, outfile)

            #####################################
            # Make an extra version of the MMI contour file
            # so that the current web rendering code can find it.
            # Delete this file once everyone has moved to new version
            # of ComCat code.

            if imtype == 'MMI':
                old_file = os.path.join(output_dir, 'cont_mi.json')
                shutil.copy(filename, old_file)
Ejemplo n.º 5
0
def draw_map(adict, override_scenario=False):
    """If adict['imtype'] is MMI, draw a map of intensity draped over
    topography, otherwise Draw IMT contour lines over hill-shaded topography.

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

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

    gd = imtgrid.getGeoDict()

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

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

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

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

    # Create the MercatorMap object, which holds a separate but identical
    # axes object used to determine collisions between city labels.
    mmap = MercatorMap(
        bounds, figsize, cities, padding=0.5,
        dimensions=[dim_left, dim_bottom, dim_width, dim_height])
    fig = mmap.figure
    ax = mmap.axes
    # this needs to be done here so that city label collision
    # detection will work
    fig.canvas.draw()

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

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

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

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

    mmimap = ColorPalette.fromPreset('mmi')

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

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

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

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

    oqimt = imt.from_string(imtype)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    _draw_title(imtype, adict)

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

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

    _draw_license(fig, adict)

    return (fig, fig2)