Plot a figure-ground diagram of a street network, defaulting to one square
    mile.

    Parameters
    ----------
    G : networkx multidigraph
    address : string
        the address to geocode as the center point if G is not passed in
    point : tuple
        the center point if address and G are not passed in
    dist : numeric
        how many meters to extend north, south, east, and west from the center
        point
    network_type : string
        what type of network to get
    street_widths : dict
        where keys are street types and values are widths to plot in pixels
    default_width : numeric
        the default street width in pixels for any street type not found in
        street_widths dict
    fig_length : numeric
        the height and width of this square diagram
    edge_color : string
        the color of the streets
    bgcolor : string
        the color of the background
    smooth_joints : bool
        if True, plot nodes same width as streets to smooth line joints and
        prevent cracks between them from showing
    filename : string
        filename to save the image as
    file_format : string
        the format of the file to save (e.g., 'jpg', 'png', 'svg')
    show : bool
        if True, show the figure
    save : bool
        if True, save the figure as an image file to disk
    close : bool
        close the figure (only if show equals False) to prevent display
    dpi : int
        the resolution of the image file if saving

    Returns
    -------
    fig, ax : tuple

def plot_figure_ground(G=None, address=None, point=None, dist=805,
                       network_type='drive_service', street_widths=None,
                       default_width=4, fig_length=8, edge_color='w',
                       bgcolor='#333333', smooth_joints=True, filename=None,
                       file_format='png', show=False, save=True, close=True,
                       dpi=300):
    """
    Plot a figure-ground diagram of a street network, defaulting to one square
    mile.

    Parameters
    ----------
    G : networkx multidigraph
    address : string
        the address to geocode as the center point if G is not passed in
    point : tuple
        the center point if address and G are not passed in
    dist : numeric
        how many meters to extend north, south, east, and west from the center
        point
    network_type : string
        what type of network to get
    street_widths : dict
        where keys are street types and values are widths to plot in pixels
    default_width : numeric
        the default street width in pixels for any street type not found in
        street_widths dict
    fig_length : numeric
        the height and width of this square diagram
    edge_color : string
        the color of the streets
    bgcolor : string
        the color of the background
    smooth_joints : bool
        if True, plot nodes same width as streets to smooth line joints and
        prevent cracks between them from showing
    filename : string
        filename to save the image as
    file_format : string
        the format of the file to save (e.g., 'jpg', 'png', 'svg')
    show : bool
        if True, show the figure
    save : bool
        if True, save the figure as an image file to disk
    close : bool
        close the figure (only if show equals False) to prevent display
    dpi : int
        the resolution of the image file if saving

    Returns
    -------
    fig, ax : tuple
    """

    multiplier = 1.2

    # if G was passed-in, use this graph in the plot, centered on the centroid
    # of its nodes
    if G is not None:
        gdf_nodes = graph_to_gdfs(G, edges=False, node_geometry=True)
        lnglat_point = gdf_nodes.unary_union.centroid.coords[0]
        point = tuple(reversed(lnglat_point))

    # otherwise, get the network by either address or point, whichever was
    # passed-in, using a distance multiplier to make sure we get more than
    # enough network. simplify in non-strict mode to not combine multiple street
    # types into single edge
    elif address is not None:
        G, point = graph_from_address(address, distance=dist*multiplier, distance_type='bbox', network_type=network_type,
                                      simplify=False, truncate_by_edge=True, return_coords=True)
        G = simplify_graph(G, strict=False)
    elif point is not None:
        G = graph_from_point(point, distance=dist*multiplier, distance_type='bbox', network_type=network_type,
                             simplify=False, truncate_by_edge=True)
        G = simplify_graph(G, strict=False)
    else:
        raise ValueError('You must pass an address or lat-long point or graph.')

    # if user did not pass in custom street widths, create a dict of default
    # values
    if street_widths is None:
        street_widths = {'footway' : 1.5,
                         'steps' : 1.5,
                         'pedestrian' : 1.5,
                         'service' : 1.5,
                         'path' : 1.5,
                         'track' : 1.5,
                         'motorway' : 6}

    # we need an undirected graph to find every edge incident to a node
    G_undir = G.to_undirected()

    # for each network edge, get a linewidth according to street type (the OSM
    # 'highway' value)
    edge_linewidths = []
    for _, _, data in G_undir.edges(keys=False, data=True):
        street_type = data['highway'][0] if isinstance(data['highway'], list) else data['highway']
        if street_type in street_widths:
            edge_linewidths.append(street_widths[street_type])
        else:
            edge_linewidths.append(default_width)

    if smooth_joints:
        # for each node, get a nodesize according to the narrowest incident edge
        node_widths = {}
        for node in G_undir.nodes():
            # first, identify all the highway types of this node's incident edges
            incident_edges_data = [G_undir.get_edge_data(node, neighbor) for neighbor in G_undir.neighbors(node)]
            edge_types = [data[0]['highway'] for data in incident_edges_data]
            if len(edge_types) < 1:
                # if node has no incident edges, make size zero
                node_widths[node] = 0
            else:
                # flatten the list of edge types
                edge_types_flat = []
                for et in edge_types:
                    if isinstance(et, list):
                        edge_types_flat.extend(et)
                    else:
                        edge_types_flat.append(et)

                # for each edge type in the flattened list, lookup the
                # corresponding width
                edge_widths = [street_widths[edge_type] if edge_type in street_widths else default_width for edge_type in edge_types_flat]

                # the node diameter will be the biggest of the edge widths, to make joints perfectly smooth
                # alternatively, use min (?) to pervent anything larger from extending past smallest street's line
                circle_diameter = max(edge_widths)

                # mpl circle marker sizes are in area, so it is the diameter
                # squared
                circle_area = circle_diameter ** 2
                node_widths[node] = circle_area

        # assign the node size to each node in the graph
        node_sizes = [node_widths[node] for node in G_undir.nodes()]
    else:
        node_sizes = 0

    # define the spatial extents of the plotting figure to make it square, in
    # projected units, and cropped to the desired area
    bbox = bbox_from_point(point, dist, project_utm=False)

    # create a filename if one was not passed
    if filename is None and save:
        filename = 'figure_ground_{}_{}'.format(point, network_type)

    # plot the figure
    fig, ax = plot_graph(G_undir, bbox=bbox, fig_height=fig_length,
                         margin=0, axis_off=True, equal_aspect=False,
                         bgcolor=bgcolor, node_size=node_sizes,
                         node_color=edge_color, edge_linewidth=edge_linewidths,
                         edge_color=edge_color, show=show, save=save,
                         close=close, filename=filename, file_format=file_format,
                         dpi=dpi)

    return fig, ax