geodb-rs 0.1.4__tar.gz → 0.1.5__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.
Files changed (47) hide show
  1. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/Cargo.lock +40 -47
  2. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/Cargo.toml +13 -5
  3. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/PKG-INFO +1 -1
  4. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/Cargo.toml +4 -4
  5. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/legacy_model/search.rs +13 -5
  6. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/model/convert.rs +23 -7
  7. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/model/search.rs +20 -12
  8. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/spatial.rs +15 -7
  9. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/traits.rs +2 -1
  10. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-py/Cargo.toml +7 -5
  11. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-py/src/lib.rs +123 -29
  12. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/README.md +0 -0
  13. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/README.md +0 -0
  14. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/benches/benchmarks.rs +0 -0
  15. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/build.rs +0 -0
  16. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/data/city_meta.json +0 -0
  17. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/data/countries+states+cities.json.gz +0 -0
  18. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/data/geodb.flat.comp.blobs.bin +0 -0
  19. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/alias.rs +0 -0
  20. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/api.rs +0 -0
  21. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/bin/profile_search.rs +0 -0
  22. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/common/mod.rs +0 -0
  23. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/common/raw.rs +0 -0
  24. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/common/raw_normalize.rs +0 -0
  25. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/error.rs +0 -0
  26. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/legacy_model/convert.rs +0 -0
  27. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/legacy_model/mod.rs +0 -0
  28. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/legacy_model/nested.rs +0 -0
  29. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/lib.rs +1 -1
  30. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/loader/binary_load.rs +0 -0
  31. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/loader/builder.rs +0 -0
  32. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/loader/common_io.rs +0 -0
  33. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/loader/mod.rs +0 -0
  34. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/model/flat.rs +0 -0
  35. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/model/mod.rs +0 -0
  36. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/prelude.rs +0 -0
  37. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/src/text.rs +0 -0
  38. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/tests/basic.rs +0 -0
  39. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-core/tests/city_meta_alias_search.rs +0 -0
  40. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-py/README.md +0 -0
  41. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-py/example.py +0 -0
  42. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-py/geodb_rs_data/city_meta.json +0 -0
  43. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-py/geodb_rs_data/countries+states+cities.json.gz +0 -0
  44. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-py/geodb_rs_data/geodb.flat.comp.blobs.bin +0 -0
  45. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/crates/geodb-py/tests/test_basic.py +0 -0
  46. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/geodb_rs_data/geodb.flat.comp.blobs.bin +0 -0
  47. {geodb_rs-0.1.4 → geodb_rs-0.1.5}/pyproject.toml +0 -0
@@ -165,9 +165,9 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
165
165
 
166
166
  [[package]]
167
167
  name = "borsh"
168
- version = "1.5.7"
168
+ version = "1.6.0"
169
169
  source = "registry+https://github.com/rust-lang/crates.io-index"
170
- checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce"
170
+ checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f"
171
171
  dependencies = [
172
172
  "cfg_aliases",
173
173
  ]
@@ -224,9 +224,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
224
224
 
225
225
  [[package]]
226
226
  name = "cc"
227
- version = "1.2.47"
227
+ version = "1.2.49"
228
228
  source = "registry+https://github.com/rust-lang/crates.io-index"
229
- checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
229
+ checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
230
230
  dependencies = [
231
231
  "find-msvc-tools",
232
232
  "shlex",
@@ -461,12 +461,6 @@ dependencies = [
461
461
  "miniz_oxide",
462
462
  ]
463
463
 
464
- [[package]]
465
- name = "fnv"
466
- version = "1.0.7"
467
- source = "registry+https://github.com/rust-lang/crates.io-index"
468
- checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
469
-
470
464
  [[package]]
471
465
  name = "form_urlencoded"
472
466
  version = "1.2.2"
@@ -537,7 +531,7 @@ dependencies = [
537
531
 
538
532
  [[package]]
539
533
  name = "geodb-cli"
540
- version = "0.1.4"
534
+ version = "0.1.5"
541
535
  dependencies = [
542
536
  "anyhow",
543
537
  "clap",
@@ -546,7 +540,7 @@ dependencies = [
546
540
 
547
541
  [[package]]
548
542
  name = "geodb-core"
549
- version = "0.1.4"
543
+ version = "0.1.5"
550
544
  dependencies = [
551
545
  "bincode",
552
546
  "criterion",
@@ -562,7 +556,7 @@ dependencies = [
562
556
 
563
557
  [[package]]
564
558
  name = "geodb-ffi"
565
- version = "0.1.4"
559
+ version = "0.1.5"
566
560
  dependencies = [
567
561
  "bincode",
568
562
  "flate2",
@@ -574,7 +568,7 @@ dependencies = [
574
568
 
575
569
  [[package]]
576
570
  name = "geodb-py"
577
- version = "0.1.4"
571
+ version = "0.1.5"
578
572
  dependencies = [
579
573
  "geodb-core",
580
574
  "pyo3",
@@ -585,14 +579,14 @@ dependencies = [
585
579
 
586
580
  [[package]]
587
581
  name = "geodb-rs"
588
- version = "0.1.4"
582
+ version = "0.1.5"
589
583
  dependencies = [
590
584
  "geodb-core",
591
585
  ]
592
586
 
593
587
  [[package]]
594
588
  name = "geodb-wasm"
595
- version = "0.1.4"
589
+ version = "0.1.5"
596
590
  dependencies = [
597
591
  "bincode",
598
592
  "console_error_panic_hook",
@@ -676,12 +670,11 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
676
670
 
677
671
  [[package]]
678
672
  name = "http"
679
- version = "1.3.1"
673
+ version = "1.4.0"
680
674
  source = "registry+https://github.com/rust-lang/crates.io-index"
681
- checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
675
+ checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
682
676
  dependencies = [
683
677
  "bytes",
684
- "fnv",
685
678
  "itoa",
686
679
  ]
687
680
 
@@ -754,9 +747,9 @@ dependencies = [
754
747
 
755
748
  [[package]]
756
749
  name = "hyper-util"
757
- version = "0.1.18"
750
+ version = "0.1.19"
758
751
  source = "registry+https://github.com/rust-lang/crates.io-index"
759
- checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56"
752
+ checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f"
760
753
  dependencies = [
761
754
  "base64",
762
755
  "bytes",
@@ -948,9 +941,9 @@ dependencies = [
948
941
 
949
942
  [[package]]
950
943
  name = "libc"
951
- version = "0.2.177"
944
+ version = "0.2.178"
952
945
  source = "registry+https://github.com/rust-lang/crates.io-index"
953
- checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
946
+ checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
954
947
 
955
948
  [[package]]
956
949
  name = "linux-raw-sys"
@@ -966,9 +959,9 @@ checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
966
959
 
967
960
  [[package]]
968
961
  name = "log"
969
- version = "0.4.28"
962
+ version = "0.4.29"
970
963
  source = "registry+https://github.com/rust-lang/crates.io-index"
971
- checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
964
+ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
972
965
 
973
966
  [[package]]
974
967
  name = "lru-slab"
@@ -993,9 +986,9 @@ dependencies = [
993
986
 
994
987
  [[package]]
995
988
  name = "minicov"
996
- version = "0.3.7"
989
+ version = "0.3.8"
997
990
  source = "registry+https://github.com/rust-lang/crates.io-index"
998
- checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b"
991
+ checksum = "4869b6a491569605d66d3952bcdf03df789e5b536e5f0cf7758a7f08a55ae24d"
999
992
  dependencies = [
1000
993
  "cc",
1001
994
  "walkdir",
@@ -1019,9 +1012,9 @@ dependencies = [
1019
1012
 
1020
1013
  [[package]]
1021
1014
  name = "mio"
1022
- version = "1.1.0"
1015
+ version = "1.1.1"
1023
1016
  source = "registry+https://github.com/rust-lang/crates.io-index"
1024
- checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
1017
+ checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
1025
1018
  dependencies = [
1026
1019
  "libc",
1027
1020
  "wasi",
@@ -1450,9 +1443,9 @@ dependencies = [
1450
1443
 
1451
1444
  [[package]]
1452
1445
  name = "rustls-pki-types"
1453
- version = "1.13.0"
1446
+ version = "1.13.1"
1454
1447
  source = "registry+https://github.com/rust-lang/crates.io-index"
1455
- checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a"
1448
+ checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c"
1456
1449
  dependencies = [
1457
1450
  "web-time",
1458
1451
  "zeroize",
@@ -1603,9 +1596,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
1603
1596
 
1604
1597
  [[package]]
1605
1598
  name = "simd-adler32"
1606
- version = "0.3.7"
1599
+ version = "0.3.8"
1607
1600
  source = "registry+https://github.com/rust-lang/crates.io-index"
1608
- checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
1601
+ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
1609
1602
 
1610
1603
  [[package]]
1611
1604
  name = "siphasher"
@@ -1677,9 +1670,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
1677
1670
 
1678
1671
  [[package]]
1679
1672
  name = "syn"
1680
- version = "2.0.110"
1673
+ version = "2.0.111"
1681
1674
  source = "registry+https://github.com/rust-lang/crates.io-index"
1682
- checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
1675
+ checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
1683
1676
  dependencies = [
1684
1677
  "proc-macro2",
1685
1678
  "quote",
@@ -1889,9 +1882,9 @@ dependencies = [
1889
1882
 
1890
1883
  [[package]]
1891
1884
  name = "tower-http"
1892
- version = "0.6.6"
1885
+ version = "0.6.8"
1893
1886
  source = "registry+https://github.com/rust-lang/crates.io-index"
1894
- checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
1887
+ checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
1895
1888
  dependencies = [
1896
1889
  "bitflags",
1897
1890
  "bytes",
@@ -1919,9 +1912,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
1919
1912
 
1920
1913
  [[package]]
1921
1914
  name = "tracing"
1922
- version = "0.1.41"
1915
+ version = "0.1.43"
1923
1916
  source = "registry+https://github.com/rust-lang/crates.io-index"
1924
- checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
1917
+ checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
1925
1918
  dependencies = [
1926
1919
  "pin-project-lite",
1927
1920
  "tracing-core",
@@ -1929,9 +1922,9 @@ dependencies = [
1929
1922
 
1930
1923
  [[package]]
1931
1924
  name = "tracing-core"
1932
- version = "0.1.34"
1925
+ version = "0.1.35"
1933
1926
  source = "registry+https://github.com/rust-lang/crates.io-index"
1934
- checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
1927
+ checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
1935
1928
  dependencies = [
1936
1929
  "once_cell",
1937
1930
  ]
@@ -2444,9 +2437,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
2444
2437
 
2445
2438
  [[package]]
2446
2439
  name = "winnow"
2447
- version = "0.7.13"
2440
+ version = "0.7.14"
2448
2441
  source = "registry+https://github.com/rust-lang/crates.io-index"
2449
- checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
2442
+ checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
2450
2443
  dependencies = [
2451
2444
  "memchr",
2452
2445
  ]
@@ -2488,18 +2481,18 @@ dependencies = [
2488
2481
 
2489
2482
  [[package]]
2490
2483
  name = "zerocopy"
2491
- version = "0.8.28"
2484
+ version = "0.8.31"
2492
2485
  source = "registry+https://github.com/rust-lang/crates.io-index"
2493
- checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90"
2486
+ checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
2494
2487
  dependencies = [
2495
2488
  "zerocopy-derive",
2496
2489
  ]
2497
2490
 
2498
2491
  [[package]]
2499
2492
  name = "zerocopy-derive"
2500
- version = "0.8.28"
2493
+ version = "0.8.31"
2501
2494
  source = "registry+https://github.com/rust-lang/crates.io-index"
2502
- checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26"
2495
+ checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
2503
2496
  dependencies = [
2504
2497
  "proc-macro2",
2505
2498
  "quote",
@@ -1,17 +1,25 @@
1
+
1
2
  [package]
2
3
  name = "geodb-rs"
3
- version = "0.1.4"
4
- edition = "2021"
4
+ version.workspace = true
5
+ edition.workspace = true
5
6
  publish = false
6
- license = "MIT"
7
+ license.workspace = true
7
8
  description = "Workspace crate for geodb-core and geodb-wasm; hosts examples and dev tooling."
8
- repository = "https://github.com/holg/geodb-rs"
9
+ repository.workspace = true
9
10
  readme = "README.md"
10
11
  [workspace]
11
12
  members = ["crates/geodb-py"]
12
13
 
13
14
  resolver = "2"
14
15
 
16
+ [workspace.package]
17
+ version = "0.1.5"
18
+ edition = "2021"
19
+ license = "MIT"
20
+ repository = "https://github.com/holg/geodb-rs"
21
+ authors = ["Holger Trahe"]
22
+
15
23
  [workspace.dependencies]
16
24
  bincode = "1"
17
25
  deunicode = "1.6"
@@ -22,7 +30,7 @@ serde = { version = "1", features = ["derive"] }
22
30
  serde-wasm-bindgen = "0.6"
23
31
  serde_json = { version = "1" }
24
32
  thiserror = "1"
25
- wasm-bindgen = "0.2"
33
+ wasm-bindgen = "=0.2.105" # Pinned to match trunk 0.21.14's bundled wasm-bindgen-cli
26
34
  web-sys = { version = "0.3", features = ["console"] }
27
35
 
28
36
  [dependencies]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geodb-rs
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Classifier: Programming Language :: Python
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -1,11 +1,11 @@
1
1
  [package]
2
2
  name = "geodb-core"
3
- version = "0.1.4"
4
- edition = "2021"
5
- authors = ["Holger Trahe"]
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ authors.workspace = true
6
6
  license = "MIT OR Apache-2.0"
7
7
  description = "A high-performance geographic database with countries, states, cities, and more"
8
- repository = "https://github.com/holg/geodb-rs"
8
+ repository.workspace = true
9
9
  readme = "README.md"
10
10
  documentation = "https://docs.rs/geodb-core"
11
11
  keywords = ["geography", "database", "countries", "cities", "geo"]
@@ -1,12 +1,12 @@
1
1
  // crates/geodb-core/src/legacy_model/search.rs
2
2
 
3
- use crate::traits::CityContext;
4
3
  use crate::alias::CityMetaIndex;
5
4
  use crate::common::{DbStats, SmartHitGeneric};
6
5
  use crate::legacy_model::nested::{City, Country, GeoDb, State};
7
- use crate::spatial::{decode_geoid, haversine_distance, distance_squared};
6
+ use crate::spatial::{decode_geoid, distance_squared, haversine_distance};
8
7
  #[allow(unused_imports)]
9
8
  use crate::text::{fold_key, match_score};
9
+ use crate::traits::CityContext;
10
10
  use crate::traits::{GeoBackend, GeoSearch};
11
11
  use std::collections::HashSet;
12
12
  type MySmartHit<'a, B> = SmartHitGeneric<'a, Country<B>, State<B>, City<B>>;
@@ -525,7 +525,11 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
525
525
 
526
526
  // crates/geodb-core/src/legacy_model/search.rs
527
527
 
528
- fn find_cities_in_radius_by_geoid(&self, geoid: u64, radius_km: f64) -> Vec<CityContext<'_, B>> {
528
+ fn find_cities_in_radius_by_geoid(
529
+ &self,
530
+ geoid: u64,
531
+ radius_km: f64,
532
+ ) -> Vec<CityContext<'_, B>> {
529
533
  let (center_lat, center_lng) = decode_geoid(geoid);
530
534
 
531
535
  // BBox calc (Same as above)
@@ -556,8 +560,12 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
556
560
  }
557
561
  }
558
562
 
559
- candidates.sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
560
- candidates.into_iter().map(|(_, city, state, country)| (city, state, country)).collect()
563
+ candidates
564
+ .sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
565
+ candidates
566
+ .into_iter()
567
+ .map(|(_, city, state, country)| (city, state, country))
568
+ .collect()
561
569
  }
562
570
 
563
571
  // ... (Ensure enrich_with_city_meta is present as a no-op or todo) ...
@@ -1,7 +1,7 @@
1
1
  // crates/geodb-core/src/model/convert.rs
2
- use crate::spatial::generate_geoid;
3
2
  use crate::alias::CityMetaIndex;
4
3
  use crate::common::raw::CountryRaw;
4
+ use crate::spatial::generate_geoid;
5
5
  // Import CountryTimezone so we can construct it
6
6
  use crate::model::flat::{City, Country, CountryTimezone, GeoDb, State};
7
7
  use crate::traits::GeoBackend;
@@ -20,7 +20,7 @@ pub fn from_raw<B: GeoBackend>(
20
20
  countries: Vec::new(),
21
21
  states: Vec::new(),
22
22
  cities: Vec::new(),
23
- spatial_index: Vec::new()
23
+ spatial_index: Vec::new(),
24
24
  };
25
25
 
26
26
  for c_raw in raw_countries {
@@ -138,8 +138,16 @@ pub fn from_raw<B: GeoBackend>(
138
138
  // ---------------------------------------------------------
139
139
  // SPATIAL INDEX LOGIC
140
140
  // ---------------------------------------------------------
141
- let lat = city_raw.latitude.as_deref().and_then(|s| s.parse::<f64>().ok()).unwrap_or(0.0);
142
- let lng = city_raw.longitude.as_deref().and_then(|s| s.parse::<f64>().ok()).unwrap_or(0.0);
141
+ let lat = city_raw
142
+ .latitude
143
+ .as_deref()
144
+ .and_then(|s| s.parse::<f64>().ok())
145
+ .unwrap_or(0.0);
146
+ let lng = city_raw
147
+ .longitude
148
+ .as_deref()
149
+ .and_then(|s| s.parse::<f64>().ok())
150
+ .unwrap_or(0.0);
143
151
 
144
152
  // Only index valid coordinates (0.0 is technically valid but "Null Island", acceptable)
145
153
  let geoid = generate_geoid(lat, lng);
@@ -156,11 +164,19 @@ pub fn from_raw<B: GeoBackend>(
156
164
  search_blob: city_search_blob,
157
165
  aliases,
158
166
  regions,
159
- lat: if lat != 0.0 { Some(B::float_from(lat)) } else { None },
160
- lng: if lng != 0.0 { Some(B::float_from(lng)) } else { None },
167
+ lat: if lat != 0.0 {
168
+ Some(B::float_from(lat))
169
+ } else {
170
+ None
171
+ },
172
+ lng: if lng != 0.0 {
173
+ Some(B::float_from(lng))
174
+ } else {
175
+ None
176
+ },
161
177
  population: city_raw.id.map(|p| p as u32),
162
178
  timezone: city_raw.timezone.map(|s| B::str_from(&s)),
163
- geoid
179
+ geoid,
164
180
  });
165
181
  }
166
182
 
@@ -1,12 +1,12 @@
1
1
  // crates/geodb-core/src/model/search.rs
2
- use crate::traits::CityContext;
3
- use crate::spatial::{generate_geoid, decode_geoid, haversine_distance};
4
2
  use crate::alias::CityMetaIndex;
5
3
  use crate::common::{DbStats, SmartHitGeneric};
6
4
  use crate::model::flat::{City, Country, GeoDb, State};
5
+ use crate::spatial::{decode_geoid, generate_geoid, haversine_distance};
7
6
  use crate::text::fold_key;
8
7
  #[cfg(not(feature = "search_blobs"))]
9
8
  use crate::text::match_score;
9
+ use crate::traits::CityContext;
10
10
  use crate::traits::{CitiesIter, GeoBackend, GeoSearch};
11
11
 
12
12
  type MySmartHit<'a, B> = SmartHitGeneric<'a, Country<B>, State<B>, City<B>>;
@@ -26,10 +26,10 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
26
26
 
27
27
  fn cities<'a>(&'a self) -> CitiesIter<'a, B> {
28
28
  // Reconstruct hierarchy on the fly using IDs
29
- let iter = self.cities.iter().map(move |city| {
30
- let state = &self.states[city.state_id as usize];
31
- let country = &self.countries[city.country_id as usize];
32
- (city, state, country)
29
+ let iter = self.cities.iter().filter_map(move |city| {
30
+ let state = self.states.get(city.state_id as usize)?;
31
+ let country = self.countries.get(city.country_id as usize)?;
32
+ Some((city, state, country))
33
33
  });
34
34
  Box::new(iter)
35
35
  }
@@ -57,7 +57,7 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
57
57
  }
58
58
  }
59
59
 
60
- fn find_country_by_iso2(&self, iso2: &str) -> Option<&Country<B>> {
60
+ fn find_country_by_iso2(&self, iso2: &str) -> Option<&Country<B>> {
61
61
  self.countries
62
62
  .iter()
63
63
  .find(|c| c.iso2.as_ref().eq_ignore_ascii_case(iso2))
@@ -371,7 +371,8 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
371
371
  (dist_metric, city)
372
372
  })
373
373
  .collect();
374
- candidates.sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
374
+ candidates
375
+ .sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
375
376
  // candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
376
377
  candidates
377
378
  .into_iter()
@@ -385,7 +386,11 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
385
386
  }
386
387
  // crates/geodb-core/src/model/search.rs
387
388
 
388
- fn find_cities_in_radius_by_geoid(&self, geoid: u64, radius_km: f64) -> Vec<CityContext<'_, B>> {
389
+ fn find_cities_in_radius_by_geoid(
390
+ &self,
391
+ geoid: u64,
392
+ radius_km: f64,
393
+ ) -> Vec<CityContext<'_, B>> {
389
394
  // 1. Decode Center
390
395
  let (center_lat, center_lng) = decode_geoid(geoid);
391
396
 
@@ -411,7 +416,6 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
411
416
 
412
417
  // BBox Filter (Fast float comparisons)
413
418
  if lat >= min_lat && lat <= max_lat && lng >= min_lng && lng <= max_lng {
414
-
415
419
  // Precise Distance (Expensive Trig)
416
420
  let dist = haversine_distance(center_lat, center_lng, lat, lng);
417
421
 
@@ -425,9 +429,13 @@ impl<B: GeoBackend> GeoSearch<B> for GeoDb<B> {
425
429
  }
426
430
 
427
431
  // 4. Sort by Distance
428
- candidates.sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
432
+ candidates
433
+ .sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
429
434
 
430
435
  // 5. Strip distance and return context
431
- candidates.into_iter().map(|(_, city, s, c)| (city, s, c)).collect()
436
+ candidates
437
+ .into_iter()
438
+ .map(|(_, city, s, c)| (city, s, c))
439
+ .collect()
432
440
  }
433
441
  }
@@ -34,9 +34,8 @@ pub fn haversine_distance(lat1: f64, lng1: f64, lat2: f64, lng2: f64) -> f64 {
34
34
  let lat1_rad = lat1.to_radians();
35
35
  let lat2_rad = lat2.to_radians();
36
36
 
37
- let a = (d_lat / 2.0).sin().powi(2) +
38
- lat1_rad.cos() * lat2_rad.cos() *
39
- (d_lng / 2.0).sin().powi(2);
37
+ let a =
38
+ (d_lat / 2.0).sin().powi(2) + lat1_rad.cos() * lat2_rad.cos() * (d_lng / 2.0).sin().powi(2);
40
39
 
41
40
  let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
42
41
  r * c
@@ -100,8 +99,14 @@ mod tests {
100
99
 
101
100
  // Precision loss is expected due to u32 quantization, but should be small
102
101
  let epsilon = 0.0001;
103
- assert!((lat - lat_out).abs() < epsilon, "Lat mismatch: {lat} vs {lat_out}");
104
- assert!((lng - lng_out).abs() < epsilon, "Lng mismatch: {lng} vs {lng_out}");
102
+ assert!(
103
+ (lat - lat_out).abs() < epsilon,
104
+ "Lat mismatch: {lat} vs {lat_out}"
105
+ );
106
+ assert!(
107
+ (lng - lng_out).abs() < epsilon,
108
+ "Lng mismatch: {lng} vs {lng_out}"
109
+ );
105
110
  }
106
111
 
107
112
  #[test]
@@ -116,6 +121,9 @@ mod tests {
116
121
  let diff_near = berlin.abs_diff(potsdam);
117
122
  let diff_far = berlin.abs_diff(nyc);
118
123
 
119
- assert!(diff_near < diff_far, "Nearby cities should have closer IDs in general");
124
+ assert!(
125
+ diff_near < diff_far,
126
+ "Nearby cities should have closer IDs in general"
127
+ );
120
128
  }
121
- }
129
+ }
@@ -175,7 +175,8 @@ pub trait GeoSearch<B: GeoBackend> {
175
175
  ///
176
176
  /// 1. Decodes the GeoID to find the center.
177
177
  /// 2. Scans for cities within the radius.
178
- fn find_cities_in_radius_by_geoid(&self, geoid: u64, radius_km: f64) -> Vec<CityContext<'_, B>>;
178
+ fn find_cities_in_radius_by_geoid(&self, geoid: u64, radius_km: f64)
179
+ -> Vec<CityContext<'_, B>>;
179
180
  }
180
181
 
181
182
  pub trait CountryTimezone<B: GeoBackend> {}
@@ -1,11 +1,13 @@
1
1
  [package]
2
2
  name = "geodb-py"
3
- version = "0.1.4"
4
- edition = "2021"
3
+ # NOTE: Maturin uses this version directly for PyPI, so we keep it explicit
4
+ # rather than using workspace inheritance to avoid any maturin compatibility issues
5
+ version = "0.1.5"
6
+ edition.workspace = true
5
7
  description = "Python bindings for geodb-rs"
6
- license = "MIT"
7
- authors = ["Holger Trahe"]
8
- repository = "https://github.com/holg/geodb-rs"
8
+ license.workspace = true
9
+ authors.workspace = true
10
+ repository.workspace = true
9
11
  homepage = "https://github.com/holg/geodb-rs"
10
12
  readme = "README.md"
11
13
  keywords = ["geodb", "geo", "countries", "regions", "cities"]
@@ -77,8 +77,13 @@ impl PyGeoDb {
77
77
  }
78
78
 
79
79
  #[staticmethod]
80
- pub fn load_from_path(path: &str, filter:Option<&[&str]>) -> PyResult<Self> {
81
- let db = GeoDb::<DefaultBackend>::load_from_path(&path, filter).into_py()?;
80
+ #[pyo3(signature = (path, filter=None))]
81
+ pub fn load_from_path(path: &str, filter: Option<Vec<String>>) -> PyResult<Self> {
82
+ let filter_refs: Option<Vec<&str>> = filter
83
+ .as_ref()
84
+ .map(|v| v.iter().map(|s| s.as_str()).collect());
85
+ let filter_slice: Option<&[&str]> = filter_refs.as_deref();
86
+ let db = GeoDb::<DefaultBackend>::load_from_path(path, filter_slice).into_py()?;
82
87
  Ok(Self { inner: db })
83
88
  }
84
89
 
@@ -87,7 +92,9 @@ impl PyGeoDb {
87
92
  // Try bundled data first
88
93
  match find_bundled_data() {
89
94
  Ok(path) => {
90
- let path_str = path.to_str().ok_or_else(|| PyRuntimeError::new_err("Invalid path"))?;
95
+ let path_str = path
96
+ .to_str()
97
+ .ok_or_else(|| PyRuntimeError::new_err("Invalid path"))?;
91
98
  // Note: load_raw_json builds cache automatically if 'builder' enabled
92
99
  // load_from_path handles both .json and .bin
93
100
  let db = GeoDb::<DefaultBackend>::load_from_path(path_str, None).into_py()?;
@@ -103,7 +110,10 @@ impl PyGeoDb {
103
110
 
104
111
  #[staticmethod]
105
112
  pub fn load_filtered(iso2_list: Vec<String>) -> PyResult<Self> {
106
- let tmp: Vec<String> = iso2_list.into_iter().map(|s| s.trim().to_string()).collect();
113
+ let tmp: Vec<String> = iso2_list
114
+ .into_iter()
115
+ .map(|s| s.trim().to_string())
116
+ .collect();
107
117
  let refs: Vec<&str> = tmp.iter().map(String::as_str).collect();
108
118
  let db = GeoDb::<DefaultBackend>::load_filtered_by_iso2(&refs).into_py()?;
109
119
  Ok(Self { inner: db })
@@ -121,7 +131,11 @@ impl PyGeoDb {
121
131
  }
122
132
 
123
133
  /// Find a country by ISO2/ISO3/code and return as dict (or None)
124
- pub fn find_country<'py>(&self, py: Python<'py>, code: &str) -> PyResult<Option<Bound<'py, PyAny>>> {
134
+ pub fn find_country<'py>(
135
+ &self,
136
+ py: Python<'py>,
137
+ code: &str,
138
+ ) -> PyResult<Option<Bound<'py, PyAny>>> {
125
139
  if let Some(c) = self.inner.find_country_by_code(code) {
126
140
  let v = to_py(py, &CountryView(c))?;
127
141
  Ok(Some(v))
@@ -131,10 +145,16 @@ impl PyGeoDb {
131
145
  }
132
146
 
133
147
  /// List all states for a given country ISO2 as dicts
134
- pub fn states_in_country<'py>(&self, py: Python<'py>, iso2: &str) -> PyResult<Option<Bound<'py, PyAny>>> {
148
+ pub fn states_in_country<'py>(
149
+ &self,
150
+ py: Python<'py>,
151
+ iso2: &str,
152
+ ) -> PyResult<Option<Bound<'py, PyAny>>> {
135
153
  if let Some(country) = self.inner.find_country_by_iso2(iso2) {
136
154
  // FIX: Use Trait method
137
- let items: Vec<_> = self.inner.states_for_country(country)
155
+ let items: Vec<_> = self
156
+ .inner
157
+ .states_for_country(country)
138
158
  .iter()
139
159
  .map(|s| StateView { country, state: s })
140
160
  .collect();
@@ -146,14 +166,25 @@ impl PyGeoDb {
146
166
  }
147
167
 
148
168
  /// List all cities for a given state code (in a country)
149
- pub fn cities_in_state<'py>(&self, py: Python<'py>, iso2: &str, state_code: &str) -> PyResult<Option<Bound<'py, PyAny>>> {
169
+ pub fn cities_in_state<'py>(
170
+ &self,
171
+ py: Python<'py>,
172
+ iso2: &str,
173
+ state_code: &str,
174
+ ) -> PyResult<Option<Bound<'py, PyAny>>> {
150
175
  if let Some(country) = self.inner.find_country_by_iso2(iso2) {
151
176
  let states = self.inner.states_for_country(country);
152
177
  if let Some(state) = states.iter().find(|s| s.state_code() == state_code) {
153
178
  // FIX: Use Trait method
154
- let items: Vec<_> = self.inner.cities_for_state(state)
179
+ let items: Vec<_> = self
180
+ .inner
181
+ .cities_for_state(state)
155
182
  .iter()
156
- .map(|city| CityView { country, state, city })
183
+ .map(|city| CityView {
184
+ country,
185
+ state,
186
+ city,
187
+ })
157
188
  .collect();
158
189
  let obj = to_py(py, &items)?;
159
190
  return Ok(Some(obj));
@@ -163,9 +194,15 @@ impl PyGeoDb {
163
194
  }
164
195
 
165
196
  /// Find countries by phone code (e.g. "+49", "1")
166
- pub fn search_countries_by_phone<'py>(&self, py: Python<'py>, phone: &str) -> PyResult<Bound<'py, PyAny>> {
197
+ pub fn search_countries_by_phone<'py>(
198
+ &self,
199
+ py: Python<'py>,
200
+ phone: &str,
201
+ ) -> PyResult<Bound<'py, PyAny>> {
167
202
  // Trait method already handles trimming
168
- let items: Vec<_> = self.inner.find_countries_by_phone_code(phone)
203
+ let items: Vec<_> = self
204
+ .inner
205
+ .find_countries_by_phone_code(phone)
169
206
  .iter()
170
207
  .map(|c| CountryView(*c))
171
208
  .collect();
@@ -173,11 +210,17 @@ impl PyGeoDb {
173
210
  }
174
211
 
175
212
  /// Find countries containing a substring
176
- pub fn search_country_substring<'py>(&self, py: Python<'py>, substr: &str) -> PyResult<Bound<'py, PyAny>> {
213
+ pub fn search_country_substring<'py>(
214
+ &self,
215
+ py: Python<'py>,
216
+ substr: &str,
217
+ ) -> PyResult<Bound<'py, PyAny>> {
177
218
  // Assuming find_countries_by_substring is exposed in GeoSearch trait now
178
219
  // (If not, use smart_search filtering)
179
220
  // Let's assume we exposed it in the trait as discussed.
180
- let items: Vec<_> = self.inner.find_countries_by_substring(substr)
221
+ let items: Vec<_> = self
222
+ .inner
223
+ .find_countries_by_substring(substr)
181
224
  .iter()
182
225
  .map(|c| CountryView(*c))
183
226
  .collect();
@@ -185,8 +228,14 @@ impl PyGeoDb {
185
228
  }
186
229
 
187
230
  /// Find states containing a substring
188
- pub fn find_states_by_substring<'py>(&self, py: Python<'py>, substr: &str) -> PyResult<Bound<'py, PyAny>> {
189
- let items: Vec<_> = self.inner.find_states_by_substring(substr)
231
+ pub fn find_states_by_substring<'py>(
232
+ &self,
233
+ py: Python<'py>,
234
+ substr: &str,
235
+ ) -> PyResult<Bound<'py, PyAny>> {
236
+ let items: Vec<_> = self
237
+ .inner
238
+ .find_states_by_substring(substr)
190
239
  .into_iter()
191
240
  .map(|(state, country)| StateView { country, state })
192
241
  .collect();
@@ -194,10 +243,20 @@ impl PyGeoDb {
194
243
  }
195
244
 
196
245
  /// Find cities containing a substring
197
- pub fn find_cities_by_substring<'py>(&self, py: Python<'py>, substr: &str) -> PyResult<Bound<'py, PyAny>> {
198
- let items: Vec<_> = self.inner.find_cities_by_substring(substr)
246
+ pub fn find_cities_by_substring<'py>(
247
+ &self,
248
+ py: Python<'py>,
249
+ substr: &str,
250
+ ) -> PyResult<Bound<'py, PyAny>> {
251
+ let items: Vec<_> = self
252
+ .inner
253
+ .find_cities_by_substring(substr)
199
254
  .into_iter()
200
- .map(|(city, state, country)| CityView { country, state, city })
255
+ .map(|(city, state, country)| CityView {
256
+ country,
257
+ state,
258
+ city,
259
+ })
201
260
  .collect();
202
261
  to_py(py, &items)
203
262
  }
@@ -205,22 +264,46 @@ impl PyGeoDb {
205
264
  // --- SPATIAL SEARCH ---
206
265
 
207
266
  /// Find nearest cities to a location (Lat, Lng)
208
- pub fn find_nearest<'py>(&self, py: Python<'py>, lat: f64, lng: f64, count: usize) -> PyResult<Bound<'py, PyAny>> {
209
- let items: Vec<_> = self.inner.find_nearest(lat, lng, count)
267
+ pub fn find_nearest<'py>(
268
+ &self,
269
+ py: Python<'py>,
270
+ lat: f64,
271
+ lng: f64,
272
+ count: usize,
273
+ ) -> PyResult<Bound<'py, PyAny>> {
274
+ let items: Vec<_> = self
275
+ .inner
276
+ .find_nearest(lat, lng, count)
210
277
  .into_iter()
211
- .map(|(city, state, country)| CityView { country, state, city })
278
+ .map(|(city, state, country)| CityView {
279
+ country,
280
+ state,
281
+ city,
282
+ })
212
283
  .collect();
213
284
  to_py(py, &items)
214
285
  }
215
286
 
216
287
  /// Find cities within radius (km)
217
- pub fn find_in_radius<'py>(&self, py: Python<'py>, lat: f64, lng: f64, radius_km: f64) -> PyResult<Bound<'py, PyAny>> {
288
+ pub fn find_in_radius<'py>(
289
+ &self,
290
+ py: Python<'py>,
291
+ lat: f64,
292
+ lng: f64,
293
+ radius_km: f64,
294
+ ) -> PyResult<Bound<'py, PyAny>> {
218
295
  // We need to generate the ID first because the trait expects GeoID
219
296
  let geoid = geodb_core::spatial::generate_geoid(lat, lng);
220
297
 
221
- let items: Vec<_> = self.inner.find_cities_in_radius_by_geoid(geoid, radius_km)
298
+ let items: Vec<_> = self
299
+ .inner
300
+ .find_cities_in_radius_by_geoid(geoid, radius_km)
222
301
  .into_iter()
223
- .map(|(city, state, country)| CityView { country, state, city })
302
+ .map(|(city, state, country)| CityView {
303
+ country,
304
+ state,
305
+ city,
306
+ })
224
307
  .collect();
225
308
  to_py(py, &items)
226
309
  }
@@ -234,9 +317,20 @@ impl PyGeoDb {
234
317
  // Serialize the View based on the Enum variant
235
318
  let v = match hit.item {
236
319
  SmartItem::Country(c) => serde_json::to_value(CountryView(c)),
237
- SmartItem::State { country, state } => serde_json::to_value(&StateView { country, state }),
238
- SmartItem::City { country, state, city } => serde_json::to_value(&CityView { country, state, city }),
239
- }.map_err(|e| PyRuntimeError::new_err(format!("serde error: {e}")))?;
320
+ SmartItem::State { country, state } => {
321
+ serde_json::to_value(&StateView { country, state })
322
+ }
323
+ SmartItem::City {
324
+ country,
325
+ state,
326
+ city,
327
+ } => serde_json::to_value(&CityView {
328
+ country,
329
+ state,
330
+ city,
331
+ }),
332
+ }
333
+ .map_err(|e| PyRuntimeError::new_err(format!("serde error: {e}")))?;
240
334
 
241
335
  out.push(v);
242
336
  }
@@ -249,4 +343,4 @@ impl PyGeoDb {
249
343
  fn geodb_rs(_py: Python, m: &Bound<PyModule>) -> PyResult<()> {
250
344
  m.add_class::<PyGeoDb>()?;
251
345
  Ok(())
252
- }
346
+ }
File without changes
@@ -9,9 +9,9 @@ pub mod loader;
9
9
  #[cfg(not(feature = "legacy_model"))]
10
10
  pub mod model; // The NEW model folder
11
11
  pub mod prelude;
12
+ pub mod spatial;
12
13
  pub mod text;
13
14
  pub mod traits;
14
- pub mod spatial;
15
15
 
16
16
  pub use crate::error::{GeoDbError, GeoError, Result};
17
17
 
File without changes