Recovering building names from addresses and plan PDFs
Hundreds of CRM deals had a location but no building name, so the map couldn’t group homes by building. Mining the address field recovered 633 of 711, and Claude reading the architects’ plan PDFs found 93 more.
The problem
Hundreds of deals had no usable building name, so the map couldn’t group homes by building.
The fix
Mine the address text with a geocoder, then have Claude read the plan PDFs for the rest.
- 633 / 711
- building names recovered from address text
- 93 / 143
- more recovered from plan PDFs
- 4
- tiers for placing a lead, best first
A dot on a map isn’t a building. In April 2026 most of AHA’s CRM deals had a rough location, but hundreds had no usable building name: the field was blank or said “NA”. Without a name, the map couldn’t group homes by building or label them, and nobody could say “we’ve done homes in your tower”. I built the recovery in two passes with Claude Code, and decided what each pass was allowed to guess.
Pass 1: mine the address field
711 deals had no building name but did have some address text. It was messy: map links pasted in, notes in brackets, instructions after a pipe. The script strips those first, then asks Google’s geocoder for what’s left.
From the answer it takes the most specific named place Google returns: a named building first, then an establishment or landmark, then the neighbourhood. If there’s none, it takes the first part of the formatted address, as long as it isn’t just a number. It writes the building name back, and a location only if the deal had none.
Every finished deal goes into a progress file, so the run can stop and resume without asking Google twice.
633 of 711 got a building name. 69 had nothing usable once cleaned, 7 the geocoder couldn’t find and 2 came back with nothing new.
Pass 2: read the plans
Some deals still had no building name but did have an electrical plan: the architect’s PDF, attached in the CRM. Architects label their work, so for those deals Claude reads three signals:
signal 1 the file name (architects often name files by area, building and client)
signal 2 the PDF’s title, subject and author fields
signal 3 the first 2,500 characters of text, where the title block usually sits
→ JSON only: { building_name, address, flat_unit }, null when not confident
The rules end with “No guessing.” The parser takes the first JSON object in the reply, so a stray sentence around it can’t break anything. The answer goes onto the deal and onto the plan’s own row, which the map’s plan viewer reads later.
Of 143 deals tried, 93 came back with something: 69 with a building name and 24 with an address only. 50 came back empty. The step now runs in the nightly sync for new deals.
Leads: say how sure you are
Leads rarely name a building at all, so each lead is placed by the best field it has, in order: the locality, then the free-text location, then the pincode, then the city. The CRM’s pincode is left out whenever a locality exists, because the two often disagree.
Each lead records which tier placed it, so a city-level dot is never mistaken for a street-level one. Once a lead is placed, or a person fixes a deal’s pin by hand, the row is locked and the nightly run leaves it alone. The map’s own convert-a-lead form starts by picking the building, from AHA’s buildings or a Maps search, so deals created there carry one from the start.
What I’d do differently
- A neighbourhood isn’t a building. Pass 1’s last resort took the neighbourhood’s name. When the deals map was built later, bare area names were among the entries it had to hide.
- “India” is an answer too. A query Google only half understands can come back as the centre of the country. The deals map later found 61 deal rows pinned exactly there, and now throws that point out.
- A failed call isn’t “nothing found”. Pass 2 files an error from the model the same way as an empty answer, so that deal is never tried again. It should be retried.
- Fix the source, or protect the fix. The nightly copy rewrites each deal’s building field from the CRM, and only rows a person has locked are protected. Recovered names belong back in the CRM, or in a layer the copy never touches, which is how the deals map keeps its corrections.
Rules I’d keep
- Clean before you geocode. A pasted link is noise, not an address.
- Take the most specific answer, and know when it’s too vague.
- Let the model say “don’t know”. A blank beats a plausible guess.
- Record how each point was placed. Precision is part of the data.
- Keep progress per record. A stopped run should resume, not restart.