q3m

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 8, 2026 License: MPL-2.0 Imports: 5 Imported by: 0

README

q3m - Géocodage en 3 mots pour la France métropolitaine

CI Release Go Reference Go Report Card

q3m encode n'importe quelle position GPS en France métropolitaine (Corse incluse) en un triplet de trois mots français, avec une précision de 1 mètre.

48.8584, 2.2945  -->  province.shootons.retirons
                 <--  48.858398, 2.294503

Pourquoi q3m ?

Le système what3words découpe le globe en cellules de 3m x 3m sur WGS84. En réalité, comme les degrés de longitude rétrécissent vers les pôles, ces cellules ne sont pas carrées.

q3m résout ce problème en utilisant la projection Lambert93 (EPSG:2154), une projection métrique officielle de l'IGN. Chaque cellule mesure exactement 1m x 1m dans le plan projeté.

Installation

go install github.com/ikarius/q3m/cmd/q3m@latest

Ou depuis les sources :

git clone https://github.com/ikarius/q3m.git
cd q3m
go build ./cmd/q3m/

Utilisation CLI

Encoder des coordonnées
q3m encode 48.8584 2.2945
# province.shootons.retirons
Décoder une adresse
q3m decode province.shootons.retirons
# 48.858398, 2.294503
Informations de la grille
q3m info
Convertir WGS84 → Lambert93
q3m tolam 48.8584 2.2945
# 648237.3015, 6862271.6816
Convertir Lambert93 → WGS84
q3m fromlam 648237.3015 6862271.6816
# 48.858400, 2.294500
Sortie JSON

Toutes les commandes acceptent le flag --json :

q3m encode 48.8584 2.2945 --json
# {"address":"province.shootons.retirons","w1":"province","w2":"shootons","w3":"retirons","lat":48.858400,"lon":2.294500}

q3m decode province.shootons.retirons --json
# {"lat":48.858398,"lon":2.294503,"address":"province.shootons.retirons"}

q3m tolam 48.8584 2.2945 --json
# {"e":648237.3015,"n":6862271.6816,"lat":48.8584,"lon":2.2945}

q3m fromlam 648237.3015 6862271.6816 --json
# {"lat":48.858400,"lon":2.294500,"e":648237.3015,"n":6862271.6816}

Utilisation comme bibliothèque Go

import "github.com/ikarius/q3m"

// Encoder
addr, err := q3m.Encode(48.8584, 2.2945)
fmt.Println(addr) // province.shootons.retirons

// Décoder
coord, err := q3m.Decode("province.shootons.retirons")
fmt.Printf("%.6f, %.6f\n", coord.Lat, coord.Lon)
API
Fonction Signature Description
Encode (lat, lon float64) -> (Address, error) Coordonnées GPS vers adresse q3m
Decode (address string) -> (Coordinate, error) Adresse q3m vers coordonnées GPS
ToLambert93 (lat, lon float64) -> (E, N float64) WGS84 vers Lambert93
FromLambert93 (E, N float64) -> (lat, lon float64) Lambert93 vers WGS84
Types
type Coordinate struct {
    Lat float64 `json:"lat"`
    Lon float64 `json:"lon"`
}

type Address struct {
    W1 string `json:"w1"`
    W2 string `json:"w2"`
    W3 string `json:"w3"`
}

Paramètres techniques

Paramètre Valeur
Projection Lambert93 / EPSG:2154 (ellipsoïde GRS80)
Emprise E 100 000 - 1 250 000 m
Emprise N 6 050 000 - 7 120 000 m
Grille 1 150 000 x 1 070 000 cellules
Total 1 230 500 000 000 cellules (~1.23 x 10^12)
Dictionnaire 10 800 mots (10 800^3 = 1.26 x 10^12)
Précision 1m x 1m (erreur max 0.71m du centre au coin)
Couverture France métropolitaine + Corse

Comment ça marche

Encodage
  1. Les coordonnées WGS84 (lat, lon) sont projetées en Lambert93 (E, N)
  2. La position est discrétisée en cellule de 1m x 1m : x = floor(E - E_min), y = floor(N - N_min)
  3. Un index linéaire est calculé : idx = y * largeur + x
  4. L'index est permuté par un réseau de Feistel (décorrélation spatiale)
  5. L'index permuté est converti en base 10 800 : trois indices de mots
  6. Chaque indice est remplacé par le mot correspondant dans le dictionnaire
Décodage

Le processus inverse exact. Le centre de la cellule (+0.5m) est retourné.

Décorrélation spatiale

Sans la permutation, deux points voisins auraient des adresses presque identiques (deux mots sur trois en commun). Le réseau de Feistel assure que des cellules adjacentes produisent des triplets complètement différents, ce qui réduit les risques de confusion.

Dictionnaire

Les 10 800 mots sont extraits de Lexique383 (lexique.org), une base lexicale française libre.

Critères de sélection :

  • 4 à 8 lettres
  • Pas d'accents (ASCII uniquement)
  • Noms, adjectifs, verbes, adverbes
  • Triés par fréquence d'usage, les plus courants en priorité

Le dictionnaire est embarqué dans le binaire via go:embed. L'outil tools/wordgen/ permet de régénérer le fichier words_fr.txt à partir de Lexique383.

Contrat de stabilité : une fois figé en v1.0, le dictionnaire et la clé de permutation ne changent plus jamais. Toute modification invaliderait les adresses existantes.

Performance

Mesurée sur AMD Ryzen 9 8945HS :

Opération Temps Allocations
Encode 133 ns/op 0
Decode 665 ns/op 1
ToLambert93 71 ns/op 0
FromLambert93 482 ns/op 0
Shuffle 103 ns/op 0

Tests

go test ./...
go test -bench . -benchmem

Structure du projet

q3m/
├── go.mod                 # Module Go
├── lambert93.go           # Projection Lambert93 <-> WGS84
├── lambert93_test.go
├── grid.go                # Grille 1m, indexation cellules
├── grid_test.go
├── shuffle.go             # Permutation Feistel (décorrélation)
├── shuffle_test.go
├── words.go               # Dictionnaire (go:embed, sync.Once)
├── words_test.go
├── words_fr.txt           # 10 800 mots français
├── q3m.go                 # API publique : Encode(), Decode()
├── q3m_test.go
├── cmd/q3m/
│   ├── main.go            # Point d'entrée CLI (Cobra)
│   ├── encode.go          # Sous-commande encode
│   ├── decode.go          # Sous-commande decode
│   ├── info.go            # Sous-commande info
│   ├── tolam.go           # Sous-commande tolam (WGS84 → Lambert93)
│   └── fromlam.go         # Sous-commande fromlam (Lambert93 → WGS84)
└── tools/wordgen/
    └── main.go            # Génération du dictionnaire (Lexique383)

Limitations

  • Couverture : France métropolitaine et Corse uniquement. Les DOM-TOM ne sont pas couverts par Lambert93.
  • Cellules en mer : tout le rectangle englobant Lambert93 est encodé, y compris les zones maritimes.
  • Pas de correction orthographique : un mot mal saisi retournera une erreur, pas une suggestion.

Licence

Ce projet est distribué sous licence Mozilla Public License 2.0.

Le dictionnaire (words_fr.txt) est dérivé de Lexique383, distribué sous CC BY-SA 4.0.

Crédits

  • Lexique383 (lexique.org) pour la base lexicale
  • IGN pour les paramètres de la projection Lambert93/RGF93

Documentation

Index

Constants

View Source
const (
	EMin = 100_000.0
	EMax = 1_250_000.0
	NMin = 6_050_000.0
	NMax = 7_120_000.0
)

Grid bounds in Lambert93 metres.

View Source
const (
	GridWidth  uint64 = 1_150_000              // EMax - EMin
	GridHeight uint64 = 1_070_000              // NMax - NMin
	TotalCells uint64 = GridWidth * GridHeight // 1_230_500_000_000
)

Grid dimensions (1m cells).

View Source
const DictSize = 10800

DictSize is the number of words in the dictionary.

Variables

This section is empty.

Functions

func CellCenter

func CellCenter(idx uint64) (E, N float64)

CellCenter returns the Lambert93 coordinates of the centre of the cell identified by idx (+0.5m offset).

func CellIndex

func CellIndex(E, N float64) (uint64, bool)

CellIndex returns the grid cell index for the given Lambert93 coordinates. Returns false if the point is outside the grid.

func FromLambert93

func FromLambert93(E, N float64) (lat, lon float64)

FromLambert93 converts Lambert93 (E, N in metres) to WGS84 (lat, lon in degrees).

func IndexOf

func IndexOf(word string) (int, bool)

IndexOf returns the index of word in the dictionary. Returns -1 and false if the word is not found.

func Shuffle

func Shuffle(idx uint64) uint64

Shuffle applies a bijective permutation on idx within [0, TotalCells). Uses cycle walking to handle the non-power-of-2 domain.

func ToLambert93

func ToLambert93(lat, lon float64) (E, N float64)

ToLambert93 converts WGS84 (lat, lon in degrees) to Lambert93 (E, N in metres).

func Unshuffle

func Unshuffle(idx uint64) uint64

Unshuffle inverts Shuffle: given a shuffled index, returns the original.

func WordAt

func WordAt(i int) string

WordAt returns the word at position i in the dictionary.

Types

type Address

type Address struct {
	W1 string `json:"w1"`
	W2 string `json:"w2"`
	W3 string `json:"w3"`
}

Address represents a q3m three-word address.

func Encode

func Encode(lat, lon float64) (Address, error)

Encode converts WGS84 coordinates to a q3m three-word address.

func (Address) String

func (a Address) String() string

String returns the dotted representation "w1.w2.w3".

type Coordinate

type Coordinate struct {
	Lat float64 `json:"lat"`
	Lon float64 `json:"lon"`
}

Coordinate represents a WGS84 position.

func Decode

func Decode(address string) (Coordinate, error)

Decode converts a q3m three-word address (dot-separated) back to WGS84 coordinates. The returned coordinate is the centre of the 1m x 1m cell.

Directories

Path Synopsis
cmd
q3m command
tools
wordgen command
Command wordgen generates the q3m word list from Lexique383.
Command wordgen generates the q3m word list from Lexique383.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL