enrichers: street-level imagery, since listing photos are not free - #145
Merged
Merged
Conversation
Anthony asked whether rental property images can come from anywhere.
The answer is no for the interior and yes for the outside, and this is
the yes.
Listing photography is locked up harder than listing data, for a
different reason: a photograph carries its own copyright owned by
whoever took it, quite separately from any licence on the listing. So
there is no free source of interior photos and there is not going to be
one. Mapillary is the way round it for the street: crowdsourced imagery
in 190-odd countries under CC BY-SA 4.0, free with a token anybody can
get. It will not show the kitchen. It will show the street, the block
and what is opposite, which for somewhere a person is deciding whether
to live is often the more useful picture, and is the part a listing's
own photographs are least likely to show honestly.
CC BY-SA is share-alike, so attribution is not optional: the
photographer's username, the image id, the licence and its URL are
stored on every result, and the stored block says in as many words that
the credit must be shown with the picture. Storing the URL without the
credit would make this the same problem it exists to avoid.
Coordinates are read in exactly the order the database's own
ndb_geo_shape() uses -- data.location, place, position, geometry, then
the bare object -- so an item this can place is precisely an item the
geographic queries can already find, and it works for anything with a
point, not just property. (0, 0) is refused: it is in the Atlantic and
means a missing coordinate. GeoJSON is handled as [lon, lat], which is
the order people get wrong.
The bbox is 0.0012 degrees either side, about 130 m. Mapillary
formalised a 0.01-degree ceiling in January 2026; this stays well
inside it, and more to the point an image four hundred metres away is
not a picture of this address.
**Probing the live endpoint caught a real bug before it shipped.**
Mapillary answers an invalid token with HTTP **500**, not 401:
500 {"error":{"message":"Invalid OAuth 2.0 Access Token","code":190}}
The first version checked the status and would have reported "mapillary
answered 500", sending somebody to Mapillary's status page instead of
to their token. The body is now read first and code 190 is named as
what it is, with a test for each of the three cases: bad token as 500,
bad token as 401, and a genuine outage that must NOT be called a bad
token.
openlisting now carries lat/lon through onto data.location, under the
key names ndb_geo_shape reads, so a seller publishing coordinates gets
street imagery with no further plumbing.
MAPILLARY_TOKEN is unset here, so the enricher is simply off until one
exists. Default-on for listings and housing.
32 new tests, 2133 across the suite, lint clean. The usual 5
Redis/Postgres integration failures this box cannot run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ralyodio
marked this pull request as ready for review
September 25, 2026 10:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You asked whether rental property images can come from anywhere. No for the interior, yes for the outside — this is the yes.
Why there's no free interior photo
Listing photography is locked up harder than listing data, for a different reason: a photograph carries its own copyright, owned by whoever took it, quite separately from any licence on the listing. There's no free source and there isn't going to be one.
Mapillary is the way round it for the street: crowdsourced imagery in 190+ countries, CC BY-SA 4.0, free with a token anybody can get. It won't show the kitchen. It shows the street, the block and what's opposite — which for somewhere a person is deciding whether to live is often the more useful picture, and is the part a listing's own photographs are least likely to show honestly.
Attribution isn't optional
CC BY-SA is share-alike, so every result stores the photographer's username, the image id, the licence and its URL — and the block says in as many words that the credit must be shown with the picture. Storing the URL without the credit would make this the same problem it exists to avoid.
It works for anything with a point
Coordinates are read in exactly the order the database's own
ndb_geo_shape()uses —data.location,place,position,geometry, then the bare object. So an item this can place is precisely an item the geographic queries can already find, and it applies to crime reports and building permits as readily as to property.(0, 0)is refused — it's in the Atlantic and means a missing coordinate[lon, lat], the order people get wrongProbing the live endpoint caught a real bug before it shipped
Mapillary answers an invalid token with HTTP 500, not 401:
My first version checked the status and would have reported "mapillary answered 500" — sending someone to Mapillary's status page instead of to their token. The body is now read first and code 190 is named as what it is, with a test for each of three cases: bad token as 500, bad token as 401, and a genuine outage that must not be called a bad token.
Also
openlistingnow carries lat/lon ontodata.locationunder the key namesndb_geo_shapereads, so a seller publishing coordinates gets street imagery with no further plumbing.MAPILLARY_TOKENis unset, so the enricher is simply off until one exists. Default-on forlistingsandhousing.Checks
32 new tests, 2,133 across the suite, lint clean. The usual 5 Redis/Postgres integration failures this dev box can't run.
🤖 Generated with Claude Code