Data Park

Project #16 from the 2013 Hack for MN

Hosted at Dev Jam Studios by Open Twin Cities

I spent this last weekend working at the HackforMN, where over 100 developers and tech-related folks got together to find ways to use open data and innovative software to address civic issues.

Our group was #16, looking at ways to help inform the urban development process. I proposed the topic after living through a year of  divisive debate about a 5 story mixed used building at the corner of 43rd and Sheridan.  The debate was full of claims about how the development would or would not change the visual appeal, parking, traffic and business climate in Linden Hills. These claims were very hard to evaluate because there was few tools or data sources.  After considering several aspects of the problem, we decided to focus on parking.  In this page I’m going to try to document the work for our project, called Data Park.  The idea was to create a tool to help developers, residents and city planners understand the parking situation in a neighborhood and predict the effect on parking of a proposed change, such as a new building or removing parking to add a bike line.  The tool could also provide insight into how people’s willingness to park further from their destination can reduce perceived congestion.

Our Team and Tools

Our team consisted of Michael Altmann, Adam Gardner, Nate Bird, Mike Nistler, and Mike Musty.  We had a varied background, some java, some Microsoft, some PHP and Open Street Maps.  One of us even had experience running a small company that did design charettes for urban projects. None of us had a VM for hosting, but I had used CartoDB for another project.  A couple of us started laying out a UI and a API contract between the UI and a service.  Others of us looked around for the geographic data.  Although Minneapolis Public Works or Open Street Map should be a good source of the shape files for roads, we didn’t manage to get either of those.  Instead, we focused on the UI and data.  The key is to be able to build up a dataset with availability of parking at various places in the neighborhood.   For our project we considered a 500 meter radius around Dev Jams, which is at 46 St and Bryant Ave S in Minneapolis.

Step 1.  Create a list of parking spaces.

We decided to approximate the parking with a list of the properties in the area and associate a few parking spaces with each.  We could not find an open data source for the properties, though the Hennepin County Property Finder will let you look up the property details given an address.

As a starting point, we used this Groovy script to generate addresses for the even numbered houses on a 3 block by 5 block region.

def avenues = ["Lyndale Ave S", "Aldrich Ave S", "Bryant Ave S", "Colfax Ave S", "Dupont Ave S"]
def starts = [4400, 4500, 4600]
def increment = 4
def housesPerBlock = 12
def f = new File("Addresses.csv")
for (String avenue : avenues) {
 for (int start : starts) {
 for (int n=0; n< housesPerBlock; n++) {
 def houseNumber = start + increment*n
 f << "\"" + houseNumber + " " + avenue + "\", 0.75\n"
 }
 }
}

This generated a CSV file with addresses and our default value of the average number of spaces that are available at that address, net of the usage by residents but not including people who park for businesses and other special source of parking demand that will be covered below.
We imported this CSV file into a new CartoDB table, called parking_supplyy.  We named the columns “address” and “spaces”, converted “spaces” to numeric and geocoded the address, using “{address}, Minneapolis, MN”  Plotting these places on a map revealed that CartoDB is not very accurate at placing addresses within a block.  It appears that they evenly distribute the 100  house numbers between 4500 and 4600 along the block, even though in Minneapolis they usually only go up to XX50. That means that the houses are all bunched up at the north end of each block. You can manually move each dot in CartoDB, but that seems like too much work. Interestingly, Google Maps does a much better job.

Step 2. Create a list of places that might generate a demand for parking.

We used the Google Places API to get a list of places (mostly businesses) within 500 meters of DevJam.  Here is the Groovy script

import groovyx.net.http.HttpResponseDecorator
import groovyx.net.http.HttpURLClient
import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.JSON
import static groovyx.net.http.ContentType.TEXT

String APPLICATION_KEY
 Properties p = new Properties()
 p.load(CrimeQuery.class.getResourceAsStream("/app.properties"))
 APPLICATION_KEY = p.getProperty("google.api.key")

HttpURLClient http = new HttpURLClient()
def lat = 44.92000
def lng = -93.29000
def distance = 500

def loc = "$lat,$lng"
HttpResponseDecorator response = http.request(
 url : 'https://maps.googleapis.com',
 path: '/maps/api/place/nearbysearch/json' ,
 query : [ key : APPLICATION_KEY,
 radius : distance,
 sensor : 'true',
 location : (loc)],
 method: GET,
 contentType: JSON
 )

List places = []
// response handler for a success response code:
if (response.getStatus() < 400) {
 def json = response.getData()
 json.results.each {
 try {
 def placelat = it.geometry.location.lat
 def placelng = it.geometry.location.lng
 def name = it.name
 def vicinity = it.vicinity
 println "$placelat , $placelng, \"$name\", \"$vicinity\" "
 } catch (Exception e) {
 e.printStackTrace()
 }
 }

} else {
 println ("HTTP Error status " + response.getStatus() )
}

After removing a couple of rows that were for neighborhoods, rather than businesses, we imported the data into CartoDB as a table called parking_demand.  Google had provided the latitude and longitude so we could use that to geocode each row, rather than using the address strings.  We created a column called cars to represent the average number of cars that are parked for that business.  Later we will deal the the variability by day of week and time of day, but for now we will interpret it as the number of cars on a Monday during the day.  With the numbers we made up, the total demand was 42.5 cars.  We uses CartoDB’s map interface to manually add a few points for obvious things that were missing, like Guse Green Grocer and DevJam Studios.  To help visualize the parking demand we used a bubble graph, by styling the graph with


#places{
 marker-fill: #FF5C00;
 marker-line-color: #FFCC00;
 marker-line-width: 2;
 marker-line-opacity: .2;
 marker-opacity: 0.3;
 marker-placement: point;
 marker-type: ellipse;
 marker-allow-overlap: true;
 marker-clip: false;
 marker-multi-policy: largest;
}
#places [ cars <= 8.0] {
 marker-width: 100.0;
}
#places [ cars <= 6.0] {
 marker-width: 90.0;
}
#places [ cars <= 4.0] {
 marker-width: 80.0;
}
#places [ cars <= 4.0] {
 marker-width: 70.0;
}
#places [ cars <= 3.0] {
 marker-width: 60.0;
}
#places [ cars <= 2.0] {
 marker-width: 50.0;
}
#places [ cars <= 1.5] {
 marker-width: 40.0;
}
#places [ cars <= 1.2] {
 marker-width: 30.0;
}
#places [ cars <= 1.2] {
 marker-width: 20.0;
}
#places [ cars <= 0.8] {
 marker-width: 10.0;
}

It should be noted that because the style expresses the marker-width in screen units of measure, rather than absolute map units, it only makes sense at a particular magnification level, which was 16.

Step 3 Modeling parking behavior

Clearly, we know that people like to park near their destination.  Without getting too fancy, we assumed that there is an intrinsic affinity between a source of parking demand and a parking location that drops off as a function of the straight-line distance between them.  For the purposes of the prototype, we assumed that the affinity declines linearly for 100 meters, at which point it reaches zero and is zero for any parking spot further than 100 meters.

Drawing1-1

As  a simplifying model, we look at the additive demand of all businesses on each parking spot.  Is is a simplification because it does not account for the fact that once a parking spot is full, drivers will be forced to park further away.  Modeling this sort of interaction would require some interesting models akin to fluid flow.  For our simple model, we assume that the people parking for business X distribute their cars at parking spots in proportion to the desirability of the spots, as given by the above graph.

We can compute the total demand using at each spot using this view:


select supply.the_geom, supply.the_geom_webmercator,
 supply.address, demand.name as demand_name, demand.the_geom as demand_geom, supply.spaces, demand.cars as demand,
 100 - ST_Distance_sphere(supply.the_geom, demand.the_geom) as affinity
 from parking_supply as supply, parking_demand as demand
 where ST_Distance_sphere(supply.the_geom, demand.the_geom) < 100)
 

We can use spatial queries to implement this algorithm and get the expected vacancy at each parking address, based on the demand for rows marked with option = null.  The new condo is flagged as option = “1”


select s.the_geom, s.the_geom_webmercator, s.address, s.spaces,
 s.spaces - COALESCE((
 select s.spaces * sum( d.cars * (100 - ST_Distance_sphere(s.the_geom, d.the_geom) ) /
 -- total affinity for this row in parking_demand
 (select
 sum( s2.spaces * (100 - ST_Distance_sphere(s2.the_geom, d.the_geom) ) )
 from parking_supply as s2
 where ST_Distance_sphere(s2.the_geom, d.the_geom) < 100)
 )
 from parking_demand as d
 where ST_Distance_sphere(s.the_geom, d.the_geom) < 100
and (option is null)
 ),0) as vacancy
from parking_supply as s

We  saved this to a separate table called parking_vacancy and styled that with

/** bubble visualization */

#parking_vacancy{
 marker-fill: #FF5C00;
 marker-line-color: #FFF;
 marker-line-width: 2;
 marker-line-opacity: 1;
 marker-opacity: 0.9;
 marker-placement: point;
 marker-type: ellipse;
 marker-allow-overlap: true;
 marker-clip: false;
 marker-multi-policy: largest;
}

#parking_vacancy [ vacancy <= 1.5] {
 marker-fill: #00FF00;
}
#parking_vacancy [ vacancy <= 1.3] {
 marker-fill: #22DD00;
}
#parking_vacancy [ vacancy <= 1.1] {
 marker-fill: #44BB00;
}
#parking_vacancy [ vacancy <= 0.9] {
 marker-fill: #669900;
}
#parking_vacancy [ vacancy <= 0.7] {
 marker-fill: #887700;
}
#parking_vacancy [ vacancy <= 0.5] {
 marker-fill: #AA5500;
}
#parking_vacancy [ vacancy <= 0.3] {
 marker-fill: #CC3300;
}
#parking_vacancy [ vacancy <= 0.1] {
 marker-fill: #DD1100;
}
#parking_vacancy [ vacancy <= 0.0] {
 marker-fill: #FF0000;
}

With this styling, we got a nicely colored graph with green for fully vacant parking spots and red indicating spots with reduced vacancy.

Step 4.  A User Interface for Decision Support

With our vacancy modeled, we were ready to create a user interface that could be used to understand  how parking would be affected  by some proposed change. See our GitHub project  for the web page, which is a couple of HTML pages with some javascript to get the data and maps from CartoDB.  You can download the project from Github or visit a running version at http://michaelaltmann.github.io/Datapark

Step 5.  What next?

There are many things to work on here.

  • We need a better source of addresses/parking locations.
  • We need some actual measure of the baseline vacancy for parking spots.  We considered options for a mobile app that residents could use to walk around and count parking spots and cars.
  • We need real data on the parking demand generated by each business.  Perhaps we could get interview business owners and ask the number of customers they get per hour,  how long a customer typically spends in the business, and what fraction of customers drive.
  • We need to validate the model of parking behavior
  • Most importantly, we need to improve the UI and talk with potential users about what would be helpful.
  • We need a section with questions to understand peoples’ perceptions.  Is there too much parking?  Too little?  Is the real issue that people don’t park legally or are noisy?
  • We need a section with information about the city planning issues related to parking.  This should make people aware of  these issues: allocation of city space for parking, supporting local businesses, encouraging different modes of transportation, providing access to people with limited mobility.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s