geodb-rs 0.1.5__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/Cargo.lock +7 -6
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/Cargo.toml +14 -2
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/PKG-INFO +1 -1
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/Cargo.toml +4 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/README.md +150 -21
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/legacy_model/search.rs +7 -3
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/loader/builder.rs +45 -3
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/model/mod.rs +2 -0
- geodb_rs-0.1.6/crates/geodb-core/src/model/query.rs +471 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/prelude.rs +1 -1
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-py/Cargo.toml +1 -1
- geodb_rs-0.1.6/crates/geodb-py/geodb_rs_data/geodb.nested.comp.blobs.bin +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-py/src/lib.rs +53 -0
- geodb_rs-0.1.5/crates/geodb-py/geodb_rs_data/city_meta.json +0 -199
- geodb_rs-0.1.5/crates/geodb-py/geodb_rs_data/countries+states+cities.json.gz +0 -0
- geodb_rs-0.1.5/geodb_rs_data/geodb.flat.comp.blobs.bin +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/README.md +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/benches/benchmarks.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/build.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/alias.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/api.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/bin/profile_search.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/common/mod.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/common/raw.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/common/raw_normalize.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/error.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/legacy_model/convert.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/legacy_model/mod.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/legacy_model/nested.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/lib.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/loader/binary_load.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/loader/common_io.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/loader/mod.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/model/convert.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/model/flat.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/model/search.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/spatial.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/text.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/src/traits.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/tests/basic.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-core/tests/city_meta_alias_search.rs +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-py/README.md +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-py/example.py +0 -0
- {geodb_rs-0.1.5/crates/geodb-core/data → geodb_rs-0.1.6/crates/geodb-py/geodb_rs_data}/city_meta.json +0 -0
- {geodb_rs-0.1.5/crates/geodb-core/data → geodb_rs-0.1.6/crates/geodb-py/geodb_rs_data}/countries+states+cities.json.gz +0 -0
- {geodb_rs-0.1.5/crates/geodb-core/data → geodb_rs-0.1.6/crates/geodb-py/geodb_rs_data}/geodb.flat.comp.blobs.bin +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/crates/geodb-py/tests/test_basic.py +0 -0
- {geodb_rs-0.1.5/crates/geodb-py → geodb_rs-0.1.6}/geodb_rs_data/geodb.flat.comp.blobs.bin +0 -0
- {geodb_rs-0.1.5 → geodb_rs-0.1.6}/pyproject.toml +0 -0
|
@@ -531,7 +531,7 @@ dependencies = [
|
|
|
531
531
|
|
|
532
532
|
[[package]]
|
|
533
533
|
name = "geodb-cli"
|
|
534
|
-
version = "0.1.
|
|
534
|
+
version = "0.1.6"
|
|
535
535
|
dependencies = [
|
|
536
536
|
"anyhow",
|
|
537
537
|
"clap",
|
|
@@ -540,7 +540,7 @@ dependencies = [
|
|
|
540
540
|
|
|
541
541
|
[[package]]
|
|
542
542
|
name = "geodb-core"
|
|
543
|
-
version = "0.1.
|
|
543
|
+
version = "0.1.6"
|
|
544
544
|
dependencies = [
|
|
545
545
|
"bincode",
|
|
546
546
|
"criterion",
|
|
@@ -556,19 +556,20 @@ dependencies = [
|
|
|
556
556
|
|
|
557
557
|
[[package]]
|
|
558
558
|
name = "geodb-ffi"
|
|
559
|
-
version = "0.1.
|
|
559
|
+
version = "0.1.6"
|
|
560
560
|
dependencies = [
|
|
561
561
|
"bincode",
|
|
562
562
|
"flate2",
|
|
563
563
|
"geodb-core",
|
|
564
564
|
"once_cell",
|
|
565
565
|
"serde",
|
|
566
|
+
"serde_json",
|
|
566
567
|
"uniffi",
|
|
567
568
|
]
|
|
568
569
|
|
|
569
570
|
[[package]]
|
|
570
571
|
name = "geodb-py"
|
|
571
|
-
version = "0.1.
|
|
572
|
+
version = "0.1.6"
|
|
572
573
|
dependencies = [
|
|
573
574
|
"geodb-core",
|
|
574
575
|
"pyo3",
|
|
@@ -579,14 +580,14 @@ dependencies = [
|
|
|
579
580
|
|
|
580
581
|
[[package]]
|
|
581
582
|
name = "geodb-rs"
|
|
582
|
-
version = "0.1.
|
|
583
|
+
version = "0.1.6"
|
|
583
584
|
dependencies = [
|
|
584
585
|
"geodb-core",
|
|
585
586
|
]
|
|
586
587
|
|
|
587
588
|
[[package]]
|
|
588
589
|
name = "geodb-wasm"
|
|
589
|
-
version = "0.1.
|
|
590
|
+
version = "0.1.6"
|
|
590
591
|
dependencies = [
|
|
591
592
|
"bincode",
|
|
592
593
|
"console_error_panic_hook",
|
|
@@ -14,7 +14,7 @@ members = ["crates/geodb-py"]
|
|
|
14
14
|
resolver = "2"
|
|
15
15
|
|
|
16
16
|
[workspace.package]
|
|
17
|
-
version = "0.1.
|
|
17
|
+
version = "0.1.6"
|
|
18
18
|
edition = "2021"
|
|
19
19
|
license = "MIT"
|
|
20
20
|
repository = "https://github.com/holg/geodb-rs"
|
|
@@ -31,7 +31,19 @@ serde-wasm-bindgen = "0.6"
|
|
|
31
31
|
serde_json = { version = "1" }
|
|
32
32
|
thiserror = "1"
|
|
33
33
|
wasm-bindgen = "=0.2.105" # Pinned to match trunk 0.21.14's bundled wasm-bindgen-cli
|
|
34
|
-
web-sys = { version = "0.3", features = [
|
|
34
|
+
web-sys = { version = "0.3", features = [
|
|
35
|
+
"console",
|
|
36
|
+
"Window",
|
|
37
|
+
"Document",
|
|
38
|
+
"Location",
|
|
39
|
+
"Element",
|
|
40
|
+
"HtmlElement",
|
|
41
|
+
"HtmlInputElement",
|
|
42
|
+
"Event",
|
|
43
|
+
"EventTarget",
|
|
44
|
+
"UrlSearchParams",
|
|
45
|
+
"CssStyleDeclaration"
|
|
46
|
+
] }
|
|
35
47
|
|
|
36
48
|
[dependencies]
|
|
37
49
|
geodb-core = { path = "crates/geodb-core" }
|
|
@@ -10,6 +10,10 @@ readme = "README.md"
|
|
|
10
10
|
documentation = "https://docs.rs/geodb-core"
|
|
11
11
|
keywords = ["geography", "database", "countries", "cities", "geo"]
|
|
12
12
|
categories = ["database", "data-structures"]
|
|
13
|
+
# Exclude all data files from published package (reduces package to <1MB)
|
|
14
|
+
# Data is automatically downloaded from GitHub on first load
|
|
15
|
+
# Download requires the 'builder' feature (enabled by default)
|
|
16
|
+
exclude = ["data/*.bin", "data/*.json.gz", "data/*.json"]
|
|
13
17
|
|
|
14
18
|
# Configure docs.rs build
|
|
15
19
|
[package.metadata.docs.rs]
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
[](https://pypi.org/project/geodb-rs/)
|
|
21
21
|
[](https://pypi.org/project/geodb-rs/)
|
|
22
22
|
|
|
23
|
+
### pub.dev (Flutter)
|
|
24
|
+
[](https://pub.dev/packages/geodb_flutter)
|
|
25
|
+
[](https://pub.dev/packages/geodb_flutter)
|
|
26
|
+
|
|
23
27
|
### App Store
|
|
24
28
|
[](https://apps.apple.com/app/geodb-rs/id6755972245)
|
|
25
29
|
[](https://testflight.apple.com/join/TuFejJEq)
|
|
@@ -33,6 +37,7 @@ This repository is a **Cargo workspace** containing:
|
|
|
33
37
|
- **`geodb-wasm`** — WebAssembly bindings + browser demo — docs: https://docs.rs/geodb-wasm
|
|
34
38
|
- **`geodb-py`** — Python bindings (published on PyPI as "geodb-rs") — https://pypi.org/project/geodb-rs/
|
|
35
39
|
- **`geodb-ffi`** — FFI bindings for mobile platforms (iOS, macOS, watchOS, Android)
|
|
40
|
+
- **`geodb_flutter`** — Flutter plugin (published on pub.dev) — https://pub.dev/packages/geodb_flutter
|
|
36
41
|
|
|
37
42
|
---
|
|
38
43
|
|
|
@@ -55,13 +60,21 @@ The dataset is adapted from
|
|
|
55
60
|
https://github.com/dr5hn/countries-states-cities-database
|
|
56
61
|
(licensed under **CC-BY-4.0**, attribution required).
|
|
57
62
|
|
|
58
|
-
> Important: Data source
|
|
63
|
+
> **Important: Data source and automatic downloading**
|
|
59
64
|
>
|
|
60
|
-
> geodb-core
|
|
65
|
+
> geodb-core uses the upstream dataset from the dr5hn/countries-states-cities-database repository:
|
|
61
66
|
>
|
|
62
67
|
> https://github.com/dr5hn/countries-states-cities-database/blob/master/json/countries%2Bstates%2Bcities.json.gz
|
|
63
68
|
>
|
|
64
|
-
>
|
|
69
|
+
> **Automatic data download and caching:**
|
|
70
|
+
> - The published crate does NOT include data files (keeps package size under 1MB)
|
|
71
|
+
> - **On first load**, the library automatically downloads the dataset from GitHub (~3.7MB)
|
|
72
|
+
> - After download, a binary cache is generated for fast subsequent loads
|
|
73
|
+
> - Download and cache generation happen only once per system
|
|
74
|
+
> - Requires the `builder` feature (enabled by default) and internet connection for first load
|
|
75
|
+
> - Downloaded data and cache stored in `crates/geodb-core/data/` directory
|
|
76
|
+
>
|
|
77
|
+
> If you update or replace the dataset, ensure it retains the same JSON structure. Please observe the CC-BY-4.0 license and attribution of the upstream project.
|
|
65
78
|
|
|
66
79
|
---
|
|
67
80
|
|
|
@@ -71,14 +84,16 @@ https://github.com/dr5hn/countries-states-cities-database
|
|
|
71
84
|
|
|
72
85
|
```toml
|
|
73
86
|
[dependencies]
|
|
74
|
-
geodb-core = "0.
|
|
87
|
+
geodb-core = "0.1"
|
|
75
88
|
```
|
|
76
89
|
|
|
90
|
+
**Note:** First load will download the dataset from GitHub (~3.7MB) and build the binary cache (requires internet connection). Subsequent loads will be instant using the cached binary.
|
|
91
|
+
|
|
77
92
|
### For WebAssembly (browser/Node)
|
|
78
93
|
|
|
79
94
|
```toml
|
|
80
95
|
[dependencies]
|
|
81
|
-
geodb-wasm = "0.
|
|
96
|
+
geodb-wasm = "0.1"
|
|
82
97
|
```
|
|
83
98
|
|
|
84
99
|
### For Swift (iOS, macOS, watchOS)
|
|
@@ -114,7 +129,25 @@ for city in results {
|
|
|
114
129
|
let nearest = engine.findNearest(lat: 52.52, lng: 13.405, count: 10)
|
|
115
130
|
```
|
|
116
131
|
|
|
117
|
-
### For Android
|
|
132
|
+
### For Flutter (iOS, Android, macOS)
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
# pubspec.yaml
|
|
136
|
+
dependencies:
|
|
137
|
+
geodb_flutter: ^0.1.8
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```dart
|
|
141
|
+
import 'package:geodb_flutter/geodb_flutter.dart';
|
|
142
|
+
|
|
143
|
+
final geodb = GeodbFlutter();
|
|
144
|
+
await geodb.initialize();
|
|
145
|
+
|
|
146
|
+
final results = await geodb.smartSearch('Berlin');
|
|
147
|
+
final nearest = await geodb.findNearest(lat: 52.52, lng: 13.405, count: 10);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### For Android (Kotlin - Native)
|
|
118
151
|
|
|
119
152
|
See the example app in `GeoDB-App/android-app/`. The app uses UniFFI-generated Kotlin bindings.
|
|
120
153
|
|
|
@@ -263,21 +296,46 @@ if let Some(us) = db.find_country_by_iso2("US") {
|
|
|
263
296
|
let countries = db.find_countries_by_phone_code("+44");
|
|
264
297
|
```
|
|
265
298
|
|
|
266
|
-
###
|
|
299
|
+
### Filter-based city search (CityQuery API)
|
|
300
|
+
|
|
301
|
+
Use the chainable `query_cities()` API to disambiguate cities with common names:
|
|
267
302
|
|
|
268
303
|
```rust
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
304
|
+
use geodb_core::prelude::*;
|
|
305
|
+
|
|
306
|
+
let db = GeoDb::<DefaultBackend>::load()?;
|
|
307
|
+
|
|
308
|
+
// Find Springfield in Illinois, US (not the 30+ other Springfields!)
|
|
309
|
+
let results = db.query_cities()
|
|
310
|
+
.filter_country("US")
|
|
311
|
+
.filter_region("Illinois")
|
|
312
|
+
.filter_city("Springfield")
|
|
278
313
|
.collect();
|
|
314
|
+
|
|
315
|
+
assert_eq!(results.len(), 1);
|
|
316
|
+
let (city, state, country) = &results[0];
|
|
317
|
+
println!("{} - {}, {}", city.name(), state.name(), country.iso2());
|
|
318
|
+
// Output: Springfield - Illinois, US
|
|
319
|
+
|
|
320
|
+
// Find Lüdinghausen in NRW (accent-insensitive search)
|
|
321
|
+
let city = db.query_cities()
|
|
322
|
+
.filter_country("DE")
|
|
323
|
+
.filter_region("Nordrhein-Westfalen")
|
|
324
|
+
.filter_city("Ludinghausen") // works without umlaut too
|
|
325
|
+
.first();
|
|
326
|
+
|
|
327
|
+
// Count all Springfields worldwide
|
|
328
|
+
let count = db.query_cities()
|
|
329
|
+
.filter_city("Springfield")
|
|
330
|
+
.count();
|
|
331
|
+
println!("Found {} cities named Springfield", count);
|
|
279
332
|
```
|
|
280
333
|
|
|
334
|
+
Filters support:
|
|
335
|
+
- **Country**: ISO2/ISO3 codes (`"US"`, `"DEU"`) or name substring (`"Germany"`)
|
|
336
|
+
- **Region**: State code (`"CA"`, `"NW"`) or name (`"California"`, `"Nordrhein-Westfalen"`)
|
|
337
|
+
- **City**: Name substring with accent-insensitive matching
|
|
338
|
+
|
|
281
339
|
---
|
|
282
340
|
|
|
283
341
|
# WebAssembly (`geodb-wasm`)
|
|
@@ -316,13 +374,31 @@ Install:
|
|
|
316
374
|
cargo install geodb-cli
|
|
317
375
|
```
|
|
318
376
|
|
|
319
|
-
|
|
377
|
+
Commands:
|
|
320
378
|
|
|
321
379
|
```bash
|
|
322
|
-
geodb-cli --help
|
|
323
|
-
geodb-cli stats
|
|
324
|
-
geodb-cli
|
|
325
|
-
geodb-cli
|
|
380
|
+
geodb-cli --help # Show all commands
|
|
381
|
+
geodb-cli stats # Database statistics
|
|
382
|
+
geodb-cli countries # List all countries
|
|
383
|
+
geodb-cli country US # Lookup country by ISO2/ISO3 code
|
|
384
|
+
geodb-cli states US # List all states for a country
|
|
385
|
+
geodb-cli cities "Springfield" # Search cities by substring
|
|
386
|
+
geodb-cli smart "Berlin" # Smart search (cities/states/countries)
|
|
387
|
+
geodb-cli nearest --lat 52.52 --lng 13.405 -n 5 # Find 5 nearest cities
|
|
388
|
+
geodb-cli radius --lat 52.52 --lng 13.405 -r 50 # Cities within 50km
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Filter-based query (disambiguate cities):
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
# Find Springfield in Illinois, US (not the 30+ other Springfields!)
|
|
395
|
+
geodb-cli query --city Springfield --country US --region Illinois
|
|
396
|
+
|
|
397
|
+
# Find Lüdinghausen in NRW, Germany
|
|
398
|
+
geodb-cli query --city "Lüdinghausen" --country DE --region "Nordrhein-Westfalen"
|
|
399
|
+
|
|
400
|
+
# List all cities in Bavaria
|
|
401
|
+
geodb-cli query --country Germany --region Bavaria -n 50
|
|
326
402
|
```
|
|
327
403
|
|
|
328
404
|
Docs.rs: https://docs.rs/geodb-cli
|
|
@@ -365,6 +441,59 @@ print(db.stats()) # (countries, states, cities)
|
|
|
365
441
|
|
|
366
442
|
---
|
|
367
443
|
|
|
444
|
+
# Flutter Plugin (`geodb_flutter`)
|
|
445
|
+
|
|
446
|
+
Cross-platform Flutter plugin with native Rust performance.
|
|
447
|
+
|
|
448
|
+
### pub.dev
|
|
449
|
+
[](https://pub.dev/packages/geodb_flutter)
|
|
450
|
+
|
|
451
|
+
### Installation
|
|
452
|
+
|
|
453
|
+
```yaml
|
|
454
|
+
dependencies:
|
|
455
|
+
geodb_flutter: ^0.1.8
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Platform Support
|
|
459
|
+
|
|
460
|
+
| Platform | Status | Architectures |
|
|
461
|
+
|----------|--------|---------------|
|
|
462
|
+
| iOS | Ready | arm64 device, arm64 simulator |
|
|
463
|
+
| macOS | Ready | arm64, x86_64 (Universal) |
|
|
464
|
+
| Android | Ready | arm64-v8a, armeabi-v7a, x86_64, x86 |
|
|
465
|
+
|
|
466
|
+
### Quick Start
|
|
467
|
+
|
|
468
|
+
```dart
|
|
469
|
+
import 'package:geodb_flutter/geodb_flutter.dart';
|
|
470
|
+
|
|
471
|
+
final geodb = GeodbFlutter();
|
|
472
|
+
|
|
473
|
+
// Initialize (required first)
|
|
474
|
+
await geodb.initialize();
|
|
475
|
+
|
|
476
|
+
// Get stats
|
|
477
|
+
final stats = await geodb.getStats();
|
|
478
|
+
print('${stats.cities} cities in ${stats.countries} countries');
|
|
479
|
+
|
|
480
|
+
// Smart search
|
|
481
|
+
final results = await geodb.smartSearch('Berlin');
|
|
482
|
+
for (final city in results) {
|
|
483
|
+
print('${city.name}, ${city.country} (${city.iso2})');
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Find nearest cities
|
|
487
|
+
final nearest = await geodb.findNearest(lat: 52.52, lng: 13.405, count: 10);
|
|
488
|
+
|
|
489
|
+
// Search within radius
|
|
490
|
+
final nearby = await geodb.findInRadius(lat: 52.52, lng: 13.405, radiusKm: 50.0);
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
For a complete example, see `GeoDB-Apps/geodb_city_autocomplete/` in the repository.
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
368
497
|
# Mobile Apps (`GeoDB-App`)
|
|
369
498
|
|
|
370
499
|
The repository includes native apps for Apple and Android platforms:
|
|
@@ -495,7 +495,7 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
|
|
|
495
495
|
}
|
|
496
496
|
None
|
|
497
497
|
}
|
|
498
|
-
fn find_nearest(&self, lat: f64, lng: f64, count: usize) -> Vec
|
|
498
|
+
fn find_nearest(&self, lat: f64, lng: f64, count: usize) -> Vec<CityContext<'_, B>> {
|
|
499
499
|
// Legacy: We don't have a spatial_index, so we must scan the whole world.
|
|
500
500
|
// This is slower (O(N)), but correct.
|
|
501
501
|
|
|
@@ -509,7 +509,7 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
|
|
|
509
509
|
|
|
510
510
|
// Squared Euclidean for fast sorting
|
|
511
511
|
let dist = distance_squared(lat, lng, c_lat, c_lng);
|
|
512
|
-
candidates.push((dist, city));
|
|
512
|
+
candidates.push((dist, city, state, country));
|
|
513
513
|
}
|
|
514
514
|
}
|
|
515
515
|
}
|
|
@@ -518,7 +518,11 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
|
|
|
518
518
|
candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
|
|
519
519
|
|
|
520
520
|
// Take top N
|
|
521
|
-
candidates
|
|
521
|
+
candidates
|
|
522
|
+
.into_iter()
|
|
523
|
+
.take(count)
|
|
524
|
+
.map(|(_, c, s, co)| (c, s, co))
|
|
525
|
+
.collect()
|
|
522
526
|
}
|
|
523
527
|
|
|
524
528
|
// crates/geodb-core/src/model/search.rs
|
|
@@ -9,16 +9,19 @@ use crate::common::raw::CountryRaw;
|
|
|
9
9
|
use crate::common::raw_normalize::apply_all_metadata;
|
|
10
10
|
use crate::error::{GeoError, Result};
|
|
11
11
|
use std::fs::{self, File};
|
|
12
|
-
use std::io::BufWriter;
|
|
12
|
+
use std::io::{BufWriter, Write};
|
|
13
13
|
use std::path::Path;
|
|
14
14
|
|
|
15
15
|
#[cfg(feature = "compact")]
|
|
16
16
|
use flate2::{write::GzEncoder, Compression};
|
|
17
17
|
|
|
18
|
+
// Raw download URL for the dataset (GitHub raw content)
|
|
19
|
+
const DATA_DOWNLOAD_URL: &str = "https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/master/json/countries%2Bstates%2Bcities.json.gz";
|
|
20
|
+
|
|
18
21
|
// Extends GeoDb with Builder/Source capabilities
|
|
19
22
|
impl GeoDb<DefaultBackend> {
|
|
20
23
|
/// **Smart Builder Logic:**
|
|
21
|
-
/// Checks cache -> Loads Binary OR Builds Source -> Writes Cache.
|
|
24
|
+
/// Checks cache -> Downloads if missing -> Loads Binary OR Builds Source -> Writes Cache.
|
|
22
25
|
pub(super) fn load_via_builder(path: &Path, filter: Option<&[&str]>) -> Result<Self> {
|
|
23
26
|
let cache_path = common_io::get_cache_path(path);
|
|
24
27
|
|
|
@@ -30,7 +33,16 @@ impl GeoDb<DefaultBackend> {
|
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
// 2.
|
|
36
|
+
// 2. Download if source file doesn't exist
|
|
37
|
+
if !path.exists() {
|
|
38
|
+
eprintln!(
|
|
39
|
+
"Data file not found at {:?}, downloading from GitHub...",
|
|
40
|
+
path
|
|
41
|
+
);
|
|
42
|
+
Self::download_dataset(path)?;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 3. Build (Slow)
|
|
34
46
|
// This converts JSON -> Active Structs (Flat or Nested)
|
|
35
47
|
let db = Self::build_from_source(path)?;
|
|
36
48
|
|
|
@@ -158,4 +170,34 @@ impl GeoDb<DefaultBackend> {
|
|
|
158
170
|
|
|
159
171
|
Ok(())
|
|
160
172
|
}
|
|
173
|
+
|
|
174
|
+
/// Downloads the dataset from GitHub to the specified path
|
|
175
|
+
fn download_dataset(dest_path: &Path) -> Result<()> {
|
|
176
|
+
// Ensure parent directory exists
|
|
177
|
+
if let Some(parent) = dest_path.parent() {
|
|
178
|
+
fs::create_dir_all(parent).map_err(GeoError::Io)?;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Download using reqwest (blocking)
|
|
182
|
+
let response = reqwest::blocking::get(DATA_DOWNLOAD_URL)
|
|
183
|
+
.map_err(|e| GeoError::InvalidData(format!("Failed to download dataset: {}", e)))?;
|
|
184
|
+
|
|
185
|
+
if !response.status().is_success() {
|
|
186
|
+
return Err(GeoError::InvalidData(format!(
|
|
187
|
+
"Failed to download dataset: HTTP {}",
|
|
188
|
+
response.status()
|
|
189
|
+
)));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Write to file
|
|
193
|
+
let mut file = File::create(dest_path).map_err(GeoError::Io)?;
|
|
194
|
+
let bytes = response
|
|
195
|
+
.bytes()
|
|
196
|
+
.map_err(|e| GeoError::InvalidData(format!("Failed to read response: {}", e)))?;
|
|
197
|
+
|
|
198
|
+
file.write_all(&bytes).map_err(GeoError::Io)?;
|
|
199
|
+
|
|
200
|
+
eprintln!("Downloaded {} bytes to {:?}", bytes.len(), dest_path);
|
|
201
|
+
Ok(())
|
|
202
|
+
}
|
|
161
203
|
}
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
pub mod flat;
|
|
55
55
|
// pub mod region;
|
|
56
56
|
pub mod convert;
|
|
57
|
+
pub mod query;
|
|
57
58
|
pub mod search;
|
|
58
59
|
// Re-exports for convenience
|
|
59
60
|
pub use super::{CityView, CountryView, StateView};
|
|
@@ -63,6 +64,7 @@ pub use crate::common::DbStats;
|
|
|
63
64
|
pub use crate::error::{GeoDbError, GeoError, Result};
|
|
64
65
|
pub use crate::text::{equals_folded, fold_ascii_lower, fold_key};
|
|
65
66
|
pub use flat::{City, Country, CountryTimezone, GeoDb, State};
|
|
67
|
+
pub use query::CityQuery;
|
|
66
68
|
#[cfg(not(feature = "compact"))]
|
|
67
69
|
pub const CACHE_SUFFIX: &str = ".flat.bin";
|
|
68
70
|
#[cfg(feature = "compact")]
|