Parking Data
Example extract from SharedStreets CurbLR using DDOT's parking zone Python script
About
Creating a rich and detailed parking zone GIS has long been a goal of both the GIS and Planning teams in DDOT. But there have been a number of problems which present considerable challenges:
- Accounting for change over time. As the signs change on each block, there must be a corresponding change/update to any parking zone information.
- Properly matching signs. In an urban environment, the parking information can be quite dense and confusing. Because of the density of information, matching signs to each other is only half the battle.
- Properly matching time/day. One must also match timebands, which are the day/time combinations that define the time aspect of the restriction or permission.
- Flagging mismatches. You may be shocked to discover that sometimes, the information on signs sometimes doesn't match up correctly! No DOT is perfect, but we must have an ability to catch these problems in an automated, data-driven way so that we can discover and fix the problems prior to causing problems for constituents.
- Flagging conflicts. When two opposing types of parking signs (a parking sign and a no parking sign) try to describe the same place in the same, this is a sign conflict.
In spite of these challenges, DDOT has endeavored to harness our GIS-based sign inventory and create a highly detailed GIS parking zone database which can be utilized by planners and the public at-large. The goals of this project are as follows:
- Harness the original GIS Sign Inventory (collected circa 2016).
- Dynamically generate linearly referenced GIS lines which represent parking zones.
- Retain the rich attribute data of sign timebands
- Analyze and compare/overlay zones to gain insight on existing curbside regulations.
- Dynamically generate easier to understand parking schedule infographics, similar to the work of Nikki Sylianteng.
Sign Inventory
In order to derive parking zones, it's best to go to the source (if you have it): a GIS-based parking sign inventory. Many cities have invested a large amount of time to maintaining a segment/line inventory of the parking restrictions. The time and effort taken to create and maintain this information would likely require a full-time staff member to maintain and keep the data current.
In 2016, DDOT captured over 203,000 street signs within the District of Columbia. Over 30% of the signage was related to parking (permissive or restrictive). That's a lot of parking signs! Here's a bit more about what we have:
Data
Our sign inventory GIS relational data model is structured like so:
- A Sign Support can have one or more
- Signs, which, individually can have multiple
- Timeband(s)
- Signs, which, individually can have multiple
Dataset Name: ParkingZones
Field Name | Type | Description | Alias Name | Domain / Lookup | Example |
ZONEID | String | Unique ID for each parking zone. Structure is “SubBlockKey-Side of the street-Count” | ZONEID | 1245633d0d3822d95dc48807d61c4ee9-left-4 | |
SIGNCODE | String | This is the code for the type of sign that makes up the parking zone. | SignType | R-NS-011 | |
SIGNTEXT | Integer | Any relevant text on the sign is stored here | SIGNTEXT | IF TOWED 727-5000 | |
ROUTEID | String | The RouteID of the SubBlock that this parking zone exists on. | ROUTEID | 11025152 | |
SIDE | String | The side of the SubBlock that this parking zone exists on (if looking towards digitizing direction) | SIDE | left | |
MEAS_FROM | Double | At which measure along the Route the parking zone begins at. | MEAS_FROM | 4988.919922 | |
MEAS_TO | Double | At which measure along the Route the parking zone ends at. | MEAS_TO | 5127.36377 | |
STARTDAY | Long | The start day of the parking restriction timeband (Monday if Mon-Friday) | STARTDAY | SignWorks_Days | 8 (ANYTIME) |
ENDDAY | Long | The end day of the parking restriction timeband (Friday if Mon-Friday) | ENDDAY | SignWorks_Days | 8 (ANYTIME) |
STARTTIME | Long | The start time of the parking restriction timeband (12pm if 12pm-5pm) | STARTTIME | SignWorks_Hours_2 | 100 (ANYTIME) |
ENDTIME | Long | The end time of the parking restriction timeband (5pm if 12pm-5pm) | ENDTIME | SignWorks_Hours_2 | 100 (ANYTIME) |
EXCEPTION | String | If there is an exception to the parking restriction, it will be stored here | EXCEPTION | <Null> | |
SIGNS | String | A comma separated string listing all signs that participate in the creation of this particular parking zone | PARTICIPATING SIGN | 9E09D365,C04E466A | |
OFFSET | Double | Offset value that only exists to space out the zones and make each one visible on the map. | OFFSET | -5 | |
BLOCKFACEKEY | String | The unique key of the blockface on which this parking zone exists. | BLOCKFACEKEY | 1245633d0d3822d95dc48807d61c4ee9 | |
SHAPE.LEN | Double | Length of the parking zone | SHAPE.LEN | 138.444474 |
Sign Supports
This is the primary GIS point feature which represents where the sign post is located, geographically. Important fields to note:
- GlobalID (primary key)
- SupportType
- Side (side of the street this support is on, relative to the centerline geometry)
Map Service: https://dcdot.esriemcs.com/server/rest/services/Signs/SignWorks_ReadOnly/FeatureServer/0
Signs
Signs is a tabular (non-spatial) table related to the Support feature class above. Thus, the location of a sign is derived from the support it is associated to. When possible, each sign type code references the USDOT MUTCD guide. However, there are many parking-type signs which are the creations of DDOT. We call these 'Non-standard' signs and the listing of these signs are available on the attached document below.
Important fields to note:
- SupportID (foreign key to GlobalID in SignSupports table above)
- Sign Type (field: MUTCD)
- Arrow direction (field: SIGNARROWDIRECTION)
- Geographic zone (field: ZONE_ID)
Map Service: https://dcdot.esriemcs.com/server/rest/services/Signs/SignWorks_ReadOnly/FeatureServer/1
TimeBands
Each day/time span shown on a specific sign is called a Timeband. Each of these time bands is stored in the below table. Important fields to note:
- SignID: (foreign key to GlobalID in Signs table above)
- StartDay/EndDay: The domain index for the day
- StartTime/EndTime: The domain index for the time (15 min increments)
- HourLimit: If a parking hourly limit applies.
Map Service: https://dcdot.esriemcs.com/server/rest/services/Signs/SignWorks_ReadOnly/FeatureServer/2
Creating Zones
The main challenge has been: how do we know which signs are logically connected, creating zones of parking/no parking? More importantly, how can we connect these signs, make them into zones and store them into a data model which allows us to query and ask questions of the data?
The sections below will provide detail on how we accomplished this.
Linear Referencing
<Developing>
Basic example of process illustrated in poster below
Python Scripting
CheckSupportSigns, BuildTIntersections, LoadSigns, MergeZones, ZoneGroups
The Parking Zones are built through a sequence of several python scripts. The purpose and methodology of each script is summarized below:
Step 1: Checking the Supports and Signs
In the first stage of building the Parking zones, we scan the Supports feature class (FC) and the Signs FC to make sure that the data was collected properly, and produces an error table to log any red flags. It checks to make sure that these standards are met in each row of data:
- Every sign should have a support with a matching SupportID (GlobalID in the Supports FC)
- Every sign should have s SupportID
- Signs that require arrows have one logged
- Signs that require an hour limit have one logged
- Speed limit signs have a valid, recorded speed limit
- Sign types that require restrictions have the necessary data (day restrictions, time restrictions, etc.) and that they were logged properly
Step 2: Building a T-intersection Table
This step allows us to take T-intersections into account when building our Parking Zones. This allows parking zones to stretch across multiple SubBlocks, as long as it isn't interrupted by an intersecting roadway/alley. We call these uninterrupted adjacent segments Block Faces.
The script finds all 3-way intersections in the city, and logs the RouteIDs and SubBlockIDs of the SubBlocks that participate in that intersection. Each row of data in the table represents one 3-way intersection.
Step 3: Loading all parking type signs into a dedicated table
This step can be the lengthiest one as it can actually serve two purposes. One of those purposes is loading all parking type signs into a dedicated table (SignTableDetail). This is the most commonly run portion of the script, as the second part is optional. The second part refreshes the SignSupport FC, and updates the LRS data associated to them. The RouteID, SubBlockID and Measure values are all updated to reflect the present.
Part 1: This part builds us a table that we can use to actually start building the Parking Zones. It utilizes the T-Intersection table created in the last Step to build Block Faces, and then insert the BlockFaceKey into SignTableDetail. Each row in SignTableDetail represents one parking type sign, and has the necessary Route, Block Face and Measure information to help in the Parking Zone building process.
Part 2: This part just allows us to push updates back up into the SignSupports FC, which is the collected data that we have.
Step 4: Building the Parking Zones
Finally, we get to the most exciting step. This is where the Parking Zones are actually created.
First, SignTableDetail is sorted ascending, prioritizing in this order: BlockFaceKey, Side, MUTCD, TimeID, Measure
This allows us to iterate through adjacent signs of the same type on the same side of a single Block Face. It loops through each sign, creating zones as it goes. The step by step process is laid out below:
- We are on the first type A sign on this side of the Block Face.
- If the arrow points left, the parking zone is created between the sign and the intersection adjacent to it. This is because there are no other signs in between, the zone is closed out.
- If the arrow points right, a parking zone begin point is created.
- We are on the next type A sign on this side of the Block Face.
- If the arrow points left, a parking zone is created between the first type A sign, and this type A sign. The arrows point at each other, and therefore create a zone between them.
- If the arrow is bidirectional, a parking zone mid-point is created (stored in a dictionary). This is to let the script know that this sign is a part of the same zone, but is not the end-point.
This loops through each sign type on each Block Face until all the Parking Zones have been created. Every parking zone created on the same Block Face has an offset value incremented by 1, so that we can offset the zones and be able to view them spread out on a map.
Step 5: Building Segmented Parking Zones
The Segmented Parking Zones are zones between two adjacent supports. The supports at the end also create a Segmented Parking Zone with the adjacent intersection/dead end. The resulting SegmentedParkingZones FC contains a field that lists all of the Parking Zones that exist between any two supports.
Basic example of process illustrated in poster below