1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! Location lookup for Indonesian villages by GPS coordinates or name.
//!
//! Returns BMKG-compatible `adm4` administrative codes (e.g., `31.71.03.1001`)
//! for 82,689 villages across Indonesia, based on Kepmendagri No 300.2.2-2430
//! Tahun 2025.
//!
//! # Quick start
//!
//! ```
//! use wilayah;
//!
//! let conn = wilayah::open().expect("database");
//! let nearest = wilayah::find_nearest(&conn, -6.1647, 106.8453, 5).expect("query");
//! assert!(!nearest.is_empty());
//! ```
//!
//! # Data
//!
//! Sourced from [cahyadsn/wilayah](https://github.com/cahyadsn/wilayah) and
//! [cahyadsn/wilayah_boundaries](https://github.com/cahyadsn/wilayah_boundaries),
//! based on official Kemendagri administrative codes with pre-computed village
//! centroids from BIG (Badan Informasi Geospasial) polygon boundaries.
//!
//! On first `cargo build`, the raw data is downloaded from GitHub and a SQLite
//! database with RTree spatial index and FTS5 full-text search is built. The
//! database is embedded into the binary at compile time. Subsequent builds
//! reuse the cached database.
pub use ;
/// Open the embedded database.
///
/// Loads the 20 MB SQLite database from the compiled binary into memory using
/// SQLite's online backup API. The database contains 82,689 villages with
/// spatial (RTree) and full-text (FTS5) indexes.
///
/// # Example
///
/// ```
/// let conn = wilayah::open()?;
/// # Ok::<_, rusqlite::Error>(())
/// ```
/// Find the nearest villages to a given latitude/longitude.
///
/// Uses a SQLite RTree spatial index for fast bounding-box filtering, followed
/// by Haversine distance calculation to find the closest villages.
///
/// # Arguments
///
/// * `conn` - Database connection from [`open()`]
/// * `lat` - Latitude (-90..90)
/// * `lon` - Longitude (-180..180)
/// * `limit` - Maximum number of results to return (clamped to 1..20)
///
/// # Example
///
/// ```
/// let conn = wilayah::open()?;
/// let results = wilayah::find_nearest(&conn, -6.1647, 106.8453, 5)?;
/// for v in results {
/// println!("{} ({:.1} km)", v.name, v.dist_km.unwrap());
/// }
/// # Ok::<_, rusqlite::Error>(())
/// ```
/// Search for villages by name.
///
/// Uses FTS5 full-text search matching against village name, district, city,
/// and province. Supports partial matches and returns results ranked by BM25.
///
/// # Arguments
///
/// * `conn` - Database connection from [`open()`]
/// * `query` - Search query (e.g., `"kemayoran"` or `"kemayoran jakarta"`)
/// * `limit` - Maximum number of results to return (clamped to 1..100)
///
/// # Example
///
/// ```
/// let conn = wilayah::open()?;
/// let results = wilayah::find_by_name(&conn, "kemayoran", 10)?;
/// for v in results {
/// println!("{} in {}, {}", v.name, v.district, v.province);
/// }
/// # Ok::<_, rusqlite::Error>(())
/// ```
/// Get the total number of villages in the database.