How it works
I think of it as a translation engine. It takes how a place feels to you and turns it into something your phone can search.
The schema takes a sentence like I need to hide and read a book in the sun and turns it into something a machine can answer. Everything else follows from it. That is the whole trick.
What I’ve been learning is that this only works if each place is saved as a few simple answers rather than a description. Nice little wall is lovely, but a phone can’t search it. Is there somewhere to sit? How much sun? What’s underfoot? How comfortable is it, from one to five? Those it can search, instantly.
The anchor
sanctuaries Think of this as planting a single blank flag in the ground. It knows nothing about the weather, the noise, or how you feel. It knows exactly where it is on the Earth and what you called it. It is the permanent anchor everything else attaches to.
What it holds
- Where is it?
- the exact spot
latitude, longitude- What did you call it?
- e.g. “The Leschi Cedar Root”
name- When did you add it?
- a date and time
created_at
The engineer view · core entity
The normalized base table. Isolating latitude and longitude from the descriptive data keeps the core location lightweight. A cryptographic hash or UUID for the id means the database syncs or backs up across private devices with no auto-increment collisions to worry about.
The sensory nutrition label
profiles This is the part that does the work. Instead of a diary entry about how peaceful a spot is, it’s more like a nutrition label for a place, because what you often need to know is whether your trousers will get muddy. How loud is it? Can people see you? Are you sitting on wet dirt or a dry log? Turn those into simple one-to-five scales and you can search a whole city for exactly what your body needs this afternoon.
What it holds
- How loud is it?
- 1 silent · 5 traffic
noise_level- Can people see you?
- 1 exposed · 5 completely hidden
visual_privacy- How busy is it?
- 1 empty · 5 crowded
foot_traffic
- How much sun?
- full sun, dappled, deep shade
sun_exposure- Out of the wind?
- yes or no
wind_block- What is underfoot?
- grass, dirt, concrete, wood
ground_condition
- Somewhere to sit?
- yes or no: a bench, a log, a wall
has_seating- How comfortable?
- 1 uncomfortable · 5 can lie down
seating_quality
The engineer view · attribute vector
Attributes separated from spatial coordinates by a foreign key (sanctuary_id). Typing the columns as strict booleans (has_seating) and integers (noise_level) guarantees the data is uniformly queryable. You cannot run a greater-than against a text paragraph. You can execute WHERE visual_privacy >= 4 instantly, which is what makes this fast on a mobile processor.
The fourth dimension
visits A park bench in July at two in the afternoon is a completely different place from that same bench in November at six. So this table adds time: how a spot changes across seasons and hours, with room for a short note to your future self. Great after the rain.
What it holds
- When were you there?
- a date and time
visited_at- What season?
- winter, spring, summer, autumn
season- What time of day?
- morning, afternoon, evening, night
time_of_day- A note to yourself
- optional, e.g. “great after the rain”
vibe_note
The engineer view · append-only event log
Rather than mutating profiles on every visit, visits is an append-only time series. That makes it a historical ledger. Later, an on-device agent can read this table for patterns: noticing that you only visit the deep-shade spots in summer, and offering them when the temperature spikes.
The whole schema, for builders
-- The core locations table. Minimal, private, edge-native.
CREATE TABLE sanctuaries (
id TEXT PRIMARY KEY, -- UUID or cryptographic hash
latitude REAL NOT NULL,
longitude REAL NOT NULL,
name TEXT, -- e.g. "The Leschi Cedar Root"
created_at INTEGER NOT NULL -- Unix timestamp
);
-- The utilitarian micro-climate and sensory profile
CREATE TABLE profiles (
id TEXT PRIMARY KEY,
sanctuary_id TEXT NOT NULL,
-- Sensory
noise_level INTEGER, -- 1 (silent) to 5 (traffic)
visual_privacy INTEGER, -- 1 (exposed) to 5 (completely hidden)
foot_traffic INTEGER, -- 1 (empty) to 5 (crowded)
-- Micro-climate
sun_exposure TEXT, -- 'Full Sun', 'Dappled', 'Deep Shade'
wind_block BOOLEAN, -- sheltered from the wind?
ground_condition TEXT, -- 'Grass', 'Dirt', 'Concrete', 'Wood'
-- Utility
has_seating BOOLEAN, -- a bench, a log, a wall?
seating_quality INTEGER, -- 1 (uncomfortable) to 5 (can lie down)
FOREIGN KEY (sanctuary_id) REFERENCES sanctuaries(id)
);
-- Time series: how does this spot change across the year?
CREATE TABLE visits (
id TEXT PRIMARY KEY,
sanctuary_id TEXT NOT NULL,
visited_at INTEGER NOT NULL,
season TEXT, -- 'Winter', 'Spring', 'Summer', 'Autumn'
time_of_day TEXT, -- 'Morning', 'Afternoon', 'Evening', 'Night'
vibe_note TEXT, -- optional: "great after the rain"
FOREIGN KEY (sanctuary_id) REFERENCES sanctuaries(id)
);
-- Spatial index for rapid, offline querying by radius
CREATE INDEX idx_sanctuaries_lat_lon ON sanctuaries(latitude, longitude); Asking the map for somewhere
When you’re overstimulated, you don’t want to scroll around a map looking at pins. You want to press one button that says what you need, somewhere low-noise where nobody can see you, and have your phone find it. Underneath, it runs this:
The query it runs
SELECT * FROM profiles
WHERE noise_level <= 2
AND visual_privacy >= 4; Declared, not guessed
Every answer in it is one you gave. The map never guesses. So it does the same thing every time you ask, and the visits table is a record you can check.
Because the database lives on your phone, there’s nothing to wait for and nothing sent anywhere. Your phone checks the results against where you’re standing and points you to the nearest one. What I like about this is that it turns a big, noisy city into a short list of places that are yours, and nobody else can read it.