Skip to content

Pivot Argus from plate scanner to building intelligence - #1

Open
jtannahill wants to merge 43 commits into
mainfrom
feature/building-intelligence
Open

Pivot Argus from plate scanner to building intelligence#1
jtannahill wants to merge 43 commits into
mainfrom
feature/building-intelligence

Conversation

@jtannahill

Copy link
Copy Markdown
Owner

Summary

  • Complete pivot from license plate scanning to real-time building identification
  • Point camera at any NYC building → instant story, ownership, value, violations, permits
  • Two-phase ID: GPS + heading (<500ms) → CLIP visual matching (1-3s async)
  • 8 NYC public data APIs (GeoClient, PLUTO, ACRIS, DOB, HPD, LPC, DOF, 311) — all free
  • Bedrock (Claude) generates building narratives
  • Free app monetized through aggregate scan analytics (heat maps, intent signals, imagery)

Changes

  • Backend (Python): New data model, NYC data provider, /identify endpoint, data assembly pipeline, Bedrock story generator, CLIP visual matching, scan analytics with heat counters
  • CDK (TypeScript): All 5 stacks updated — new Lambdas, Step Functions chain, CLIP SageMaker endpoint, S3 buckets for index/embeddings
  • iOS (Swift 6): BuildingIdentifier replaces PlateDetector, bottom sheet with 5 tabs, Explore map view, heading tracking
  • Dashboard (Next.js 16): Building heat map, building detail page, Mapbox dark style
  • ML: CLIP ViT-B/32 inference handler, Street View seeding script
  • Cleanup: Removed all plate-specific code (plates, geofences, alerts, pattern, diplomatic, old ML models)

Stats

  • 91 files changed, +5,705 / -3,136 lines
  • 127+ Python tests passing
  • 34 CDK tests passing
  • Dashboard builds cleanly (3 static routes)

Test plan

  • Register NYC GeoClient API key
  • Package CLIP model to SageMaker
  • cdk deploy --all
  • Seed 5K Manhattan landmarks from Street View
  • Smoke test: point at building → story appears
  • TestFlight build

🤖 Generated with Claude Code

jtannahill and others added 30 commits March 19, 2026 14:59
Replace plate-scanning key patterns (PLATE#, SIGHTING#, ENRICHMENT#, GEOFENCE#, CONNECTION#, ALERT#) with building-specific patterns (BLDG#, SCAN#, GEO#, HEAT#, ANALYTICS#, PROFILE, STORY, OWNER#, VIOLATION#, PERMIT#, IMAGE#). Add geohash6 encoder and 14 passing unit tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…PC, DOF integration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
POST /identify receives {latitude, longitude, heading}, queries NYCDataProvider.resolve_location(), and checks DynamoDB cache for PROFILE and STORY records on each candidate BBL. Returns enriched candidates list with any cached data attached.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ation

Records individual scan events (SCAN#{userId}#{timestamp}/SCAN) and atomically
increments daily building heat counters (HEAT#{date}/BLDG#{bbl}) via DynamoDB
ADD. Scheduled aggregator rolls up yesterday's heat partition into an
ANALYTICS#daily summary and flags trending buildings (scanCount >= 5).
13 tests, all passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements Task 4: NYCDataProvider-backed Step Functions task that assembles
building data from all NYC public APIs (PLUTO, ACRIS, DOB, HPD, LPC, DOF)
and writes normalized DynamoDB records (PROFILE, OWNER, VIOLATION, PERMIT).
Adds Bedrock Claude story generator that reads the PROFILE and produces a
headline, narrative, and fun facts, persisted as a STORY record. 23 tests,
all passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ml/clip_embedding/inference.py: SageMaker handler with model_fn/input_fn/predict_fn/output_fn for CLIP ViT-B/32, lazy-imports torch+clip for testability, returns 512-dim L2-normalized embeddings
- ml/clip_embedding/requirements.txt: torch>=2.0, openai/CLIP, Pillow>=10.0, numpy>=1.24
- api/visual_match/handler.py: Lambda that fetches user photo from S3, invokes CLIP endpoint, computes cosine similarity against DynamoDB IMAGE# reference embeddings, returns best BBL match with confidence/method/corrected fields and stores new embedding in building index
- Tests: 25 passing (10 inference, 15 handler) — all mocked, no GPU required

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…embly, CLIP, analytics)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace plate-centric UI with building intelligence dashboard. New home page shows Mapbox dark map with GeoJSON heat layer (green→yellow→red by scan density). /buildings?bbl=XXX shows tabbed detail: story, owner, value, violations, permits. Removes SearchBar, AlertsPanel, DiplomaticBadge, PlotMode, TimelineScrubber, and /plates route. api.ts rewritten with getBuilding, getRecentScans, getTrending, searchBuildings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…fication

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… diplomatic, ML models)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds ml/seed_index/seed_streetview.py — a CLI tool that queries NYC PLUTO via
Socrata for Manhattan buildings, fetches 2-4 Street View images per building at
configurable headings (default 0/90/180/270°), uploads each to S3, generates a
CLIP embedding via SageMaker, and stores an indexed record in DynamoDB. Skips
sub-5 KB Street View placeholder responses. Supports --dry-run, --limit, and
full endpoint/bucket/table overrides.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…resh button

- /identify now fetches ACRIS ownership + DOB/HPD violations for top candidate
- Generates AI story via Bedrock on first scan (cached in DynamoDB after)
- Refresh button on camera view for manual re-identification
- Search radius increased to 100m default
- Fix optional String types in iOS tab views

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…zeros

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Crosshair reticle at center of camera view
- Ring shrinks as cone narrows (Nearby=120px, Far=40px)
- Green when building found, yellow when identifying, dim when idle
- Pulse animation during identification
- Camera pauses when bottom sheet is open, resumes on dismiss

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tracks device altitude (CLLocation) and pitch angle (CMMotionManager) to
project the camera ray to ground level when the user is elevated and
pointing downward. The API shifts the search origin to the ground
intersection point when altitude is set and pitch < -5°.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LocationManager tracks altitude (GPS) + pitch (CoreMotion gyroscope)
- API projects look vector to ground level when user is elevated
- /pins Lambda wired in CDK with POST/GET/DELETE routes
- Fix pitch convention: 0°=horizon, negative=looking down

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…didates at far range

- Far mode cone tightened from 15° to 8°
- Block mode from 30° to 20°, Street from 60° to 45°
- Far range sorts by heading alignment first, distance second
- Returns 5 candidates at far range (vs 3 for nearby)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…display

- Detect diplomatic/foreign government ownership from PLUTO owner name patterns
- Calculate unused FAR and air rights sq ft from PLUTO zoning data
- Display owner name, diplomatic badge, tax exempt status, air rights in Value tab
- Bedrock story prompt includes diplomatic/government context when detected

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Lambda GET /search/address?q={query} calls NYC Geoclient v2, returns top 5
normalised results (bbl, address, borough, zipCode, lat/lon). iOS AddressSearchView
provides debounced typeahead; tapping a result calls /identify and opens
BuildingSheet. Search button (magnifying glass) added to ScanView top bar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds AirRightsOverlay.swift — a 2D camera-view overlay that renders a
translucent green ghost block above the reticle representing unused
development rights. Height scales to the unusedFar/builtFar ratio,
labels show sq ft, FAR delta, and potential additional floors with a
subtle pulse animation. ScanView gets a new AIR toggle button (disabled
when no air rights data) and auto-dismisses the overlay on building change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Translucent green ghost block above reticle shows unused development
rights. Sized by unusedFar/builtFar ratio. Pulsing animation, sqft
label, potential floors count. Toggle button in camera UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jtannahill and others added 13 commits March 19, 2026 18:00
…etry

Shoots a ray from user's 3D position (GPS + altitude) in look direction
(heading + pitch) and finds which building volume it hits first. Tall
buildings correctly occlude shorter ones behind them. 10 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mages)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- NYC Building Footprints dataset (u9wf-3gbt) provides LiDAR-measured
  roof heights in feet, converted to metres
- Raycaster resolves height via: BIN match → nearest centroid → PLUTO fallback
- Height cache with 15-min TTL and thread-safe locking
- Graceful fallback when 3D data unavailable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Old handlers imported removed plate-scanner model functions.
Rewritten for building scans with correct model imports.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… images

- package_model.sh: downloads CLIP ViT-B/32, packages for SageMaker, uploads to S3
- backfill_embeddings.py: processes all S3 images through SageMaker endpoint,
  writes real embeddings to DynamoDB (replacing placeholder empty arrays)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New Skyline mode: 8km range, 3° cone, filters to buildings 15+ floors
- Distance displayed on reticle (meters < 1km, miles > 1km)
- Reticle shrinks to 24px dot in Skyline mode
- PLUTO query returns up to 500 candidates in Skyline mode
- Enables identifying Empire State from Battery Park (5+ miles)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ShareableSnapshot renders building card with address, BBL, metrics grid,
  owner, landmark, diplomatic status, air rights, assessed value, story headline,
  GPS coordinates, heading, distance, and timestamp
- Share button in BuildingSheet header exports image + text via UIActivityViewController
- Dark theme, monospace metrics, Argus branding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Triangle warning icon in BuildingSheet header
- Tap opens CorrectionSheet: shows wrong address, search for correct one
- Geoclient v2 typeahead to find the right building
- "Just flag as wrong" option for quick reports
- Turns green checkmark after flagging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant