gboeing / osmnx

OSMnx is a Python package to easily download, model, analyze, and visualize street networks and other geospatial features from OpenStreetMap.
https://osmnx.readthedocs.io
MIT License
4.88k stars 827 forks source link

Include parking space data in nodes #1110

Closed EwoutH closed 8 months ago

EwoutH commented 9 months ago

Contributing guidelines

Documentation

Existing issues

What problem does your feature proposal solve?

Currently OSMnx does not directly associate parking spaces with nearby nodes in the street network. Users interested in urban planning, traffic flow analysis, or transportation research often need detailed information about parking availability near specific locations. This includes data on different types of parking (car, motorcycle, bicycle), the nature of these parking spaces (public vs. private), and their capacity.

What is your proposed solution?

I propose an enhancement to OSMnx that automatically associates parking spaces with the nearest nodes. Key features of this enhancement would include:

  1. Type Distinction: Differentiate between car, motorcycle, and bicycle parking.
  2. Public/Private Distinction: Distinguish between public and private parking facilities.
  3. Inferred Capacity: Infer the number of parking places from the area of the parking facility if the exact number is unknown.
  4. Default Node Assignment: Assign all parking spaces to the closest node using a Voronoi-based approach.
  5. Radius Argument: Introduce a radius parameter to only include parking spaces within a certain maximum distance from each node.
  6. Simplification Options: Provide options to merge the number of nearby parking spaces when simplifying networks.
  7. Data Storage: Store parking data at each node, either as an integer (total number of spaces for each type) or as a dictionary including the number of spaces and their distance from the node.

What alternatives have you considered?

Currently, users have to manually process OpenStreetMap data to associate parking information with nodes, which is time-consuming and inefficient. There are no known alternatives within OSMnx that offer this functionality.

Additional context

A typical use case might involve urban planners wanting to analyze the accessibility of parking near commercial areas. They would benefit from being able to quickly assess the availability of different types of parking (e.g., bicycle vs. car) within walking distance from key nodes.

Example Code:

import osmnx as ox

place = "Delft, Netherlands"
G = ox.graph_from_place(place, network_type='drive')

# Proposed function enhancement
G = ox.add_parking_to_nodes(G, max_radius=500, parking_types=['car', 'motorcycle', 'bicycle'], only_public=True, include_distances=False)

In this example, the add_parking_to_nodes function would be an enhancement to OSMnx, automatically fetching and associating parking data with the closest nodes, with a maximum of 500-meter of each node. The function would differentiate between car, motorcycle, and bicycle parking, providing an integrated view of parking availability and capacity around each node in the network. This functionality would significantly help in urban and transportation planning research, allowing for a more comprehensive analysis of parking impacts on traffic flow and urban accessibility.

gboeing commented 9 months ago

@EwoutH thanks and sorry for the delay. I'm still reflecting on this proposal. One thing I'm thinking about is if this could be generalized to a attach_features_to_nodes function that would take arbitrary OSM features and add them to the nearest nodes. If it could be generalized in such a way, this could be a broadly useful enhancement.

EwoutH commented 9 months ago

Thanks! That's an excellent idea, it might also be useful to see what stores, parks, public transport stops, bodies of water or anything else is close to certain nodes.

On thing that might be difficult is how to generalize. For example, in the case of parking spots I might want to know how many there are, so I want to take the sum of capacity. But simultaneously I want to keep bike and car parking separate. Maybe we can make some aggregation function for if there are multiple, for example.

EwoutH commented 9 months ago

Simplest way is just to start with adding the feature data and doing aggregation manually.

So if I want to add parking spaces to a node:

# Proposed function enhancement
G = ox.add_parking_to_nodes(G, max_radius=500, parking_types=['car', 'motorcycle', 'bicycle'], only_public=True, include_distances=False) 

You might get something like this:

# Example representation of parking spaces at a node
print(G.edges[0][1].feature_data)
[
    {
        'feature_type': 'Parking Lot',
        'capacity': 20,
        'fee': True,
        'accessible': True,  # Whether the parking lot is accessible to all
        'security_level': 'High',  # Security level of the parking lot
        'electric_charging': False  # Whether electric vehicle charging is available
    },
    {
        'feature_type': 'Bicycle Rack',
        'capacity': 10,
        'covered': False,  # Whether the bicycle rack is covered
        'secured': True  # Whether the bicycle rack is secured
    },
    {
        'feature_type': 'Motorcycle Parking',
        'capacity': 5,
        'fee': False,
        'covered': True,  # Whether the motorcycle parking is covered
        'security_level': 'Medium'  # Security level of the motorcycle parking
    }
]

And then users can aggerate the data however they like, and maybe in the future we could support some simple functions for it.

gboeing commented 9 months ago

I'll have some time to take a considered look at this soon.

gboeing commented 8 months ago

Ok, I've had some time to reflect on this. In general, the OSMnx project follows 3 principles when adding new functionality: 1) it is useful to a broad set of users, 2) it generalizes well, and 3) it is not trivially easy for users to implement themselves. Your proposal satisfies point 1 but I don't think it satisfies points 2 and 3. Let me explain my thinking...

Let's say you create a street network graph and a GeoDataFrame of features, like this:

import osmnx as ox
import pandas as pd
place = "Piedmont, CA, USA"
G = ox.graph.graph_from_place(place, network_type="drive")
features = ox.features.features_from_place(place, {"amenity": "parking"})

It is trivially easy to attach your features to your graph's nearest nodes as attributes:

nn = ox.distance.nearest_nodes(G, features.centroid.x, features.centroid.y)
useful_tags = ["access", "parking", "surface", "capacity", "fee"]
for node, feature in zip(nn, features[useful_tags].to_dict(orient="records")):
    feature = {k: v for k, v in feature.items() if pd.notna(v)}
    G.nodes[node].update({"parking": feature})

I didn't initially consider how simple it would be to implement a basic version of this when I first read your proposal. Furthermore, I can imagine nearly infinite variation in how users would proceed from here with aggregation or customized attachment for their specific analytical goals. Because it's trivially easy to implement in just a few lines of code and it needs extreme flexibility to proceed from there for various analyses, this seems like the ideal use case to demonstrate in the examples gallery rather than building into the codebase itself.

Long story short: I think it's a great use case for OSMnx but is best served in the examples gallery rather than as a new feature in the codebase. A basic implementation is trivially simple, and a sufficiently flexible implementation to capture all use cases would be overly complex.

gboeing commented 8 months ago

See https://github.com/gboeing/osmnx-examples/issues/83

EwoutH commented 8 months ago

Thanks for getting back! I also didn't realise it would be this simple, in that case I agree an example is more than sufficient.

BWotka commented 2 months ago

I feel like this issue is the closest thing to what i would need and my problem is too small for its own issue:

I am trying to connect some features to a traffic network and i adapted the code you provided, but this obviously only connects to nodes.

In my case i am looking at features/areas which are next to roads and should be connected to the closest edge, i however only get the closest node which in most cases is not what i want.

Here is a screenshot of this, the lower right node is an old node from the network the other nodes are nodes i want to connect into the network. They should connect themselves to the edge right next to them but instead connect to the closest node.

grafik

In a perfect world i would like to insert a node at the edge closest to my feature and connect the feature and the new node.

This can be done using to get the closest edge ox.distance.nearest_edges and geopandas.GeoSeries.shortest_line to get the specific line/ edge to insert.

I am pretty sure there is an elegant way to do this so i wont share my code but if you could provide another example where this is done in a good way, this would help the next person with a similar issue.

I can create a new issue if this is to far removed from the orginal question.

gboeing commented 2 months ago

@BWotka thanks for suggesting this. Since this is a "how-to" usage question, would you please ask at StackOverflow, where we try to redirect all such inquiries away from the issue tracker here. And if you end up with a succinct usage example that would be a helpful addition to the Examples Gallery, please propose it with some sample code at that repo.

EwoutH commented 2 months ago

@gboeing you could enable GitHub Discussions for these kind of questions