Parcel-Page Sitemap Generator
Status: Planned Issue: #164Bundle: SEO infrastructure for the Ag Bundle (depends on One-Click Parcel Report)
Goal
Phase a static-generation pipeline for indexable parcel-report URLs (townshipcanada.com/parcel/SE-14-29-21-W2). Target 100K+ Canadian quarter sections by week 16. This is the AcreValue-for-Canada SEO wedge — search-driven acquisition for the Ag Bundle.
Phased rollout
| Phase | Week | Parcel pages | Selection |
|---|---|---|---|
| 1 | 5 | 10,000 | Representative sample across Prairie provinces |
| 2 | 8 | 60,000 | All AB quarter sections + sample SK/MB |
| 3 | 16 | 120,000 | All AB + all SK + sample MB + initial BC/ON cells |
Phasing matters because:
- Sudden submission of 120K URLs to Search Console tends to be deprioritized by Googlebot
- Phase 1 lets us measure crawl rate, click-through, conversion before committing to the full surface
- Each phase's selection can favour high-search-volume areas (Red Deer / Lacombe / Calgary corridor first)
Selection criteria for parcel inclusion
Not every quarter section in the DLS database should be in the sitemap. Selection rules:
- Coverage filter — only include quarters where we can populate the free-tier report cards with non-empty data:
- Legal description (always available — parser-driven)
- LSRS score (available wherever AAFC LSRS coverage exists)
- Crop history (available for Prairie + parts of ON/BC)
- Province balance — target distribution across AB / SK / MB / BC / ON proportional to the agricultural land base in each
- Geographic spread — avoid clustering all 10K phase-1 pages in two RMs; distribute across at least 50 RMs
- Recency bias — favour parcels with recent AAFC crop inventory data over parcels in fallow / non-cultivated zones
Generator script
A new node script at scripts/generate-parcel-sitemap.js runs against the PostGIS quarter-section table and emits a list of LLD slugs that meet the selection criteria for the current phase. Output is data/parcel-sitemap-phase-N.json — committed to source so the runtime sitemap handler can read it without hitting the database on every request.
node scripts/generate-parcel-sitemap.js --phase=1 --target=10000
→ writes data/parcel-sitemap-phase-1.json
The script is idempotent — re-running it for the same phase produces the same output unless the source data has changed.
Sitemap handler
The existing server/api/__sitemap__/urls.get.ts is extended to read data/parcel-sitemap-phase-N.json for the current phase and emit one <url> entry per parcel with:
loc:/parcel/<lld-slug>changefreq:monthlypriority:0.5(parcel pages are deeper than primary marketing pages)lastmod: the date the parcel-sitemap-phase-N.json file was generated
The handler also splits the sitemap once URL count exceeds Google's 50K-per-sitemap limit. Phase 3 will require a sitemap index pointing to multiple sub-sitemaps.
Server-side rendering
Every parcel URL in the sitemap must render as static HTML on first request (no client-side hydration before the bot sees content). The Vue page app/pages/parcel/[lld].vue (defined in #157) uses defineRouteRules({ prerender: false }) plus the useAsyncData SSR pattern to ensure the bot crawl sees a complete document.
ISR is preferred over full prerender at this scale — 120K static files would inflate the build artifact significantly. ISR with a 24-hour revalidation window keeps the generated HTML fresh enough for crawler indexing without bloating builds.
Search Console submission
After each phase ships:
- Verify a sample of generated URLs render correctly in production
- Submit the updated
sitemap.xmlto Google Search Console - Track "Indexed pages" as the leading SEO metric (the acceptance criterion)
- Monitor crawl stats and 4xx/5xx rates — the runtime handler should never 404 on a sitemap-listed URL
Acceptance criteria progress
-
scripts/generate-parcel-sitemap.js— phased selection logic -
data/parcel-sitemap-phase-1.json— initial 10K entries - Sitemap handler extension to include parcel URLs
- Sitemap index for phase 3 (split across multiple sub-sitemaps)
- Server-side rendering of
/parcel/[lld]pages (depends on #157) - Search Console resubmission process documented
- Indexed-pages metric tracked weekly
Open questions
- Selection bias — should phase 1 favour high-search-volume areas (more conversions per indexed page) or distributed coverage (better long-tail capture)?
- Lastmod cadence — bumping
lastmodon every sitemap regeneration causes Googlebot to recrawl frequently. Strategy: only bumplastmodwhen the underlying data for that specific parcel has changed (e.g., new AAFC year released).
Related features
- One-Click Parcel Report — the page that serves at each sitemap URL
- LSRS Productivity Score, AAFC Crop Inventory — data sources for the report cards on each parcel page