feat(b08): 거리이정표 [+] 모달 · 주소 검색 재활용 · 방위 자동 · 시점 모달 · 팬 줌 · 경로 탭 = 길찾기 그림 (244-4)
- 공사지 · 기준점 [+] = 모달(항목 · 업체 이름 · 목적지 + B01 주소 검색 · 지도) · 방위 고르기 없앰 · 좌표로 자동 자리 - 상자 누름 = 모달(업체 = 항목 정보 · 구간 km · 도로 구분 · 고침 · 삭제 / 공사지 = 등록 주소 · VWorld 주소 · 지도 / 기준점 = 공통 구간) - 다이어그램 팬 · 줌 = 공용 b_svg_zoom_pan - 경로 하위 탭 = 그림만 · [다음 지도에서 길찾기] 새 창 · [화면 캡처] · 하위 탭 [추가] · [삭제] 없앰 - 서버 site-address(좌표 → 주소 · VWorld getAddress) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
This commit is contained in:
@@ -114,3 +114,30 @@ def _search_sync(query: str, size: int) -> list[dict[str, Any]]:
|
||||
|
||||
async def search_address(query: str, size: int = 10) -> list[dict[str, Any]]:
|
||||
return await asyncio.to_thread(_search_sync, query.strip(), size)
|
||||
|
||||
|
||||
def _reverse_sync(lon: float, lat: float) -> str | None:
|
||||
"""좌표 → 주소 한 줄(VWorld getAddress · 도로명 먼저 · 없으면 지번)."""
|
||||
query = urllib.parse.urlencode(
|
||||
{
|
||||
"service": "address",
|
||||
"request": "getAddress",
|
||||
"version": "2.0",
|
||||
"crs": "epsg:4326",
|
||||
"point": f"{lon},{lat}",
|
||||
"type": "both",
|
||||
"format": "json",
|
||||
"key": VWORLD_API_KEY,
|
||||
}
|
||||
)
|
||||
try:
|
||||
body = json.loads(_fetch(f"{_GEOCODE_URL}?{query}").decode("utf-8"))
|
||||
except Exception:
|
||||
return None
|
||||
items = (body.get("response") or {}).get("result") or []
|
||||
texts = {item.get("type"): item.get("text") for item in items if isinstance(item, dict)}
|
||||
return texts.get("road") or texts.get("parcel") or None
|
||||
|
||||
|
||||
async def reverse_geocode(lon: float, lat: float) -> str | None:
|
||||
return await asyncio.to_thread(_reverse_sync, lon, lat)
|
||||
|
||||
@@ -19,6 +19,8 @@ export interface AddressFieldHandle {
|
||||
root: HTMLElement;
|
||||
/** 저장할 한 줄 주소. 비면 null. */
|
||||
value: () => string | null;
|
||||
/** 고른(또는 지도 확인으로 찾은) 좌표. 모르면 null. */
|
||||
point: () => { lat: number; lon: number } | null;
|
||||
}
|
||||
|
||||
/** 지번으로 찾은 줄은 `parcel` 쪽이 시·도까지 갖춘 온전한 주소다. */
|
||||
@@ -94,7 +96,7 @@ export function buildAddressField(initial: string | null): AddressFieldHandle {
|
||||
label: "지도 확인",
|
||||
variant: "ghost",
|
||||
onClick: async function onB01_Address_Map_Click() {
|
||||
await map.show(base.input.value.trim());
|
||||
picked = await map.show(base.input.value.trim());
|
||||
},
|
||||
});
|
||||
mapBtn.hidden = true;
|
||||
@@ -102,6 +104,7 @@ export function buildAddressField(initial: string | null): AddressFieldHandle {
|
||||
root.append(searchRow, list, row(base.root, zip), row(detail.root), row(mapBtn), map.root);
|
||||
|
||||
let manual = false;
|
||||
let picked: { lat: number; lon: number } | null = null;
|
||||
|
||||
function pick(item: AddressCandidate): void {
|
||||
base.input.value = candidateLine(item);
|
||||
@@ -109,6 +112,7 @@ export function buildAddressField(initial: string | null): AddressFieldHandle {
|
||||
zip.hidden = !item.zipcode;
|
||||
list.hidden = true;
|
||||
list.innerHTML = "";
|
||||
picked = { lat: item.lat, lon: item.lon };
|
||||
map.showPoint(item.lat, item.lon);
|
||||
detail.input.focus();
|
||||
}
|
||||
@@ -160,7 +164,7 @@ export function buildAddressField(initial: string | null): AddressFieldHandle {
|
||||
});
|
||||
|
||||
// 이미 있는 주소는 열자마자 지도로 보여 준다.
|
||||
if (initial && initial.trim()) void map.show(initial.trim());
|
||||
if (initial && initial.trim()) void map.show(initial.trim()).then((p) => (picked ??= p));
|
||||
|
||||
return {
|
||||
root,
|
||||
@@ -168,5 +172,6 @@ export function buildAddressField(initial: string | null): AddressFieldHandle {
|
||||
const merged = `${base.input.value.trim()} ${detail.input.value.trim()}`.trim();
|
||||
return merged || null;
|
||||
},
|
||||
point: () => picked,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ function tileIndex(
|
||||
|
||||
export function buildAddressMap(): {
|
||||
root: HTMLElement;
|
||||
show: (address: string) => Promise<void>;
|
||||
/** 찾은 좌표를 돌려줌 — 못 찾으면 null. */
|
||||
show: (address: string) => Promise<{ lat: number; lon: number } | null>;
|
||||
/** 좌표를 이미 아는 경우 — 다시 찾지 않고 바로 그린다. */
|
||||
showPoint: (lat: number, lon: number) => void;
|
||||
} {
|
||||
@@ -110,17 +111,18 @@ export function buildAddressMap(): {
|
||||
note.textContent = message;
|
||||
}
|
||||
|
||||
const show = async (address: string): Promise<void> => {
|
||||
const show = async (address: string): Promise<{ lat: number; lon: number } | null> => {
|
||||
if (!address.trim()) {
|
||||
fail("주소를 먼저 입력하십시오.");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
const point = await geocodeAddress(address).catch(() => null);
|
||||
if (!point) {
|
||||
fail("그 주소를 찾지 못했습니다. 도로명 또는 지번 주소로 다시 넣으십시오.");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
draw(point.lat, point.lon);
|
||||
return { lat: point.lat, lon: point.lon };
|
||||
};
|
||||
|
||||
return { root, show, showPoint: draw };
|
||||
|
||||
Reference in New Issue
Block a user