计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
urban-morphology【效率与协作】Build projected-CRS urban morphology graphs from building footprints and movement segments, including barrier-aware tessellation, place/movement composition, network-distance filters, fallbacks, and multi-distance outputs.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Use this skill when the task is to turn building footprints and street-like LineString segments into a place/movement urban morphology graph, or when the task needs one of its three typed relation layers independently. The runtime API is GeoDataFrame-first and can return NetworkX only as a compatibility output. It does not require a notebook, Overture download, live service, or source checkout at execution time.
Read the bundled reference that matches the next decision:
references/input-contracts.md — required geometry, ID, CRS, barrier, and
output contracts.references/workflows.md — end-to-end, pairwise, and multi-distance recipes.references/troubleshooting.md — validation failures, empty/degraded
tessellations, distance surprises, and barrier diagnostics.| Need | Entry point | What it returns |
|---|---|---|
| Complete morphology | city2graph.morphology.morphological_graph | (nodes, edges) dictionaries, or a deprecated-compatible NetworkX graph |
| Several network budgets | morphological_graphs | {float_distance: (nodes, edges)} or {float_distance: nx.Graph} |
| Place adjacency | place_to_place_graph | place nodes and touched_to edges |
| Place/street interface | place_to_movement_graph | combined nodes and faced_to edges |
| Street-segment connectivity | movement_to_movement_graph | movement nodes and connected_to edges |
| Explicit segment topology | segments_to_graph | endpoint nodes and segment edges |
| Standalone tessellation | city2graph.utils.create_tessellation | polygon cells with tess_id; enclosed cells also have enclosure_index |
Prefer GeoDataFrame output. For downstream graph conversion, pass the returned
nodes/edges to the package's graph-conversion API rather than relying on the
deprecated as_nx argument.
morphological_graph(buildings_gdf, segments_gdf, ...).morphological_graphs with a
non-empty list/tuple of distances. It computes the movement graph and one
reachability field once and reuses the largest-distance tessellation context.place_id and movement rows have movement_id before calling
place_to_movement_graph or place_to_place_graph.segments_to_graph for endpoint
topology, or movement_to_movement_graph for the dual graph whose nodes are
segments.non_movement_barrier_col; supply primary_barrier_col separately when a
row's alternative geometry should be used as the tessellation/facing
geometry. These switches are orthogonal.as_nx=True remains available but is deprecated; any
duplicate_edges=True, as_nx=True combination is invalid.Polygon/MultiPolygon footprints and movement LineString
segments into GeoDataFrames.segments_to_graph deduplicates exact endpoint coordinate tuples, not fuzzy
near-matches.barrier_geometry column
was made, keep that column geometry-aware or rebuild it. The implementation
reprojects a CRS-tagged alternative barrier column to the active segment CRS;
a CRS-less geometry column adopts the segment CRS.from city2graph.morphology import morphological_graph
nodes, edges = morphological_graph(
buildings_gdf=buildings,
segments_gdf=segments,
center_point=center, # optional Point/one-row GeoSeries/GeoDataFrame
distance=500.0, # optional network budget in projected units
clipping_buffer=300.0, # tessellation context beyond the budget
extent_buffer=100.0, # independent street-access cap
primary_barrier_col="barrier_geometry",
contiguity="queen",
keep_buildings=True,
keep_segments=True,
tolerance=1e-6,
include_unenclosed_buildings=False,
tessellation_fallback=False,
tessellation_n_jobs=1,
)
The pipeline validates and CRS-aligns inputs, assigns movement_id from the
segment index on an owned copy, optionally splits barrier-only rows, builds a
segment topology and (when requested) one shared reachability field, creates
an enclosed tessellation, filters cells/buildings/segments, then composes the
three relation layers. Caller-owned frames are not mutated.
center_point without distance does not activate network-distance filtering.
distance without a center also does not activate it. A distance-filtered run
uses the center snapped to its nearest usable network edge; a center far away
is still snapped to that edge, so it is not a reason to expect an empty result
unless the budget is too small or the network is unusable.
With GeoDataFrame output, nodes has exactly the keys "place" and
"movement". In the complete morphology path:
nodes["place"] is indexed by place_id; geometry is the cell centroid,
and tessellation_geometry preserves the original polygon. With
keep_buildings=True, building_geometry and available building attributes
are attached.nodes["movement"] is indexed by movement_id; geometry is the segment
centroid. With keep_segments=True, segment_geometry preserves the
original LineString and original segment attributes remain available.edges[("place", "touched_to", "place")] has a MultiIndex of
from_place_id, to_place_id; it represents same-enclosure contiguity by
default and includes a weight/edge geometry when non-empty.edges[("movement", "connected_to", "movement")] has
from_movement_id, to_movement_id and is the segment dual/topological
relation.edges[("place", "faced_to", "movement")] has place_id, movement_id
and LineString edges between place and movement centroids.Node geometries are centroids for graph conversion, not the original spatial
objects. Always use tessellation_geometry, building_geometry, or
segment_geometry when computing polygon/line features.
touched_to)place_to_place_graph(place_gdf, group_col=None, contiguity="queen") requires
place_id for non-empty input. queen joins cells sharing a boundary or
vertex; rook requires shared edge contact. If group_col is set, only cells
with the same group value connect. The complete morphology pipeline uses
group_col="enclosure_index" when that column exists, preventing adjacency
across enclosed street barriers. Duplicate place_id rows are deduplicated
for graph construction. Empty/single-cell input returns nodes and empty edges.
faced_to)place_to_movement_graph(place_gdf, movement_gdf, ...) requires place_id and
movement_id for non-empty frames. It first uses a spatial-index dwithin
query from movement geometry to place geometry with tolerance (map units).
If a place cell has no match, it receives one nearest-movement fallback edge
unless the nearest distance exceeds max_connection_distance. In a complete
morphology graph this cap is extent_buffer; direct pairwise calls default to
infinite. Regular tolerance matches are not removed by the fallback cap. A
missing/empty movement layer produces empty interface edges.
If primary_barrier_col exists, it replaces the active movement geometry for
this proximity query only. It does not remove the row from movement nodes.
Null/empty alternative geometries do not become barrier/facing query
geometries, although the segment remains a movement node.
connected_to)movement_to_movement_graph uses movement_id when present. Otherwise it
creates a temporary _edge_id on a copy. It converts segment endpoint
connections to a dual graph: each LineString is a movement node, and connected
segments are linked. segments_to_graph is the lower-level endpoint graph:
its node GDF contains unique endpoint Points indexed by node_id; its edge GDF
preserves input segment rows and uses (from_node_id, to_node_id[, edge_key]).
The default is multigraph=True, directed=True; set directed=False to
canonicalize reverse-drawn endpoint pairs, and keep multigraph support when
parallel segments are valid.
primary_barrier_col selects an alternative geometry for tessellation
barriers and place/movement proximity. It substitutes geometry only;
those rows still become movement nodes and participate in movement-distance
computation using their active LineString geometry.non_movement_barrier_col is a boolean flag. Truthy rows are removed from
movement nodes, dual connectivity, and reachability, but appended to the
tessellation context as barrier-only rows. False/missing values remain normal
movement rows. When a center and distance are supplied, barrier-only rows are
clipped by Euclidean radius distance + clipping_buffer (or distance when
clipping_buffer is infinite) before being added to context. They are not
granted reachability by being barriers.clipping_buffer is context, not walking distance. It must be non-negative and
at least extent_buffer. With a center and distance, movement segments within
the network budget are the final movement layer; a wider buffered network out
to distance + clipping_buffer supplies tessellation context and barriers.
Without a center/distance, no network filtering is applied and clipping does
not prune the movement layer.
extent_buffer is an independent perpendicular access cap. A building/cell is
retained only when the network cost to a projected street foot is within
distance and the straight-line access from that foot is within
extent_buffer; access is never added to the walking-network budget. This
prevents a disconnected or barrier-crossing straight-line leg from making a
place reachable. The same two-cap rule is used for buildings and cells. A
segment straddling the distance boundary is retained whole when its nearer
reachable endpoint is within budget.
create_tessellation(buildings) makes an un-enclosed morphological tessellation.
create_tessellation(buildings, primary_barriers=barriers) makes an enclosed
one. The enclosed default limit is a 100-unit buffered union of buildings and
barriers and is clipped; an explicit limit is forwarded and uses the
explicit-limit semantics described in input-contracts.md.
The tessellation utility handles known momepy/GEOS degeneracies with a retry
ladder (coarser grid_size=1e-3, then deterministic vertex jitter), validates
overlap/coverage, salvages polygon parts of GeometryCollections, and may
return a uniform empty enclosed schema. Unknown exceptions propagate.
morphological_graph(..., tessellation_fallback=True) additionally uses one
building footprint per eligible/reachable building when enclosed tessellation
cannot be made or produces no retained cells. Fallback IDs look like
fallback_<source-index>. With include_unenclosed_buildings=True, only
buildings missed by an otherwise usable enclosed tessellation are appended as
footprint cells; they are filtered using the same reachability/access caps
first. Both fallback paths log warnings. Default False preserves the empty
or error behavior instead of silently changing the place layer.
When center_point and distance are active, place cells with no faced_to
edge are removed along with affected touched_to edges. Without a distance
budget, isolated place cells are not pruned.
from city2graph.morphology import morphological_graphs
layers = morphological_graphs(
buildings, segments, distances=[250.0, 500.0, 1000.0],
center_point=center, clipping_buffer=200.0,
extent_buffer=75.0, tessellation_n_jobs=1,
)
for radius, (radius_nodes, radius_edges) in layers.items():
...
distances must be non-empty; values are normalized to floats and dictionary
keys are those float values. A shared segment graph and single-source
reachability field are prepared once. Enclosed tessellation context is built
once from the largest requested distance and reused, so smaller-radius output
can differ slightly near the clipping boundary from separate
morphological_graph calls. Each distance still redoes the cheap segment,
cell, relation, and retention filters. The return is a dict even for one
requested distance. as_nx=True returns one NetworkX graph per key.
Before treating a morphology result as usable, check:
clipping_buffer >= extent_buffer >= 0;nodes and edges contain the documented keys and index names;faced_to fallback edges do not exceed the configured access cap;faced_to edge;Native repository tests are verification evidence for maintainers, not a runtime dependency of this skill. Do not require notebook execution or live Overture/OSM access to run a synthetic morphology workflow.