feat: add catastro SSOT integration, 3DGS hybrid pipeline, casa demo v3
- SPEC: add User Story 0 (Property Setup via Catastro) as P1 entry - SPEC: add catastro_data and catastro_imports tables to data model - PLAN: add Phase 2A (Catastro Integration) with 8 tasks - AGENTS.md: add 3 key decisions (Catastro SSOT, 3DGS hybrid, plan inputs) - Demo v3: Spanish apartment (C/ Altabix 24, 3ºB, Elche), 7 rooms, upload panel for catastro/DXF, proper collisions on all doors
This commit is contained in:
@@ -8,9 +8,22 @@
|
||||
|
||||
## User Scenarios & Testing
|
||||
|
||||
### User Story 0 — Property Setup via Catastro (Priority: P1) 🎯 MVP Entry
|
||||
|
||||
As a real estate agent, I enter a property address and InteriorScan fetches the building's exterior footprint from the Spanish Cadastro (INSPIRE WFS), so the 3D model starts with verified exterior walls before I upload any photos.
|
||||
|
||||
**Why this priority**: The Catastro provides the SSOT for exterior geometry — wall outlines, building height, and number of floors. This eliminates manual exterior wall drawing and ensures dimensional accuracy. Interior walls come from photos or user-uploaded floor plans.
|
||||
|
||||
**Independent Test**: Enter "C/ Altabix 24, Elche" → refcat is resolved → building polygon is displayed on a minimap → exterior walls are generated in the 3D scene.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
1. **Given** I am creating a new project, **When** I type a Spanish address and submit, **Then** the system resolves it to a cadastral reference (refcat) and fetches the building footprint polygon from INSPIRE WFS.
|
||||
2. **Given** a building footprint is fetched, **When** the 3D scene initializes, **Then** exterior walls appear at the correct dimensions matching the Catastro polygon, with the correct number of floors.
|
||||
3. **Given** the Catastro lookup fails, **When** no polygon is found, **Then** the system falls back to manual wall drawing or DXF/SVG upload.
|
||||
|
||||
### User Story 1 — Upload & Process (Priority: P1) 🎯 MVP Core
|
||||
|
||||
As a real estate agent, I upload 30-100 photos of a property and InteriorScan reconstructs the interior as a 3D walkthrough, so I can share a link with potential buyers who navigate it like a first-person game.
|
||||
As a real estate agent, I upload 30-100 photos of a property and InteriorScan reconstructs the interior as a 3D walkthrough, so I can share a link where potential buyers navigate it like a first-person game.
|
||||
|
||||
**Why this priority**: Without reconstruction, nothing else matters. This is the core value proposition.
|
||||
|
||||
|
||||
@@ -148,6 +148,31 @@ export const processingJobs = pgTable('processing_jobs', {
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// ── Catastro Data ─────────────────────────────────────
|
||||
|
||||
export const catastroData = pgTable('catastro_data', {
|
||||
id: cuid2('id').primaryKey(),
|
||||
projectId: cuid2('project_id').references(() => projects.id).notNull(),
|
||||
refcat: text('refcat'), // 14-20 digit cadastral reference
|
||||
addressNormalized: text('address_normalized'), // Normalized address from Catastro
|
||||
buildingFootprint: jsonb('building_footprint'), // GeoJSON polygon from INSPIRE WFS
|
||||
buildingHeight: text('building_height'), // e.g., "12.5" meters
|
||||
numberOfFloors: integer('number_of_floors'), // From Catastro
|
||||
floorArea: text('floor_area'), // m² from Catastro
|
||||
gmlSource: text('gml_source'), // URL of the GML source file
|
||||
fetchedAt: timestamp('fetched_at').defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const catastroImports = pgTable('catastro_imports', {
|
||||
id: cuid2('id').primaryKey(),
|
||||
projectId: cuid2('project_id').references(() => projects.id).notNull(),
|
||||
format: text('format').notNull(), // 'dxf', 'svg', 'png', 'gml'
|
||||
originalKey: text('original_key'), // S3 key for uploaded file
|
||||
parsedWalls: jsonb('parsed_walls'), // Array of {x1, z1, x2, z2, height, thickness}
|
||||
source: text('source').notNull(), // 'catastro', 'upload', 'manual'
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// ── Subscriptions ───────────────────────────────────
|
||||
|
||||
export const subscriptions = pgTable('subscriptions', {
|
||||
|
||||
@@ -46,9 +46,22 @@ See `data-model.md` (separate document — same directory).
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Photo Upload & Processing Pipeline (P1) 🎯 MVP Core
|
||||
## Phase 2: Property Setup & Photo Upload (P1) 🎯 MVP Core
|
||||
|
||||
### 2A: Upload API
|
||||
### 2A: Catastro Integration
|
||||
|
||||
- [ ] T007 Create `packages/catastro/` — Catastro client package
|
||||
- [ ] T008 Write failing test: address "C/ Altabix 24, Elche" → refcat resolved
|
||||
- [ ] T009 Implement OVK address search (SOAP/REST → refcat)
|
||||
- [ ] T010 Write failing test: refcat → building polygon GML from INSPIRE WFS
|
||||
- [ ] T011 Implement INSPIRE WFS client (GML → GeoJSON polygon)
|
||||
- [ ] T012 Implement GML polygon → wall segments converter (`{x1,z1,x2,z2,height,thickness}`)
|
||||
- [ ] T13 Write failing test: POST /api/v1/projects with address → catastro_data populated
|
||||
- [ ] T014 Implement project creation with Catastro lookup (address → refcat → footprint → walls)
|
||||
|
||||
**Checkpoint**: Enter "C/ Altabix 24, Elche" → building polygon rendered in minimap → exterior walls in 3D scene.
|
||||
|
||||
### 2B: Photo Upload API
|
||||
|
||||
- [ ] T007 Write failing test: POST /api/v1/projects with photos returns 201 + project ID
|
||||
- [ ] T008 Implement project creation endpoint with Zod validation
|
||||
|
||||
Reference in New Issue
Block a user