[
    {
        "role": "user",
        "content": "what%20are%20parameters%20for%20ox.simplification._is_endpoint%20as%20of%20osmnx%20version%202%3F"
    },
    {
        "role": "assistant",
        "content": "In OSMnx v2 (e.g. version 2.0.6), the function `osmnx.simplification._is_endpoint(G, node, node_attrs_include, edge_attrs_differ)` is used internally to decide whether a graph node is a \"true\" endpoint in the simplified network (i.e. an intersection, dead-end, or other node you want to preserve). Below are the complete parameters and how the logic works, based on the latest official docs (as of April 2026).\n\n---\n\n### Function signature\n```python\nosmnx.simplification._is_endpoint(\n    G: MultiDiGraph,\n    node: int,\n    node_attrs_include: Iterable[str] | None,\n    edge_attrs_differ: Iterable[str] | None\n) \u2192 bool\n```\n- **G**: A `networkx.MultiDiGraph` representing the street network. ([osmnx.readthedocs.io](https://osmnx.readthedocs.io/en/latest/internals.html?utm_source=openai))  \n- **node**: The node ID (an integer) to test whether it should be considered an endpoint. ([osmnx.readthedocs.io](https://osmnx.readthedocs.io/en/latest/internals.html?utm_source=openai))  \n- **node_attrs_include**: Either an iterable of node-attribute names (strings), or `None`. If not `None`, having *any* of those node attributes causes the node to be classified as an endpoint. ([osmnx.readthedocs.io](https://osmnx.readthedocs.io/en/latest/internals.html?utm_source=openai))  \n- **edge_attrs_differ**: Either an iterable of edge-attribute names, or `None`. If not `None` and the incident edges of the node differ in *any* of those attributes, the node becomes an endpoint. ([osmnx.readthedocs.io](https://osmnx.readthedocs.io/en/latest/internals.html?utm_source=openai))  \n\n---\n\n### Logic: when is a node an endpoint?\nA node is considered a true endpoint if **any** of the following conditions (rules) apply. If none apply, the node is *not* an endpoint:\n\n1. **Self-loop**: The node is its own neighbor (i.e. there is a self-loop edge). ([osmnx.readthedocs.io](https://osmnx.readthedocs.io/en/latest/internals.html?utm_source=openai))  \n2. **Dead-end**: The node has all incident edges inbound or all outbound (i.e. no incoming *or* no outgoing edges). ([osmnx.readthedocs.io](https://osmnx.readthedocs.io/en/latest/internals.html?utm_source=openai))  \n3. **Degree and neighbor count**: It does *not* have exactly two neighbors *and* a degree of 2 or 4. If that condition fails, it's an endpoint. ([osmnx.readthedocs.io](https://osmnx.readthedocs.io/en/latest/internals.html?utm_source=openai))  \n4. **Node attributes inclusion**: If `node_attrs_include` is provided and the node has one or more attributes in that set, it is an endpoint. ([osmnx.readthedocs.io](https://osmnx.readthedocs.io/en/latest/internals.html?utm_source=openai))  \n5. **Edge attribute differences**: If `edge_attrs_differ` is provided and the incident edges of the node differ in at least one of those edge attributes, the node is an endpoint. ([osmnx.readthedocs.io](https://osmnx.readthedocs.io/en/latest/internals.html?utm_source=openai))  \n\n---\n\n### Defaults in typical usage\n- In many simplification calls (e.g. `simplify_graph()`), by default `node_attrs_include` and `edge_attrs_differ` are `None`, meaning rules #4 and #5 are *not* applied unless you explicitly pass them. ([osmnx.readthedocs.io](https://osmnx.readthedocs.io/en/stable/internals-reference.html?utm_source=openai))  \n- The typical \u201cstrict\u201d mode corresponds to this default behavior: only rules 1-3 are considered. In non-strict mode, one or both of \u201cedge_attrs_differ\u201d or \u201cnode_attrs_include\u201d may be set so that additional nodes are preserved. ([blog.csdn.net](https://blog.csdn.net/qq_40206371/article/details/120407459?utm_source=openai))  \n\n---\n\nIf you want, I can walk you through a few examples using actual graph data (showing when nodes are endpoints vs. not) so you can see how this behaves in practice. Do you want to see that?"
    }
]