0

"Momepy is a library for quantitative analysis of urban form - urban morphometrics. It is part of PySAL (Python Spatial Analysis Library) and is built on top of GeoPandas, other PySAL modules and networkX." momepy documentation

Using code (relevant portion below) from Fleischmann, Martin, Alessandra Feliciotti, and William Kerr. “Evolution of Urban Patterns: Urban Morphology as an Open Reproducible Data Science.” Geographical Analysis, July 15, 2021, gean.12302. https://doi.org/10.1111/gean.12302.

I am using OSM data to examine the morphometric character of a four small cities in central Illinois, USA. For a reason I cannot understand, I am encountering problems with the calculation of morphological tessellation cells in only one of the cities. I have included a link to an image of the tessellation cells below (I was not allowed to embed a photo).

Thank you for any assistance!

%%capture --no-stdout

for i, coords in cases.origin.iteritems():
    s = time()
    """
    donwload and process elements of urban form
    """
    # download buildings from OSM
    point = tuple(map(float, coords[1:-1].split(', ')))[::-1]
    buildings = osmnx.geometries.geometries_from_point(point, dist=1000, tags={'building':True})
    buildings = osmnx.projection.project_gdf(buildings)
    buildings = buildings[buildings.geom_type.isin(['Polygon', 'MultiPolygon'])]
    buildings = buildings.reset_index(drop=True).explode().reset_index(drop=True)
    buildings = buildings[["geometry"]]

    # download streets from OSM
    streets_graph = osmnx.graph_from_point(point, 1250, network_type='drive')
    streets_graph = osmnx.get_undirected(streets_graph)
    streets_graph = osmnx.projection.project_graph(streets_graph)
    streets = osmnx.graph_to_gdfs(streets_graph, nodes=False, edges=True,
                                            node_geometry=False, fill_edge_geometry=True)
    streets = streets[["geometry"]]

    # check CRS
    assert buildings.crs.equals(streets.crs)

    # generate tessellation
    buildings['uID'] = range(len(buildings))
    tessellation = mm.Tessellation(buildings, "uID", limit=box(*buildings.total_bounds), verbose=False).tessellation

    """
    measure morphometric characters
    """
    # cell area
    tessellation['cell_area'] = tessellation.area

    # building area
    buildings['blg_area'] = buildings.area

    # coverage area ratio
    tessellation["car"] = mm.AreaRatio(tessellation, buildings, "cell_area", buildings.area, "uID").series

    # segment length
    streets["length"] = streets.length

    # segment linearity
    streets["linearity"] = mm.Linearity(streets, verbose=False).series

    # street profile
    profile = mm.StreetProfile(streets, buildings, distance=3)
    streets["width"] = profile.w
    streets["width_deviation"] = profile.wd
    streets["openness"] = profile.o

    # perimeter wall
    buildings["wall"] = mm.PerimeterWall(buildings, verbose=False).series

    # generate binary contiguity-based W objects of first order and inclusive order 3
    W1 = libpysal.weights.Queen.from_dataframe(tessellation, ids="uID")
    W3 = mm.sw_high(k=3, weights=W1)

    # building adjacency
    buildings["adjacency"] = mm.BuildingAdjacency(buildings, W3, "uID", verbose=False).series

    # interbuilding distance
    buildings['neighbour_distance'] = mm.NeighborDistance(buildings, W1, 'uID', verbose=False).series

    # generate networkx.MultiGraph object
    G = mm.gdf_to_nx(streets)

    # meshedness
    G = mm.meshedness(G, verbose=False)

    # convert networkx.MultiGraph to geopandas.GeoDataFrames
    nodes, edges = mm.nx_to_gdf(G)

    """
    link all characters to tessellation cells
    """
    # merge street network edges based on proximity
    edges["nID"] = range(len(edges))
    buildings["nID"] = mm.get_network_id(buildings, edges, "nID", 500, verbose=False)
    buildings = buildings.merge(edges.drop(columns="geometry"), on="nID", how="left")

    # merge nodes based on edge ID and proximity
    buildings["nodeID"] = mm.get_node_id(buildings, nodes, edges, "nodeID", "nID", verbose=False)
    buildings = buildings.merge(nodes.drop(columns="geometry"), on="nodeID", how="left")

    # merge buildings based on a shared attribute
    tessellation = tessellation.merge(buildings.drop(columns="geometry"), on="uID", how="left")

    """
    save all to GeoPackage
    """
    tessellation.to_file(f"~/work/{cases.loc[i, 'case']}.gpkg", layer="tessellation", driver="GPKG")
    buildings.to_file(f"~/work/{cases.loc[i, 'case']}.gpkg", layer="buildings", driver="GPKG")
    edges.to_file(f"~/work/{cases.loc[i, 'case']}.gpkg", layer="edges", driver="GPKG")
    nodes.to_file(f"~/work/{cases.loc[i, 'case']}.gpkg", layer="nodes", driver="GPKG")

    print(f"{cases.loc[i, 'case']} done in {time() - s} seconds.")

Notice the dense bundles of tessellation cells - they do not correspond correctly to the underlying buildings.

matthiett
  • 1
  • 4
  • This is caused by significantly overlapping or duplicated building footprint polygons. If you can share `x, y` coordinates of the Peoria case study, I can draft a workaround. – martinfleis Feb 23 '22 at 09:28
  • Dr. Fleischmann, I solved my problem in the short term by selecting an alternate x,y for the Peoria. In the long term, however, I will want to deal with such problems directly. Thank you very much for your reply and offer! – matthiett Feb 24 '22 at 17:35

0 Answers0