jageocoder 2.1.7.dev2__tar.gz → 2.1.7.dev3__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.
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/PKG-INFO +1 -1
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/__init__.py +1 -1
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/node.py +25 -1
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/remote.py +24 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/rtree.py +194 -97
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/tree.py +50 -12
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/pyproject.toml +1 -1
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/LICENSE +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/README.md +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/__main__.py +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/address.py +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/aza_master.py +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/dataset.py +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/exceptions.py +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/itaiji.py +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/itaiji_dic.json +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/module.py +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/result.py +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/strlib.py +0 -0
- {jageocoder-2.1.7.dev2 → jageocoder-2.1.7.dev3}/jageocoder/trie.py +0 -0
|
@@ -19,7 +19,7 @@ running the following steps.
|
|
|
19
19
|
>>> jageocoder.searchNode('<Japanese-address>')
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
__version__ = '2.1.7.
|
|
22
|
+
__version__ = '2.1.7.dev3' # The package version
|
|
23
23
|
__dictionary_version__ = '20230927' # Compatible dictionary version
|
|
24
24
|
__author__ = 'Takeshi Sagara <sagara@info-proto.com>'
|
|
25
25
|
|
|
@@ -70,6 +70,30 @@ class AddressNodeTable(PortableTab.BaseTable):
|
|
|
70
70
|
node.table = self
|
|
71
71
|
return node
|
|
72
72
|
|
|
73
|
+
def search_ids_on(
|
|
74
|
+
self,
|
|
75
|
+
attr: str,
|
|
76
|
+
value: str,
|
|
77
|
+
) -> list:
|
|
78
|
+
"""
|
|
79
|
+
Search id list from the table on the specified attribute.
|
|
80
|
+
|
|
81
|
+
Paramters
|
|
82
|
+
---------
|
|
83
|
+
attr: str
|
|
84
|
+
The name of target attribute.
|
|
85
|
+
value: str
|
|
86
|
+
The target value.
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
List[int]
|
|
91
|
+
List of node ids.
|
|
92
|
+
"""
|
|
93
|
+
trie = self.open_trie_on(attr)
|
|
94
|
+
positions = trie.get(value, [])
|
|
95
|
+
return [p[0] for p in positions]
|
|
96
|
+
|
|
73
97
|
def create_indexes(self) -> None:
|
|
74
98
|
"""
|
|
75
99
|
Create TRIE index on "name" and "note" columns.
|
|
@@ -514,7 +538,7 @@ class AddressNode(object):
|
|
|
514
538
|
"""
|
|
515
539
|
new_node = copy.copy(self)
|
|
516
540
|
for child in self.iter_children():
|
|
517
|
-
if child.
|
|
541
|
+
if child.has_valid_coordinate_values():
|
|
518
542
|
new_node.x, new_node.y = child.x, child.y
|
|
519
543
|
logger.debug((
|
|
520
544
|
"Node {}({}) has no coordinates. "
|
|
@@ -184,6 +184,30 @@ class RemoteNodeTable(object):
|
|
|
184
184
|
|
|
185
185
|
return nodes
|
|
186
186
|
|
|
187
|
+
def search_ids_on(
|
|
188
|
+
self,
|
|
189
|
+
attr: str,
|
|
190
|
+
value: str,
|
|
191
|
+
) -> list:
|
|
192
|
+
"""
|
|
193
|
+
Search id from the table on the specified attribute on the remote server.
|
|
194
|
+
|
|
195
|
+
Paramters
|
|
196
|
+
---------
|
|
197
|
+
attr: str
|
|
198
|
+
The name of target attribute.
|
|
199
|
+
value: str
|
|
200
|
+
The target value.
|
|
201
|
+
|
|
202
|
+
Returns
|
|
203
|
+
-------
|
|
204
|
+
List[Record]
|
|
205
|
+
List of records.
|
|
206
|
+
"""
|
|
207
|
+
nodes = self.search_records_on(attr, value)
|
|
208
|
+
ids = [node.id for node in nodes]
|
|
209
|
+
return ids
|
|
210
|
+
|
|
187
211
|
|
|
188
212
|
class RemoteTree(AddressTree):
|
|
189
213
|
"""
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import ABC
|
|
2
|
+
import copy
|
|
2
3
|
from logging import getLogger
|
|
3
4
|
import os
|
|
4
5
|
from typing import Iterable, List, Optional, Tuple
|
|
@@ -15,6 +16,16 @@ from jageocoder.node import AddressNode, AddressNodeTable
|
|
|
15
16
|
logger = getLogger(__name__)
|
|
16
17
|
|
|
17
18
|
|
|
19
|
+
class NodeDist(object):
|
|
20
|
+
|
|
21
|
+
def __init__(self, dist: float, node: AddressNode) -> None:
|
|
22
|
+
self.dist = dist
|
|
23
|
+
self.node = node
|
|
24
|
+
|
|
25
|
+
def __repr__(self) -> str:
|
|
26
|
+
return f"NodeDist({self.dist}, {self.node})"
|
|
27
|
+
|
|
28
|
+
|
|
18
29
|
class DelaunayTriangle(ABC):
|
|
19
30
|
|
|
20
31
|
@classmethod
|
|
@@ -131,8 +142,8 @@ class DelaunayTriangle(ABC):
|
|
|
131
142
|
cls,
|
|
132
143
|
x: float,
|
|
133
144
|
y: float,
|
|
134
|
-
nodes: List[
|
|
135
|
-
) -> List[
|
|
145
|
+
nodes: List[NodeDist]
|
|
146
|
+
) -> List[NodeDist]:
|
|
136
147
|
"""
|
|
137
148
|
Select the 3 nodes that make the smallest triangle
|
|
138
149
|
surrounding the target point.
|
|
@@ -143,13 +154,14 @@ class DelaunayTriangle(ABC):
|
|
|
143
154
|
The longitude of the target point.
|
|
144
155
|
y: float
|
|
145
156
|
The latitude of the target point.
|
|
146
|
-
nodes: List[
|
|
147
|
-
The candidate
|
|
157
|
+
nodes: List[NodeDist]
|
|
158
|
+
The candidate list of (distance, node).
|
|
148
159
|
|
|
149
160
|
Returns
|
|
150
161
|
-------
|
|
151
|
-
List[
|
|
152
|
-
Up to 3 nodes surrounding the target point
|
|
162
|
+
List[NodeDist]
|
|
163
|
+
Up to 3 nodes surrounding the target point
|
|
164
|
+
and their distance.
|
|
153
165
|
"""
|
|
154
166
|
def kval(t: Tuple[int, int, int]) -> int:
|
|
155
167
|
sval = sorted(t)
|
|
@@ -161,9 +173,9 @@ class DelaunayTriangle(ABC):
|
|
|
161
173
|
for p2 in range(p1 + 1, len(nodes)):
|
|
162
174
|
if cls.p_contained_triangle(
|
|
163
175
|
(x, y),
|
|
164
|
-
(nodes[p0].x, nodes[p0].y),
|
|
165
|
-
(nodes[p1].x, nodes[p1].y),
|
|
166
|
-
(nodes[p2].x, nodes[p2].y)
|
|
176
|
+
(nodes[p0].node.x, nodes[p0].node.y),
|
|
177
|
+
(nodes[p1].node.x, nodes[p1].node.y),
|
|
178
|
+
(nodes[p2].node.x, nodes[p2].node.y)
|
|
167
179
|
):
|
|
168
180
|
triangle = [p0, p1, p2]
|
|
169
181
|
break
|
|
@@ -187,10 +199,10 @@ class DelaunayTriangle(ABC):
|
|
|
187
199
|
continue
|
|
188
200
|
|
|
189
201
|
if cls.p_contained_circumcircle(
|
|
190
|
-
(nodes[i].x, nodes[i].y),
|
|
191
|
-
(nodes[triangle[0]].x, nodes[triangle[0]].y),
|
|
192
|
-
(nodes[triangle[1]].x, nodes[triangle[1]].y),
|
|
193
|
-
(nodes[triangle[2]].x, nodes[triangle[2]].y)
|
|
202
|
+
(nodes[i].node.x, nodes[i].node.y),
|
|
203
|
+
(nodes[triangle[0]].node.x, nodes[triangle[0]].node.y),
|
|
204
|
+
(nodes[triangle[1]].node.x, nodes[triangle[1]].node.y),
|
|
205
|
+
(nodes[triangle[2]].node.x, nodes[triangle[2]].node.y)
|
|
194
206
|
):
|
|
195
207
|
new_triangle = None
|
|
196
208
|
for j in range(3):
|
|
@@ -202,9 +214,9 @@ class DelaunayTriangle(ABC):
|
|
|
202
214
|
|
|
203
215
|
if cls.p_contained_triangle(
|
|
204
216
|
(x, y),
|
|
205
|
-
(nodes[tt[0]].x, nodes[tt[0]].y),
|
|
206
|
-
(nodes[tt[1]].x, nodes[tt[1]].y),
|
|
207
|
-
(nodes[tt[2]].x, nodes[tt[2]].y)
|
|
217
|
+
(nodes[tt[0]].node.x, nodes[tt[0]].node.y),
|
|
218
|
+
(nodes[tt[1]].node.x, nodes[tt[1]].node.y),
|
|
219
|
+
(nodes[tt[2]].node.x, nodes[tt[2]].node.y)
|
|
208
220
|
):
|
|
209
221
|
new_triangle = tt
|
|
210
222
|
break
|
|
@@ -309,6 +321,8 @@ class Index(object):
|
|
|
309
321
|
node_table: AddressNodeTable = self._tree.address_nodes
|
|
310
322
|
|
|
311
323
|
max_id = node_table.count_records()
|
|
324
|
+
|
|
325
|
+
logger.info("Building RTree for reverse geocoding...")
|
|
312
326
|
id = AddressNode.ROOT_NODE_ID
|
|
313
327
|
with tqdm(total=max_id, mininterval=0.5, ascii=True) as pbar:
|
|
314
328
|
prev_id = 0
|
|
@@ -317,22 +331,19 @@ class Index(object):
|
|
|
317
331
|
prev_id = id
|
|
318
332
|
|
|
319
333
|
node = node_table.get_record(pos=id)
|
|
320
|
-
if node.level > AddressLevel.
|
|
321
|
-
id = node.sibling_id
|
|
322
|
-
continue
|
|
323
|
-
elif node.level < AddressLevel.OAZA:
|
|
334
|
+
if node.level <= AddressLevel.WARD or node.level > AddressLevel.BLOCK:
|
|
324
335
|
id += 1
|
|
325
336
|
continue
|
|
326
|
-
|
|
337
|
+
|
|
338
|
+
if not node.has_valid_coordinate_values():
|
|
327
339
|
node = node.add_dummy_coordinates()
|
|
328
|
-
if not node.has_valid_coordinate_values():
|
|
329
|
-
id += 1
|
|
330
|
-
continue
|
|
331
340
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
341
|
+
if node.has_valid_coordinate_values():
|
|
342
|
+
file_idx.insert(
|
|
343
|
+
id=id,
|
|
344
|
+
coordinates=(node.x, node.y, node.x, node.y),
|
|
345
|
+
)
|
|
346
|
+
|
|
336
347
|
id += 1
|
|
337
348
|
|
|
338
349
|
return file_idx
|
|
@@ -366,20 +377,29 @@ class Index(object):
|
|
|
366
377
|
"""
|
|
367
378
|
node_table = self._tree.address_nodes
|
|
368
379
|
node = node_table.get_record(pos=node_table.count_records() // 2)
|
|
369
|
-
while node.level < AddressLevel.OAZA:
|
|
370
|
-
node = node_table.get_record(pos=node.id + 1)
|
|
371
380
|
|
|
372
|
-
while
|
|
373
|
-
node
|
|
381
|
+
while True:
|
|
382
|
+
while node.level < AddressLevel.BLOCK:
|
|
383
|
+
node = node_table.get_record(pos=node.id + 1)
|
|
384
|
+
|
|
385
|
+
while node.level > AddressLevel.BLOCK:
|
|
386
|
+
node = node.parent
|
|
387
|
+
|
|
388
|
+
if node.has_valid_coordinate_values():
|
|
389
|
+
break
|
|
390
|
+
|
|
391
|
+
node = node_table.get_record(pos=node.sibling_id)
|
|
374
392
|
|
|
375
|
-
|
|
393
|
+
results = tuple(self.idx.nearest((node.x, node.y, node.x, node.y), 20))
|
|
394
|
+
return len(results) > 0 and node.id in results
|
|
376
395
|
|
|
377
396
|
def _sort_by_dist(
|
|
378
397
|
self,
|
|
379
398
|
lon: float,
|
|
380
399
|
lat: float,
|
|
381
|
-
id_list: Iterable[int]
|
|
382
|
-
|
|
400
|
+
id_list: Iterable[int],
|
|
401
|
+
node_map: Optional[dict] = None,
|
|
402
|
+
) -> Tuple[List[NodeDist], dict]:
|
|
383
403
|
"""
|
|
384
404
|
Sort nodes by real(projected) distance from the target point.
|
|
385
405
|
|
|
@@ -394,17 +414,26 @@ class Index(object):
|
|
|
394
414
|
|
|
395
415
|
Returns
|
|
396
416
|
-------
|
|
397
|
-
List[
|
|
398
|
-
The sorted list of address
|
|
417
|
+
Tuple[List[NodeDist], dict]
|
|
418
|
+
The sorted list of (distance, address node) and a node map.
|
|
399
419
|
"""
|
|
420
|
+
node_map = node_map or {}
|
|
400
421
|
results = []
|
|
401
|
-
for node_id in id_list:
|
|
402
|
-
node = self._tree.
|
|
403
|
-
|
|
404
|
-
|
|
422
|
+
for node_id in set(id_list):
|
|
423
|
+
node = self._tree.get_node_by_id(node_id=node_id)
|
|
424
|
+
if not node.has_valid_coordinate_values():
|
|
425
|
+
node = node.add_dummy_coordinates()
|
|
405
426
|
|
|
406
|
-
|
|
407
|
-
|
|
427
|
+
key = (node.x, node.y)
|
|
428
|
+
if key in node_map:
|
|
429
|
+
node_map[key].append(node)
|
|
430
|
+
else:
|
|
431
|
+
node_map[key] = [node]
|
|
432
|
+
dist = self.distance(node.x, node.y, lon, lat)
|
|
433
|
+
results.append(NodeDist(dist, node))
|
|
434
|
+
|
|
435
|
+
results.sort(key=lambda x: x.dist)
|
|
436
|
+
return (results, node_map)
|
|
408
437
|
|
|
409
438
|
def nearest(
|
|
410
439
|
self,
|
|
@@ -434,95 +463,163 @@ class Index(object):
|
|
|
434
463
|
[{"candidate":AddressNode or dict, "dist":float}]
|
|
435
464
|
Returns the results of retrieval up to 3 nodes.
|
|
436
465
|
"""
|
|
437
|
-
level = level or AddressLevel.AZA
|
|
438
|
-
|
|
439
|
-
# Search nodes by Rtree Index
|
|
440
|
-
nodes = []
|
|
441
|
-
ancestors = set()
|
|
442
|
-
max_level = 0
|
|
443
|
-
for node in self._sort_by_dist(x, y, self.idx.nearest((x, y, x, y), 10)):
|
|
444
|
-
if node.id in ancestors:
|
|
445
|
-
continue
|
|
446
|
-
|
|
447
|
-
if not node.has_valid_coordinate_values():
|
|
448
|
-
node = node.add_dummy_coordinates()
|
|
449
466
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
cur = cur.parent
|
|
458
|
-
|
|
459
|
-
if level > max_level:
|
|
460
|
-
# Search points in the higher levels
|
|
461
|
-
local_idx = index.Rtree() # Create local rtree on memory
|
|
462
|
-
for node in nodes:
|
|
463
|
-
child_id = node.id
|
|
464
|
-
while child_id < node.sibling_id:
|
|
465
|
-
child_node = self._tree.get_address_node(id=child_id)
|
|
466
|
-
if child_node.level > level:
|
|
467
|
-
child_id = child_node.parent.sibling_id
|
|
468
|
-
continue
|
|
469
|
-
elif not child_node.has_valid_coordinate_values():
|
|
470
|
-
child_node = child_node.add_dummy_coordinates()
|
|
471
|
-
if not child_node.has_valid_coordinate_values():
|
|
472
|
-
child_id += 1
|
|
473
|
-
continue
|
|
474
|
-
|
|
475
|
-
local_idx.insert(
|
|
476
|
-
id=child_id,
|
|
477
|
-
coordinates=(
|
|
478
|
-
child_node.x, child_node.y,
|
|
479
|
-
child_node.x, child_node.y))
|
|
480
|
-
child_id += 1
|
|
467
|
+
def _remove_parent_nodes(
|
|
468
|
+
candidates: Iterable[NodeDist]
|
|
469
|
+
) -> List[NodeDist]:
|
|
470
|
+
ancestors = set()
|
|
471
|
+
max_level = 0
|
|
472
|
+
if len(candidates) == 0:
|
|
473
|
+
return []
|
|
481
474
|
|
|
482
475
|
nodes = []
|
|
483
|
-
|
|
484
|
-
|
|
476
|
+
for v in candidates:
|
|
477
|
+
dist, node = v.dist, v.node
|
|
485
478
|
if node.id in ancestors:
|
|
486
479
|
continue
|
|
487
480
|
|
|
488
481
|
if not node.has_valid_coordinate_values():
|
|
489
482
|
node = node.add_dummy_coordinates()
|
|
490
483
|
|
|
491
|
-
nodes.append(node)
|
|
492
|
-
|
|
484
|
+
nodes.append(NodeDist(dist, node))
|
|
485
|
+
max_level = max(max_level, node.level)
|
|
486
|
+
|
|
487
|
+
# List ancestor nodes of registering node.
|
|
493
488
|
cur = node.parent
|
|
494
489
|
while cur is not None:
|
|
495
|
-
nodes = [node for node in nodes if node.id != cur.id]
|
|
490
|
+
# nodes = [node for node in nodes if node.id != cur.id]
|
|
496
491
|
ancestors.add(cur.id)
|
|
497
492
|
cur = cur.parent
|
|
498
493
|
|
|
494
|
+
# Exclude ancestor nodes
|
|
495
|
+
nodes = [node for node in nodes if node.node.id not in ancestors]
|
|
496
|
+
return nodes
|
|
497
|
+
|
|
498
|
+
def _get_k_nearest_child_nodes(
|
|
499
|
+
aza_node_dists: List[NodeDist],
|
|
500
|
+
*,
|
|
501
|
+
candidates: Optional[List[NodeDist]] = None,
|
|
502
|
+
node_map: Optional[dict] = None,
|
|
503
|
+
k: Optional[int] = 20,
|
|
504
|
+
min_k: Optional[int] = 0,
|
|
505
|
+
max_dist: Optional[float] = 500.0,
|
|
506
|
+
) -> Tuple[List[NodeDist], dict]:
|
|
507
|
+
candidates = candidates or []
|
|
508
|
+
node_map = node_map or {}
|
|
509
|
+
for v in aza_node_dists:
|
|
510
|
+
dist, node = v.dist, v.node
|
|
511
|
+
child_id = node.id + 1
|
|
512
|
+
for child_id in range(node.id + 1, node.sibling_id):
|
|
513
|
+
child_node = self._tree.get_node_by_id(
|
|
514
|
+
node_id=child_id)
|
|
515
|
+
if child_node.level > level:
|
|
516
|
+
continue
|
|
517
|
+
|
|
518
|
+
if not child_node.has_valid_coordinate_values():
|
|
519
|
+
if child_node.level == level:
|
|
520
|
+
continue
|
|
521
|
+
|
|
522
|
+
child_node = child_node.add_dummy_coordinates()
|
|
523
|
+
if not child_node.has_valid_coordinate_values():
|
|
524
|
+
continue
|
|
525
|
+
|
|
526
|
+
key = (child_node.x, child_node.y)
|
|
527
|
+
if key in node_map:
|
|
528
|
+
# A node with the same coordinates are already registered
|
|
529
|
+
node_map[key].append(child_node)
|
|
530
|
+
continue
|
|
531
|
+
|
|
532
|
+
dist = self.distance(x, y, child_node.x, child_node.y)
|
|
533
|
+
i = len(candidates)
|
|
534
|
+
while i > 0:
|
|
535
|
+
if dist >= candidates[i - 1].dist:
|
|
536
|
+
break
|
|
537
|
+
|
|
538
|
+
i -= 1
|
|
539
|
+
|
|
540
|
+
if i < min_k or (i < k and dist <= max_dist):
|
|
541
|
+
candidates.insert(i, NodeDist(dist, child_node))
|
|
542
|
+
node_map[key] = [child_node]
|
|
543
|
+
n = len(candidates)
|
|
544
|
+
if n > k:
|
|
545
|
+
delnode = candidates[k].node
|
|
546
|
+
del candidates[k]
|
|
547
|
+
delkey = (delnode.x, delnode.y)
|
|
548
|
+
node_map[delkey].remove(delnode)
|
|
549
|
+
if len(node_map[delkey]) == 0:
|
|
550
|
+
del node_map[delkey]
|
|
551
|
+
|
|
552
|
+
elif n > min_k and candidates[min_k].dist > max_dist:
|
|
553
|
+
delnode = candidates[min_k].node
|
|
554
|
+
del candidates[min_k]
|
|
555
|
+
delkey = (delnode.x, delnode.y)
|
|
556
|
+
node_map[delkey].remove(delnode)
|
|
557
|
+
if len(node_map[delkey]) == 0:
|
|
558
|
+
del node_map[delkey]
|
|
559
|
+
|
|
560
|
+
return (candidates, node_map)
|
|
561
|
+
|
|
562
|
+
level = level or AddressLevel.AZA
|
|
563
|
+
|
|
564
|
+
# Retrieve top k-nearest nodes using the R-tree index.
|
|
565
|
+
nearests = self.idx.nearest((x, y, x, y), 20)
|
|
566
|
+
node_dists, node_map = self._sort_by_dist(x, y, nearests)
|
|
567
|
+
node_dists = _remove_parent_nodes(node_dists)
|
|
568
|
+
|
|
569
|
+
if level > AddressLevel.BLOCK:
|
|
570
|
+
candidates, node_map = _get_k_nearest_child_nodes(
|
|
571
|
+
node_dists,
|
|
572
|
+
candidates=copy.copy(node_dists),
|
|
573
|
+
node_map=node_map,
|
|
574
|
+
min_k=1)
|
|
575
|
+
|
|
576
|
+
node_dists = _remove_parent_nodes(candidates)
|
|
577
|
+
|
|
499
578
|
# Select the 3 nodes that make the smallest triangle
|
|
500
579
|
# surrounding the target point
|
|
501
|
-
if len(
|
|
580
|
+
if len(node_dists) == 0:
|
|
502
581
|
return []
|
|
503
582
|
|
|
504
|
-
if
|
|
583
|
+
if len(node_dists) <= 3 or node_dists[0].dist < 1.0e-02:
|
|
505
584
|
# If the distance between the nearest point and the search point is
|
|
506
585
|
# less than 1 cm, it returns three points in order of distance.
|
|
507
586
|
# This is because the nearest point may not be included in
|
|
508
587
|
# the search results due to a calculation error.
|
|
509
|
-
|
|
588
|
+
node_dists = node_dists[0:3]
|
|
510
589
|
else:
|
|
511
|
-
|
|
590
|
+
node_dists = DelaunayTriangle.select(x, y, node_dists)
|
|
591
|
+
|
|
592
|
+
# Restore nodes with the same coordinates
|
|
593
|
+
if node_map is not None:
|
|
594
|
+
_node_dists = []
|
|
595
|
+
for v in node_dists:
|
|
596
|
+
dist, node = v.dist, v.node
|
|
597
|
+
key = (node.x, node.y)
|
|
598
|
+
for n in node_map[key]:
|
|
599
|
+
_node_dists.append(NodeDist(dist, n))
|
|
600
|
+
|
|
601
|
+
node_dists = _node_dists
|
|
512
602
|
|
|
513
603
|
# Convert nodes to the dict format.
|
|
514
604
|
results = []
|
|
515
605
|
registered = set()
|
|
516
|
-
for
|
|
606
|
+
for v in node_dists:
|
|
607
|
+
dist, node = v.dist, v.node
|
|
517
608
|
while node.level > level:
|
|
518
609
|
node = node.parent
|
|
610
|
+
dist = None
|
|
611
|
+
if not node.has_valid_coordinate_values():
|
|
612
|
+
node.x, node.y = v.node.x, v.node.y
|
|
519
613
|
|
|
520
614
|
if node.id in registered:
|
|
521
615
|
continue
|
|
522
616
|
|
|
617
|
+
if dist is None:
|
|
618
|
+
dist = self.distance(x, y, node.x, node.y)
|
|
619
|
+
|
|
523
620
|
results.append({
|
|
524
621
|
"candidate": node.as_dict() if as_dict else node,
|
|
525
|
-
"dist":
|
|
622
|
+
"dist": dist
|
|
526
623
|
})
|
|
527
624
|
registered.add(node.id)
|
|
528
625
|
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
from collections import OrderedDict
|
|
2
|
-
import csv
|
|
3
|
-
import json
|
|
4
2
|
from logging import getLogger
|
|
5
3
|
import os
|
|
6
4
|
from pathlib import Path
|
|
7
5
|
import re
|
|
8
6
|
import site
|
|
9
7
|
import sys
|
|
10
|
-
from typing import Any, Union, List, Set,
|
|
8
|
+
from typing import Any, Union, List, Set, Optional
|
|
11
9
|
|
|
12
10
|
from deprecated import deprecated
|
|
13
11
|
|
|
14
|
-
import jageocoder
|
|
15
12
|
from jageocoder.address import AddressLevel
|
|
16
13
|
from jageocoder.aza_master import AzaMaster
|
|
17
14
|
from jageocoder.exceptions import AddressTreeException
|
|
@@ -285,11 +282,13 @@ class AddressTree(object):
|
|
|
285
282
|
node_id: int
|
|
286
283
|
The target node id.
|
|
287
284
|
|
|
288
|
-
|
|
289
|
-
|
|
285
|
+
Returns
|
|
286
|
+
-------
|
|
290
287
|
AddressNode
|
|
291
288
|
"""
|
|
292
|
-
|
|
289
|
+
node = self.address_nodes.get_record(node_id)
|
|
290
|
+
node.tree = self
|
|
291
|
+
return node
|
|
293
292
|
|
|
294
293
|
def search_nodes_by_codes(
|
|
295
294
|
self,
|
|
@@ -316,6 +315,31 @@ class AddressTree(object):
|
|
|
316
315
|
|
|
317
316
|
return nodes
|
|
318
317
|
|
|
318
|
+
def search_ids_by_codes(
|
|
319
|
+
self,
|
|
320
|
+
category: str,
|
|
321
|
+
value: str) -> List[AddressNode]:
|
|
322
|
+
"""
|
|
323
|
+
Search node ids by category and value.
|
|
324
|
+
|
|
325
|
+
Parameters
|
|
326
|
+
----------
|
|
327
|
+
category: str
|
|
328
|
+
Category name such as 'jisx0402' or 'postcode'.
|
|
329
|
+
value: str
|
|
330
|
+
Target value.
|
|
331
|
+
|
|
332
|
+
Returns
|
|
333
|
+
-------
|
|
334
|
+
List[int]
|
|
335
|
+
"""
|
|
336
|
+
ids = []
|
|
337
|
+
pattern = '{}:{}'.format(category, value)
|
|
338
|
+
ids = self.address_nodes.search_ids_on(
|
|
339
|
+
attr="note", value=pattern) # exact match
|
|
340
|
+
|
|
341
|
+
return ids
|
|
342
|
+
|
|
319
343
|
@deprecated("Use 'node.get_fullname()' instead of this method.")
|
|
320
344
|
def get_node_fullname(self, node: Union[AddressNode, int]) -> List[str]:
|
|
321
345
|
if isinstance(node, int):
|
|
@@ -965,6 +989,7 @@ class AddressTree(object):
|
|
|
965
989
|
|
|
966
990
|
return results
|
|
967
991
|
|
|
992
|
+
@deprecated(reason="Use 'get_node_by_id'.", version="2.1.7")
|
|
968
993
|
def get_address_node(self, id: int) -> AddressNode:
|
|
969
994
|
"""
|
|
970
995
|
Get address node from the tree by its id.
|
|
@@ -1171,17 +1196,30 @@ class AddressTree(object):
|
|
|
1171
1196
|
"""
|
|
1172
1197
|
if len(id) == 12:
|
|
1173
1198
|
# jisx0402(5digits) + aza_id(7digits)
|
|
1174
|
-
|
|
1199
|
+
citynode = self.search_by_citycode(code=id[0:5])
|
|
1200
|
+
if len(citynode) == 0:
|
|
1201
|
+
return []
|
|
1202
|
+
|
|
1203
|
+
citynode = citynode[0]
|
|
1204
|
+
candidates = self.search_ids_by_codes(
|
|
1175
1205
|
category="aza_id",
|
|
1176
1206
|
value=id[-7:])
|
|
1177
|
-
nodes = [
|
|
1207
|
+
nodes = [self.address_nodes.get_record(x)
|
|
1208
|
+
for x in candidates
|
|
1209
|
+
if x >= citynode.id and x < citynode.sibling_id]
|
|
1178
1210
|
elif len(id) == 13:
|
|
1179
1211
|
# lasdec(6digits) + aza_id(7digits)
|
|
1180
|
-
|
|
1212
|
+
citynode = self.search_by_citycode(code=id[0:6])
|
|
1213
|
+
if len(citynode) == 0:
|
|
1214
|
+
return []
|
|
1215
|
+
|
|
1216
|
+
citynode = citynode[0]
|
|
1217
|
+
candidates = self.search_ids_by_codes(
|
|
1181
1218
|
category="aza_id",
|
|
1182
1219
|
value=id[-7:])
|
|
1183
|
-
nodes = [x
|
|
1184
|
-
|
|
1220
|
+
nodes = [self.address_nodes.get_record(x)
|
|
1221
|
+
for x in candidates
|
|
1222
|
+
if x >= citynode.id and x < citynode.sibling_id]
|
|
1185
1223
|
else:
|
|
1186
1224
|
nodes = self.search_nodes_by_codes(
|
|
1187
1225
|
category="aza_id",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|