Skip to main content

Local town.word.word geocodes built from filtered word lists and British town names.

Project description

what-town-two-words

:warning: :robot: this tool was vibe coded with Codex

Cartoon die showing a house, strong arm, and carrot

A local, hackable town.word.word encoder inspired by what3words-style codes, but built from your own word list and British town names. The reason for doing this is because it's funnier with a placename!

The package includes:

  • profanity blacklist filtering (not uploaded herein)
  • optional CMUdict homophone rejection via pronouncing
  • minimum Levenshtein distance filtering
  • optional metaphone collision rejection via Metaphone
  • plural/singular pair rejection
  • reversible integer and approximate lat/lon grid encoding

Install

python3 -m pip install -e ".[phonetics,dev]"

The package works without optional dependencies, but CMUdict and true metaphone checks are best with .[phonetics].

Quick Use

from what_town_two_words import LexiconBuilder, LocalWhat2Words

words = ["meeting", "penguin", "klutz", "button", "rocket", "pickle"]
towns = ["Bristol", "York", "Bath"]

lexicon = LexiconBuilder(min_levenshtein=3).build(words)
coder = LocalWhat2Words(towns=towns, words=lexicon.words)

code = coder.encode_int(12)
assert coder.decode_code(code) == 12

For coordinates:

coder = LocalWhat2Words.from_builtin()
code = coder.encode_latlon(51.5074, -0.1278, resolution_m=5000)
cell = coder.decode_latlon_code(code, resolution_m=5000)
print(code, cell.center)

Comedy Scoring Experiment

There is experimental Ollama helper code for scoring words by comic, positive, or smile-value. It was used to explore the "funny" angle of the word list, not as a required part of the build pipeline.

Run a small local model, for example:

ollama pull llama3.2:1b

Then:

from what_town_two_words import score_words_with_ollama

scores = score_words_with_ollama(
    ["meeting", "penguin", "klutz"],
    model="llama3.2:1b",
)

The expected direction for the comedy experiment is:

meeting < penguin < klutz

Treat these scores as exploratory data. The normal build should rely on word rank, morphology, and collision filters rather than broad LLM weighting.

Data Pipeline

what-town-two-words extract-towns data/parsed/os-open-names/*.csv \
  --include-multiword \
  --out data/build/city-towns-villages-hamlets.txt

what-town-two-words extract-towns data/parsed/os-open-names/*.csv \
  --include-multiword \
  --allowed-type City \
  --allowed-type Town \
  --out data/build/cities-towns.txt

what-town-two-words extract-scowl data/parsed/scowl/final/english-words.* \
  --out data/build/candidate_words.txt

what-town-two-words filter-kaikki \
  --kaikki data/parsed/kaikki/kaikki.org-dictionary-English.jsonl.gz \
  --words data/build/candidate_words.txt \
  --out data/build/morph_words.txt \
  --metadata-out data/build/morph_metadata.tsv \
  --rejected-out data/build/morph_rejected.tsv

what-town-two-words extract-scowl-ranked \
  --db data/parsed/scowl/wordlist/scowl.db \
  --out data/build/word_ranks.tsv \
  --max-size 60 \
  --spelling B

what-town-two-words build \
  --words data/build/morph_words.txt \
  --ranks data/build/word_ranks.tsv \
  --out data/build/filtered_words.txt \
  --rejected-out data/build/rejected_words.tsv

what-town-two-words encode-int 12345 \
  --words data/build/filtered_words.txt \
  --towns data/build/city-towns-villages-hamlets.txt

what-town-two-words encode-latlon 51.5074 -0.1278 \
  --words data/build/filtered_words.txt \
  --towns data/build/city-towns-villages-hamlets.txt

Use data/build/cities-towns.txt instead for a stricter City/Town-only first component.

This is intentionally local-first: no central lookup service, no remote API, and no baked-in global address database.

The bundled lists are only seeds for demos and tests. For fine coordinate resolutions, use a larger filtered word list so town_count * word_count^2 comfortably exceeds the number of grid cells in your chosen bounding box.

When collision filters find similar words, the builder processes lower-ranked words first, so the more common/preferred word is kept. SCOWL size is a coarse rank where lower means more common. A stronger corpus-frequency rank file in word<TAB>rank format should still be preferred whenever available.

Design Notes

This project came out of a few experiments rather than a finished theory. The current bias is deliberately collision-first:

  • First component: OS Open Names settlements. The broad file keeps City, Town, Village, Hamlet, Suburban Area, and Other Settlement; the stricter alternative keeps only City and Town.
  • Word source: SCOWL/ESDB British words, with SCOWL size used as a rough commonness rank. Lower SCOWL size wins collisions before any aesthetic preference is considered.
  • Morphology: Kaikki/Wiktionary JSONL can remove inflected junk more cleanly than suffix rules. The intended policy keeps noun lemmas, adjective lemmas, and gerunds/present participles; it rejects plurals, pure adverbs, pure base verbs, third-person forms, past forms, and most comparative or superlative forms.
  • Collisions: words are removed if they are too close by Levenshtein distance, share a metaphone key, are CMUdict homophones when that optional dependency is installed, appear in a user-supplied blacklist, or are plural/singular collisions.

The "funny" angle was an experiment, not a production rule. Early experiments used an Ollama model to score words for fun/comical/positive energy, but broad scoring was too noisy and risked skewing the address space. The working definition of smile-value preferred, in order:

  1. Directly comic or playful meanings: tickle, giggle, joke, clown, farce.
  2. Comic mishap, awkwardness, slapstick, embarrassment, lewdness, bodily comedy, or haplessness: klutz, wobble, pratfall, bonk.
  3. Words frequent in jokes, pub chat, comic scenes, innuendo, or stock comic situations: bar, priest, banana, trousers.
  4. Cute, endearing, or inherently odd referents: penguin, aardvark.
  5. Funny sound or mouthfeel: pickle, noodle, kazoo.
  6. Neutral vivid concrete words: rocket, lantern.
  7. Abstract, administrative, technical, medical, hostile, or bleak words, unless the concept itself is comic.

This means tickle scored above pickle in the comedy experiment, while clearly more common words still belong ahead of funnier rare words in the actual address vocabulary.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

what_town_two_words-0.1.0.tar.gz (22.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

what_town_two_words-0.1.0-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

File details

Details for the file what_town_two_words-0.1.0.tar.gz.

File metadata

  • Download URL: what_town_two_words-0.1.0.tar.gz
  • Upload date:
  • Size: 22.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for what_town_two_words-0.1.0.tar.gz
Algorithm Hash digest
SHA256 862a4b42c05a492c710734f2168e4f109e4008eac5501ed0ea8d6322aba57602
MD5 2429b8555b3f58fb59d5e13c204186de
BLAKE2b-256 493468d0dfee26fadcf5c0c6177984e7c6e88a0a2e1325b58dd78611e1423b82

See more details on using hashes here.

Provenance

The following attestation bundles were made for what_town_two_words-0.1.0.tar.gz:

Publisher: python-publish.yml on matteoferla/what-town-two-words

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file what_town_two_words-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for what_town_two_words-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c5b8a637c3cc9a164d3bfbe2d00d8bb25c58c620d574f0d600b1262cb9892d9a
MD5 1a946e25e34a7a32c832efbb4fcbd72e
BLAKE2b-256 2a337e81979880132a9482f417855b333885477bc240f3e7870717cbceb21064

See more details on using hashes here.

Provenance

The following attestation bundles were made for what_town_two_words-0.1.0-py3-none-any.whl:

Publisher: python-publish.yml on matteoferla/what-town-two-words

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page