Skip to content

Commit

Permalink
HCM 2000 unsignalized
Browse files Browse the repository at this point in the history
  • Loading branch information
wenbei committed Mar 3, 2024
1 parent f6fbb94 commit 01b4c31
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 20 deletions.
8 changes: 6 additions & 2 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,15 @@
<td> {row.delay[0]} ({Math.round(parseFloat(row.delay[1]))})</td>
<td>
{#each row.vc as vc, index}
{#if parseFloat(vc) >= criticaVC}
{#if row.type != "hcm-unsignalized"}
{#if parseFloat(vc) >= criticaVC}
{row.movements[index]} ({vc.padEnd(4, "0")})<br />
{/if}
{:else}
{row.movements[index]} ({vc.padEnd(4, "0")})<br />
{/if}
{:else}
Error
--
{/each}
</td>
</tr>
Expand Down
64 changes: 47 additions & 17 deletions src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ type rowGroup = row[];
export interface intersectionData {
name: string;
type: string;
control: string;
delay: string[];
movements: string[];
vc: string[];
[key: string]: any;
}
export type results = {
[key: string]: intersectionData;
Expand Down Expand Up @@ -43,32 +45,43 @@ export function parseResults(results: rowGroup[]) {
let table: results = {};

results.forEach((intersection) => {
let rowData: any = {
let rowData: Partial<intersectionData> = {
delay: ["Error", "0"],
vc: [],
} satisfies Partial<intersectionData>;
};
let name = intersection[1][1];
rowData.name = name;

intersection.forEach((line) => {
let [label, data] = parseRow(line);
if (label) rowData[label as string] = data;
});
rowData.movements = parseLaneConfig(rowData);

// skip synchro unsignalized
if (rowData.type == "synchro" && rowData.signal == "Unsignalized") return;
if (rowData.type == "synchro" && rowData.control == "Unsignalized") return;

// skip queues
if (rowData.type == "synchro-queues") return;

// identify side street delay
// identify unsignalized delay and v/c
if (rowData.type == "hcm-unsignalized") {
let control = rowData.sign;
let delay = rowData.movementDelay;
if (rowData.los) {
// all-way stop
rowData.control = "All-way Stop";
rowData.delay = [rowData.los, rowData.delay];
} else {
// side street stop
rowData.control = "Side-street Stop";
let delays: number[] = rowData.movementDelay.map((d: string) => parseFloat(d));
let i = delays.indexOf(Math.max(...delays));
rowData.delay = [rowData.movementLOS[i], rowData.movementDelay[i]];
rowData.vc = [rowData.movementVC[i]];
rowData.movements = [rowData.movements[i]];
}
}

rowData.movements = parseLaneConfig(rowData.laneGroup, rowData.laneConfig);
table[name] = rowData;
table[name] = rowData as intersectionData;
});

return table;
Expand All @@ -91,29 +104,40 @@ export function parseRow(line: row) {
case "Lane Configurations": // synchro
case "Lanes": // HCM2000 unsignalized
return ["laneConfig", line.slice(2)];
case "Control Type": // HCM2000 signalized
return ["signal", line[1]];
case "Control Type": // synchro
return ["control", line[1]];
case "Direction, Lane #": // HCM2000 unsignalized
return ["lanes", line.slice(2)];
case "Sign Control": // HCM2000 unsignalized
return ["sign", line.slice(2)];

case "Intersection Signal Delay": // synchro
return ["delay", [line[7], line[1]]];
case "HCM 2000 Control Delay": // HCM2000 signalized
return ["delay", [line[10], line[4]]];
case "v/c Ratio": // synchro and HCM2000 signalized
return ["vc", line.slice(2)];

case "Level of Service": // HCM2000 unsignalized, all-way stop
return ["los", line[4]];
case "Delay": // HCM2000 unsignalized, all-way stop
return ["delay", line[4]];
case "Control Delay (s)": // HCM2000 unsignalized, per movement
return ["movementDelay", line.slice(2)];
// TODO: need LOS
// case "Average Delay": // HCM2000 unsignalized, intersection average
// return ["delay", line[4]];
case "v/c Ratio":
return ["vc", line.slice(2)];
case "Lane LOS": // HCM2000 unsignalized, per movement
return ["movementLOS", line.slice(2)];
case "Volume to Capacity": // HCM2000 unsignalized, per movement
return ["movementVC", line.slice(2)];
}
return [];
}

export function parseLaneConfig(group: string[], config: string[]) {
export function parseLaneConfig(rowData: Partial<intersectionData>) {
let movements: string[] = [];
group.forEach((lane, index) => {

let group: string[] = rowData.laneGroup;
let config: string[] = rowData.laneConfig;
group.forEach((lane, index: number) => {
if (config[index] == "0" || config[index] == "") {
movements.push("n/a");
return;
Expand All @@ -130,5 +154,11 @@ export function parseLaneConfig(group: string[], config: string[]) {
movements.push(movement);
});

if (rowData.type == "hcm-unsignalized") {
let lanes: string[] = rowData.lanes;

movements = lanes;
}

return movements;
}
12 changes: 11 additions & 1 deletion tests/HCM2000-Signalized.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,20 @@ describe("parse HCM2000 signalized results", () => {
let groups = groupByIntersection(data);
let results = parseResults(groups);

test("names", () => {
test("name", () => {
expect(Object.keys(results)).toEqual(["Side1 & Main", "Side2 & Main", "Main & Side3"]);
});

test("type", () => {
let type = [];
let control = [];
for (const intersection of Object.values(results)) {
type.push(intersection.type);
control.push(intersection.control);
}
expect(type).toEqual(["hcm-signalized", "hcm-signalized", "hcm-signalized"]);
});

test("lane configuration", () => {
let config = [];
for (const intersection of Object.values(results)) {
Expand Down
88 changes: 88 additions & 0 deletions tests/HCM2000-Unsignalized.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { readFileSync } from "fs";
import { describe, expect, test } from "vitest";

import { groupByIntersection, parseCSV, parseResults } from "src/parse";

const input = readFileSync("tests/sample-output/HCM2000_Unsignalized.txt", "utf-8").replaceAll(":", " ");

test("read file into intersections", () => {
let data = parseCSV(input);
expect(data).toHaveLength(119);

let groups = groupByIntersection(data);
expect(groups).toHaveLength(3);
});

describe("parse HCM2000 unsignalized results", () => {
let data = parseCSV(input);
let groups = groupByIntersection(data);
let results = parseResults(groups);

test("names", () => {
expect(Object.keys(results)).toEqual(["Side4 & Main", "Side5 & Main", "Side6 & Main"]);
});

test("type", () => {
let type = [];
let control = [];
for (const intersection of Object.values(results)) {
type.push(intersection.type);
control.push(intersection.control);
}
expect(type).toEqual(["hcm-unsignalized", "hcm-unsignalized", "hcm-unsignalized"]);
expect(control).toEqual(["Side-street Stop", "All-way Stop", "Side-street Stop"]);
});

test("lane configuration", () => {
let config = [];
for (const intersection of Object.values(results)) {
config.push(intersection.movements);
}
expect(config).toMatchInlineSnapshot(`
[
[
"SB 1",
],
[
"EB 1",
"WB 1",
"NB 1",
"SB 1",
],
[
"NB 1",
],
]
`);
});

test("delay", () => {
let delays = [];
for (const intersection of Object.values(results)) {
delays.push(intersection.delay);
}
expect(delays).toEqual([
["F", "225.3"],
["C", "20.9"],
["B", "11.5"],
]);
});

test("v/c", () => {
let vc = [];
for (const intersection of Object.values(results)) {
vc.push(intersection.vc);
}
expect(vc).toMatchInlineSnapshot(`
[
[
"1.32",
],
[],
[
"0.30",
],
]
`);
});
});
140 changes: 140 additions & 0 deletions tests/sample-output/HCM2000_Unsignalized.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
HCM Unsignalized Intersection Capacity Analysis
11: Side4 & Main 02-27-2024


Movement EBL EBT EBR WBL WBT WBR NBL NBT NBR SBL SBT SBR
Lanes 1 2> 0 0 <2 1 0 1> 0 0 <1 0
Traffic Volume (veh/h) 101 102 103 104 105 106 0 108 109 110 111 0
Future Volume (Veh/h) 101 102 103 104 105 106 0 108 109 110 111 0
Sign Control Free Free Stop Stop
Grade 0% 0% 0% 0%
Peak Hour Factor 0.92 0.92 0.92 0.92 0.92 0.92 0.92 0.92 0.92 0.92 0.92 0.92
Hourly flow rate (vph) 110 111 112 113 114 115 0 117 118 120 121 0
Pedestrians
Lane Width (m)
Walking Speed (m/s)
Percent Blockage
Right turn flare (veh)
Median type None None
Median storage veh)
Upstream signal (m)
pX, platoon unblocked
vC, conflicting volume 229 223 730 842 112 792 783 57
vC1, stage 1 conf vol
vC2, stage 2 conf vol
vCu, unblocked vol 229 223 730 842 112 792 783 57
tC, single (s) 4.1 4.1 7.5 6.5 6.9 7.5 6.5 6.9
tC, 2 stage (s)
tF (s) 2.2 2.2 3.5 4.0 3.3 3.5 4.0 3.3
p0 queue free % 92 92 100 53 87 13 56 100
cM capacity (veh/h) 1336 1343 179 252 920 138 272 997

Direction, Lane # EB 1 EB 2 EB 3 WB 1 WB 2 WB 3 NB 1 SB 1
Volume Total 110 74 149 151 76 115 235 241
Volume Left 110 0 0 113 0 0 0 120
Volume Right 0 0 112 0 0 115 118 0
cSH 1336 1700 1700 1343 1700 1700 396 183
Volume to Capacity 0.08 0.04 0.09 0.08 0.04 0.07 0.59 1.32
Queue Length 95th (m) 2.1 0.0 0.0 2.2 0.0 0.0 29.6 110.4
Control Delay (s) 7.9 0.0 0.0 6.1 0.0 0.0 26.5 225.3
Lane LOS A A D F
Approach Delay (s) 2.6 2.7 26.5 225.3
Approach LOS D F

Intersection Summary
Average Delay 54.1
Intersection Capacity Utilization 49.7% ICU Level of Service A
Analysis Period (min) 15



Scenario 1 5:08 pm 10-27-2022 Baseline Synchro 11 Report
Page 0

HCM Unsignalized Intersection Capacity Analysis
12: Side5 & Main 02-27-2024


Movement EBL EBT EBR WBL WBT WBR NBL NBT NBR SBL SBT SBR
Lanes 0 <1> 0 0 <1> 0 0 1> 0 0 <1> 0
Sign Control Stop Stop Stop Stop
Traffic Volume (vph) 101 102 103 104 105 106 0 108 109 110 111 112
Future Volume (vph) 101 102 103 104 105 106 0 108 109 110 111 112
Peak Hour Factor 0.92 0.92 0.92 0.92 0.92 0.92 0.92 0.92 0.92 0.92 0.92 0.92
Hourly flow rate (vph) 110 111 112 113 114 115 0 117 118 120 121 122

Direction, Lane # EB 1 WB 1 NB 1 SB 1
Volume Total (vph) 333 342 235 363
Volume Left (vph) 110 113 0 120
Volume Right (vph) 112 115 118 122
Hadj (s) -0.10 -0.10 -0.27 -0.10
Departure Headway (s) 6.8 6.8 7.0 6.8
Degree Utilization, x 0.63 0.65 0.46 0.69
Capacity (veh/h) 481 485 442 491
Control Delay (s) 20.9 21.5 15.9 23.4
Approach Delay (s) 20.9 21.5 15.9 23.4
Approach LOS C C C C

Intersection Summary
Delay 20.9
Level of Service C
Intersection Capacity Utilization 64.5% ICU Level of Service C
Analysis Period (min) 15



Scenario 1 5:08 pm 10-27-2022 Baseline Synchro 11 Report
Page 0

HCM Unsignalized Intersection Capacity Analysis
13: Side6 & Main 02-27-2024


Movement EBL EBT EBR WBL WBT WBR NBL NBT NBR SBL SBT SBR
Lanes 1> 0 0 1 1> 0
Traffic Volume (veh/h) 102 103 0 105 107 109
Future Volume (Veh/h) 102 103 0 105 107 109
Sign Control Free Free Stop
Grade 0% 0% 0%
Peak Hour Factor 0.92 0.92 0.92 0.92 0.92 0.92
Hourly flow rate (vph) 111 112 0 114 116 118
Pedestrians
Lane Width (m)
Walking Speed (m/s)
Percent Blockage
Right turn flare (veh)
Median type None None
Median storage veh)
Upstream signal (m)
pX, platoon unblocked
vC, conflicting volume 223 281 167
vC1, stage 1 conf vol
vC2, stage 2 conf vol
vCu, unblocked vol 223 281 167
tC, single (s) 4.1 6.4 6.2
tC, 2 stage (s)
tF (s) 2.2 3.5 3.3
p0 queue free % 100 84 87
cM capacity (veh/h) 1346 709 877

Direction, Lane # EB 1 WB 1 NB 1
Volume Total 223 114 234
Volume Left 0 0 116
Volume Right 112 0 118
cSH 1700 1700 785
Volume to Capacity 0.13 0.07 0.30
Queue Length 95th (m) 0.0 0.0 10.0
Control Delay (s) 0.0 0.0 11.5
Lane LOS B
Approach Delay (s) 0.0 0.0 11.5
Approach LOS B

Intersection Summary
Average Delay 4.7
Intersection Capacity Utilization 30.9% ICU Level of Service A
Analysis Period (min) 15



Scenario 1 5:08 pm 10-27-2022 Baseline Synchro 11 Report
Page 0
Loading

0 comments on commit 01b4c31

Please sign in to comment.