A web application for visualizing and rating pizza places on an interactive map, built with Astro. Data is maintained in a Google Sheets document and automatically converted to JSON for the web application.
| Name | Rating | Value | Location | Country | Date | Price | Crust | Dough | Sauce | Cheese | Basil | Sliced | Sloppiness | Saltiness | Oiliness |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Monellis | 8.5 | 7.3 | Hastings | England | 19/10/2024 | Β£13.50 | 9 | 9 | 9 | 9 | 5 | 5 | 8 | 8 | 9 |
| SOL Taphouse | 8.4 | 8.0 | Busan | South Korea | 10/10/2025 | Β£11.14 | 7 | 8 | 8 | 9 | 10 | 10 | 9 | 8 | 9 |
| Oro di Napoli | 8.2 | 8.8 | Saint-RaphaΓ«l | France | 03/05/2026 | Β£8.64 | 7 | 8 | 9 | 8 | 10 | 10 | 7 | 8 | 8 |
| Bonturo Pizzeria Astrusa | 8.2 | 9.9 | Lucca | Italy | 08/08/2026 | Β£6.85 | 8 | 9 | 9 | 9 | 5 | 5 | 7 | 8 | 8 |
| Pizzarelli | 8 | 7.3 | St Leonards-on-Sea | England | 22/08/2025 | Β£11.99 | 8 | 7 | 8 | 9 | 5 | 10 | 9 | 8 | 9 |
| Le Bellini | 8 | 7.7 | Toulouse | France | 23/09/2024 | Β£10.80 | 9 | 8 | 8 | 8 | 5 | 5 | 9 | 8 | 8 |
| Dora's | 7.8 | 7.8 | St Leonards-on-Sea | England | 14/06/2026 | Β£10.00 | 8 | 7 | 8 | 7 | 10 | 10 | 7 | 8 | 8 |
| La Pizza | 7.6 | 7.3 | València | Spain | 30/08/2025 | £10.82 | 8 | 7 | 8 | 7 | 10 | 0 | 8 | 8 | 8 |
| Marghe | 7.6 | 8.2 | Milan | Italy | 31/07/2026 | Β£8.55 | 9 | 8 | 8 | 7 | 10 | 0 | 6 | 6 | 8 |
| Brewing Brothers The Imperial | 7.2 | 7.4 | Hastings | England | 25/04/2025 | Β£9.50 | 8 | 7 | 9 | 7 | 0 | 10 | 7 | 8 | 7 |
| The Crust | 7.2 | 6.6 | Bude | England | 13/08/2024 | Β£12.00 | 8 | 8 | 7 | 7 | 5 | 5 | 7 | 8 | 7 |
| Rustico | 7.1 | 7.3 | Hastings | England | 03/05/2025 | Β£9.50 | 8 | 7 | 8 | 6 | 5 | 5 | 7 | 8 | 8 |
| Dough Ray Me | 6.9 | 6.6 | St Leonards-on-Sea | England | 17/07/2025 | Β£11.00 | 7 | 7 | 8 | 6 | 5 | 5 | 7 | 8 | 7 |
| Homeslice | 6.9 | 5.8 | City of London | England | 02/02/2026 | Β£14.00 | 7 | 7 | 8 | 7 | 5 | 5 | 6 | 7 | 6 |
| Tredici | 6.6 | 5.6 | Cannes | France | 09/08/2025 | Β£14.00 | 7 | 6 | 8 | 6 | 5 | 0 | 7 | 8 | 7 |
| Magpie | 6.6 | 6.0 | Seoul | South Korea | 07/10/2025 | Β£12.14 | 6 | 6 | 7 | 7 | 5 | 10 | 7 | 7 | 6 |
| Porlock Weir Hotel | 6.6 | 5.7 | Porlock Weir | England | 23/07/2026 | Β£13.50 | 6 | 6 | 7 | 7 | 5 | 10 | 7 | 7 | 6 |
| La Saporita | 6.3 | 6.3 | St Leonards-on-Sea | England | 31/10/2025 | Β£9.95 | 6 | 6 | 7 | 7 | 0 | 10 | 7 | 7 | 7 |
| The Rising Sun | 6.1 | 6.1 | Chelmsford | England | 01/02/2026 | Β£10.00 | 7 | 6 | 7 | 6 | 0 | 10 | 6 | 7 | 7 |
| PizzaExpress | 6.1 | 4.8 | Hastings | England | 01/04/2026 | Β£15.90 | 6 | 6 | 7 | 7 | 0 | 5 | 6 | 8 | 7 |
| Marina Fountain | 5.8 | 5.4 | St Leonards-on-Sea | England | 08/05/2026 | Β£11.50 | 6 | 6 | 7 | 5 | 0 | 10 | 6 | 8 | 7 |
| Bellissimo | 5.6 | 6.1 | Corfu | Greece | 26/10/2024 | Β£8.33 | 5 | 6 | 6 | 6 | 0 | 5 | 7 | 8 | 7 |
| The Boathouse | 5.6 | 4.7 | Christchurch | England | 19/04/2025 | Β£14.00 | 5 | 6 | 7 | 6 | 0 | 10 | 5 | 6 | 6 |
| Wetherspoons | 5.5 | 4.8 | Gatwick | England | 19/09/2024 | Β£13.25 | 6 | 6 | 6 | 6 | 0 | 0 | 7 | 5 | 6 |
| Casa de Pier | 5.2 | 5.2 | St Leonards-on-Sea | England | 16/05/2025 | Β£10.00 | 5 | 5 | 6 | 5 | 0 | 10 | 6 | 6 | 7 |
| Wetherspoons (Cornfield Garage) | 5.1 | 5.3 | Eastbourne | England | 08/03/2026 | Β£9.28 | 5 | 5 | 6 | 6 | 0 | 0 | 6 | 5 | 6 |
βββ scripts/ # Data and local dev helper scripts
β βββ csv-to-json.js # Convert CSV data to JSON format
β βββ enrich-google-places.js # Store Google place IDs for restaurant photos
β βββ update-readme-table.js # Sync this README's ratings table from the CSV
βββ src/ # Web application source
β βββ pages/
β β βββ index.astro # Route: / (prerendered static page)
β β βββ api/ # On-demand routes: Google Places photo proxy
β βββ layouts/
β β βββ Layout.astro # <html> shell: meta tags, CSS import, main.js script
β βββ components/ # Header, MobileFilters, Sidebar, MobileDetailView
β βββ lib/
β β βββ photos.js # Shared helpers for the photo API routes
β βββ scripts/
β β βββ main.js # Application logic and Leaflet integration
β βββ styles/
β β βββ main.css # Tailwind CSS and Leaflet styles
β βββ data/ # Data directory
β βββ google-places.json # Stored Google place IDs by restaurant
β βββ ratings.csv # CSV data fetched from Google Sheets
βββ public/
β βββ data/
β βββ ratings.json # Processed JSON data for the app (generated, gitignored)
βββ dist/ # Production build output (generated): client/ (static assets) + server/ (Worker)
βββ astro.config.mjs # Astro build configuration (Cloudflare adapter, Tailwind CSS v4 Vite plugin)
βββ wrangler.jsonc # Cloudflare Worker/deploy configuration
βββ mise.toml # Development environment config
The application uses Astro with the @astrojs/cloudflare adapter, so the whole app β static homepage and the Google Places photo-proxy API β builds and deploys as a single Cloudflare Worker (no separate hand-written worker script):
- Templating:
.astrocomponents (layout + Header/Sidebar/MobileFilters/MobileDetailView) compose the single page today, and provide a template for adding more routes (e.g. a page per restaurant) later - API routes:
src/pages/api/place-photo/[placeId].jsandsrc/pages/api/place-photo-media/[placeId].jsrun on-demand (export const prerender = false), reading bindings viaimport { env } from 'cloudflare:workers'andcontext.locals.cfContext.waitUntil - CSS: Tailwind CSS v4 via the
@tailwindcss/viteplugin, imported in the layout - Leaflet CSS: Imported from node_modules via CSS
@importinmain.css - Leaflet: Bundled as ES module
- HTML: Minified by Astro by default
- Code Splitting: Automatic chunk splitting for optimal loading
- Minification: JavaScript minified with esbuild
- Tree Shaking: Unused code eliminated
Note:
wranglerand@cloudflare/vite-pluginare pinned to specific versions inpackage.json/pnpm-workspace.yaml, andastro.config.mjssetsprerenderEnvironment: 'node'on the adapter β both work around active upstream bugs. See the comments in those files andAGENTS.mdbefore upgrading.
pnpm dev- Astro dev server onhttp://127.0.0.1:8787, serving the frontend and/api/*routes togetherpnpm check- Non-mutating CI validation: regenerate data and buildpnpm build- Production build with full minification and optimization (also updates the README ratings table)pnpm preview- Builds and serves the production Worker locally viawrangler devonhttp://localhost:8788pnpm ship- Build and deploy to Cloudflare Workers
- Node.js and pnpm
- A Google Sheets document with restaurant data
GOOGLE_MAPS_API_KEYin.dev.varsfor local photo API calls, and in your shell environment forpnpm enrich-google-places/pnpm update-data
-
Clone the repository
git clone <repository-url> cd margmap
-
Install dependencies
pnpm install
-
Fetch and build data
pnpm fetch-data
-
Start local development server
pnpm dev
-
Access the application
Open your browser to
http://127.0.0.1:8787pnpm devrunsastro devas a background daemon; useastro dev status,astro dev logs, orastro dev stopto manage it.
pnpm dev- Start the Astro dev server (frontend +/api/*routes) on port 8787pnpm check- Run the CI validation build without updating READMEpnpm build- Build optimized production bundle and update README ratings tablepnpm preview- Preview the production Worker locally (viawrangler dev) on port 8788pnpm ship- Build and deploy to Cloudflare Workerspnpm fetch-csv- Fetch the raw CSV from Google Sheets onlypnpm fetch-data- Fetch CSV from Google Sheets and convert to JSONpnpm enrich-google-places- Match restaurants to Google place IDs for photo lookupspnpm prepare-data- Convert existing CSV to JSONpnpm update-data- Fetch fresh data, enrich missing Google place IDs, regenerate JSON, and sync READMEpnpm update-readme- Sync README ratings table from CSVpnpm clean- Remove generated data files and build output
The application pulls data from a Google Sheets document. The sheet should contain restaurant information with columns for:
- Restaurant names
- Addresses/locations
- Ratings
- Additional metadata
Since data is committed to the repository for simplified builds:
-
Update the Google Sheets document with new restaurants or ratings
-
Regenerate local data:
pnpm update-data
-
Commit and push changes:
git add src/data/ README.md git commit -m "Update restaurant data" git push origin main
The application is deployed manually to Cloudflare Workers from a local machine via:
pnpm shipGOOGLE_MAPS_API_KEY must be set as a Worker secret in production: wrangler secret put GOOGLE_MAPS_API_KEY.
- Frontend: Astro with the
@astrojs/cloudflareadapter, CSS (Tailwind v4), JavaScript - Mapping: Leaflet.js for interactive maps
- Data: Google Sheets β CSV β JSON pipeline
- Build: Astro/Vite with Node.js helper scripts, pnpm package manager
- Deployment: Single Cloudflare Worker β static assets + on-demand photo API routes (via
pnpm ship) - Environment: Mise for development tooling
- The application uses a responsive design that works on both desktop and mobile
- Map view is available on larger screens, with a list-only view on mobile
- Data conversion happens automatically during the build process
- Photo lookups are served by on-demand Astro API routes running on the same Worker, so local development should use
pnpm dev(notwrangler devdirectly) to get both the frontend and those routes pnpm update-datarequiresGOOGLE_MAPS_API_KEYwhen new restaurants need Google place IDs- All source code is in the
src/directory; the site is a single page today but is structured as an Astro template (layout + components) to support adding more pages, e.g. one per restaurant
- Update restaurant data in the Google Sheets document
- Run
pnpm update-datato regenerate data, place IDs, and the README table - Test locally with
pnpm dev - Commit and push changes to trigger deployment