Playbook · View 01Location IntelligenceInternal · AdminPipeline OK
Location Intelligence
Every Google Maps Platform signal for 8630 N Green Hills Road fused with the property's classification, tagging, and incumbent-use history — to generate the full building & land use-case universe, score it against the proximity business census, traffic, and demographics, and rank the optimum uses, tenants, and buyers ready for campaign testing.
WF SCORE 84OFFICE · CLASS A10,500 SF · 1 STORYB3-3 ZONING1.29 ACBUILT 2020PKG 4.0/1,000APN 19-3.0-08-200-001-003.00039.2511, −94.6316
top LUS across runs§08 TB blend stays pinned to promoted run lus-1.0 · r1 until a newer run is promoted
Run
Completed
POIs
Optimum use
Top LUS
Δ vs prev
Duration
Artifacts
Live wiring — what this console mocks
In production the Run button invokes a Supabase Edge Function li-run that executes the §10 calls server-side and streams NDJSON events over SSE while inserts land in li_pois / li_use_case_scores and fan out on Realtime channel li:1616772. The page subscribes to both: SSE paints this stream, Realtime patches the map, census, and rankings — the same DOM updates this mock performs. Automations are the identical function on a pg_cron schedule or fired by webhooks (listing status change, Clay enrichment complete, Apify scrape landed). Full event schema → §10 · DATA-04.
§ 01
Map Command Center
Maps JS · Places (New) · Street View · Tiles 3D · Routes
The property pinned at 39.2511, −94.6316 with the full proximity census plotted and filterable. Modes swap Google map renderers: Roadmap → Satellite → 3D Tiles → Traffic → Demand Heat. Every marker is a live Places record; the rail on the right is the synced result set. Frontage imagery below is pulled per-heading from the Street View Static API.
This static build draws the basemap as inline SVG so the view works with zero keys. In production the stage swaps to the Maps JavaScript API vector map (mapId: WF_LI_DARK) with the identical marker, ring, traffic, and heat layers — request bodies in §10 API Console. Filters, modes, and the rail already speak the live data contract.
§ 02
Classification, Tags & Building History
inputs → use-case generator
The generator's raw inputs: the property's classification and metadata, its tag set, and the incumbent-use history (Doc 20). Every use case in §03 is derived from these fields plus the zoning schedule — nothing is hand-picked.
classification.metadatasource: property record · Doc 01
Asset Class
Office · Class A · single-tenant configuration
Structure
10,500 SF · 1 story · built 2020 · ADA, no elevator
Site
1.29 AC · APN 19-3.0-08-200-001-003.000 · Platte County
Zoning
B3-3 — office, medical, professional services as-of-right
Parking
~4.00 / 1,000 SF surface · exceeds 3.5 medical minimum
Coordinates
39.2511, −94.6316 · geohash 9yuw6
Corridor
Tiffany Springs / Coves North · MO-152 × N Green Hills interchange · I-29 immediate · KCI 15–30 min · Zona Rosa 2 mi
Turnkey inheritance: prof. services = low convert · medical = low–mod (add plumbing) · 3–4 suite demise = moderate
Previous-user inference: incumbent fit-out is the strongest prior. Uses inheriting reception / private-room / conference infrastructure score a buildout bonus inside ZB (§04); uses demanding demolition (retail showroom, assembly) carry a conversion discount.
§ 03
Use-Case Universe
25 generated · zoning-gated
The comprehensive candidate list generated from §02 inputs crossed with the B3-3 schedule of uses. Grouped by entitlement path: as-of-right, conditional (CUP required), and conversion (repositioning risk). Chips show the three hard gates — zoning, buildout inheritance, parking. Click any card to isolate its evidence layer on the §01 map.
Generator recipe
tags ∪ zoning_schedule(B3-3) ∪ incumbent_adjacency(Doc 20) ∪ demand_categories(Doc 03/10) → dedupe → gate by entitlement → 25 candidates. The universe is intentionally wider than the 4 headline segments in Doc 21; §05 scoring is what compresses it back down.
§ 04
The LUS Scoring Model
Location-Use Score · v1.0 · deterministic
Every candidate is scored 0–100 by the Location-Use Score — eight weighted components computed from the §06 census, §07 demographics & traffic, and the §02 gates, then multiplied by an entitlement gate. All weights ship in scoring_config.json (§10) so runs are reproducible.
Co-Tenancy Synergy CS · complementary POIs ≤ 0.5 mi
12%
Competition Saturation CP · inverse of direct rivals ≤ 2 mi
12%
Parking & Access PA · ratio have ÷ need · ADA
6%
Corridor Momentum CM · dev sites · KCI · jobs · comp premium
6%
component formulasinputs cited to §06–§07
DG = 100 · clamp(1 − ρ/ρbench, 0, 1) · Didxρ = rivals(3mi) ÷ pop(3mi)/10k · ρbench = KC metro density · Didx = category demand index (Doc 03)DF = 25·m(HHI) + 25·m(age) + 25·m(pop≥45k) + 25·m(growth≥2%) m() = band match vs category ideal, §07TV = 0.5·Vfront + 0.3·Vsec + 0.2·Vis V = 100·min(AADT/AADTref,1) · GreenHills/12k · Barry/25k · Vis = corner+signage scoreCS = min(100, Σₖ wₖ·min(nₖ,capₖ)) nₖ = complementary POIs ≤0.5mi by type k (§06) · wₖ, capₖ per categoryCP = 100 − 100·min(Ndirect(2mi)/Ncap, 1) Ncap = tolerable rival count · e.g. chiro Ncap 5 → 5 rivals ⇒ CP 0ZB = 0.5·Z + 0.5·B Z: as-of-right 100 · conditional 60 · prohibited 0 | B (Doc 20): low 100 · low-mod 80 · mod 60 · high 30PA = 100·min(4.0/need, 1) − ADAgapneed = category stalls/1,000 SF · ADAgap = 0 here (single-story)CM = min(40,10·devsites≤1mi) + 20·KCI + 20·jobs + 20·comps 4 dev sites (§06) · $1.5B KCI · +35k metro jobs · $140 vs $105/SF medical premium
worked example — click any §05 row to recomputeuse_case: profsvc
loading…
Tier bands · shared with fw.scoring (Doc 10)
A · Hot 80–100 → campaign-test immediately, human first touch · B · Warm 60–79 → 5-touch sequence + LinkedIn audience · C · Nurture 40–59 → drip + ad retargeting · D · Monitor 20–39 → newsletter awareness only. Same bands the prospect matrix uses, so use-case tiers and prospect tiers join cleanly in §08.
§ 05
Use-Case Rankings
LUS computed live in-page · sorted desc
All 25 candidates scored by the §04 model against the §06–§07 evidence. Component bars are the eight sᵢ values in weight order; drivers cite the controlling inputs. Click a row → the §01 map isolates that use case's evidence categories and the §04 worked example recomputes.
#Use caseTierComponents · DG DF TV CS CP ZB PA CMControlling driversLUS
§ 06
Proximity Business Census
Places API (New) · 31 POIs harvested · 3 rings
Every business inside the 3-mile study area, harvested by places:searchNearby sweeps per category (§10 · API-02) and aggregated per ring. These counts are the raw inputs to DG (demand gap), CS (co-tenancy synergy) and CP (competitive pressure) in §04. Ring counts below are computed live from the same POI array that renders the §01 map.
key establishments · places.searchNearby result samplefieldmask: id,displayName,types,rating,userRatingCount,location · 12 of 31 shown
place_id (mock)
Name
Primary type
Rating
Dist
Ring
Role in model
plc_ne01
Northland Eye Specialists · 8660 N Green Hills
ophthalmologist
4.7 · 210
0.06 mi
≤0.5
CS+ medical anchor · CP− for opto
plc_mc01
Magnolia Family Chiropractic · Barry Plaza
chiropractor
4.9 · 88
0.40 mi
≤0.5
CP− for chiro (1 of 5+ in 2 mi)
plc_bp01
Shoppes at Barry Plaza · 8560 N Green Hills
shopping_mall
4.2 · 340
0.40 mi
≤0.5
CS+ retail amenity · lunch traffic
plc_qt01
QuikTrip #247 · MO-152 & Green Hills
gas_station
4.5 · 520
0.30 mi
≤0.5
TV corroboration · interchange draw
plc_sb01
Starbucks · Tiffany Springs Pkwy
coffee_shop
4.3 · 610
0.9 mi
≤1
CS+ professional amenity
plc_pc01
Price Chopper · Barry Rd
grocery_store
4.4 · 1.4k
1.1 mi
≤3
DF corroboration · daytime pop
plc_sl01
Saint Luke's North Hospital · Barry Rd
hospital
3.9 · 480
2.3 mi
≤3
CS++ referral engine for all medical UCs
plc_zr01
Zona Rosa Town Center
shopping_mall
4.3 · 8.2k
2.0 mi
≤3
CS+ regional draw · comps for retail conv.
plc_pt01
Athletico Physical Therapy · Barry Rd
physiotherapist
4.8 · 130
1.6 mi
≤3
CP− for pt (1 of 3 in 2 mi)
plc_dv01
Dev site — 8433 N Green Hills
vacant_land*
—
0.25 mi
≤0.5
CM+ momentum (1 of 4 dev sites ≤1 mi)
plc_dv02
Dev site — 4511 NW Barry Rd
vacant_land*
—
0.55 mi
≤1
CM+ momentum
plc_ur00
Urgent care — none found · 2 mi radius
urgent_care ∅
—
—
—
DG++ for urgent · zero-result is a signal
Zero-result sweeps are kept, not discarded. A searchNearby call for med_spa or urgent_care that returns Ø inside 2 mi is the strongest demand-gap evidence in the model — it drives DG=95 for medspa and DG=88 for urgent in §05. * vacant_land is not a Places type; dev sites are ingested from the county parcel layer (Doc 22) and unioned into the same li_pois table with src='parcel'.
§ 07
Demographics & Traffic
ACS 5-yr · MoDOT TMS · Routes API
The demand-side evidence: who lives around the pin, how fast the trade area is growing, and how many vehicles pass the frontage. These feed DF (demand fundamentals) and TV (traffic & visibility) directly — every threshold used in §04 is shown against its measured value here.
DF = 100/100 for population-serving categories — all four bands hit. Professional-services DF swaps the age band for a white-collar employment share band (also met at 5-mi), so profsvc DF = 88 rather than 100 (3-mi HHI floor of $68k trims one band to partial credit).
traffic counts · aadtMoDOT TMS station pulls · est. = modeled
Segment
AADT
Ref
V-score
Source
N Green Hills Rd (frontage)
8–12k
12k
83
MoDOT TMS
NW Barry Rd (secondary)
25k+
25k
100
MoDOT TMS
MO-152 (interchange)
38–44k
—
halo
MoDOT TMS · est.
I-29 (1 exit)
55–65k
—
halo
MoDOT TMS · est.
// TV for a storefront-visibility use case (e.g. urgent)
Vfront = 100·min(10k/12k,1) = 83 Vsec = 100·min(25k/25k,1) = 100 Vis = 62(mid-block, monument sign, no corner)
TV = 0.5·83 + 0.3·100 + 0.2·62 = 41.5 + 30 + 12.4 = ≈ 78
Referral proximity for every medical use case (CS)
8630 N Green Hills → Downtown KC
14.6 mi
19 min
Reverse-commute owner-occupant pitch — I-29 one exit away
§ 08
Optimum Users — Ranked & Testable
TB = 0.6·ProspectScore + 0.4·LUS
Named prospects from the Windfield scoring matrix (Doc 10) joined to the §05 use-case tiers. Total-Buyer index blends the 6-dimension prospect score (60%) with the location score of the use case the prospect would occupy (40%) — a strong company in a weak use case ranks below a strong company in a strong one. All eight are live in the outreach engine.
Capital-markets shop → dual path: investor-buyer channel for its clients or debt/equity partner on the 1031 story. 7.2% in-place vs 6.8–7.6% corridor caps.
uc: invest · Bseg: capital partnerch: broker-to-brokermsg: C-1
Expansion-stage operator · use case is Tier-B (education support), while full childcare would gate to conditional-use — pitch is admin + tutoring hub, not licensed daycare.
uc: edu · Bseg: tenantch: email seqmsg: A-1 variant
Top prospect score, weak location math — 3 PT rivals in 2 mi drags CP to 25. Reframe: owner-occupier consolidating satellites, where CP matters less than control & parking.
Boutique operator with strong prospect fundamentals · fitness build-out cost (ZB drag) and Zona Rosa gym density cap the ceiling — nurture, don't lead.
uc: fit · Cseg: tenantch: retargeting poolmsg: hold
Read of the board
The blend surfaces the real strategy: lead with owner-occupant professional services (two CPA firms at 91+), keep capital-markets and DSO channels warm in parallel, and reframe rather than drop high-prospect / low-location names like SERC. Nobody below 80 makes the first campaign wave.
§ 09
Positioning & Message Tests
6 cells · every hook cites a §04–§07 number
Each cell pairs a ranked use case with a hook built from the location evidence — no claim appears in copy that isn't a computed value on this page. Cells map 1:1 to campaign audiences in the outreach engine; KPI gates decide which survive to wave two.
MSG-B1Medical group · parking-ledLinkedIn + direct · 1p
"3.5 per 1,000 is the medical minimum. This building parks 4.0."
Proof stack: PA 100 · B3-3 permits all medical uses, no variance (Doc 21) · plumbing-only conversion (Doc 20) · St. Luke's North 7 min · LUS 84.9 (rank #2).
aud: practice admins, DSO RE leadsuc: medgroup / dsotier A/B
KPI gate → CTR ≥ 0.8% · 1 tour / 30 d
MSG-B2Med spa · demand-gapLinkedIn + IG · 1p
"Zero med-spa competition on an 8–12k VPD corridor."
Proof stack: DG 95 — searchNearby returns Ø med spas ≤ 2 mi (§06) · $89.4k median HHI at 5 mi · CP 95 · Northland Eye anchor 100 yds for aesthetic-adjacent referrals.
aud: med-spa founders, multi-unit aestheticsuc: medspatier A
The full Google Maps Platform surface this playbook consumes, with the exact request shapes. Keys are injected server-side (never in the browser for REST calls); the Maps JS key is domain-restricted. Each card names the section it powers.
render plane · maps javascript
JSAPI-01 · Maps JavaScript API — base map + vector stylingSKU: Dynamic MapsPOWERS → §01
Cloud styling: the WF_LI_DARK map ID carries the Windfield palette (land #0d141d, water #0a1622, roads #1b2632) so the live map matches this mock 1:1. A twin WF_LI_LIGHT ID backs the light theme. Toggling §01 modes swaps mapTypeId (roadmap/hybrid) and layer objects — no reload.
POST https://places.googleapis.com/v1/places:searchNearby
Content-Type: application/json
X-Goog-Api-Key: {SERVER_KEY}
X-Goog-FieldMask: places.id,places.displayName,places.types,places.location,
places.rating,places.userRatingCount,places.businessStatus
{
"includedTypes": ["doctor","dentist","physiotherapist","chiropractor","pharmacy"],
"maxResultCount": 20,
"locationRestriction": { "circle": {
"center": { "latitude": 39.2511, "longitude": -94.6316 },
"radius": 4828.0// 3 mi in meters — outer study ring
}},
"rankPreference": "DISTANCE"
}
Sweep plan — 9 calls, one per model category: medical (types above) · professional (accounting, lawyer, insurance_agency, real_estate_agency, finance) · food (restaurant, coffee_shop) · retail (shopping_mall, grocery_store, clothing_store) · fitness (gym, yoga_studio) · education (school, tutoring*) · auto (gas_station, car_repair) · civic (post_office, library, local_government_office) · anchors (hospital). Each response is distance-bucketed into rings ≤0.5 / ≤1 / ≤3 mi and written to li_pois. Zero-result sweeps are logged too — they set DG. (*tutoring via searchText, no native type.)
POSTAPI-03 · Places searchText — gap probes & typeless queriesSKU: Text Search ProPOWERS → §06 zero-result rows · DG
Text probes catch what typed sweeps miss. The Ø med spa and Ø urgent care results inside 2 mi are the two highest-leverage numbers on this page — they set DG 95 / 88 and write the §09 B2 / D1 hooks verbatim.
GETAPI-04 · Place Details — enrichment on keepersSKU: Place Details ProPOWERS → §06 table · rail cards
GET https://places.googleapis.com/v1/places/{PLACE_ID}
X-Goog-Api-Key: {SERVER_KEY}
X-Goog-FieldMask: id,displayName,nationalPhoneNumber,websiteUri,regularOpeningHours,
rating,userRatingCount,priceLevel,businessStatus,googleMapsUri
Called only for the ~30 keepers after dedupe — not the full sweep — to hold the Details SKU spend flat. websiteUri feeds the Clay enrichment waterfall (Doc 13) so a POI can graduate into a §08 prospect.
Count-only queries are far cheaper than result sweeps — used to compute category density ρ here vs. the metro benchmark ring (same call centered on the KC North reference centroid). The ratio is DG's ρ/ρbench term in §04.
context plane · imagery, geocode, terrain
GETAPI-06 · Geocoding — canonical pin + APN crosswalkSKU: GeocodingPOWERS → every lat/lng on this page
GET https://maps.googleapis.com/maps/api/geocode/json
?address=8630%20N%20Green%20Hills%20Rd%2C%20Kansas%20City%2C%20MO%2064154
&key={SERVER_KEY}
// → 39.2511, -94.6316 · plus_code 87F4+XW · stored once in li_runs, reused everywhere
GETAPI-07 · Street View Static + metadata — frontage stripSKU: Static Street ViewPOWERS → §01 sv-strip
// 1) free metadata check — never render a "no imagery" tile
GET https://maps.googleapis.com/maps/api/streetview/metadata
?location=39.2511,-94.6316&key={SERVER_KEY}
// 2) four headings, matches the mock strip in §01
GET https://maps.googleapis.com/maps/api/streetview
?size=640x400&location=39.2511,-94.6316
&fov=80&pitch=0&heading=235&key={SERVER_KEY} // SW · building face// heading=000 N Green Hills north · 090 toward I-29 · 180 toward NW Barry
Headings were chosen from the parcel bearing so frame 1 is always the facade. Images are cached to li_assets_streetview with the metadata pano_id + capture date — stale imagery (> 24 mo) flags for a manual drone refresh.
GETAPI-08 · Map Tiles API — photorealistic 3D tilesSKU: 3D Tiles (photorealistic)POWERS → §01 mode: 3D
GET https://tile.googleapis.com/v1/3dtiles/root.json?key={SERVER_KEY}
// consumed by CesiumJS / deck.gl Tiles3DLoader — massing + roof context
// session-token flow per tile fetch; render clipped to the 1-mi ring to cap tile spend
Used for the buyer-facing flyover clip (§09 creative) and roof/HVAC massing checks. The §01 "3D" mode mocks the extruded-parcel view this renders.
GET https://roads.googleapis.com/v1/snapToRoads
?path=39.2515,-94.6316|39.2507,-94.6316&interpolate=true&key={SERVER_KEY}
// → placeId per road segment → joined to MoDOT TMS station AADT in li_traffic_counts
POST https://aerialview.googleapis.com/v1/videos:renderVideo
{ "address": "8630 N Green Hills Rd, Kansas City, MO 64154" }
// poll videos:lookupVideo until ACTIVE → mp4 URIs for LinkedIn ad unit
POST https://addressvalidation.googleapis.com/v1:validateAddress
{ "address": { "regionCode": "US", "addressLines": ["8630 N Green Hills Rd, Kansas City MO 64154"] },
"enableUspsCass": true }
-- category density inside the 3-mi study circle vs KC-North benchmark circleWITH study AS (
SELECTCOUNT(*) n, 'study' ring
FROM `places_insights___us.places`
WHERE'doctor'INUNNEST(types)
ANDST_DWITHIN(point, ST_GEOGPOINT(-94.6316, 39.2511), 4828)
), bench AS (
SELECTCOUNT(*) n, 'bench' ring
FROM `places_insights___us.places`
WHERE'doctor'INUNNEST(types)
ANDST_DWITHIN(point, ST_GEOGPOINT(-94.6096, 39.2154), 4828) -- KC-North centroid
)
SELECT * FROM study UNION ALLSELECT * FROM bench;
Run once per quarter per category; the ratio feeds ρ/ρbench. Everything else on this page runs at request time.
SQLDATA-02 · Supabase schema — where every number landspostgres 15 · postgisPOWERS → all data-bind-source tags
create table li_runs (
id textprimary key, -- 'LI-1616772-0714'
property_id bigint not null, -- 1616772
centroid geography(Point,4326), run_at timestamptz default now(),
config jsonb-- frozen scoring_config.json snapshot
);
create table li_pois (
id text primary key, run_id text references li_runs,
place_id text, name text, cat text, types text[],
loc geography(Point,4326), dist_mi numeric, ring text,
rating numeric, reviews int, src text default'places'-- 'places' | 'parcel'
);
create index on li_pois using gist(loc);
create table li_use_cases (id text primary key, label text, grp text, gate numeric);
create table li_use_case_scores (run_id text, uc_id text,
dg int, df int, tv int, cs int, cp int, zb int, pa int, cm int,
lus numeric generated always as (/* §04 formula in SQL */) stored,
primary key(run_id, uc_id));
create table li_demographics (run_id text, radius_mi int, pop int, hh int,
hhi numeric, med_age numeric, growth numeric, pct_65 numeric);
create table li_traffic_counts (run_id text, segment text, aadt_lo int, aadt_hi int,
source text, is_estimate boolean, road_place_id text);
create table li_assets_streetview(run_id text, heading int, pano_id text,
captured_on date, storage_url text);
create table li_prospect_matches (run_id text, prospect text, uc_id text,
prospect_score int, tb numeric, msg_cell text);
-- RLS: enable on all; policy = auth.jwt() ->> 'org' = 'windfield'
CFGDATA-03 · scoring_config.json — the model, versionedfrozen per run into li_runs.configPOWERS → §04 · §05
GET https://{project}.functions.supabase.co/li-run?property=1616772
Accept: text/event-stream
// NDJSON events — exactly what the §00 console renders:
data: {"t":"stage", "id":"sweep", "n":2, "of":7}
data: {"t":"call", "api":"places:searchNearby", "cat":"medical", "rows":12, "ms":284}
data: {"t":"gap", "probe":"med spa", "rows":0, "ring_mi":2}
data: {"t":"score", "uc":"profsvc", "lus":86.4, "tier":"A"}
data: {"t":"done", "run":"LI-1616772-0714-r2", "dur_s":9.8}
// row inserts fan out on Realtime — the page patches map / census / rankings:
supabase.channel('li:1616772')
.on('postgres_changes', { event: 'INSERT', table: 'li_pois' }, patchMap)
.on('postgres_changes', { event: 'INSERT', table: 'li_use_case_scores' }, patchRanks)
.subscribe();
// automations — same function on a schedule, or fired by webhooks:select cron.schedule('li-nightly', '0 6 * * *', $$ select net.http_post(edge_url) $$);
Exports render server-side from the same payload the §00 buttons download client-side: JSON is the li_* row set verbatim, CSV is li_use_case_scores flattened, and the HTML report is a template pass over the payload (open → print for PDF). A run is immutable once snapshotted; §08's TB blend reads the promoted run only, so campaign math never drifts under an unreviewed re-score.