xiangshan

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 2, 2026 License: MIT Imports: 17 Imported by: 0

README

香山/xiangshan

Xiangshan is a Go reverse geocoder for OvertureMaps administrative divisions. It builds a size-prefixed FlatBuffers dataset and queries it through mmap with a two-tier grid index.

Install

go get github.com/ringsaturn/xiangshan

Download prebuilt data from(No reliablity guarantee, consider building from source if you need it for production use), based on 2026-04-15.0 OvertureMaps data:

Under ODbL license
© OpenStreetMap contributors, Overture Maps Foundation
https://dataset.ringsaturn.me/xiangshan/divisions.cf.bin

Build for low memory environments(AWS Lambda, Cloudflare Workers, etc), based on 2026-04-15.0 OvertureMaps data:
https://dataset.ringsaturn.me/xiangshan/divisions.xs-poly
https://dataset.ringsaturn.me/xiangshan/divisions.xs-index.gz
Build Data

The default pipeline reads OvertureMaps division parquet files under data/divisions and writes build/divisions.cf.bin.

make pipeline

Individual stages are available as make extract, make simplify, and make encode.

Go API

package main

import (
	"fmt"

	"github.com/ringsaturn/xiangshan"
)

func main() {
	finder, err := xiangshan.NewFinder("build/divisions.cf.bin")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	defer finder.Close()

	res := finder.Query(2.2945, 48.8584)
	fmt.Println(res.Country, res.Region, res.County, res.LocalAdmin)
	// Output: France Île-de-France Paris Paris
}

Finder is safe for concurrent reads after construction. Query input is longitude, latitude in WGS84 degrees.

CLI

go install github.com/ringsaturn/xiangshan/cmd/xs-query@latest
xs-query -data build/divisions.cf.bin -lng 2.2945 -lat 48.8584
xs-query -data build/divisions.cf.bin -format json -lng 139.6917 -lat 35.6895
printf '2.2945,48.8584\n139.6917,35.6895\n' | xs-query -data build/divisions.cf.bin -stdin

HTTP Server

go install github.com/ringsaturn/xiangshan/cmd/xs-serve@latest
xs-serve -data build/divisions.cf.bin -addr :8080
curl 'http://localhost:8080/query?lng=2.2945&lat=48.8584'

Response:

{
  "country": "France",
  "region": "Île-de-France",
  "county": "Paris",
  "local_admin": "Paris",
  "locality": "Paris",
  "country_id": "51bc7545-7602-435d-8b11-90117246a387",
  "region_id": "ad3154a9-92ec-40ef-ba0d-8443d8e024fd",
  "county_id": "a86c4ba9-8261-4a37-9fac-0c6aa9456d05",
  "local_admin_id": "4e5c3982-82ce-43ba-aef2-6b501d542604",
  "locality_id": "97b66514-3f41-47ac-a348-9cfd51d983d5"
}

Benchmarks

go test -bench=. -benchmem ./...
go test -bench=BenchmarkQuery -benchmem -memprofile=mem.out .
go tool pprof -alloc_objects mem.out

A sample benchmark run on Apple M3 Max:

go test -bench=. -benchmem ./...
goos: darwin
goarch: arm64
pkg: github.com/ringsaturn/xiangshan
cpu: Apple M3 Max
BenchmarkQuery_WorldCities-16             145689             11687 ns/op               0 B/op          0 allocs/op
BenchmarkQuery_DenseEurope-16             156381             14802 ns/op               0 B/op          0 allocs/op
BenchmarkQuery_Parallel-16               1000000              1084 ns/op               0 B/op          0 allocs/op
PASS

License

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Finder

type Finder struct {
	// contains filtered or unexported fields
}

func NewCompressedFinder

func NewCompressedFinder(path string) (*Finder, error)

func NewFinder

func NewFinder(path string) (*Finder, error)

func (*Finder) Close

func (f *Finder) Close() error

func (*Finder) Query

func (f *Finder) Query(lng, lat float64) Result

func (*Finder) QueryI18n

func (f *Finder) QueryI18n(lng, lat float64, lang string) Result

QueryI18n returns a Result with names resolved in lang (e.g. "zh", "en", "ja"). Falls back to the primary name when a translation is unavailable.

type HTTPRangeFetcher added in v0.2.0

type HTTPRangeFetcher struct {
	URL    string
	Client *http.Client // nil uses http.DefaultClient
}

HTTPRangeFetcher implements SlabFetcher against any HTTP server that supports Range requests (S3, R2, GCS signed URLs, plain HTTP file servers).

func (*HTTPRangeFetcher) FetchRange added in v0.2.0

func (f *HTTPRangeFetcher) FetchRange(ctx context.Context, offset uint64, length uint32) ([]byte, error)

type RemoteFinder added in v0.2.0

type RemoteFinder struct {
	// contains filtered or unexported fields
}

RemoteFinder queries geographic divisions using a remote polygon slab.

The compact index (~26 MB raw, ~16 MB gzipped) holds only subtype, bbox, and slab offsets per division — no names or IDs. All string metadata is read from the slab chunk after polygon containment is confirmed, so name and ID resolution always comes from the same fetch that checks the polygon.

This design minimises cold-start index size and in-memory footprint (~43 MB for the index structures) at the cost of at most one extra Range request per query on the short-circuit path (single unambiguous candidate, interior point) and for the country preindex path.

func NewRemoteFinder added in v0.2.0

func NewRemoteFinder(indexBytes []byte, fetcher SlabFetcher, cacheSize int) (*RemoteFinder, error)

NewRemoteFinder constructs a RemoteFinder from a raw (uncompressed) compact index (XSCI format) and a SlabFetcher for the .xs-poly slab. cacheSize is the number of slab chunks kept in an LRU cache (0 = disabled).

func NewRemoteFinderFromGzip added in v0.2.0

func NewRemoteFinderFromGzip(gzipBytes []byte, fetcher SlabFetcher, cacheSize int) (*RemoteFinder, error)

NewRemoteFinderFromGzip accepts a gzip-compressed index produced by xs-remote-split --compress.

func (*RemoteFinder) Query added in v0.2.0

func (f *RemoteFinder) Query(ctx context.Context, lng, lat float64) (Result, error)

Query returns the geographic result for (lng, lat) using primary names.

func (*RemoteFinder) QueryI18n added in v0.2.0

func (f *RemoteFinder) QueryI18n(ctx context.Context, lng, lat float64, lang string) (Result, error)

QueryI18n returns the geographic result with names resolved in lang (e.g. "zh", "en", "ja"). Falls back to the primary name when unavailable.

type Result

type Result struct {
	Country      string
	CountryID    string
	Region       string
	RegionID     string
	County       string
	CountyID     string
	LocalAdmin   string
	LocalAdminID string
	Locality     string
	LocalityID   string
}

type SlabFetcher added in v0.2.0

type SlabFetcher interface {
	FetchRange(ctx context.Context, offset uint64, length uint32) ([]byte, error)
}

SlabFetcher fetches a byte range from the polygon slab.

Directories

Path Synopsis
cmd
xs-query command
xs-serve command
generated
internal
cmd/xs-compress command
cmd/xs-encode command
cmd/xs-remote-split command
xs-remote-split splits a .cf.bin compressed divisions file into two files:
xs-remote-split splits a .cf.bin compressed divisions file into two files:
cmd/xs-simplify command
compactidx
Package compactidx provides a compact binary format for the remote finder index.
Package compactidx provides a compact binary format for the remote finder index.
geom
Package geom provides zero-dependency 2-D geometry primitives and a YStripes-indexed point-in-polygon query.
Package geom provides zero-dependency 2-D geometry primitives and a YStripes-indexed point-in-polygon query.
topology
Package topology provides topology-aware polygon simplification and deduplication for division boundary data.
Package topology provides topology-aware polygon simplification and deduplication for division boundary data.

Jump to

Keyboard shortcuts

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