diff --git a/.github/workflows/rebuild-cache.yml b/.github/workflows/rebuild-cache.yml index 426ba5e..b559459 100644 --- a/.github/workflows/rebuild-cache.yml +++ b/.github/workflows/rebuild-cache.yml @@ -52,6 +52,15 @@ jobs: - name: Install dependencies run: npm install + - name: Import CSV to database + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_KEY: ${{ secrets.SUPABASE_SERVICE_KEY }} + STAGING: ${{ steps.env.outputs.STAGING }} + run: | + echo "Importing events.csv to ${{ steps.env.outputs.ENV_NAME }} database..." + node import-csv.js + - name: Rebuild cache env: SUPABASE_URL: ${{ secrets.SUPABASE_URL }} diff --git a/.github/workflows/update-cache.yml b/.github/workflows/update-cache.yml new file mode 100644 index 0000000..126443a --- /dev/null +++ b/.github/workflows/update-cache.yml @@ -0,0 +1,107 @@ +name: Update Data Cache + +on: + push: + branches: + - main + - staging + paths: + - 'events.csv' + - 'geometries/**' + + workflow_dispatch: + inputs: + environment: + description: 'Environment to update' + required: true + default: 'staging' + type: choice + options: + - staging + - production + +jobs: + update-cache: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Determine environment + id: env + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT + elif [ "${{ github.ref }}" = "refs/heads/main" ]; then + echo "environment=production" >> $GITHUB_OUTPUT + else + echo "environment=staging" >> $GITHUB_OUTPUT + fi + + - name: Import CSV to database + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_KEY: ${{ secrets.SUPABASE_SERVICE_KEY }} + STAGING: ${{ steps.env.outputs.environment == 'staging' }} + GITHUB_ACTIONS: true + run: | + if [ "${{ steps.env.outputs.environment }}" = "staging" ]; then + STAGING=true node import-csv.js + else + node import-csv.js + fi + + - name: Upload geometry files to storage + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_KEY: ${{ secrets.SUPABASE_SERVICE_KEY }} + STAGING: ${{ steps.env.outputs.environment == 'staging' }} + GITHUB_ACTIONS: true + run: | + if [ "${{ steps.env.outputs.environment }}" = "staging" ]; then + STAGING=true node upload-geometries.js + else + node upload-geometries.js + fi + + - name: Sync geometries table with storage + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_KEY: ${{ secrets.SUPABASE_SERVICE_KEY }} + STAGING: ${{ steps.env.outputs.environment == 'staging' }} + GITHUB_ACTIONS: true + run: | + if [ "${{ steps.env.outputs.environment }}" = "staging" ]; then + STAGING=true node sync-geometries-table.js + else + node sync-geometries-table.js + fi + + - name: Rebuild cache + env: + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_SERVICE_KEY: ${{ secrets.SUPABASE_SERVICE_KEY }} + STAGING: ${{ steps.env.outputs.environment == 'staging' }} + GITHUB_ACTIONS: true + run: | + if [ "${{ steps.env.outputs.environment }}" = "staging" ]; then + STAGING=true node rebuild-cache.js + else + node rebuild-cache.js + fi + + - name: Summary + run: | + echo "āœ… Cache updated successfully for ${{ steps.env.outputs.environment }}" + echo "Branch: ${{ github.ref_name }}" + echo "Commit: ${{ github.sha }}" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3616310..b1d136f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ Each row in `events.csv` represents one change to a service. Service creation ev ## CSV structure -14 columns capture service attributes: +15 columns capture service attributes: | Column | Description | Example | Required? | | ---------------- | -------------------------- | -------------------------------------------- | ------------- | @@ -32,6 +32,7 @@ Each row in `events.csv` represents one change to a service. Service creation ev | `platform` | Booking app | `Robotaxi` | If applicable | | `fares` | Charges fares? | `Yes` / `No` | If applicable | | `direct_booking` | Can book AV directly? | `Yes` / `No` | If applicable | +| `service_model` | Service model | `Flexible` / `Stop-to-Stop` | If applicable | | `supervision` | Supervision level | `Autonomous` / `Safety Driver` | If applicable | | `access` | Access policy | `Public` / `Waitlist` | If applicable | | `fleet_partner` | Fleet partnerships | `Moove` | If applicable | @@ -43,10 +44,10 @@ Each row in `events.csv` represents one change to a service. Service creation ev For `service_created` events, fill in all service attributes: ```csv -2025-09-10,service_created,Zoox,Las Vegas,zoox-las-vegas-september-10-2025-boundary.geojson,Zoox Robotaxi,Zoox,No,Yes,Autonomous,Public,,https://techcrunch.com/2025/09/10/zoox-opens-its-las-vegas-robotaxi-service-to-the-public/,Zoox Las Vegas service +2025-09-10,service_created,Zoox,Las Vegas,zoox-las-vegas-september-10-2025-boundary.geojson,Zoox Robotaxi,Zoox,No,Yes,Stop-to-Stop,Autonomous,Public,,https://techcrunch.com/2025/09/10/zoox-opens-its-las-vegas-robotaxi-service-to-the-public/,Zoox Las Vegas service ``` -Required: `date`, `event_type`, `company`, `city`, `vehicles`, `platform`, `fares`, `direct_booking`, `supervision`, `access`, `source_url` +Required: `date`, `event_type`, `company`, `city`, `vehicles`, `platform`, `fares`, `direct_booking`, `service_model`, `supervision`, `access`, `source_url` ## Updating a service @@ -80,6 +81,12 @@ Note: Multiple platforms separated by `;` (semicolon). This allows filtering by 2024-11-12,access_policy_changed,Waymo,Los Angeles,,,,,,,Public,,https://waymo.com/blog/2024/11/waymo-one-open-to-all-in-los-angeles,Access policy update ``` +### Service model change + +```csv +2026-03-15,service_model_updated,Zoox,Las Vegas,,,,,,,Flexible,,,https://example.com,Service now allows flexible travel +``` + ## Event types **Service lifecycle:** @@ -95,6 +102,7 @@ Note: Multiple platforms separated by `;` (semicolon). This allows filtering by - `fares_policy_changed` - Fare policy changes - `access_policy_changed` - Access changes - `supervision_updated` - Supervision level changes +- `flexibility_updated` - Travel flexibility changes - `fleet_partner_changed` - Fleet partnership changes ## Field values @@ -113,6 +121,8 @@ Add new values when documenting companies, vehicles, platforms, or policies not **Direct Booking:** `Yes` (book AV directly), `No` (may or may not get AV, like Waymo on UberX) +**Flexibility:** `Point-to-Point` (riders can travel freely between any points in the service area), `Stop-to-Stop` (riders can only travel to/from predetermined stops) + **Supervision:** `Autonomous`, `Safety Driver`, `Safety Attendant` **Access:** `Public`, `Waitlist` diff --git a/events.csv b/events.csv index 9b6f0b7..1ab02e1 100644 --- a/events.csv +++ b/events.csv @@ -1,37 +1,50 @@ -date,event_type,company,city,geometry_file,vehicles,platform,fares,direct_booking,supervision,access,fleet_partner,source_url,notes -2017-04-25,service_created,Waymo,Phoenix,waymo-phoenix-april-25-2017-boundary.geojson,Chrysler Pacifica Hybrid,Waymo,No,Yes,Safety Driver,Waitlist,,https://www.technologyreview.com/2017/04/25/152152/waymo-has-invited-the-public-to-hop-into-its-self-driving-cars/,Waymo Phoenix service -2017-11-07,supervision_updated,Waymo,Phoenix,,,,,,Autonomous,,,https://waymo.com/blog/2017/11/waymos-fully-self-driving-vehicles-are-here,Supervision level update -2020-10-08,fares_policy_changed,Waymo,Phoenix,,,,Yes,,,,,https://waymo.com/blog/2020/10/waymo-is-opening-its-fully-driverless-service-in-phoenix,Fares policy update -2020-10-08,vehicle_types_updated,Waymo,Phoenix,,Jaguar I-Pace;Chrysler Pacifica Hybrid,,,,,,,https://techcrunch.com/2019/06/17/waymos-self-driving-jaguar-i-pace-vehicles-are-now-testing-on-public-roads/,Vehicle fleet expansion - adding Jaguar I-Pace -2020-10-08,access_policy_changed,Waymo,Phoenix,,,,,,,Public,,https://waymo.com/blog/2020/10/waymo-is-opening-its-fully-driverless-service-in-phoenix,Access policy update -2020-10-08,geometry_updated,Waymo,Phoenix,waymo-phoenix-october-8-2020-boundary.geojson,,,,,,,,https://waymo.com/blog/2020/10/waymo-is-opening-its-fully-driverless-service-in-phoenix,Service area boundary update -2022-11-01,geometry_updated,Waymo,Phoenix,waymo-phoenix-november-1-2022-boundary.geojson,,,,,,,,https://techcrunch.com/2022/11/01/waymo-launches-autonomous-rides-to-phoenix-airport/,Service area boundary update -2022-12-16,service_created,Waymo,San Francisco,waymo-san-francisco-december-16-2022-boundary.geojson,Jaguar I-Pace,Waymo,No,Yes,Autonomous,Waitlist,,https://waymo.com/blog/2022/12/wheels-up-for-waymo-as-we-expand,Waymo San Francisco service -2023-03-30,vehicle_types_updated,Waymo,Phoenix,,Jaguar I-Pace,,,,,,,https://techcrunch.com/2023/03/30/waymo-retires-its-self-driving-chrysler-pacifica-minivan/,Vehicle fleet change - retiring Chrysler Pacifica Hybrid -2023-05-04,geometry_updated,Waymo,Phoenix,waymo-phoenix-may-4-2023-boundary.geojson,,,,,,,,https://waymo.com/blog/2023/05/waymo-one-doubles-service-area-in,Service area boundary update -2023-08-21,fares_policy_changed,Waymo,San Francisco,,,,Yes,,,,,https://waymo.com/blog/2023/08/waymos-next-chapter-in-san-francisco,Fares policy update -2023-08-21,geometry_updated,Waymo,San Francisco,waymo-san-francisco-august-21-2023-boundary.geojson,,,,,,,,https://waymo.com/blog/2023/08/waymos-next-chapter-in-san-francisco,Service area boundary update -2023-10-26,platform_updated,Waymo,Phoenix,,,Waymo;Uber,,,,,,https://waymo.com/blog/2023/10/the-waymo-driver-now-available-on-uber-in-phoenix,Booking platform update -2024-03-13,service_created,Waymo,Los Angeles,waymo-los-angeles-march-13-2024-boundary.geojson,Jaguar I-Pace,Waymo,No,Yes,Autonomous,Waitlist,,https://waymo.com/blog/2024/03/scaling-waymo-one-safely-across-four-cities-this-year,Waymo Los Angeles service -2024-04-10,fares_policy_changed,Waymo,Los Angeles,,,,Yes,,,,,https://www.nbcnews.com/tech/innovation/waymo-will-launch-paid-robotaxi-service-los-angeles-wednesday-rcna147101,Fares policy update -2024-06-05,geometry_updated,Waymo,Phoenix,waymo-phoenix-june-5-2024-boundary.geojson,,,,,,,,https://waymo.com/blog/2024/06/largest-autonomous-ride-hail-territory-in-us-now-even-larger,Service area boundary update -2024-06-25,access_policy_changed,Waymo,San Francisco,,,,,,,Public,,https://waymo.com/blog/2024/06/waymo-one-is-now-open-to-everyone-in-san-francisco,Access policy update -2024-08-06,geometry_updated,Waymo,Los Angeles,waymo-los-angeles-august-6-2024-boundary.geojson,,,,,,,,https://waymo.com/blog/2024/08/expanding-destinations-for-san-francisco-and-los-angeles-riders,Service area boundary update -2024-08-06,geometry_updated,Waymo,San Francisco,waymo-san-francisco-august-6-2024-boundary.geojson,,,,,,,,https://waymo.com/blog/2024/08/expanding-destinations-for-san-francisco-and-los-angeles-riders,Service area boundary update -2024-11-12,access_policy_changed,Waymo,Los Angeles,,,,,,,Public,,https://waymo.com/blog/2024/11/waymo-one-open-to-all-in-los-angeles,Access policy update -2024-12-05,fleet_partner_changed,Waymo,Phoenix,,,,,,,,Moove,https://www.prnewswire.com/news-releases/moove-partners-with-waymo-to-redefine-the-future-of-urban-mobility-302324144.html,Fleet partnership with Moove established -2025-03-04,service_created,Waymo,Austin,waymo-austin-march-4-2025-boundary.geojson,Jaguar I-Pace,Uber,Yes,No,Autonomous,Public,,https://www.cnbc.com/2025/03/04/waymo-uber-begin-offering-robotaxi-rides-in-austin-ahead-of-sxsw.html,Waymo Austin service -2025-03-11,service_created,Waymo,Silicon Valley,waymo-silicon-valley-march-11-2025-boundary.geojson,Jaguar I-Pace,Waymo,Yes,Yes,Autonomous,Public,,https://www.mercurynews.com/2025/03/11/alphabets-waymo-to-offer-self-driving-rides-in-silicon-valley/,Waymo Silicon Valley service -2025-06-17,geometry_updated,Waymo,Silicon Valley,waymo-silicon-valley-june-17-2025-boundary.geojson,,,,,,,,https://www.theverge.com/news/688202/waymo-sf-la-service-area-expand-robotaxi,Service area boundary update -2025-06-17,geometry_updated,Waymo,San Francisco,waymo-san-francisco-june-17-2025-boundary.geojson,,,,,,,,https://www.theverge.com/news/688202/waymo-sf-la-service-area-expand-robotaxi,Service area boundary update -2025-06-18,geometry_updated,Waymo,Los Angeles,waymo-los-angeles-june-18-2025-boundary.geojson,,,,,,,,https://techcrunch.com/2025/06/17/waymo-robotaxis-are-pushing-into-even-more-california-cities/,Service area boundary update -2025-06-22,service_created,Tesla,Austin,tesla-austin-june-22-2025-boundary.geojson,Tesla Model Y,Robotaxi,Yes,Yes,Safety Attendant,Waitlist,,https://www.cnbc.com/2025/06/20/tesla-robotaxi-launch-austin.html,Tesla Austin service -2025-06-24,service_created,Waymo,Atlanta,waymo-atlanta-june-24-2025-boundary.geojson,Jaguar I-Pace,Uber,Yes,No,Autonomous,Public,,https://www.uber.com/newsroom/waymo-on-uber-atl/,Waymo Atlanta service -2025-07-14,geometry_updated,Tesla,Austin,tesla-austin-july-14-2025-boundary.geojson,,,,,,,,https://www.businessinsider.com/tesla-new-robotaxi-geofence-austin-shape-elon-musk-bigger-waymo-2025-7,Service area boundary update -2025-07-17,geometry_updated,Waymo,Austin,waymo-austin-july-17-2025-boundary.geojson,,,,,,,,https://www.axios.com/local/austin/2025/07/17/waymo-expands-austin-service-area,Service area boundary update -2025-07-31,service_created,Tesla,Bay Area,tesla-bay-area-july-31-2025-boundary.geojson,Tesla Model Y,Robotaxi,Yes,Yes,Safety Driver,Waitlist,,https://www.businessinsider.com/teslas-ride-hailing-service-live-in-san-francisco-musk-says-2025-7,Tesla Bay Area service -2025-08-03,geometry_updated,Tesla,Austin,tesla-austin-august-3-2025-boundary.geojson,,,,,,,,https://www.pcmag.com/news/teslas-robotaxi-coverage-expands-four-times-the-size-of-initial-austin,Service area boundary update -2025-08-26,geometry_updated,Tesla,Austin,tesla-austin-august-26-2025-boundary.geojson,,,,,,,,https://electrek.co/2025/08/27/tesla-announces-50-increase-in-austin-robotaxi-but-50-from-what/,Service area boundary update -2025-09-01,supervision_updated,Tesla,Austin,,,,,,Safety Driver,,,https://mashable.com/article/tesla-robotaxi-human-safety-monitor-drivers-seat#:~:text=Tesla%20now%20puts%20their%20robotaxi%20safety%20monitors%20in%20the%20driver%27s&text=Texas%20SB%202807%2C%20which%20went%20into%20effect%20on%20Sept.,Supervision level update -2025-09-10,service_created,May Mobility,Atlanta,may-mobility-atlanta-september-10-2025-boundary.geojson,Toyota Sienna,Lyft,Yes,No,Safety Driver,Public,,https://maymobility.com/posts/lyft-and-may-mobility-deploy-their-first-autonomous-vehicle-fleet-in-atlanta/,May Mobility Atlanta service -2025-09-10,service_created,Zoox,Las Vegas,zoox-las-vegas-september-10-2025-boundary.geojson,Zoox Robotaxi,Zoox,No,Yes,Autonomous,Public,,https://techcrunch.com/2025/09/10/zoox-opens-its-las-vegas-robotaxi-service-to-the-public/,Zoox Las Vegas service +date,event_type,company,city,geometry_file,vehicles,platform,fares,direct_booking,service_model,supervision,access,fleet_partner,company_link,booking_platform_link,source_url,notes +2017-04-25,service_created,Waymo,Phoenix,waymo-phoenix-april-25-2017-boundary.geojson,Chrysler Pacifica Hybrid,Waymo,No,Yes,Flexible,Safety Driver,Waitlist,,https://waymo.com/rides/phoenix/,https://www.uber.com/newsroom/waymo-on-uber/,https://www.technologyreview.com/2017/04/25/152152/waymo-has-invited-the-public-to-hop-into-its-self-driving-cars/,Waymo Phoenix service +2017-11-07,supervision_updated,Waymo,Phoenix,,,,,,,Autonomous,,,,https://waymo.com/blog/2017/11/waymos-fully-self-driving-vehicles-are-here,Supervision level update +2019-07-25,service_created,May Mobility,Grand Rapids MI,may-mobility-grand-rapids-mi-25-july-2019-boundary.geojson,Polaris GEM,,No,Yes,Stop-to-Stop,Safety Driver,Public,,https://maymobility.com/locations/grand-rapids-michigan/,https://www.grandrapidsmi.gov/Government/Departments/Mobile-GR/Grand-Rapids-Autonomous-Vehicle-Initiative,https://www.michiganbusiness.org/press-releases/2019/07/public-private-partners-launch-grand-rapids-autonomous-vehicle-initiative-avgr/,Downtown Grand Rapids autonomous shuttle service +2020-10-08,access_policy_changed,Waymo,Phoenix,,,,,,,,Public,,,https://waymo.com/blog/2020/10/waymo-is-opening-its-fully-driverless-service-in-phoenix,Access policy update +2020-10-08,fares_policy_changed,Waymo,Phoenix,,,,Yes,,,,,,,https://waymo.com/blog/2020/10/waymo-is-opening-its-fully-driverless-service-in-phoenix,Fares policy update +2020-10-08,geometry_updated,Waymo,Phoenix,waymo-phoenix-october-8-2020-boundary.geojson,,,,,,,,,,https://waymo.com/blog/2020/10/waymo-is-opening-its-fully-driverless-service-in-phoenix,Service area boundary update +2020-10-08,vehicle_types_updated,Waymo,Phoenix,,Jaguar I-Pace;Chrysler Pacifica Hybrid,,,,,,,,,https://techcrunch.com/2019/06/17/waymos-self-driving-jaguar-i-pace-vehicles-are-now-testing-on-public-roads/,Vehicle fleet expansion - adding Jaguar I-Pace +2021-07-26,geometry_updated,May Mobility,Grand Rapids MI,may-mobility-grand-rapids-mi-26-july-2021-boundary.geojson,,,,,,,,,,,https://www.prnewswire.com/news-releases/may-mobility-announces-on-demand-autonomous-service-in-grand-rapids-michigan-301351595.html, +2021-07-26,platform_updated,May Mobility,Grand Rapids MI,,,May Mobility,,,,,,,,,https://www.prnewswire.com/news-releases/may-mobility-announces-on-demand-autonomous-service-in-grand-rapids-michigan-301351595.html, +2021-07-26,vehicle_types_updated,May Mobility,Grand Rapids MI,,Polaris GEM;Lexus RX 450h,,,,,,,,,,https://www.prnewswire.com/news-releases/may-mobility-announces-on-demand-autonomous-service-in-grand-rapids-michigan-301351595.html, +2022-02-02,service_created,Cruise,San Francisco,cruise-san-francisco-february-1-2022-boundary.geojson,Chevrolet Bolt EV,Cruise,Yes,Yes,Flexible,Autonomous,Waitlist,,https://web.archive.org/web/20230928020703/https://getcruise.com/rides/,,https://fortune.com/2022/02/01/cruise-robotaxi-self-driving-car-san-francisco/,Cruise San Francisco commercial service launch +2022-04-29,service_ended,May Mobility,Grand Rapids MI,,,,,,,,,,,,https://www.grandrapidsmi.gov/Government/Departments/Mobile-GR/Grand-Rapids-Autonomous-Vehicle-Initiative, +2022-09-28,service_created,May Mobility,Grand Rapids MN,may-mobility-grand-rapids-mn-september-28-2022-boundary.geojson,Toyota Sienna,May Mobility,No,Yes,Stop-to-Stop,Safety Driver,Public,,https://maymobility.com/locations/grand-rapids-minnesota/,https://gomarti.com/,https://maymobility.com/posts/may-mobility-and-via-launch-first-rural-transit-program-to-use-wheelchair-accessible-ada-compliant-autonomous-vehicless/, +2022-11-01,geometry_updated,Cruise,San Francisco,cruise-san-francisco-november-1-2022-boundary.geojson,,,,,,,,,,,https://sfstandard.com/2022/11/01/self-driving-cruise-cars-are-expanding-to-most-of-sf-says-ceo/,CPUC approved public expansion and 24/7 operations +2022-11-01,geometry_updated,Waymo,Phoenix,waymo-phoenix-november-1-2022-boundary.geojson,,,,,,,,,,https://techcrunch.com/2022/11/01/waymo-launches-autonomous-rides-to-phoenix-airport/,Service area boundary update +2022-12-16,service_created,Waymo,San Francisco,waymo-san-francisco-december-16-2022-boundary.geojson,Jaguar I-Pace,Waymo,No,Yes,Flexible,Autonomous,Waitlist,,https://waymo.com/rides/san-francisco/,,https://waymo.com/blog/2022/12/wheels-up-for-waymo-as-we-expand,Waymo San Francisco service +2023-03-30,vehicle_types_updated,Waymo,Phoenix,,Jaguar I-Pace,,,,,,,,,https://techcrunch.com/2023/03/30/waymo-retires-its-self-driving-chrysler-pacifica-minivan/,Vehicle fleet change - retiring Chrysler Pacifica Hybrid +2023-05-04,geometry_updated,Waymo,Phoenix,waymo-phoenix-may-4-2023-boundary.geojson,,,,,,,,,,https://waymo.com/blog/2023/05/waymo-one-doubles-service-area-in,Service area boundary update +2023-08-21,fares_policy_changed,Waymo,San Francisco,,,,Yes,,,,,,,https://waymo.com/blog/2023/08/waymos-next-chapter-in-san-francisco,Fares policy update +2023-08-21,geometry_updated,Waymo,San Francisco,waymo-san-francisco-august-21-2023-boundary.geojson,,,,,,,,,,https://waymo.com/blog/2023/08/waymos-next-chapter-in-san-francisco,Service area boundary update +2023-10-24,service_ended,Cruise,San Francisco,,,,,,,,,,,,https://www.wired.com/story/cruise-robotaxi-self-driving-permit-revoked-california/,California DMV suspended operations due to pedestrian dragging incident +2023-10-26,platform_updated,Waymo,Phoenix,,,Waymo;Uber,,,,,,,,https://waymo.com/blog/2023/10/the-waymo-driver-now-available-on-uber-in-phoenix,Booking platform update +2024-03-13,service_created,Waymo,Los Angeles,waymo-los-angeles-march-13-2024-boundary.geojson,Jaguar I-Pace,Waymo,No,Yes,Flexible,Autonomous,Waitlist,,https://waymo.com/rides/los-angeles/,,https://waymo.com/blog/2024/03/scaling-waymo-one-safely-across-four-cities-this-year,Waymo Los Angeles service +2024-04-10,fares_policy_changed,Waymo,Los Angeles,,,,Yes,,,,,,,https://www.nbcnews.com/tech/innovation/waymo-will-launch-paid-robotaxi-service-los-angeles-wednesday-rcna147101,Fares policy update +2024-06-05,geometry_updated,Waymo,Phoenix,waymo-phoenix-june-5-2024-boundary.geojson,,,,,,,,,,https://waymo.com/blog/2024/06/largest-autonomous-ride-hail-territory-in-us-now-even-larger,Service area boundary update +2024-06-25,access_policy_changed,Waymo,San Francisco,,,,,,,,Public,,,https://waymo.com/blog/2024/06/waymo-one-is-now-open-to-everyone-in-san-francisco,Access policy update +2024-08-06,geometry_updated,Waymo,Los Angeles,waymo-los-angeles-august-6-2024-boundary.geojson,,,,,,,,,,https://waymo.com/blog/2024/08/expanding-destinations-for-san-francisco-and-los-angeles-riders,Service area boundary update +2024-08-06,geometry_updated,Waymo,San Francisco,waymo-san-francisco-august-6-2024-boundary.geojson,,,,,,,,,,https://waymo.com/blog/2024/08/expanding-destinations-for-san-francisco-and-los-angeles-riders,Service area boundary update +2024-09-12,service_created,May Mobility,Martinez CA,may-mobility-martinez-september-12-2024-boundary.geojson,Toyota Sienna,May Mobility,No,Yes,Stop-to-Stop,Safety Driver,Public,,https://maymobility.com/locations/martinez-california/,https://ridepresto.com/martinez/,https://ccta.net/news/contra-costa-transportation-authority-and-may-mobility-launch-autonomous-vehicle-service-in-martinez-california/, +2024-10-07,service_created,May Mobility,Peachtree Corners,may-mobility-peachtree-corners-october-7-2024-boundary.geojson,Toyota Sienna,May Mobility,No,Yes,Stop-to-Stop,Safety Driver,Public,,https://maymobility.com/locations/peachtree-corners-georgia/,,"https://maymobility.com/posts/peachtree-corners-launch-autonomous-vehicle-may-mobility-t-mobile/#:~:text=Peachtree%20Corners%2C%20Ga.,Corners%20City%20Manager%20Brian%20Johnson.",Peachtree Corners autonomous shuttle service +2024-11-12,access_policy_changed,Waymo,Los Angeles,,,,,,,,Public,,,https://waymo.com/blog/2024/11/waymo-one-open-to-all-in-los-angeles,Access policy update +2024-12-05,fleet_partner_changed,Waymo,Phoenix,,,,,,,,,Moove,,https://www.prnewswire.com/news-releases/moove-partners-with-waymo-to-redefine-the-future-of-urban-mobility-302324144.html,Fleet partnership with Moove established +2025-02-16,supervision_updated,May Mobility,Peachtree Corners,,,,,,,Autonomous,,,,,https://www.iotworldtoday.com/transportation-logistics/may-mobility-launches-first-commercial-driverless-car-service, +2025-03-04,service_created,Waymo,Austin,waymo-austin-march-4-2025-boundary.geojson,Jaguar I-Pace,Uber,Yes,No,Flexible,Autonomous,Public,,https://waymo.com/waymo-on-uber/,https://www.uber.com/us/en/u/waymo-on-uber/,https://www.cnbc.com/2025/03/04/waymo-uber-begin-offering-robotaxi-rides-in-austin-ahead-of-sxsw.html,Waymo Austin service +2025-03-11,service_created,Waymo,Silicon Valley,waymo-silicon-valley-march-11-2025-boundary.geojson,Jaguar I-Pace,Waymo,Yes,Yes,Flexible,Autonomous,Public,,https://support.google.com/waymo/answer/15976820?hl=en,,https://www.mercurynews.com/2025/03/11/alphabets-waymo-to-offer-self-driving-rides-in-silicon-valley/,Waymo Silicon Valley service +2025-06-17,geometry_updated,Waymo,San Francisco,waymo-san-francisco-june-17-2025-boundary.geojson,,,,,,,,,,https://www.theverge.com/news/688202/waymo-sf-la-service-area-expand-robotaxi,Service area boundary update +2025-06-17,geometry_updated,Waymo,Silicon Valley,waymo-silicon-valley-june-17-2025-boundary.geojson,,,,,,,,,,https://www.theverge.com/news/688202/waymo-sf-la-service-area-expand-robotaxi,Service area boundary update +2025-06-18,geometry_updated,Waymo,Los Angeles,waymo-los-angeles-june-18-2025-boundary.geojson,,,,,,,,,,https://techcrunch.com/2025/06/17/waymo-robotaxis-are-pushing-into-even-more-california-cities/,Service area boundary update +2025-06-22,service_created,Tesla,Austin,tesla-austin-june-22-2025-boundary.geojson,Tesla Model Y,Robotaxi,Yes,Yes,Flexible,Safety Attendant,Waitlist,,https://www.tesla.com/support/robotaxi/,,https://www.cnbc.com/2025/06/20/tesla-robotaxi-launch-austin.html,Tesla Austin service +2025-06-24,service_created,Waymo,Atlanta,waymo-atlanta-june-24-2025-boundary.geojson,Jaguar I-Pace,Uber,Yes,No,Flexible,Autonomous,Public,,https://waymo.com/waymo-on-uber/,https://www.uber.com/us/en/u/waymo-on-uber/,https://www.uber.com/newsroom/waymo-on-uber-atl/,Waymo Atlanta service +2025-07-14,geometry_updated,Tesla,Austin,tesla-austin-july-14-2025-boundary.geojson,,,,,,,,,,https://www.businessinsider.com/tesla-new-robotaxi-geofence-austin-shape-elon-musk-bigger-waymo-2025-7,Service area boundary update +2025-07-17,geometry_updated,Waymo,Austin,waymo-austin-july-17-2025-boundary.geojson,,,,,,,,,,https://www.axios.com/local/austin/2025/07/17/waymo-expands-austin-service-area,Service area boundary update +2025-07-31,service_created,Tesla,Bay Area,tesla-bay-area-july-31-2025-boundary.geojson,Tesla Model Y,Robotaxi,Yes,Yes,Flexible,Safety Driver,Waitlist,,https://www.tesla.com/support/robotaxi/,,https://www.businessinsider.com/teslas-ride-hailing-service-live-in-san-francisco-musk-says-2025-7,Tesla Bay Area service +2025-08-03,geometry_updated,Tesla,Austin,tesla-austin-august-3-2025-boundary.geojson,,,,,,,,,,https://www.pcmag.com/news/teslas-robotaxi-coverage-expands-four-times-the-size-of-initial-austin,Service area boundary update +2025-08-26,geometry_updated,Tesla,Austin,tesla-austin-august-26-2025-boundary.geojson,,,,,,,,,,https://electrek.co/2025/08/27/tesla-announces-50-increase-in-austin-robotaxi-but-50-from-what/,Service area boundary update +2025-09-01,supervision_updated,Tesla,Austin,,,,,,,Safety Driver,,,,https://mashable.com/article/tesla-robotaxi-human-safety-monitor-drivers-seat#:~:text=Tesla%20now%20puts%20their%20robotaxi%20safety%20monitors%20in%20the%20driver%27s&text=Texas%20SB%202807%2C%20which%20went%20into%20effect%20on%20Sept.,Supervision level update +2025-09-02,geometry_updated,May Mobility,Grand Rapids MN,may-mobility-grand-rapids-mn-september-2-2025-boundary.geojson,,,,,,,,,,,https://www.kaxe.org/local-news/2025-08-11/grand-rapids-autonomous-transit-expanding-to-nearby-cities-tribal-stops, +2025-09-10,service_created,May Mobility,Atlanta,may-mobility-atlanta-september-10-2025-boundary.geojson,Toyota Sienna,Lyft,Yes,No,Flexible,Safety Driver,Public,,https://maymobility.com/locations/atlanta-georgia/,https://www.lyft.com/autonomous/maymobility,https://maymobility.com/posts/lyft-and-may-mobility-deploy-their-first-autonomous-vehicle-fleet-in-atlanta/,May Mobility Atlanta service +2025-09-10,service_created,Zoox,Las Vegas,zoox-las-vegas-september-10-2025-boundary.geojson,Zoox Robotaxi,Zoox,No,Yes,Stop-to-Stop,Autonomous,Public,,https://zoox.com/las-vegas/,,https://techcrunch.com/2025/09/10/zoox-opens-its-las-vegas-robotaxi-service-to-the-public/,Zoox Las Vegas service diff --git a/geometries/cruise-san-francisco-february-1-2022-boundary.geojson b/geometries/cruise-san-francisco-february-1-2022-boundary.geojson new file mode 100644 index 0000000..c2f21e9 --- /dev/null +++ b/geometries/cruise-san-francisco-february-1-2022-boundary.geojson @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"name": "cruise-san-francisco-february-16-2022-boundary", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "id": null }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -122.508343556090693, 37.73793792328955 ], [ -122.512269297911203, 37.764235679698452 ], [ -122.512705491446837, 37.77509694264144 ], [ -122.502345894976003, 37.775614105845655 ], [ -122.502782088511623, 37.781302662321522 ], [ -122.492858685576337, 37.781819782112471 ], [ -122.492749637192446, 37.783715857059512 ], [ -122.472466637786397, 37.784577693228456 ], [ -122.472139492634668, 37.786215154261882 ], [ -122.466032783136072, 37.786473697424327 ], [ -122.465814686368262, 37.784836242119411 ], [ -122.445749783729966, 37.787507861029027 ], [ -122.447821703024132, 37.80017519063076 ], [ -122.443786912819704, 37.798796539000811 ], [ -122.437353058169407, 37.799399702255066 ], [ -122.436371622714248, 37.796297667337626 ], [ -122.424594397252662, 37.797417861639765 ], [ -122.423831058565341, 37.798710372419578 ], [ -122.420559607048204, 37.798451872072825 ], [ -122.420123413512584, 37.796470006028166 ], [ -122.416633865227695, 37.796211497841568 ], [ -122.416197671692075, 37.795608308555003 ], [ -122.40998191380956, 37.796211497841568 ], [ -122.409545720273954, 37.79397105566575 ], [ -122.407582849363692, 37.793798711145705 ], [ -122.406928559060248, 37.790007029991763 ], [ -122.40976381704175, 37.78948996748592 ], [ -122.409327623506115, 37.786818420233914 ], [ -122.420777703816, 37.785094790105873 ], [ -122.419905316744803, 37.780699351326177 ], [ -122.422958671494101, 37.780182223696535 ], [ -122.422958671494101, 37.77845843880241 ], [ -122.430810155135148, 37.777165573752185 ], [ -122.430592058367367, 37.774665970541101 ], [ -122.436916864633773, 37.773890214429606 ], [ -122.436262574330357, 37.770097511825817 ], [ -122.435172090491321, 37.768373491785297 ], [ -122.43528113887524, 37.767080450377684 ], [ -122.437244009785459, 37.766908043148455 ], [ -122.43942497746356, 37.765959796203106 ], [ -122.441278799989917, 37.76501153709939 ], [ -122.441387848373836, 37.763804644292215 ], [ -122.44051546130261, 37.76311498241725 ], [ -122.441605945141646, 37.762597731790883 ], [ -122.441933090293347, 37.761994268154979 ], [ -122.445531686962184, 37.761304589399494 ], [ -122.446731219185111, 37.758804450004625 ], [ -122.451965541612481, 37.757942313379019 ], [ -122.453928412522757, 37.756390442133231 ], [ -122.4561093802008, 37.756390442133231 ], [ -122.457745105959361, 37.755010973697019 ], [ -122.458944638182331, 37.754924756065741 ], [ -122.460907509092593, 37.758373382947767 ], [ -122.462543234851154, 37.758545810071936 ], [ -122.46319752515457, 37.753200382340964 ], [ -122.464615154145321, 37.752338180407925 ], [ -122.464615154145321, 37.751303524826241 ], [ -122.465378492832627, 37.750355077833674 ], [ -122.465378492832627, 37.747337211071809 ], [ -122.467668508894633, 37.743284453538024 ], [ -122.47541094415179, 37.743111990847943 ], [ -122.476065234455206, 37.748199471276394 ], [ -122.493403927495862, 37.747595890188073 ], [ -122.493294879111957, 37.738369109399159 ], [ -122.493294879111957, 37.738369109399159 ], [ -122.493512975879796, 37.738282872378157 ], [ -122.498638249923275, 37.738282872378157 ], [ -122.508343556090693, 37.73793792328955 ] ] ] ] } } +] +} diff --git a/geometries/cruise-san-francisco-november-1-2022-boundary.geojson b/geometries/cruise-san-francisco-november-1-2022-boundary.geojson new file mode 100644 index 0000000..f5038f2 --- /dev/null +++ b/geometries/cruise-san-francisco-november-1-2022-boundary.geojson @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"name": "cruise-san-francisco-november-1-2023-boundary", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "fid": 1, "DN": 255 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -122.434677831968642, 37.707452804157235 ], [ -122.438247746517717, 37.706987163129099 ], [ -122.438247746517717, 37.710401864002129 ], [ -122.44135202003865, 37.710867505030265 ], [ -122.442904156799116, 37.712109214438641 ], [ -122.447560567080515, 37.710246650326077 ], [ -122.449423131193072, 37.708228872537468 ], [ -122.453303473094252, 37.707452804157235 ], [ -122.454079541474485, 37.708228872537468 ], [ -122.456097319263094, 37.708228872537468 ], [ -122.455942105587042, 37.710867505030265 ], [ -122.458735951755884, 37.71102271870631 ], [ -122.459822447488207, 37.712109214438641 ], [ -122.461529797924726, 37.712109214438641 ], [ -122.464478857769606, 37.710401864002129 ], [ -122.47006655010729, 37.712574855466784 ], [ -122.473170823628223, 37.712885282818874 ], [ -122.474412533036599, 37.714592633255386 ], [ -122.474257319360561, 37.718783402508649 ], [ -122.470376977459381, 37.721732462353536 ], [ -122.474102105684509, 37.722974171761912 ], [ -122.474722960388704, 37.724836735874476 ], [ -122.470842618487524, 37.72732015469122 ], [ -122.471153045839628, 37.728406650423551 ], [ -122.47627509714917, 37.730269214536108 ], [ -122.47922415699405, 37.728251436747499 ], [ -122.483570139923359, 37.729493146155875 ], [ -122.484656635655682, 37.728251436747499 ], [ -122.487140054472434, 37.727941009395408 ], [ -122.490244327993366, 37.729337932479829 ], [ -122.492572533134066, 37.727630582043318 ], [ -122.494124669894546, 37.727475368367266 ], [ -122.499246721204088, 37.729803573507972 ], [ -122.499557148556178, 37.731355710268438 ], [ -122.498781080175945, 37.73275263335286 ], [ -122.501419712668735, 37.732907847028905 ], [ -122.503437490457344, 37.734459983789371 ], [ -122.505610481922005, 37.734459983789371 ], [ -122.508714755442938, 37.73678818893007 ], [ -122.511974242639909, 37.763950582238252 ], [ -122.510577319555495, 37.765192291646628 ], [ -122.508404328090833, 37.765502718998718 ], [ -122.507938687062705, 37.766589214731049 ], [ -122.512284669992013, 37.768451778843605 ], [ -122.512905524696194, 37.77512596691362 ], [ -122.514612875132713, 37.778075026758508 ], [ -122.51445766145666, 37.779937590871064 ], [ -122.512905524696194, 37.781955368659673 ], [ -122.511042960583637, 37.781024086603395 ], [ -122.509490823823171, 37.78117930027944 ], [ -122.508404328090833, 37.782421009687816 ], [ -122.502661422077111, 37.781644941307583 ], [ -122.500798857964554, 37.783352291744094 ], [ -122.498625866499893, 37.783197078068049 ], [ -122.497384157091517, 37.782110582335719 ], [ -122.49521116562687, 37.782421009687816 ], [ -122.494900738274779, 37.783973146448282 ], [ -122.495676806655013, 37.785680496884794 ], [ -122.494745524598727, 37.786766992617125 ], [ -122.492417319458028, 37.787232633645267 ], [ -122.490709969021509, 37.788939984081779 ], [ -122.486829627120343, 37.790026479814102 ], [ -122.485277490359877, 37.791268189222478 ], [ -122.483725353599411, 37.795303744799696 ], [ -122.482173216838945, 37.796235026855975 ], [ -122.482018003162892, 37.798718445672726 ], [ -122.478603302289869, 37.802133146545749 ], [ -122.47891372964196, 37.804150924334358 ], [ -122.476430310825208, 37.808341693587622 ], [ -122.468514413346824, 37.806168702122967 ], [ -122.467738344966591, 37.805237420066689 ], [ -122.467738344966591, 37.803064428602035 ], [ -122.466651849234268, 37.801977932869704 ], [ -122.465099712473801, 37.802288360221802 ], [ -122.463702789389373, 37.803685283306223 ], [ -122.461529797924726, 37.803685283306223 ], [ -122.460443302192402, 37.802598787573892 ], [ -122.458115097051689, 37.802754001249937 ], [ -122.457028601319365, 37.801512291841561 ], [ -122.455010823530756, 37.801357078165516 ], [ -122.453924327798433, 37.801977932869704 ], [ -122.454855609854718, 37.804306138010404 ], [ -122.45345868677029, 37.804926992714591 ], [ -122.436540396081199, 37.80663434315111 ], [ -122.432660054180033, 37.805237420066689 ], [ -122.4318839857998, 37.803685283306223 ], [ -122.430797490067462, 37.803685283306223 ], [ -122.426917148166297, 37.804150924334358 ], [ -122.426606720814206, 37.806168702122967 ], [ -122.423657660969312, 37.806479129475058 ], [ -122.421795096856755, 37.807876052559479 ], [ -122.413723985702333, 37.808807334615764 ], [ -122.409378002773011, 37.808496907263667 ], [ -122.40642894292813, 37.807410411531343 ], [ -122.400686036914394, 37.803064428602035 ], [ -122.391218002675544, 37.792975539658997 ], [ -122.388113729154611, 37.788629556729688 ], [ -122.388113729154611, 37.781489727631538 ], [ -122.390907575323453, 37.777609385730365 ], [ -122.387958515478573, 37.775746821617808 ], [ -122.386872019746235, 37.770003915604079 ], [ -122.385785524013912, 37.76829656516756 ], [ -122.388113729154611, 37.763950582238252 ], [ -122.388113729154611, 37.757431607844289 ], [ -122.385940737689964, 37.751533488154514 ], [ -122.384388600929498, 37.751067847126372 ], [ -122.384388600929498, 37.750136565070093 ], [ -122.386561592394145, 37.749826137717996 ], [ -122.389045011210897, 37.747963573605439 ], [ -122.393080566788115, 37.749205283013815 ], [ -122.394477489872529, 37.748894855661717 ], [ -122.393856635168348, 37.745790582140785 ], [ -122.398047404421604, 37.743462377000085 ], [ -122.398047404421604, 37.742686308619852 ], [ -122.397271336041371, 37.741444599211476 ], [ -122.397426549717423, 37.739116394070777 ], [ -122.395253558252762, 37.738650753042634 ], [ -122.394167062520438, 37.739582035098913 ], [ -122.394787917224619, 37.741289385535431 ], [ -122.394322276196476, 37.743151949647988 ], [ -122.390597147971363, 37.74563536846474 ], [ -122.388734583858806, 37.74563536846474 ], [ -122.387027233422288, 37.744548872732409 ], [ -122.387337660774378, 37.741444599211476 ], [ -122.379732190648085, 37.739116394070777 ], [ -122.379576976972047, 37.73647776157798 ], [ -122.386251165042054, 37.733683915409138 ], [ -122.386561592394145, 37.732442206000762 ], [ -122.385940737689964, 37.731510923944484 ], [ -122.384078173577393, 37.731510923944484 ], [ -122.382836464169017, 37.730269214536108 ], [ -122.38066347270437, 37.730269214536108 ], [ -122.379421763295994, 37.729337932479829 ], [ -122.379266549619956, 37.724215881170288 ], [ -122.382215609464836, 37.723129385437957 ], [ -122.384233387253445, 37.724526308522378 ], [ -122.385630310337859, 37.724526308522378 ], [ -122.387648088126468, 37.722818958085867 ], [ -122.389200224886935, 37.722818958085867 ], [ -122.393235780464153, 37.724060667494243 ], [ -122.394011848844386, 37.724991949550521 ], [ -122.399754754858122, 37.726388872634942 ], [ -122.400375609562303, 37.724836735874476 ], [ -122.399444327506032, 37.719559470888882 ], [ -122.402238173674874, 37.718472975156558 ], [ -122.40642894292813, 37.719559470888882 ], [ -122.408136293364649, 37.721111607649348 ], [ -122.409688430125115, 37.720956393973303 ], [ -122.41139578056162, 37.719249043536792 ], [ -122.41325834467419, 37.719093829860746 ], [ -122.414655267758604, 37.720335539269115 ], [ -122.417604327603499, 37.720490752945167 ], [ -122.421795096856755, 37.722818958085867 ], [ -122.423968088321416, 37.722818958085867 ], [ -122.424433729349545, 37.720801180297258 ], [ -122.423657660969312, 37.716299983691904 ], [ -122.425520225081883, 37.715058274283528 ], [ -122.431107917419567, 37.715834342663761 ], [ -122.431263131095605, 37.714592633255386 ], [ -122.430331849039334, 37.713350923847017 ], [ -122.431107917419567, 37.711488359734453 ], [ -122.430176635363281, 37.709470581945844 ], [ -122.434677831968642, 37.707452804157235 ] ], [ [ -122.457028601319365, 37.735856906873792 ], [ -122.452837832066109, 37.735701693197747 ], [ -122.452837832066109, 37.737719470986356 ], [ -122.451440908981681, 37.739116394070777 ], [ -122.451906550009824, 37.740513317155191 ], [ -122.450199199573305, 37.741599812887522 ], [ -122.446008430320049, 37.739426821422867 ], [ -122.446474071348192, 37.736322547901935 ], [ -122.445698002967958, 37.734770411141461 ], [ -122.439799883278184, 37.733683915409138 ], [ -122.435298686672823, 37.734149556437281 ], [ -122.435298686672823, 37.736943402606123 ], [ -122.440265524306326, 37.738029898338446 ], [ -122.440265524306326, 37.742841522295898 ], [ -122.438558173869808, 37.742996735971943 ], [ -122.439489455926079, 37.743617590676131 ], [ -122.436695609757251, 37.743772804352176 ], [ -122.436540396081199, 37.747963573605439 ], [ -122.438092532841665, 37.747963573605439 ], [ -122.439489455926079, 37.749515710365905 ], [ -122.441507233714688, 37.749981351394048 ], [ -122.441817661066793, 37.753396052267071 ], [ -122.440110310630274, 37.753861693295214 ], [ -122.440110310630274, 37.755258616379635 ], [ -122.439023814897951, 37.755879471083823 ], [ -122.439179028573989, 37.757586821520334 ], [ -122.44135202003865, 37.758673317252665 ], [ -122.440110310630274, 37.759604599308943 ], [ -122.440731165334455, 37.761932804449643 ], [ -122.445542789291906, 37.760846308717319 ], [ -122.446318857672139, 37.759138958280801 ], [ -122.447405353404463, 37.758983744604755 ], [ -122.447870994432606, 37.76115673606941 ], [ -122.452682618390057, 37.761932804449643 ], [ -122.45345868677029, 37.76457143694244 ], [ -122.462150652628907, 37.762553659153831 ], [ -122.461995438952869, 37.761311949745455 ], [ -122.463081934685192, 37.760691095041274 ], [ -122.463392362037283, 37.759294171956853 ], [ -122.461840225276816, 37.757897248872432 ], [ -122.461374584248674, 37.755879471083823 ], [ -122.45997766116426, 37.756345112111966 ], [ -122.459046379107974, 37.75541383005568 ], [ -122.458115097051689, 37.75541383005568 ], [ -122.457339028671456, 37.756500325788011 ], [ -122.456252532939132, 37.756500325788011 ], [ -122.456252532939132, 37.75541383005568 ], [ -122.457804669699598, 37.753706479619169 ], [ -122.461219370572636, 37.753551265943123 ], [ -122.463547575713335, 37.758362889900567 ], [ -122.465099712473801, 37.757586821520334 ], [ -122.464478857769606, 37.753396052267071 ], [ -122.462150652628907, 37.751067847126372 ], [ -122.461529797924726, 37.748274000957529 ], [ -122.458425524403793, 37.747187505225206 ], [ -122.457028601319365, 37.745324941112642 ], [ -122.460132874840298, 37.745169727436597 ], [ -122.461685011600764, 37.743772804352176 ], [ -122.465254926149839, 37.743772804352176 ], [ -122.466496635558215, 37.739116394070777 ], [ -122.460443302192402, 37.735546479521702 ], [ -122.457028601319365, 37.735856906873792 ] ], [ [ -122.409688430125115, 37.738340325690537 ], [ -122.405497660871845, 37.739582035098913 ], [ -122.405497660871845, 37.743772804352176 ], [ -122.410309284829296, 37.743928018028221 ], [ -122.410930139533491, 37.738029898338446 ], [ -122.409688430125115, 37.738340325690537 ] ], [ [ -122.454545182502613, 37.768917419871748 ], [ -122.458425524403793, 37.768917419871748 ], [ -122.458270310727741, 37.766589214731049 ], [ -122.454079541474485, 37.766744428407094 ], [ -122.454545182502613, 37.768917419871748 ] ], [ [ -122.410774925857439, 37.779937590871064 ], [ -122.40611851557604, 37.782576223363861 ], [ -122.400375609562303, 37.778075026758508 ], [ -122.399133900153927, 37.778075026758508 ], [ -122.390597147971363, 37.784283573800373 ], [ -122.39044193429531, 37.788163915701546 ], [ -122.394787917224619, 37.791268189222478 ], [ -122.394011848844386, 37.792975539658997 ], [ -122.399133900153927, 37.796855881560163 ], [ -122.405963301899988, 37.796700667884117 ], [ -122.40642894292813, 37.798563231996674 ], [ -122.412482276293957, 37.799184086700862 ], [ -122.411240566885581, 37.791423402898523 ], [ -122.413413558350229, 37.791423402898523 ], [ -122.413413558350229, 37.789405625109922 ], [ -122.416207404519071, 37.788319129377591 ], [ -122.415431336138838, 37.783817932772237 ], [ -122.417138686575356, 37.78350750542014 ], [ -122.417759541279537, 37.785214855856658 ], [ -122.421795096856755, 37.784904428504561 ], [ -122.422571165236988, 37.78117930027944 ], [ -122.422105524208845, 37.779161522490831 ], [ -122.420553387448379, 37.778385454110598 ], [ -122.414189626730462, 37.778385454110598 ], [ -122.410774925857439, 37.779937590871064 ] ], [ [ -122.423657660969312, 37.791268189222478 ], [ -122.423347233617221, 37.790026479814102 ], [ -122.421174242152574, 37.788939984081779 ], [ -122.421019028476522, 37.787853488349448 ], [ -122.41822518230768, 37.788163915701546 ], [ -122.418069968631642, 37.789871266138057 ], [ -122.421019028476522, 37.789871266138057 ], [ -122.421639883180717, 37.791578616574576 ], [ -122.42272637891304, 37.792199471278764 ], [ -122.423812874645364, 37.792199471278764 ], [ -122.423657660969312, 37.791268189222478 ] ], [ [ -122.425209797729792, 37.788319129377591 ], [ -122.42536501140583, 37.789716052462012 ], [ -122.426606720814206, 37.789871266138057 ], [ -122.426141079786063, 37.788474343053636 ], [ -122.425209797729792, 37.788319129377591 ] ] ], [ [ [ -122.458580738079831, 37.755724257407778 ], [ -122.458115097051689, 37.756189898435913 ], [ -122.457804669699598, 37.756189898435913 ], [ -122.458425524403793, 37.755569043731725 ], [ -122.459046379107974, 37.755569043731725 ], [ -122.458580738079831, 37.755724257407778 ] ] ] ] } } +] +} diff --git a/geometries/may-mobility-grand-rapids-mi-25-july-2019-boundary.geojson b/geometries/may-mobility-grand-rapids-mi-25-july-2019-boundary.geojson new file mode 100644 index 0000000..ac608ef --- /dev/null +++ b/geometries/may-mobility-grand-rapids-mi-25-july-2019-boundary.geojson @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"name": "may-mobility-grand-rapids-mi-25-july-2019-boundary", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "fid": 1, "DN": 198 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -85.666332393295974, 42.967897703874492 ], [ -85.666289796507158, 42.964887638390699 ], [ -85.670367526938222, 42.961808888658076 ], [ -85.674770517473391, 42.963758562902811 ], [ -85.676200788284163, 42.96382013055679 ], [ -85.676752804573781, 42.963546282926998 ], [ -85.67658990808637, 42.963872075901804 ], [ -85.677377241108829, 42.96381777707267 ], [ -85.677703034083635, 42.963329087610461 ], [ -85.679793539005331, 42.963220489952185 ], [ -85.683920250019582, 42.963410535854159 ], [ -85.684761881871168, 42.963763478243536 ], [ -85.688589949325177, 42.967075706820765 ], [ -85.700019852858063, 42.967075706820765 ], [ -85.704662402749094, 42.971501061395251 ], [ -85.702816242558512, 42.973673014560646 ], [ -85.703440679093561, 42.976849496065036 ], [ -85.702544748412834, 42.977202438454412 ], [ -85.701784564804953, 42.977745426745763 ], [ -85.701811714219517, 42.978777104499322 ], [ -85.701458771830133, 42.981383448297798 ], [ -85.699531163395847, 42.982306528393089 ], [ -85.695730245356415, 42.982306528393089 ], [ -85.69564879711271, 42.9804603682025 ], [ -85.695132958235931, 42.979673035180049 ], [ -85.695078659406789, 42.978179817378837 ], [ -85.694752866431983, 42.977935472647737 ], [ -85.693232499216208, 42.97758253025836 ], [ -85.693232499216208, 42.975654921824066 ], [ -85.693096752143376, 42.975383427678395 ], [ -85.680255079052984, 42.974976186459884 ], [ -85.680255079052984, 42.974704692314212 ], [ -85.679304849543115, 42.973808761633485 ], [ -85.679033355397451, 42.973238623927571 ], [ -85.678408398422889, 42.971115222474388 ], [ -85.673949947635478, 42.971266634130593 ], [ -85.673440575996551, 42.973320072171269 ], [ -85.673141932436309, 42.973455819244109 ], [ -85.671458668733138, 42.97334722158584 ], [ -85.670508439223269, 42.97334722158584 ], [ -85.670426990979564, 42.972967129781892 ], [ -85.669748255615389, 42.972532739148818 ], [ -85.669150968494904, 42.971582509638957 ], [ -85.667685542098752, 42.970537101812937 ], [ -85.667620423423557, 42.970004427209851 ], [ -85.667563598564996, 42.968770894012572 ], [ -85.666332393295974, 42.967897703874492 ] ] ] ] } } +] +} diff --git a/geometries/may-mobility-grand-rapids-mi-26-july-2021-boundary.geojson b/geometries/may-mobility-grand-rapids-mi-26-july-2021-boundary.geojson new file mode 100644 index 0000000..aa1679e --- /dev/null +++ b/geometries/may-mobility-grand-rapids-mi-26-july-2021-boundary.geojson @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"name": "may-mobility-grand-rapids-mi-26-july-2021-boundary", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "fid": 1, "DN": 198 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -85.669341014396878, 42.956894676357976 ], [ -85.670291243906732, 42.957057572845386 ], [ -85.671214324002023, 42.957736308209569 ], [ -85.67371207014223, 42.957682009380434 ], [ -85.673793518385935, 42.957872055282408 ], [ -85.674825196139494, 42.958523641232027 ], [ -85.675992620965886, 42.959012330694236 ], [ -85.675911172722195, 42.960586996739146 ], [ -85.675721126820221, 42.961021387372227 ], [ -85.676182666867859, 42.961401479176175 ], [ -85.67726864345056, 42.962894696977379 ], [ -85.677132896377728, 42.963356237025025 ], [ -85.676752804573781, 42.963546282926998 ], [ -85.67658990808637, 42.963872075901804 ], [ -85.677377241108829, 42.96381777707267 ], [ -85.677703034083635, 42.963329087610461 ], [ -85.679793539005331, 42.963220489952185 ], [ -85.680933814417159, 42.962894696977379 ], [ -85.683051468753419, 42.962677501660842 ], [ -85.683920250019582, 42.963410535854159 ], [ -85.684761881871168, 42.963763478243536 ], [ -85.688589949325177, 42.967075706820765 ], [ -85.700019852858063, 42.967075706820765 ], [ -85.704662402749094, 42.971501061395251 ], [ -85.702816242558512, 42.973673014560646 ], [ -85.703440679093561, 42.976849496065036 ], [ -85.702544748412834, 42.977202438454412 ], [ -85.701784564804953, 42.977745426745763 ], [ -85.701811714219517, 42.978777104499322 ], [ -85.701458771830133, 42.981383448297798 ], [ -85.699531163395847, 42.982306528393089 ], [ -85.695730245356415, 42.982306528393089 ], [ -85.69564879711271, 42.9804603682025 ], [ -85.695132958235931, 42.979673035180049 ], [ -85.695078659406789, 42.978179817378837 ], [ -85.694752866431983, 42.977935472647737 ], [ -85.693232499216208, 42.97758253025836 ], [ -85.693232499216208, 42.975654921824066 ], [ -85.693096752143376, 42.975383427678395 ], [ -85.680255079052984, 42.974976186459884 ], [ -85.680255079052984, 42.974704692314212 ], [ -85.679304849543115, 42.973808761633485 ], [ -85.679033355397451, 42.973238623927571 ], [ -85.677621585839944, 42.972396992075979 ], [ -85.676535609257243, 42.971392463736983 ], [ -85.676399862184397, 42.971093820176741 ], [ -85.675341035016274, 42.971473911980688 ], [ -85.673522024240256, 42.972939980367329 ], [ -85.673440575996551, 42.973320072171269 ], [ -85.673141932436309, 42.973455819244109 ], [ -85.672218852341018, 42.973618715731511 ], [ -85.671458668733138, 42.97334722158584 ], [ -85.670508439223269, 42.97334722158584 ], [ -85.670426990979564, 42.972967129781892 ], [ -85.669748255615389, 42.972532739148818 ], [ -85.669150968494904, 42.971582509638957 ], [ -85.667684900108256, 42.970577981299961 ], [ -85.667576302449987, 42.969519154131831 ], [ -85.667331957718886, 42.969003315255051 ], [ -85.665485797528305, 42.967320051551873 ], [ -85.663992579727093, 42.966288373798307 ], [ -85.663422442021172, 42.965528190190419 ], [ -85.663015200802661, 42.965256696044747 ], [ -85.663042350217225, 42.964740857167968 ], [ -85.663911131483388, 42.964170719462047 ], [ -85.66380253382512, 42.963763478243536 ], [ -85.663368143192045, 42.963356237025025 ], [ -85.66328669494834, 42.962948995806514 ], [ -85.664101177385362, 42.961537226249007 ], [ -85.664644165676705, 42.9601254566915 ], [ -85.66592018816138, 42.959012330694236 ], [ -85.666381728209018, 42.958822284792262 ], [ -85.66700616474408, 42.958116400013516 ], [ -85.668146440155908, 42.957220469332789 ], [ -85.668607980203547, 42.956921825772547 ], [ -85.669341014396878, 42.956894676357976 ] ] ] ] } } +] +} diff --git a/geometries/may-mobility-grand-rapids-mn-september-2-2025-boundary.geojson b/geometries/may-mobility-grand-rapids-mn-september-2-2025-boundary.geojson new file mode 100644 index 0000000..56b10d6 --- /dev/null +++ b/geometries/may-mobility-grand-rapids-mn-september-2-2025-boundary.geojson @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"name": "may-mobility-grand-rapids-mn-september-2-2025-boundary", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "fid": 1, "DN": 162 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -93.518444910733024, 47.202243986765993 ], [ -93.518684083603276, 47.202243986765993 ], [ -93.519760361519374, 47.200211017368915 ], [ -93.52023870725985, 47.200211017368915 ], [ -93.520597466565221, 47.199732671628432 ], [ -93.52107581230571, 47.199732671628432 ], [ -93.524304646054006, 47.197938875101599 ], [ -93.532197350772051, 47.198417220842089 ], [ -93.536382876001312, 47.201885227460622 ], [ -93.538296258963271, 47.205592406949407 ], [ -93.538535431833509, 47.205592406949407 ], [ -93.539013777573999, 47.209180000003059 ], [ -93.538535431833509, 47.218148982637203 ], [ -93.539013777573999, 47.218866501247938 ], [ -93.540807574100825, 47.219464433423546 ], [ -93.542960129933022, 47.219584019858665 ], [ -93.547384828032534, 47.217431464026475 ], [ -93.549298210994479, 47.216953118285986 ], [ -93.553722909093992, 47.216953118285986 ], [ -93.557071329277406, 47.217790223331839 ], [ -93.558745539369113, 47.218986087683056 ], [ -93.559463057979841, 47.219105674118182 ], [ -93.56651865765204, 47.218148982637203 ], [ -93.570106250705692, 47.219344846988427 ], [ -93.573335084453987, 47.222334507866471 ], [ -93.573095911583749, 47.226520033095738 ], [ -93.569388732094964, 47.228194243187446 ], [ -93.566757830522278, 47.228313829622572 ], [ -93.561735200247156, 47.228313829622572 ], [ -93.559104298674484, 47.226878792401102 ], [ -93.552766217613012, 47.226998378836228 ], [ -93.552048699002285, 47.227835483882082 ], [ -93.55013531604034, 47.228194243187446 ], [ -93.549298210994479, 47.228672588927935 ], [ -93.54917862455936, 47.229748866844034 ], [ -93.548939451689122, 47.229748866844034 ], [ -93.548461105948633, 47.23106431763037 ], [ -93.547743587337891, 47.23154266337086 ], [ -93.545830204375946, 47.232021009111349 ], [ -93.542242611322294, 47.232021009111349 ], [ -93.539492123314488, 47.231183904065496 ], [ -93.53710039461204, 47.229509693973789 ], [ -93.535306598085214, 47.229031348233299 ], [ -93.533991147298877, 47.229150934668425 ], [ -93.533991147298877, 47.230705558325006 ], [ -93.534230320169115, 47.230705558325006 ], [ -93.534469493039367, 47.23154266337086 ], [ -93.536024116695955, 47.232618941286958 ], [ -93.538176672528138, 47.232858114157203 ], [ -93.540090055490097, 47.233934392073301 ], [ -93.553962081964244, 47.233934392073301 ], [ -93.555157946315461, 47.234891083554274 ], [ -93.555397119185699, 47.235847775035246 ], [ -93.555636292055951, 47.235847775035246 ], [ -93.555636292055951, 47.237043639386471 ], [ -93.554918773445209, 47.237402398691835 ], [ -93.545112685765218, 47.237402398691835 ], [ -93.5449930993301, 47.241109578180613 ], [ -93.544753926459848, 47.241109578180613 ], [ -93.54451475358961, 47.242903374707446 ], [ -93.536502462436431, 47.243142547577683 ], [ -93.536382876001312, 47.245175516974761 ], [ -93.541883852016923, 47.245055930539635 ], [ -93.544395167154477, 47.246251794890853 ], [ -93.544753926459848, 47.246849727066468 ], [ -93.545591031505708, 47.247328072806951 ], [ -93.545591031505708, 47.249241455768903 ], [ -93.544275580719358, 47.250796079425491 ], [ -93.541525092711552, 47.251513598036219 ], [ -93.536143703131074, 47.251513598036219 ], [ -93.535187011650095, 47.25091566586061 ], [ -93.531719005031562, 47.25091566586061 ], [ -93.531001486420834, 47.251752770906464 ], [ -93.528729344153518, 47.252948635257681 ], [ -93.528370584848147, 47.25354656743329 ], [ -93.530403554245225, 47.255101191089878 ], [ -93.530881899985701, 47.255101191089878 ], [ -93.532914869382779, 47.256416641876221 ], [ -93.533154042253017, 47.257373333357194 ], [ -93.53231693720717, 47.257971265532802 ], [ -93.529566449199365, 47.258808370578656 ], [ -93.526457201886203, 47.258808370578656 ], [ -93.524663405359362, 47.258330024838166 ], [ -93.522271676656928, 47.256655814746466 ], [ -93.521434571611081, 47.254503258914269 ], [ -93.52251084952718, 47.252589875952317 ], [ -93.524543818924244, 47.251513598036219 ], [ -93.524543818924244, 47.251274425165974 ], [ -93.521673744481319, 47.251154838730855 ], [ -93.520836639435473, 47.250198147249883 ], [ -93.518684083603276, 47.249839387944512 ], [ -93.514857317679372, 47.250078560814757 ], [ -93.509595514534013, 47.24840435072305 ], [ -93.50899758235839, 47.24792600498256 ], [ -93.508877995923271, 47.247088899936706 ], [ -93.508160477312543, 47.246251794890853 ], [ -93.506127507915465, 47.245055930539635 ], [ -93.506127507915465, 47.242903374707446 ], [ -93.505529575739857, 47.242544615402075 ], [ -93.504812057129129, 47.24230544253183 ], [ -93.502779087732051, 47.242544615402075 ], [ -93.498832735373028, 47.244577584799146 ], [ -93.497038938846202, 47.245055930539635 ], [ -93.496680179540846, 47.245414689844999 ], [ -93.495962660930104, 47.245414689844999 ], [ -93.487591610471569, 47.242903374707446 ], [ -93.484362776723287, 47.240631232440123 ], [ -93.484004017417917, 47.240272473134759 ], [ -93.484123603853035, 47.225682928049885 ], [ -93.484362776723287, 47.224008717958178 ], [ -93.484601949593525, 47.224008717958178 ], [ -93.484960708898896, 47.22281285360696 ], [ -93.487113264731079, 47.221497402820617 ], [ -93.489026647693038, 47.220899470645008 ], [ -93.490222512044255, 47.221138643515253 ], [ -93.495962660930104, 47.223171612912324 ], [ -93.499909013289127, 47.225324168744521 ], [ -93.505409989304738, 47.227357138141592 ], [ -93.506007921480347, 47.227835483882082 ], [ -93.506605853655955, 47.227835483882082 ], [ -93.50899758235839, 47.229031348233299 ], [ -93.510432619579859, 47.229509693973789 ], [ -93.513781039763273, 47.229390107538663 ], [ -93.513781039763273, 47.22843341605769 ], [ -93.507682131572054, 47.228313829622572 ], [ -93.507323372266683, 47.226041687355249 ], [ -93.503137847037422, 47.224487063698668 ], [ -93.502659501296932, 47.224128304393304 ], [ -93.502659501296932, 47.223649958652814 ], [ -93.50337701990766, 47.22281285360696 ], [ -93.504572884258891, 47.222454094301597 ], [ -93.504453297823758, 47.220062365599155 ], [ -93.504931643564248, 47.219584019858665 ], [ -93.507682131572054, 47.219464433423546 ], [ -93.513900626198392, 47.2203015384694 ], [ -93.514020212633511, 47.210375864354283 ], [ -93.514378971938882, 47.210256277919157 ], [ -93.517009873511569, 47.205114061208917 ], [ -93.517249046381806, 47.205114061208917 ], [ -93.518444910733024, 47.202243986765993 ] ] ] ] } } +] +} diff --git a/geometries/may-mobility-grand-rapids-mn-september-28-2022-boundary.geojson b/geometries/may-mobility-grand-rapids-mn-september-28-2022-boundary.geojson new file mode 100644 index 0000000..280a4b4 --- /dev/null +++ b/geometries/may-mobility-grand-rapids-mn-september-28-2022-boundary.geojson @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"name": "may-mobility-grand-rapids-mn-september-28-2022-boundary", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "fid": 1, "DN": 57961 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -93.529499239075861, 47.208682691673985 ], [ -93.530473554932271, 47.209061592284812 ], [ -93.531339613471303, 47.209927650823836 ], [ -93.531339613471303, 47.219454294753156 ], [ -93.567364309134248, 47.219513873226866 ], [ -93.567335171499678, 47.219508423411845 ], [ -93.568471873332157, 47.219724938046596 ], [ -93.569121417236431, 47.220590996585628 ], [ -93.569121417236431, 47.2237845874483 ], [ -93.568417744673468, 47.224650645987325 ], [ -93.567768200769194, 47.224758903304703 ], [ -93.531339613471303, 47.224758903304703 ], [ -93.531339613471303, 47.234989219796979 ], [ -93.535561648849068, 47.234989219796979 ], [ -93.5364277073881, 47.235584635042564 ], [ -93.536535964705479, 47.235801149677322 ], [ -93.536590093364168, 47.245436050924013 ], [ -93.541840573257033, 47.245490179582703 ], [ -93.543302047041635, 47.245706694217461 ], [ -93.544059848263288, 47.246031466169597 ], [ -93.544980035461009, 47.246951653367319 ], [ -93.545142421437077, 47.247384682636827 ], [ -93.545142421437077, 47.248521384469306 ], [ -93.544980035461009, 47.249116799714891 ], [ -93.543951590945909, 47.250199372888673 ], [ -93.541894701915723, 47.250903045451636 ], [ -93.533125859208056, 47.251444332038524 ], [ -93.529499239075861, 47.254096636314301 ], [ -93.529499239075861, 47.255503981440228 ], [ -93.52895795248898, 47.256099396685805 ], [ -93.52798363663257, 47.25658655461401 ], [ -93.526684548824022, 47.25658655461401 ], [ -93.526035004919748, 47.256315911320563 ], [ -93.525060689063338, 47.255395724122849 ], [ -93.525439589674164, 47.253771864362164 ], [ -93.525764361626301, 47.253392963751345 ], [ -93.528037765291259, 47.25182323264935 ], [ -93.528579051878154, 47.251606718014592 ], [ -93.530798326884408, 47.250091115571294 ], [ -93.530960712860477, 47.249712214960468 ], [ -93.52322031466791, 47.249658086301778 ], [ -93.522787285398394, 47.249658086301778 ], [ -93.522354256128878, 47.24949570032571 ], [ -93.521975355518052, 47.249008542397512 ], [ -93.521758840883294, 47.247980097882412 ], [ -93.51206981097792, 47.247980097882412 ], [ -93.510445951217235, 47.247601197271585 ], [ -93.509579892678204, 47.24684339604994 ], [ -93.509525764019514, 47.236504822240285 ], [ -93.509904664630341, 47.235638763701253 ], [ -93.510933109145441, 47.235043348455669 ], [ -93.512935869516937, 47.234826833820911 ], [ -93.513098255493006, 47.234826833820911 ], [ -93.521055168320331, 47.234772705162221 ], [ -93.521055168320331, 47.233690131988439 ], [ -93.520297367098692, 47.233040588084165 ], [ -93.519810209170487, 47.232932330766786 ], [ -93.518673507338008, 47.23222865820383 ], [ -93.518511121361939, 47.232336915521209 ], [ -93.51840286404456, 47.232012143569072 ], [ -93.518132220751113, 47.232174529545141 ], [ -93.518186349409802, 47.231958014910383 ], [ -93.516887261601255, 47.230929570395283 ], [ -93.516400103673064, 47.230117640514948 ], [ -93.516291846355685, 47.223946973424361 ], [ -93.507901904258844, 47.223946973424361 ], [ -93.507523003648018, 47.223730458789611 ], [ -93.506981717061123, 47.223026786226647 ], [ -93.506981717061123, 47.222214856346305 ], [ -93.50725236035457, 47.221619441100728 ], [ -93.507901904258844, 47.221186411831212 ], [ -93.516345975014374, 47.221186411831212 ], [ -93.516345975014374, 47.209927650823836 ], [ -93.516887261601255, 47.209278106919562 ], [ -93.518132220751113, 47.208845077650054 ], [ -93.518186349409802, 47.208682691673985 ], [ -93.529499239075861, 47.208682691673985 ] ] ] ] } } +] +} diff --git a/geometries/may-mobility-martinez-september-12-2024-boundary.geojson b/geometries/may-mobility-martinez-september-12-2024-boundary.geojson new file mode 100644 index 0000000..c76cc55 --- /dev/null +++ b/geometries/may-mobility-martinez-september-12-2024-boundary.geojson @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"name": "may-mobility-martinez-september-12-2024-boundary", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "fid": 1, "DN": 191 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -122.118995600476254, 37.985521255178163 ], [ -122.119366112390381, 37.984780231349895 ], [ -122.1216262350666, 37.983816900373149 ], [ -122.122107900554965, 37.9839651051388 ], [ -122.124516227996835, 37.987003302834694 ], [ -122.123441743445852, 37.98918932312808 ], [ -122.126257633993262, 37.990597268401785 ], [ -122.129592241220465, 37.996229049496613 ], [ -122.133556718701684, 37.99574738400824 ], [ -122.135261073506697, 37.996970073324874 ], [ -122.135520431846587, 37.99989711744653 ], [ -122.134223640147127, 38.006640434283753 ], [ -122.133667872275922, 38.007381458112022 ], [ -122.133667872275922, 38.007900174791807 ], [ -122.133000950830493, 38.008492993854418 ], [ -122.141300417707072, 38.017088870262313 ], [ -122.14141157128131, 38.020460528680921 ], [ -122.138669783116725, 38.020868091786468 ], [ -122.140707598644454, 38.025462439521718 ], [ -122.137113633077362, 38.026055258584336 ], [ -122.134594152061254, 38.022127832294522 ], [ -122.129444036454814, 38.019200788172867 ], [ -122.128628910243719, 38.018274508387535 ], [ -122.128591859052307, 38.015829129754259 ], [ -122.1263687875675, 38.013494904695214 ], [ -122.130592623388623, 38.011049526061939 ], [ -122.126035326844786, 38.006455178326689 ], [ -122.124886739910963, 38.004824925904501 ], [ -122.124034562508456, 38.002638905611114 ], [ -122.117772911159605, 38.001045704380346 ], [ -122.115809198014702, 38.000008271020768 ], [ -122.116068556354605, 37.999378400766744 ], [ -122.114364201549591, 38.000045322212181 ], [ -122.109065881177486, 37.99900788885261 ], [ -122.105768325141696, 37.996377254262264 ], [ -122.109065881177486, 37.991004831507333 ], [ -122.110362672876946, 37.990300858870484 ], [ -122.120699955281268, 37.990337910061896 ], [ -122.122478412469107, 37.989708039807866 ], [ -122.118995600476254, 37.985521255178163 ] ], [ [ -122.116105607546018, 37.999378400766744 ], [ -122.116105607546018, 37.999341349575332 ], [ -122.116068556354605, 37.999341349575332 ], [ -122.116068556354605, 37.999378400766744 ], [ -122.116105607546018, 37.999378400766744 ] ] ], [ [ [ -122.129444036454814, 37.993820722054743 ], [ -122.129481087646226, 37.993820722054743 ], [ -122.129481087646226, 37.99422828516029 ], [ -122.129444036454814, 37.99422828516029 ], [ -122.129444036454814, 37.993820722054743 ] ] ] ] } } +] +} diff --git a/geometries/may-mobility-peachtree-corners-october-7-2024-boundary.geojson b/geometries/may-mobility-peachtree-corners-october-7-2024-boundary.geojson new file mode 100644 index 0000000..7f3a479 --- /dev/null +++ b/geometries/may-mobility-peachtree-corners-october-7-2024-boundary.geojson @@ -0,0 +1,8 @@ +{ +"type": "FeatureCollection", +"name": "may-mobility-peachtree-corners-october-7-2024-boundary", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "fid": 1, "DN": 0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -84.231719471144785, 33.953602611711553 ], [ -84.234632677117332, 33.954237541218383 ], [ -84.234352561158431, 33.956086306547114 ], [ -84.234352561158431, 33.956515817684092 ], [ -84.233680282857065, 33.95690798002655 ], [ -84.233082702144756, 33.957505560738866 ], [ -84.232354400651616, 33.958065792656662 ], [ -84.230748402487265, 33.958159164642957 ], [ -84.230169496172209, 33.958271211026521 ], [ -84.227984591692802, 33.959783837204569 ], [ -84.226061128775044, 33.961856695300419 ], [ -84.223166597199764, 33.963649437437368 ], [ -84.220776274350499, 33.964620506094882 ], [ -84.21963713611764, 33.965572900355134 ], [ -84.219599787323119, 33.96566627234143 ], [ -84.221000367117611, 33.968037920793435 ], [ -84.219674484912161, 33.970260174067363 ], [ -84.218666067460134, 33.970334871656405 ], [ -84.216331767802643, 33.970334871656405 ], [ -84.214053491336941, 33.969102361437251 ], [ -84.213493259419138, 33.966506620218126 ], [ -84.213474585021885, 33.964956645245557 ], [ -84.214539025665701, 33.96432171573872 ], [ -84.216014303049221, 33.96432171573872 ], [ -84.216555860569756, 33.96407894857434 ], [ -84.2171907900766, 33.963313298286685 ], [ -84.217489580432755, 33.961931392889454 ], [ -84.218292579514937, 33.961016347423723 ], [ -84.219767856898457, 33.960381417916885 ], [ -84.221896738186089, 33.960512138697702 ], [ -84.222867806843595, 33.958644698971717 ], [ -84.223353341172356, 33.957337491163528 ], [ -84.223876224295637, 33.956964003218324 ], [ -84.224772595364115, 33.95644112009505 ], [ -84.227872545309253, 33.954947168314263 ], [ -84.230225519363998, 33.954032122848524 ], [ -84.231514052774926, 33.953583937314292 ], [ -84.231719471144785, 33.953602611711553 ] ] ] ] } } +] +} diff --git a/import-csv.js b/import-csv.js new file mode 100644 index 0000000..010022b --- /dev/null +++ b/import-csv.js @@ -0,0 +1,246 @@ +#!/usr/bin/env node + +/** + * Import CSV to Supabase + * + * This script reads events.csv and syncs it to the Supabase database. + * It will create/update events in the database based on the CSV. + * + * Usage: + * node import-csv.js # Import to production + * STAGING=true node import-csv.js # Import to staging + */ + +import { createClient } from '@supabase/supabase-js' +import { config } from 'dotenv' +import fs from 'fs' +import { parse } from 'csv-parse/sync' + +// Load .env file +if (!process.env.GITHUB_ACTIONS) { + config() +} + +const supabaseUrl = process.env.SUPABASE_URL +const supabaseServiceKey = process.env.SUPABASE_SERVICE_KEY || process.env.SUPABASE_ANON_KEY + +if (!supabaseUrl || !supabaseServiceKey) { + console.error('āŒ Missing required environment variables!') + console.error('Please set SUPABASE_URL and SUPABASE_SERVICE_KEY in .env') + process.exit(1) +} + +const isStaging = process.env.STAGING === 'true' +const environment = isStaging ? 'staging' : 'production' +const eventsTable = isStaging ? 'av_events_staging' : 'av_events' + +console.log(`šŸŒ Environment: ${environment.toUpperCase()}`) +console.log(`šŸ“‹ Events table: ${eventsTable}`) +console.log('') + +const supabase = createClient(supabaseUrl, supabaseServiceKey) + +// Map CSV column names to database field names +const FIELD_MAPPING = { + 'date': 'event_date', + 'event_type': 'event_type', + 'company': 'company', + 'city': 'city', + 'geometry_file': 'geometry_name', + 'vehicles': 'vehicle_types', + 'platform': 'platform', + 'fares': 'fares', + 'direct_booking': 'direct_booking', + 'service_model': 'service_model', + 'supervision': 'supervision', + 'access': 'access', + 'fleet_partner': 'fleet_partner', + 'company_link': 'company_link', + 'booking_platform_link': 'booking_platform_link', + 'source_url': 'event_url', + 'notes': 'notes' +} + +function csvRowToEvent(row) { + const company = row.company?.trim() + const city = row.city?.trim() + const date = row.date?.trim() + + if (!company || !city || !date) { + throw new Error('Missing required fields: company, city, or date') + } + + // Create aggregate_id (unique identifier for the service) + const aggregateId = `${company.toLowerCase().replace(/\s+/g, '-')}-${city.toLowerCase().replace(/\s+/g, '-')}` + + // Build event_data object with all service attributes + const eventData = { + name: city, // city becomes "name" in event_data + company: company // company goes in event_data too + } + + // Determine which fields to include in event_data based on event type + const isUpdateEvent = [ + 'fares_policy_changed', + 'access_policy_changed', + 'supervision_updated', + 'platform_updated', + 'vehicle_types_updated', + 'fleet_partner_changed', + 'service_model_updated', + 'geometry_updated' + ].includes(row.event_type) + + const isServiceCreated = row.event_type === 'service_created' + + // Map all CSV fields to event_data + Object.keys(row).forEach(csvKey => { + const value = row[csvKey]?.trim() + if (!value) return // Skip empty values + + const dbKey = FIELD_MAPPING[csvKey] + if (dbKey && !['event_date', 'event_type'].includes(dbKey)) { + // Convert geometry_file to geometry_name format (without .geojson extension) + if (csvKey === 'geometry_file') { + eventData.geometry_name = value.replace(/\.geojson$/, '') + } + // For service_created, include all fields + // For update events, skip the field being updated (will be added as new_* below) + // Always include company, city, notes, and source_url + else if (isServiceCreated || + csvKey === 'company' || + csvKey === 'city' || + csvKey === 'notes' || + csvKey === 'source_url') { + eventData[dbKey] = value + } + // For update events, only include fields that aren't being updated + else if (isUpdateEvent) { + const isFieldBeingUpdated = + (row.event_type === 'fares_policy_changed' && csvKey === 'fares') || + (row.event_type === 'access_policy_changed' && csvKey === 'access') || + (row.event_type === 'supervision_updated' && csvKey === 'supervision') || + (row.event_type === 'platform_updated' && csvKey === 'platform') || + (row.event_type === 'vehicle_types_updated' && csvKey === 'vehicles') || + (row.event_type === 'fleet_partner_changed' && csvKey === 'fleet_partner') || + (row.event_type === 'service_model_updated' && csvKey === 'service_model') + + if (!isFieldBeingUpdated) { + eventData[dbKey] = value + } + } + } + }) + + // Add the new_* fields for update events + if (row.event_type === 'fares_policy_changed' && row.fares) { + eventData.new_fares = row.fares + } else if (row.event_type === 'access_policy_changed' && row.access) { + eventData.new_access = row.access + } else if (row.event_type === 'supervision_updated' && row.supervision) { + eventData.new_supervision = row.supervision + } else if (row.event_type === 'platform_updated' && row.platform) { + eventData.new_platform = row.platform + } else if (row.event_type === 'vehicle_types_updated' && row.vehicles) { + eventData.new_vehicle_types = row.vehicles + } else if (row.event_type === 'fleet_partner_changed' && row.fleet_partner) { + eventData.new_fleet_partner = row.fleet_partner + } else if (row.event_type === 'service_model_updated' && row.service_model) { + eventData.new_service_model = row.service_model + } + + return { + aggregate_id: aggregateId, + aggregate_type: 'service_area', + event_date: date, + event_type: row.event_type, + event_data: eventData + } +} + +async function importCSV() { + console.log('šŸ“– Reading events.csv...') + + try { + // Read CSV file + const csvContent = fs.readFileSync('./events.csv', 'utf-8') + const records = parse(csvContent, { + columns: true, + skip_empty_lines: true, + trim: true, + relax_column_count: true // Allow rows with different column counts + }) + + console.log(` Found ${records.length} events in CSV\n`) + + // Convert CSV rows to event objects + console.log('šŸ”„ Converting CSV to event format...') + const events = records.map((row, index) => { + try { + return csvRowToEvent(row) + } catch (error) { + console.error(` āŒ Error on row ${index + 2}:`, error.message) + throw error + } + }) + console.log(` āœ… Converted ${events.length} events\n`) + + // Clear existing events in the table + console.log(`šŸ—‘ļø Clearing ${eventsTable} table...`) + const { error: deleteError } = await supabase + .from(eventsTable) + .delete() + .neq('id', '00000000-0000-0000-0000-000000000000') + + if (deleteError) throw deleteError + console.log(' āœ… Table cleared\n') + + // Insert events in batches + console.log('šŸ“„ Importing events to database...') + const BATCH_SIZE = 50 + let imported = 0 + + for (let i = 0; i < events.length; i += BATCH_SIZE) { + const batch = events.slice(i, i + BATCH_SIZE) + const { error: insertError } = await supabase + .from(eventsTable) + .insert(batch) + + if (insertError) { + console.error(` āŒ Error inserting batch ${Math.floor(i/BATCH_SIZE) + 1}:`, insertError) + throw insertError + } + + imported += batch.length + console.log(` Progress: ${imported}/${events.length} events`) + } + + console.log(` āœ… Imported ${imported} events\n`) + + // Verify count + console.log('šŸ” Verifying import...') + const { count, error: countError } = await supabase + .from(eventsTable) + .select('id', { count: 'exact', head: true }) + + if (countError) throw countError + + console.log(` CSV events: ${events.length}`) + console.log(` Database events: ${count}\n`) + + if (count === events.length) { + console.log('āœ… Import complete! All events synced successfully.') + console.log('\nšŸ“ Next steps:') + console.log(` Run: ${isStaging ? 'STAGING=true ' : ''}node rebuild-cache.js`) + } else { + console.error('āŒ Count mismatch! Import may have failed.') + process.exit(1) + } + + } catch (error) { + console.error('āŒ Import failed:', error) + process.exit(1) + } +} + +importCSV() diff --git a/package-lock.json b/package-lock.json index c5ef78b..b3b759d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,8 @@ "": { "dependencies": { "@supabase/supabase-js": "^2.58.0", + "@turf/area": "^7.2.0", + "csv-parse": "^6.1.0", "dotenv": "^16.4.5" } }, @@ -83,6 +85,53 @@ "@supabase/storage-js": "2.12.2" } }, + "node_modules/@turf/area": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-7.2.0.tgz", + "integrity": "sha512-zuTTdQ4eoTI9nSSjerIy4QwgvxqwJVciQJ8tOPuMHbXJ9N/dNjI7bU8tasjhxas/Cx3NE9NxVHtNpYHL0FSzoA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@turf/meta": "^7.2.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.2.0.tgz", + "integrity": "sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==", + "license": "MIT", + "dependencies": { + "@types/geojson": "^7946.0.10", + "tslib": "^2.8.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/meta": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.2.0.tgz", + "integrity": "sha512-igzTdHsQc8TV1RhPuOLVo74Px/hyPrVgVOTgjWQZzt3J9BVseCdpfY/0cJBdlSRI4S/yTmmHl7gAqjhpYH5Yaw==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.2.0", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.6.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz", @@ -107,6 +156,12 @@ "@types/node": "*" } }, + "node_modules/csv-parse": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-6.1.0.tgz", + "integrity": "sha512-CEE+jwpgLn+MmtCpVcPtiCZpVtB6Z2OKPTr34pycYYoL7sxdOkXDdQ4lRiw6ioC0q6BLqhc6cKweCVvral8yhw==", + "license": "MIT" + }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", @@ -125,6 +180,12 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/undici-types": { "version": "7.13.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", diff --git a/package.json b/package.json index 53e7f95..9f8edb8 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,8 @@ "type": "module", "dependencies": { "@supabase/supabase-js": "^2.58.0", + "@turf/area": "^7.2.0", + "csv-parse": "^6.1.0", "dotenv": "^16.4.5" } } diff --git a/rebuild-cache.js b/rebuild-cache.js index 2138f8c..0792f49 100644 --- a/rebuild-cache.js +++ b/rebuild-cache.js @@ -19,6 +19,7 @@ import { createClient } from '@supabase/supabase-js' import { config } from 'dotenv' import { execSync } from 'child_process' +import area from '@turf/area' // Load .env file for local development (GitHub Actions sets env vars directly) if (!process.env.GITHUB_ACTIONS) { @@ -141,11 +142,36 @@ async function rebuildCache() { console.log(`āœ… Geometries: ${successful.length} loaded, ${failed.length} failed`) + // Create geometry lookup map with calculated areas + const geometryMap = new Map() + geometriesWithData.forEach(geo => { + if (geo.geojson_data) { + // Calculate area in square meters and convert to square miles + const areaSquareMeters = area(geo.geojson_data) + const areaSquareMiles = areaSquareMeters / 2589988.11 // 1 sq mi = 2,589,988.11 sq m + geometryMap.set(geo.geometry_name, { + geojson: geo.geojson_data, + area_square_miles: Math.round(areaSquareMiles * 100) / 100 // Round to 2 decimals + }) + } + }) + // STEP 3: Process service areas console.log('āš™ļø Processing service areas...') - const serviceAreas = buildServiceAreasFromEvents(events) + const serviceAreas = buildServiceAreasFromEvents(events, geometryMap) // STEP 4: Create final data structure + // Transform geometries to camelCase for frontend + const geometriesForFrontend = geometriesWithData.map(geo => ({ + geometryName: geo.geometry_name, + displayName: geo.display_name, + fileSize: geo.file_size, + createdAt: geo.created_at, + storageUrl: geo.storage_url, + geojsonData: geo.geojson_data, + error: geo.error + })); + const cacheData = { metadata: { generated_at: new Date().toISOString(), @@ -160,7 +186,7 @@ async function rebuildCache() { } }, events: events, - geometries: geometriesWithData, + geometries: geometriesForFrontend, service_areas: serviceAreas, date_range: { start: '2017-04-25T00:00:00+00:00', @@ -222,8 +248,45 @@ async function rebuildCache() { } } +// Helper function to transform snake_case to camelCase for frontend compatibility +function transformEventData(eventData) { + const transformed = { ...eventData } + + // Transform snake_case fields to camelCase + if (transformed.direct_booking !== undefined) { + transformed.directBooking = transformed.direct_booking + delete transformed.direct_booking + } + if (transformed.vehicle_types !== undefined) { + transformed.vehicleTypes = transformed.vehicle_types + delete transformed.vehicle_types + } + if (transformed.fleet_partner !== undefined) { + transformed.fleetPartner = transformed.fleet_partner + delete transformed.fleet_partner + } + if (transformed.geometry_name !== undefined) { + transformed.geojsonPath = transformed.geometry_name + delete transformed.geometry_name + } + if (transformed.service_model !== undefined) { + transformed.serviceModel = transformed.service_model + delete transformed.service_model + } + if (transformed.company_link !== undefined) { + transformed.companyLink = transformed.company_link + delete transformed.company_link + } + if (transformed.booking_platform_link !== undefined) { + transformed.bookingPlatformLink = transformed.booking_platform_link + delete transformed.booking_platform_link + } + + return transformed +} + // Service area processing logic -function buildServiceAreasFromEvents(events) { +function buildServiceAreasFromEvents(events, geometryMap) { const currentServiceStates = new Map() const allStates = [] @@ -237,14 +300,22 @@ function buildServiceAreasFromEvents(events) { const currentState = currentServiceStates.get(serviceId) || { isActive: false } if (event.event_type === 'service_created') { + const transformedData = transformEventData(event.event_data) + const geojsonPath = transformedData.geojsonPath || event.event_data.geometry_name + + // Add calculated area from geometry + const geometryData = geometryMap.get(geojsonPath) + const areaSquareMiles = geometryData?.area_square_miles || null + const newState = { - ...event.event_data, + ...transformedData, id: `${serviceId}-${event.event_date}`, serviceId: serviceId, effectiveDate: eventDate, lastUpdated: eventDate, isActive: true, - geojsonPath: event.event_data.geometry_name + geojsonPath: geojsonPath, + area_square_miles: areaSquareMiles } currentServiceStates.set(serviceId, newState) @@ -266,9 +337,14 @@ function buildServiceAreasFromEvents(events) { const lastStateDate = lastState ? new Date(lastState.effectiveDate).getTime() : 0 const currentEventDate = eventDate.getTime() + const newGeojsonPath = event.event_data.geometry_name || event.event_data.new_geometry_name || lastState?.geojsonPath + const geometryData = geometryMap.get(newGeojsonPath) + const areaSquareMiles = geometryData?.area_square_miles || null + if (lastState && lastStateDate === currentEventDate) { // Same date - update existing state in place - lastState.geojsonPath = event.event_data.geometry_name || event.event_data.new_geometry_name || lastState.geojsonPath + lastState.geojsonPath = newGeojsonPath + lastState.area_square_miles = areaSquareMiles lastState.lastUpdated = eventDate currentServiceStates.set(serviceId, lastState) } else { @@ -278,7 +354,8 @@ function buildServiceAreasFromEvents(events) { id: `${serviceId}-${event.event_date}`, effectiveDate: eventDate, lastUpdated: eventDate, - geojsonPath: event.event_data.geometry_name || event.event_data.new_geometry_name || currentState.geojsonPath + geojsonPath: newGeojsonPath, + area_square_miles: areaSquareMiles } if (lastState) { @@ -290,9 +367,9 @@ function buildServiceAreasFromEvents(events) { } } - } else if (['service_updated', 'fares_policy_changed', 'access_policy_changed', 'vehicle_types_updated', 'platform_updated', 'supervision_updated', 'fleet_partner_changed'].includes(event.event_type)) { + } else if (['service_updated', 'fares_policy_changed', 'access_policy_changed', 'vehicle_types_updated', 'platform_updated', 'supervision_updated', 'service_model_updated', 'fleet_partner_changed'].includes(event.event_type)) { if (currentState.isActive) { - const shouldCreateNewState = ['fares_policy_changed', 'access_policy_changed', 'vehicle_types_updated', 'platform_updated', 'supervision_updated', 'fleet_partner_changed'].includes(event.event_type) + const shouldCreateNewState = ['fares_policy_changed', 'access_policy_changed', 'vehicle_types_updated', 'platform_updated', 'supervision_updated', 'service_model_updated', 'fleet_partner_changed'].includes(event.event_type) if (shouldCreateNewState) { // Check if last state has same effectiveDate - if so, update in place instead of creating new state @@ -301,19 +378,21 @@ function buildServiceAreasFromEvents(events) { const currentEventDate = eventDate.getTime() if (lastState && lastStateDate === currentEventDate) { - // Same date - update existing state in place + // Same date - update existing state in place (using camelCase for frontend) if (event.event_type === 'fares_policy_changed') { lastState.fares = event.event_data.new_fares } else if (event.event_type === 'access_policy_changed') { lastState.access = event.event_data.new_access } else if (event.event_type === 'vehicle_types_updated') { - lastState.vehicleTypes = event.event_data.new_vehicle_types + lastState.vehicleTypes = event.event_data.new_vehicle_types || event.event_data.vehicle_types } else if (event.event_type === 'platform_updated') { lastState.platform = event.event_data.new_platform } else if (event.event_type === 'supervision_updated') { lastState.supervision = event.event_data.new_supervision + } else if (event.event_type === 'service_model_updated') { + lastState.serviceModel = event.event_data.new_service_model } else if (event.event_type === 'fleet_partner_changed') { - lastState.fleet_partner = event.event_data.new_fleet_partner + lastState.fleetPartner = event.event_data.new_fleet_partner || event.event_data.fleet_partner } lastState.lastUpdated = eventDate currentServiceStates.set(serviceId, lastState) @@ -326,19 +405,21 @@ function buildServiceAreasFromEvents(events) { lastUpdated: eventDate } - // Apply field updates + // Apply field updates (using camelCase for frontend) if (event.event_type === 'fares_policy_changed') { newState.fares = event.event_data.new_fares } else if (event.event_type === 'access_policy_changed') { newState.access = event.event_data.new_access } else if (event.event_type === 'vehicle_types_updated') { - newState.vehicleTypes = event.event_data.new_vehicle_types + newState.vehicleTypes = event.event_data.new_vehicle_types || event.event_data.vehicle_types } else if (event.event_type === 'platform_updated') { newState.platform = event.event_data.new_platform } else if (event.event_type === 'supervision_updated') { newState.supervision = event.event_data.new_supervision + } else if (event.event_type === 'service_model_updated') { + newState.serviceModel = event.event_data.new_service_model } else if (event.event_type === 'fleet_partner_changed') { - newState.fleet_partner = event.event_data.new_fleet_partner + newState.fleetPartner = event.event_data.new_fleet_partner || event.event_data.fleet_partner } if (lastState) { diff --git a/sync-geometries-table.js b/sync-geometries-table.js new file mode 100644 index 0000000..55a6fc1 --- /dev/null +++ b/sync-geometries-table.js @@ -0,0 +1,132 @@ +#!/usr/bin/env node + +/** + * Sync Geometries Table with Storage Bucket + * + * This script ensures all GeoJSON files in the storage bucket are registered + * in the service_area_geometries table with proper metadata. + * + * Usage: + * STAGING=true node sync-geometries-table.js (syncs staging) + * node sync-geometries-table.js (syncs production) + */ + +import { createClient } from '@supabase/supabase-js' +import { config } from 'dotenv' + +// Load .env file +if (!process.env.GITHUB_ACTIONS) { + config() +} + +const supabaseUrl = process.env.SUPABASE_URL +const supabaseServiceKey = process.env.SUPABASE_SERVICE_KEY || process.env.SUPABASE_ANON_KEY + +if (!supabaseUrl || !supabaseServiceKey) { + console.error('āŒ Missing required environment variables!') + console.error('Please set SUPABASE_URL and SUPABASE_SERVICE_KEY in .env') + process.exit(1) +} + +const supabase = createClient(supabaseUrl, supabaseServiceKey) + +// Determine environment +const isStaging = process.env.STAGING === 'true' +const bucket = isStaging ? 'staging-service-area-boundaries' : 'service-area-boundaries' +const table = isStaging ? 'service_area_geometries_staging' : 'service_area_geometries' +const env = isStaging ? 'STAGING' : 'PRODUCTION' + +console.log(`šŸŒ Environment: ${env}`) +console.log(`šŸ“¦ Bucket: ${bucket}`) +console.log(`šŸ“‹ Table: ${table}\n`) + +async function syncGeometriesTable() { + // Step 1: List all files in storage bucket + console.log('šŸ“” Fetching files from storage bucket...') + const { data: files, error: listError } = await supabase.storage + .from(bucket) + .list('', { limit: 1000 }) + + if (listError) { + console.error('āŒ Error listing files:', listError) + process.exit(1) + } + + const geojsonFiles = files.filter(f => f.name.endsWith('.geojson')) + console.log(` Found ${geojsonFiles.length} geometry files in bucket\n`) + + // Step 2: Get existing entries in database table + console.log('šŸ“Š Fetching existing entries from database...') + const { data: existingEntries, error: dbError } = await supabase + .from(table) + .select('geometry_name') + + if (dbError) { + console.error('āŒ Error fetching from database:', dbError) + process.exit(1) + } + + const existingNames = new Set(existingEntries?.map(e => e.geometry_name) || []) + console.log(` Found ${existingNames.size} existing entries in table\n`) + + // Step 3: Insert missing entries + console.log('šŸ“ Syncing table with storage...') + + let inserted = 0 + let skipped = 0 + let failed = 0 + + for (const file of geojsonFiles) { + const geometryName = file.name.replace('.geojson', '') + + if (existingNames.has(geometryName)) { + console.log(` ā­ļø ${geometryName} (already exists)`) + skipped++ + continue + } + + // Create public URL + const { data: { publicUrl } } = supabase.storage + .from(bucket) + .getPublicUrl(file.name) + + // Insert into table + const { error: insertError } = await supabase + .from(table) + .insert({ + geometry_name: geometryName, + display_name: geometryName.replace(/-/g, ' '), + storage_url: publicUrl, + file_size: file.metadata?.size || 0, + created_at: file.created_at || new Date().toISOString() + }) + + if (insertError) { + console.error(` āŒ ${geometryName}: ${insertError.message}`) + failed++ + } else { + console.log(` āœ… ${geometryName} (inserted)`) + inserted++ + } + } + + console.log('\nšŸ“Š Sync Summary:') + console.log(` āœ… Inserted: ${inserted}`) + console.log(` ā­ļø Skipped (existing): ${skipped}`) + console.log(` āŒ Failed: ${failed}`) + console.log(` šŸ“ Total files: ${geojsonFiles.length}`) + + if (failed === 0) { + console.log('\nāœ… Geometries table synced successfully!') + console.log('\nšŸ“ Next steps:') + console.log(` Run: ${isStaging ? 'STAGING=true ' : ''}node rebuild-cache.js`) + } else { + console.log('\nāš ļø Some entries failed to insert. Please check errors above.') + process.exit(1) + } +} + +syncGeometriesTable().catch(error => { + console.error('āŒ Fatal error:', error) + process.exit(1) +}) diff --git a/tests/test_validation.py b/tests/test_validation.py index 1a93174..945e8c2 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -40,7 +40,7 @@ def test_csv_headers(csv_file): """Test that CSV has correct headers.""" expected_headers = [ 'date', 'event_type', 'company', 'city', 'geometry_file', - 'vehicles', 'platform', 'fares', 'direct_booking', 'supervision', + 'vehicles', 'platform', 'fares', 'direct_booking', 'service_model', 'supervision', 'access', 'fleet_partner', 'source_url', 'notes' ] @@ -87,7 +87,7 @@ def test_event_types(csv_file): valid_event_types = [ 'service_created', 'service_ended', 'geometry_updated', 'vehicle_types_updated', 'supervision_updated', 'fares_policy_changed', - 'access_policy_changed', 'platform_updated', 'fleet_partner_changed' + 'access_policy_changed', 'service_model_updated', 'platform_updated', 'fleet_partner_changed' ] errors = [] @@ -112,7 +112,7 @@ def test_service_created_events(csv_file): for row_num, row in enumerate(reader, start=2): if row.get('event_type') == 'service_created': required_fields = ['company', 'city', 'vehicles', 'platform', 'fares', - 'direct_booking', 'supervision', 'access'] + 'direct_booking', 'service_model', 'supervision', 'access'] for field in required_fields: if not row.get(field, '').strip(): errors.append(f"Row {row_num}: service_created event missing required field: {field}") @@ -209,6 +209,11 @@ def test_service_attribute_values(csv_file): if direct_booking and direct_booking not in ['Yes', 'No']: errors.append(f"Row {row_num}: direct_booking must be 'Yes' or 'No', got: {direct_booking}") + service_model = row.get('service_model', '').strip() + valid_service_model = ['Flexible', 'Stop-to-Stop'] + if service_model and service_model not in valid_service_model: + errors.append(f"Row {row_num}: service_model must be one of {valid_service_model}, got: {service_model}") + supervision = row.get('supervision', '').strip() valid_supervision = ['Autonomous', 'Safety Driver', 'Safety Attendant'] if supervision and supervision not in valid_supervision: diff --git a/upload-geometries.js b/upload-geometries.js new file mode 100644 index 0000000..5a48148 --- /dev/null +++ b/upload-geometries.js @@ -0,0 +1,117 @@ +#!/usr/bin/env node + +/** + * Upload Geometry Files to Supabase Storage + * + * This script uploads all GeoJSON files from the local geometries/ folder to Supabase storage. + * It compares with existing files and only uploads new or changed files. + * + * Usage: + * STAGING=true node upload-geometries.js (uploads to staging) + * node upload-geometries.js (uploads to production) + */ + +import { createClient } from '@supabase/supabase-js' +import { config } from 'dotenv' +import { readFileSync, readdirSync } from 'fs' +import { join } from 'path' + +// Load .env file +if (!process.env.GITHUB_ACTIONS) { + config() +} + +const supabaseUrl = process.env.SUPABASE_URL +const supabaseServiceKey = process.env.SUPABASE_SERVICE_KEY || process.env.SUPABASE_ANON_KEY + +if (!supabaseUrl || !supabaseServiceKey) { + console.error('āŒ Missing required environment variables!') + console.error('Please set SUPABASE_URL and SUPABASE_SERVICE_KEY in .env') + process.exit(1) +} + +const supabase = createClient(supabaseUrl, supabaseServiceKey) + +// Determine environment +const isStaging = process.env.STAGING === 'true' +const bucket = isStaging ? 'staging-service-area-boundaries' : 'service-area-boundaries' +const env = isStaging ? 'STAGING' : 'PRODUCTION' + +console.log(`šŸŒ Environment: ${env}`) +console.log(`šŸ“¦ Bucket: ${bucket}\n`) + +async function uploadGeometries() { + console.log('šŸ“‚ Reading local geometry files...') + + const geometriesPath = './geometries' + const localFiles = readdirSync(geometriesPath).filter(f => f.endsWith('.geojson')) + + console.log(` Found ${localFiles.length} local geometry files\n`) + + // Get list of existing files in bucket + console.log('šŸ“” Fetching existing files from bucket...') + const { data: existingFiles, error: listError } = await supabase.storage + .from(bucket) + .list('', { limit: 1000 }) + + if (listError) { + console.error('āŒ Error listing files:', listError) + process.exit(1) + } + + const existingFileNames = new Set(existingFiles?.map(f => f.name) || []) + console.log(` Found ${existingFileNames.size} existing files in bucket\n`) + + // Upload files + console.log('šŸ“¤ Uploading geometry files...') + + let uploaded = 0 + let skipped = 0 + let failed = 0 + + for (const filename of localFiles) { + const filePath = join(geometriesPath, filename) + const fileContent = readFileSync(filePath) + + try { + // Upload or update file + const { error: uploadError } = await supabase.storage + .from(bucket) + .upload(filename, fileContent, { + contentType: 'application/json', + upsert: true // Overwrite if exists + }) + + if (uploadError) { + console.error(` āŒ ${filename}: ${uploadError.message}`) + failed++ + } else { + const action = existingFileNames.has(filename) ? 'Updated' : 'New' + console.log(` āœ… ${filename} (${action})`) + uploaded++ + } + } catch (error) { + console.error(` āŒ ${filename}: ${error.message}`) + failed++ + } + } + + console.log('\nšŸ“Š Upload Summary:') + console.log(` āœ… Uploaded/Updated: ${uploaded}`) + console.log(` āŒ Failed: ${failed}`) + console.log(` šŸ“ Total: ${localFiles.length}`) + + if (failed === 0) { + console.log('\nāœ… All geometry files uploaded successfully!') + console.log('\nšŸ“ Next steps:') + console.log(` Run: ${isStaging ? 'STAGING=true ' : ''}node rebuild-cache.js`) + } else { + console.log('\nāš ļø Some files failed to upload. Please check errors above.') + process.exit(1) + } +} + +uploadGeometries().catch(error => { + console.error('āŒ Fatal error:', error) + process.exit(1) +}) diff --git a/verify-links.js b/verify-links.js new file mode 100644 index 0000000..936ecc3 --- /dev/null +++ b/verify-links.js @@ -0,0 +1,22 @@ +const response = await fetch('https://vbqijqcveavjycsfoszy.supabase.co/storage/v1/object/public/staging-data-cache/all-data.json'); +const data = await response.json(); + +console.log('Checking service links in cache...\n'); + +// Check Waymo Phoenix +const waymoPhoenix = data.service_areas.find(s => s.company === 'Waymo' && s.name === 'Phoenix' && !s.endDate); +console.log('Waymo Phoenix:'); +console.log(' companyLink:', waymoPhoenix?.companyLink); +console.log(' bookingPlatformLink:', waymoPhoenix?.bookingPlatformLink); + +// Check May Mobility Atlanta +const mayMobility = data.service_areas.find(s => s.company === 'May Mobility' && !s.endDate); +console.log('\nMay Mobility Atlanta:'); +console.log(' companyLink:', mayMobility?.companyLink); +console.log(' bookingPlatformLink:', mayMobility?.bookingPlatformLink); + +// Check Zoox Las Vegas +const zoox = data.service_areas.find(s => s.company === 'Zoox' && !s.endDate); +console.log('\nZoox Las Vegas:'); +console.log(' companyLink:', zoox?.companyLink); +console.log(' bookingPlatformLink:', zoox?.bookingPlatformLink || '(none)');