diff --git a/AGENTS.md b/AGENTS.md index 6869f90..c2987ce 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -66,7 +66,7 @@ Each **destination** has: **`data/config.json`** may include **`parkingPrivateUnknown`** (`lotAssumedDollars`, `garageAssumedDollars`, **`cardCopy`**) for assumed private-lot/garage dollars when OSM items lack `pricing`—used where the app shows planning estimates for private pins. -Parking JSON lives under **`data/parking/public/`** (Grand Rapids Visitor Parking ArcGIS map via `scripts/fetch_car_parking_arcgis.py` for **`garages-arcgis.json`** and **`lots-arcgis.json`**; meters; OSM bike racks via `scripts/fetch_bike_parking.py`) and **`data/parking/private/`** (**`garages-osm.json`** and **`lots-osm.json`** from OpenStreetMap via `scripts/fetch_car_parking_osm.py`; **`garages-ellis.json`** / **`lots-ellis.json`** from `scripts/fetch_car_parking_ellis.py` — loaded as **`ellisGarages`** / **`ellisLots`**. After overrides, pins with **`manager`: `"AirGarage"`** are split from OSM into **`airGarageGarages`** / **`airGarageLots`** in **`appData`** (still used for `#/visit` and the modes page); **`#/data/parking`** merges OSM + AirGarage + Ellis into one **Private Parking Garages** and one **Private Parking Lots** dataset in the dropdown (`dataset=osmGarages` / `osmLots`). **`#/visit`** merges OSM + AirGarage for private pins under **`private-garage` / `private-lot`** toggles. Lime micromobility via `scripts/fetch_lime_parking.py`. OSM drive **garages** (resp. **lots**) within **~0.06 mi** (Haversine) of a public ArcGIS **garage** (resp. **lot**) centroid are omitted in **`loadData()`** and when regenerating private JSON, so official names/pricing win over near-duplicate OSM pins of the same facility type (a surface lot beside a ramp is kept). Lime snapshot buckets stay in `data/parking/.lime/`. `loadData()` merges these into **`appData.parking`** and the **data** view (`#/data/parking`) shows them with a map and mode filters. Each category applies to one or more transport **modes** (`drive`, `bike`, `micromobility`). +Parking JSON lives under **`data/parking/public/`** (Grand Rapids Visitor Parking ArcGIS map via `scripts/fetch_car_parking_arcgis.py` for **`garages-arcgis.json`** and **`lots-arcgis.json`**; meters; OSM bike racks via `scripts/fetch_bike_parking.py`) and **`data/parking/private/`** (**`garages-osm.json`** and **`lots-osm.json`** from OpenStreetMap via `scripts/fetch_car_parking_osm.py`; **`garages-ellis.json`** / **`lots-ellis.json`** from `scripts/fetch_car_parking_ellis.py` — loaded as **`ellisGarages`** / **`ellisLots`**. After overrides, pins with **`owner`: `"AirGarage"`** are split from OSM into **`airGarageGarages`** / **`airGarageLots`** in **`appData`** (still used for `#/visit` and the modes page); **`#/data/parking`** merges OSM + AirGarage + Ellis into one **Private Parking Garages** and one **Private Parking Lots** dataset in the dropdown (`dataset=osmGarages` / `osmLots`). **`#/visit`** merges OSM + AirGarage for private pins under **`private-garage` / `private-lot`** toggles. Lime micromobility via `scripts/fetch_lime_parking.py`. OSM drive **garages** (resp. **lots**) within **~0.06 mi** (Haversine) of a public ArcGIS **garage** (resp. **lot**) centroid are omitted in **`loadData()`** and when regenerating private JSON, so official names/pricing win over near-duplicate OSM pins of the same facility type (a surface lot beside a ramp is kept). Lime snapshot buckets stay in `data/parking/.lime/`. `loadData()` merges these into **`appData.parking`** and the **data** view (`#/data/parking`) shows them with a map and mode filters. Each category applies to one or more transport **modes** (`drive`, `bike`, `micromobility`). ### Schema @@ -80,14 +80,14 @@ Each parking file has: Each **item** (parking location) has: -| Attribute | Type | Required | Description | -| -------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `location` | object | yes | Coordinates: `{ "latitude": number, "longitude": number }`. | -| `name` | string | no | Display name for the map and popup (e.g. "Arena Place Garage"). | -| `address` | string | no | Street address. | -| `pricing` | object | no | Price info; shown in map popups. See below. | -| `availability` | string | no | e.g. "Good availability". | -| `manager` | string | no | Who operates the facility (e.g. **AirGarage** on a private lot). Official public **`garages`** / **`lots`** from ArcGIS default to **`City`** in `loadData()` when omitted. The **`#/visit`** map shows **`(manager)`** after the category line only for **private** garages and lots when set. | +| Attribute | Type | Required | Description | +| -------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `location` | object | yes | Coordinates: `{ "latitude": number, "longitude": number }`. | +| `name` | string | no | Display name for the map and popup (e.g. "Arena Place Garage"). | +| `address` | string | no | Street address. | +| `pricing` | object | no | Price info; shown in map popups. See below. | +| `availability` | string | no | e.g. "Good availability". | +| `owner` | string | no | Who owns or operates the facility (e.g. **AirGarage** on a private lot). Official public **`garages`** / **`lots`** from ArcGIS default to **`City`** in `loadData()` when omitted. The **`#/visit`** map shows **`(owner)`** after the category line only for **private** garages and lots when set. | **`pricing`** (optional): an object. **Dollar tiers** are stored as JSON **numbers** (dollars) or **`[low, high]`** arrays for ranges—not `"$…"` strings. **`hourly`** is always **per hour** (Ellis half-hour source rates are stored ×2). Optional metadata: **`rateLabel`**, **`rateNote`**, **`hourlyFreeWhen`**. Per-unit source rates (e.g. half-hour) are stored on **`hourly`** (×2 when needed) at fetch time; **`rate`** is for labeled flat tiers such as early bird. Non-price keys on meters: **`maxDuration`**, **`enforcement`**, **`free`**. Display formatting lives in **`src/shared/parking-pricing.mjs`**. The **data view** lists tiers in order: `events`, `evening`, `hourly`, `rate`, `daily`. The **`#/visit`** popup prefers **`events`**, then **`daily`**, **`evening`**, **`rate`**, with **`hourly`** in parentheses when paired. If none are present, private OSM shows **"Not listed"**; other categories show **"Free"**. Example: `{ "daily": 27, "evening": 30, "events": [27, 30], "hourly": 6 }`. diff --git a/data/overrides.json b/data/overrides.json index 3b460cd..9bdcb36 100644 --- a/data/overrides.json +++ b/data/overrides.json @@ -56,7 +56,7 @@ "longitude": -85.67193 }, "name": "Downtown Grand Rapids Parking", - "manager": "AirGarage", + "owner": "AirGarage", "address": "45 Ottawa Ave NW, Grand Rapids, MI 49503", "pricing": { "hourly": 4.0, @@ -71,7 +71,7 @@ "longitude": -85.646478 }, "name": "East Hills Cherry Street Parking", - "manager": "AirGarage", + "owner": "AirGarage", "address": "946 Cherry St SE, Grand Rapids, MI 49506", "pricing": { "hourly": 4.99, @@ -86,7 +86,7 @@ "longitude": -85.677352 }, "name": "Swift Printing Parking", - "manager": "AirGarage", + "owner": "AirGarage", "address": "349 Mt Vernon Ave NW, Grand Rapids, MI 49504", "pricing": { "hourly": 4.99, @@ -101,7 +101,7 @@ "longitude": -85.681926 }, "name": "Bridge Street Parking", - "manager": "AirGarage", + "owner": "AirGarage", "address": "433 Bridge St NW, Grand Rapids, MI 49504", "pricing": { "hourly": 4.99, @@ -116,7 +116,7 @@ "longitude": -85.679696 }, "name": "Red Lion Lot", - "manager": "AirGarage", + "owner": "AirGarage", "address": "412 Bridge St NW, Grand Rapids, MI 49504", "pricing": { "hourly": 4.99, diff --git a/data/parking/private/garages-ellis.json b/data/parking/private/garages-ellis.json index 1e2bd19..bec138b 100644 --- a/data/parking/private/garages-ellis.json +++ b/data/parking/private/garages-ellis.json @@ -8,7 +8,7 @@ "latitude": 42.967794, "longitude": -85.668944 }, - "manager": "Ellis", + "owner": "Ellis", "name": "210 Ionia", "address": "210 Ionia Ave NW, Grand Rapids, MI, 49503", "availability": "Grand Rapids; 204 spaces; Monthly from $210; North of Commerce Building corner of Lyon and Ionia Early Bird Discount program available, please call (616) 458-1179 for details. Residential Rate : $245 (24/7 access) Monthly Rate: $210 (Monday - Friday 6am - 6pm) ", @@ -23,7 +23,7 @@ "latitude": 42.964667, "longitude": -85.667546 }, - "manager": "Ellis", + "owner": "Ellis", "name": "9-17 Library", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 125 spaces; Monthly from $195; Corner of Library and Division Monthly parking is on a Waiting list, call (616) 458-1179 for details", @@ -38,7 +38,7 @@ "latitude": 42.961379, "longitude": -85.672669 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Arena Place", "address": "55 Ottawa Ave SW, Grand Rapids, MI, 49503", "availability": "Grand Rapids; 250 spaces; Located near the Van Andel Arena", @@ -53,7 +53,7 @@ "latitude": 42.954605, "longitude": -85.670565 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Downtown Market", "address": "435 Ionia Ave SW, Grand Rapids, MI, 49503", "availability": "Grand Rapids; 225 spaces; Surface parking lot  1st 1/2 hour free $3.00 per half upto a $24.00 Max", @@ -68,7 +68,7 @@ "latitude": 42.971496, "longitude": -85.672041 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Michigan State University Innovation Park (GRIP)", "address": "432 Monroe Ave NW, Grand Rapids, MI, 49503", "availability": "Grand Rapids; 600 spaces; Monthly from $168.83; Please call to be added to the waitlist 616-458-1179", @@ -83,7 +83,7 @@ "latitude": 42.966771, "longitude": -85.671237 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Midtown Parking Garage", "address": "130 Lyon Street NW, Grand Rapids, MI, 49503", "availability": "Grand Rapids; 665 spaces; Monthly from $240; Midtown Multi-Level Parking Garage Behind Waters Buliding Entrances off both Pearl and Lyon Street between Monroe and Ottawa. Overnight Parking:  $38.00 per 24 hour period  No in/out p rivileges   Monthly Rate: $240 (Monda\u2026", @@ -98,7 +98,7 @@ "latitude": 42.965954, "longitude": -85.670936 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Sears", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 56 spaces; On Pearl Between Ledyard and McKay Tower   Overnight Parking:  $38.00 per 24 hour period  No in/out p rivileges  ", @@ -113,7 +113,7 @@ "latitude": 42.966456, "longitude": -85.67011 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Warner Ramp", "address": "151 Ionia Ave NW, Grand Rapids, MI, 49503", "availability": "Grand Rapids; 125 spaces; Entrance on Ionia    Overnight Max: $38.00 per 24 hour period", diff --git a/data/parking/private/lots-ellis.json b/data/parking/private/lots-ellis.json index 7d3b586..2cdae3c 100644 --- a/data/parking/private/lots-ellis.json +++ b/data/parking/private/lots-ellis.json @@ -8,7 +8,7 @@ "latitude": 42.96016, "longitude": -85.673232 }, - "manager": "Ellis", + "owner": "Ellis", "name": "100 Grandville", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 22 spaces; Across from the Intersection Lounge Event Parking Only", @@ -21,7 +21,7 @@ "latitude": 42.960635, "longitude": -85.670101 }, - "manager": "Ellis", + "owner": "Ellis", "name": "120 South Ionia", "address": "120 South Ionia, Grand Rapids, Michigan, 49503", "availability": "Grand Rapids; 25 spaces; 120 South Ionia Event Parking Only", @@ -34,7 +34,7 @@ "latitude": 42.96239, "longitude": -85.669971 }, - "manager": "Ellis", + "owner": "Ellis", "name": "28 Ionia", "address": "28 Ionia Ave, Grand Rapids, MI", "availability": "Grand Rapids; 18 spaces; Corner of Ionia and Weston Event Parking only", @@ -48,7 +48,7 @@ "latitude": 42.961369, "longitude": -85.674391 }, - "manager": "Ellis", + "owner": "Ellis", "name": "90 Market", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 180 spaces; Monthly from $220; Overnight Parking:  $35.00 per 24 hour period  No in/out p rivileges   No Trailers allowed in the Lot Call for Monthly parking details 616-458-1179", @@ -63,7 +63,7 @@ "latitude": 42.970861, "longitude": -85.67718 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Bridgewater Place", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 1650 spaces; Corner of Bridge and Scribner", @@ -78,7 +78,7 @@ "latitude": 42.968184, "longitude": -85.671215 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Fifth Third", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 650 spaces; Between Ottawa and Monroe", @@ -93,7 +93,7 @@ "latitude": 42.968624, "longitude": -85.669348 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Frey", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 100 spaces; Between County Courthouse and State of Michigan Building", @@ -108,7 +108,7 @@ "latitude": 42.96071, "longitude": -85.673747 }, - "manager": "Ellis", + "owner": "Ellis", "name": "G.W.L.", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 20 spaces; Across street south of Grand Woods Lodge Event Parking Only", @@ -121,7 +121,7 @@ "latitude": 42.969997, "longitude": -85.677395 }, - "manager": "Ellis", + "owner": "Ellis", "name": "GR Ford Museum", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 240 spaces; Monthly from $100; Corner of Bridge and Scribner Monday - Friday Monthly rate $100 24/7 monthly rate $150.00", @@ -136,7 +136,7 @@ "latitude": 42.966268, "longitude": -85.665894 }, - "manager": "Ellis", + "owner": "Ellis", "name": "GRCC Bostwick", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 2200 spaces; Corner of Bostwick and Lyon Radier card Rate $3.50 per use", @@ -151,7 +151,7 @@ "latitude": 42.965217, "longitude": -85.658601 }, - "manager": "Ellis", + "owner": "Ellis", "name": "GRCC DeVos Campus", "address": "432 Fountain, Grand Rapids, Michigan, 49503", "availability": "Grand Rapids; 250 spaces; GRCC DeVos Campus 432 Fountain Street, NE   Raider Card Rate $3.50 per use", @@ -166,7 +166,7 @@ "latitude": 42.967666, "longitude": -85.666859 }, - "manager": "Ellis", + "owner": "Ellis", "name": "GRCC Lyon", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 768 spaces; Corner of Lyon and Bostwick   Raider Card Rate  $3.50 per use", @@ -181,7 +181,7 @@ "latitude": 42.964761, "longitude": -85.67394 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Oldtown", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 145 spaces; Monthly from $0; On Louis-Campau across from Z's", @@ -198,7 +198,7 @@ "latitude": 42.960923, "longitude": -85.672366 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Studio Park(ing) Ramp", "address": "120 Ottawa Ave SW, Grand Rapids, MI, 49503", "availability": "Grand Rapids; 933 spaces; Monthly from $185; 1st 4 hours of Movie Parking is free with a ticket purchase and validation form inside the Theater. This location has ChargePoint EV chargers in the Ramp.      ", @@ -213,7 +213,7 @@ "latitude": 42.961259, "longitude": -85.670593 }, - "manager": "Ellis", + "owner": "Ellis", "name": "West Michigan Community Bank", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 22 spaces; On Ionia behind arena. Event Parking Only for Nights and Weekends.", @@ -226,7 +226,7 @@ "latitude": 42.970437, "longitude": -85.654811 }, - "manager": "Ellis", + "owner": "Ellis", "name": "Womens Health Center", "address": "Grand Rapids, MI", "availability": "Grand Rapids; 440 spaces; Monthly from $150; Womens Health Center at Michigan and Union", diff --git a/index.html b/index.html index f8e1bc4..4d1340e 100644 --- a/index.html +++ b/index.html @@ -122,6 +122,12 @@

How each mode works