jageocoder 2.2.0__tar.gz → 2.2.1__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 (27) hide show
  1. {jageocoder-2.2.0 → jageocoder-2.2.1}/PKG-INFO +4 -1
  2. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/__init__.py +1 -1
  3. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/__main__.py +5 -1
  4. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/aza_master.py +0 -1
  5. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/dbm/base_table.py +3 -4
  6. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/module.py +12 -7
  7. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/node.py +2 -2
  8. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/remote.py +1 -1
  9. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/rtree.py +44 -24
  10. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/strlib.py +1 -1
  11. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/trie.py +0 -1
  12. {jageocoder-2.2.0 → jageocoder-2.2.1}/pyproject.toml +5 -2
  13. {jageocoder-2.2.0 → jageocoder-2.2.1}/LICENSE +0 -0
  14. {jageocoder-2.2.0 → jageocoder-2.2.1}/README.md +0 -0
  15. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/address.py +0 -0
  16. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/dataset.py +0 -0
  17. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/dbm/__init__.py +0 -0
  18. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/dbm/abstract_table.py +0 -0
  19. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/dbm/base_index.py +0 -0
  20. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/dbm/exceptions.py +0 -0
  21. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/dbm/types.py +0 -0
  22. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/exceptions.py +0 -0
  23. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/itaiji.py +0 -0
  24. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/itaiji_dic.json +0 -0
  25. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/local.py +0 -0
  26. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/result.py +0 -0
  27. {jageocoder-2.2.0 → jageocoder-2.2.1}/jageocoder/tree.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: jageocoder
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: A Japanese-address geocoder for Python.
5
5
  License: MIT
6
6
  Keywords: geocoder,Japanese address
@@ -17,6 +17,7 @@ Provides-Extra: doc
17
17
  Provides-Extra: test
18
18
  Requires-Dist: deprecated (>=1.2.13)
19
19
  Requires-Dist: docopt-ng
20
+ Requires-Dist: flake8 ; extra == "dev"
20
21
  Requires-Dist: geographiclib (>=2.0)
21
22
  Requires-Dist: jaconv (>=0.3.9)
22
23
  Requires-Dist: marisa-trie (>=1.3.1)
@@ -24,6 +25,8 @@ Requires-Dist: pytest (>=8.4.1) ; extra == "test"
24
25
  Requires-Dist: requests
25
26
  Requires-Dist: rtree (>=1.0.0)
26
27
  Requires-Dist: sphinx-rtd-theme (>=1.2.0) ; extra == "doc"
28
+ Requires-Dist: tox (>=4.0.0) ; extra == "dev"
29
+ Requires-Dist: tox-uv ; extra == "dev"
27
30
  Requires-Dist: tqdm (>=4.0.0)
28
31
  Requires-Dist: twine (>=5.1.1) ; extra == "dev"
29
32
  Project-URL: Documentation, https://jageocoder.readthedocs.io/ja/latest/
@@ -17,7 +17,7 @@ running the following steps.
17
17
  >>> import jageocoder
18
18
  >>> jageocoder.init()
19
19
  >>> jageocoder.searchNode('新宿区西新宿2-8-1')
20
- [{"node": {"id": ..., "name": "8番", "name_index": "8.番", "x": 139.6..., "y": 35.6..., "level": 7, "priority": 3, "note": "", "parent_id": ..., "sibling_id": ...}, "matched": "新宿区西新宿2-8-"}]
20
+ [{"node": {"id": ..., "name": "8番", "name_index": "8.番", "x": 139.6..., "y": 35.6..., "level": 7, "priority": 3, "note": "", "parent_id": ..., "sibling_id": ...}, "matched": "新宿区西新宿2-8-"}] # noqa: E501
21
21
  """
22
22
 
23
23
  from importlib.metadata import version
@@ -127,7 +127,11 @@ def main():
127
127
 
128
128
  elif args['reverse']:
129
129
  from jageocoder.address import AddressLevel
130
- jageocoder.init(db_dir=args['--db-dir'], mode='r', url=args['--url'])
130
+ jageocoder.init(
131
+ db_dir=args['--db-dir'],
132
+ mode='r',
133
+ debug=args['--debug'],
134
+ url=args['--url'])
131
135
  print(json.dumps(
132
136
  jageocoder.reverse(
133
137
  x=float(args['<longitude>']),
@@ -3,7 +3,6 @@ import datetime
3
3
  import json
4
4
  from logging import getLogger
5
5
  from pathlib import Path
6
- from typing import Optional
7
6
  import re
8
7
  from typing import Dict, Union
9
8
 
@@ -397,7 +397,7 @@ class BaseTable(object):
397
397
 
398
398
  # Create empty table
399
399
  cur = self.get_conn().cursor()
400
- cur.execute(f"DROP TABLE IF EXISTS records")
400
+ cur.execute("DROP TABLE IF EXISTS records")
401
401
  cur.execute(self._get_create_table_statement())
402
402
  self.commit()
403
403
 
@@ -493,7 +493,6 @@ class BaseTable(object):
493
493
  if from_pos < 0 or to_pos > self.count_records():
494
494
  raise ValueError("Out of range.")
495
495
 
496
- limits = to_pos - from_pos
497
496
  conn = self.get_conn()
498
497
  conn.row_factory = sqlite3.Row
499
498
  cur = conn.cursor()
@@ -762,7 +761,7 @@ class BaseTable(object):
762
761
  raise ValueError(f"Field '{k}' is not in this table.")
763
762
  elif k == self.pos_field:
764
763
  raise ValueError(
765
- f"The value of the pos field can not modified.")
764
+ "The value of the pos field can not modified.")
766
765
 
767
766
  fields.append(k)
768
767
  if isinstance(self.schema[k], (dict, list)):
@@ -830,7 +829,7 @@ class BaseTable(object):
830
829
  raise ValueError(f"Field '{k}' is not in this table.")
831
830
  elif k == pos_field:
832
831
  raise ValueError(
833
- f"The value of the pos field can not modified.")
832
+ "The value of the pos field can not modified.")
834
833
 
835
834
  cols.append(k)
836
835
  if isinstance(self.schema[k], (dict, list)):
@@ -4,15 +4,14 @@ import shutil
4
4
  from typing import Any, Dict, List, Optional, Union
5
5
  import urllib.request
6
6
  from urllib.error import URLError
7
+ import zipfile
7
8
 
8
9
  import jageocoder
9
- from .dataset import Dataset
10
10
  from .exceptions import JageocoderError
11
11
  from .local import LocalTree
12
12
  from .tree import AddressTree, get_db_dir
13
13
  from .remote import RemoteTree
14
14
  from .result import Result
15
- from .rtree import Index
16
15
 
17
16
  _tree: Optional[AddressTree] = None # The default AddressTree
18
17
  logger = logging.getLogger(__name__)
@@ -193,7 +192,6 @@ def get_module_tree() -> AddressTree:
193
192
  AddressTree
194
193
  The singleton object.
195
194
  """
196
- global _tree
197
195
  if _tree is None:
198
196
  raise JageocoderError("Tree is not initialized")
199
197
 
@@ -268,10 +266,17 @@ def install_dictionary(
268
266
  # Unzip the archive
269
267
  os.makedirs(db_dir, exist_ok=True)
270
268
  shutil.rmtree(db_dir)
271
- shutil.unpack_archive(
272
- filename=str(path),
273
- extract_dir=str(db_dir),
274
- )
269
+ # shutil.unpack_archive(
270
+ # filename=str(path),
271
+ # extract_dir=str(db_dir),
272
+ # )
273
+ with zipfile.ZipFile(path, "r") as zipf:
274
+ zipinfo_list = zipf.infolist()
275
+ n = len(zipinfo_list)
276
+ for index, zipinfo in enumerate(zipinfo_list):
277
+ logger.info(f"[{index+1}/{n}] Extractiong '{zipinfo.filename}'")
278
+ zipf.extract(zipinfo, db_dir)
279
+
275
280
  for readme_fname in ("README.txt", "README.md",):
276
281
  readme_path = os.path.join(db_dir, readme_fname)
277
282
  if os.path.exists(readme_path):
@@ -122,7 +122,7 @@ class AddressNodeTable(AbstractTable):
122
122
  notes = []
123
123
  for attr in re.split(r'(?<!\\)/', note):
124
124
  try:
125
- k, v = re.split(r'(?<!\\):', attr, 1)
125
+ k, v = re.split(r'(?<!\\):', attr, maxsplit=1)
126
126
  except ValueError:
127
127
  k, v = '', attr
128
128
 
@@ -539,7 +539,7 @@ class AddressNode(object):
539
539
  notes = []
540
540
  for attr in re.split(r'(?<!\\)/', self.note):
541
541
  try:
542
- k, v = re.split(r'(?<!\\):', attr, 1)
542
+ k, v = re.split(r'(?<!\\):', attr, maxsplit=1)
543
543
  except ValueError:
544
544
  k, v = '', attr
545
545
 
@@ -3,7 +3,7 @@ from collections import OrderedDict
3
3
  import json
4
4
  from logging import getLogger
5
5
  import os
6
- from typing import Any, Dict, List, NoReturn, Optional, Tuple, Union
6
+ from typing import Any, Dict, List, NoReturn, Optional, Union
7
7
  import uuid
8
8
 
9
9
  import requests
@@ -356,10 +356,25 @@ class Index(object):
356
356
  index.Rtree
357
357
  Created rtree index.
358
358
  """
359
- import time
360
359
  file_idx = index.Rtree(str(treepath)) # Filename must be passed as str
361
360
  node_table: AddressNodeTable = self._tree.address_nodes
362
361
 
362
+ def _insert_node_to_rtree(
363
+ id: int,
364
+ coordinates: Tuple[float, float, float, float],
365
+ ) -> None:
366
+ file_idx.insert(
367
+ id=id,
368
+ coordinates=coordinates,
369
+ )
370
+
371
+ def _get_limit_bdr(cx: float, cy: float, radius: float) -> Tuple[float, float, float, float]:
372
+ # cx, cy を中心とし、半径 radius (m)の円を内包する BDR の (x0, y0, x1, y1) を返す
373
+ import math
374
+ delta_lat = radius / 111320.0
375
+ delta_lon = radius / (111320.0 * math.cos(math.radians(cy)))
376
+ return (cx - delta_lon, cy - delta_lat, cx + delta_lon, cy + delta_lat)
377
+
363
378
  max_id = AddressNode.ROOT_NODE_ID + node_table.count_records()
364
379
  registered_coordinates = set()
365
380
 
@@ -367,47 +382,49 @@ class Index(object):
367
382
  id = AddressNode.ROOT_NODE_ID
368
383
  with tqdm(total=max_id, mininterval=0.5, ascii=True) as pbar:
369
384
  mode = ""
385
+ bdr = None
386
+ first_block_node = None
387
+ parent_node = None
370
388
  sibling_id = AddressNode.ROOT_NODE_ID
371
389
  for node in node_table.get_nodes_by_id(
372
390
  AddressNode.ROOT_NODE_ID, max_id):
373
391
  id = node.id
374
392
  pbar.update(1)
375
393
 
376
- if mode == "block":
394
+ if mode == "block" and parent_node is not None:
377
395
  if id < sibling_id:
378
396
  if node.has_valid_coordinate_values():
379
- if bdr is None:
397
+ if bdr is None or first_block_node is None:
380
398
  bdr = (node.x, node.y, node.x, node.y)
399
+ first_block_node = node
400
+ bdr_limit = _get_limit_bdr(
401
+ node.x, node.y, 500.0)
381
402
  else:
382
- bdr = (
383
- min(node.x, bdr[0]),
384
- min(node.y, bdr[1]),
385
- max(node.x, bdr[2]),
386
- max(node.y, bdr[3]),
387
- )
403
+ if node.x < bdr_limit[0] or node.y < bdr_limit[1] \
404
+ or node.x > bdr_limit[2] or node.y > bdr_limit[3]:
405
+ # The node is too far from the first node.
406
+ pass
407
+ else:
408
+ bdr = (
409
+ min(node.x, bdr[0]),
410
+ min(node.y, bdr[1]),
411
+ max(node.x, bdr[2]),
412
+ max(node.y, bdr[3]),
413
+ )
388
414
 
389
415
  if id == sibling_id - 1:
390
416
  mode = ""
391
417
  if bdr:
392
- file_idx.insert(
418
+ _insert_node_to_rtree(
393
419
  id=parent_node.id,
394
420
  coordinates=bdr,
395
421
  )
396
- else:
397
- # All child nodes have invalid coordinate values
398
- key = (parent_node.x, parent_node.y)
399
- if parent_node.has_valid_coordinate_values() and \
400
- key not in registered_coordinates:
401
- file_idx.insert(
402
- id=parent_node.id,
403
- coordinates=(node.x, node.y,
404
- node.x, node.y),
405
- )
406
- registered_coordinates.add(key)
407
422
 
408
423
  if id >= sibling_id:
409
- import pdb
410
- pdb.set_trace()
424
+ raise RTreeError((
425
+ f"データファイルが異常です。"
426
+ f"The child node ({id}) of the parent_node ({parent_node.id}) "
427
+ f"exceeded the sibling_id:{sibling_id}."))
411
428
 
412
429
  continue
413
430
 
@@ -425,7 +442,7 @@ class Index(object):
425
442
  if key in registered_coordinates:
426
443
  continue
427
444
 
428
- file_idx.insert(
445
+ _insert_node_to_rtree(
429
446
  id=id,
430
447
  coordinates=(node.x, node.y, node.x, node.y),
431
448
  )
@@ -441,8 +458,11 @@ class Index(object):
441
458
 
442
459
  if node.has_valid_coordinate_values():
443
460
  bdr = (node.x, node.y, node.x, node.y)
461
+ first_block_node = node
462
+ bdr_limit = _get_limit_bdr(node.x, node.y, 500.0)
444
463
  else:
445
464
  bdr = None
465
+ first_block_node = None
446
466
 
447
467
  continue
448
468
 
@@ -1,6 +1,6 @@
1
1
  from logging import getLogger
2
2
  import re
3
- from typing import Optional, Union
3
+ from typing import Union
4
4
 
5
5
  logger = getLogger(__name__)
6
6
 
@@ -1,6 +1,5 @@
1
1
  from logging import getLogger
2
2
  import os
3
- from pathlib import Path
4
3
 
5
4
  import marisa_trie
6
5
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jageocoder"
3
- version = "2.2.0"
3
+ version = "2.2.1"
4
4
  description = "A Japanese-address geocoder for Python."
5
5
  authors = [{name = "Takeshi Sagara", email = "sagara@info-proto.com"}]
6
6
  readme = "README.md"
@@ -28,7 +28,10 @@ dependencies = [
28
28
 
29
29
  [project.optional-dependencies]
30
30
  dev = [
31
- "twine >=5.1.1"
31
+ "flake8",
32
+ "twine >=5.1.1",
33
+ "tox >=4.0.0",
34
+ "tox-uv"
32
35
  ]
33
36
  doc = [
34
37
  "sphinx-rtd-theme >=1.2.0"
File without changes
File without changes