mirror of
https://github.com/15000rpm/Pole-Mapper.git
synced 2026-10-09 10:45:10 +09:00
99 lines
2.9 KiB
JavaScript
99 lines
2.9 KiB
JavaScript
const VWORLD_ENDPOINT = 'https://api.vworld.kr/req/data';
|
|
const VWORLD_ADDRESS_ENDPOINT = 'https://api.vworld.kr/req/address';
|
|
|
|
function credentials() {
|
|
return {
|
|
key: process.env.VWORLD_API_KEY || process.env.VITE_VWORLD_API_KEY || '',
|
|
domain: process.env.VWORLD_DOMAIN || 'localhost',
|
|
};
|
|
}
|
|
|
|
async function fetchVworldFeatures(data, attrFilter) {
|
|
const { key, domain } = credentials();
|
|
if (!key) throw new Error('VWORLD_API_KEY 환경변수가 설정되지 않았습니다.');
|
|
|
|
const size = 1000;
|
|
const base = new URLSearchParams({
|
|
service: 'data',
|
|
request: 'GetFeature',
|
|
data,
|
|
format: 'json',
|
|
geometry: 'false',
|
|
size: String(size),
|
|
key,
|
|
});
|
|
if (attrFilter) base.set('attrFilter', attrFilter);
|
|
base.set('domain', domain);
|
|
|
|
const features = [];
|
|
let page = 1;
|
|
let total = Infinity;
|
|
|
|
while (features.length < total && page <= 20) {
|
|
const params = new URLSearchParams(base);
|
|
params.set('page', String(page));
|
|
|
|
const res = await fetch(`${VWORLD_ENDPOINT}?${params}`);
|
|
if (!res.ok) throw new Error(`VWorld 요청 실패 (HTTP ${res.status})`);
|
|
const json = await res.json();
|
|
|
|
const response = json.response;
|
|
if (response?.status !== 'OK') {
|
|
throw new Error(`VWorld 조회 실패: ${JSON.stringify(response ?? json)}`);
|
|
}
|
|
|
|
const result = response.result?.featureCollection?.features ?? [];
|
|
features.push(...result);
|
|
|
|
total = Number(response.record?.total ?? 0) || total;
|
|
if (result.length === 0) break;
|
|
page += 1;
|
|
}
|
|
return features;
|
|
}
|
|
|
|
export async function fetchGus() {
|
|
const features = await fetchVworldFeatures('LT_C_ADSIGG_INFO', 'full_nm:like:서울');
|
|
return features.map((f) => ({
|
|
code: f.properties.sig_cd,
|
|
name: f.properties.sig_kor_nm,
|
|
}));
|
|
}
|
|
|
|
export async function fetchDongs(guCode) {
|
|
const features = await fetchVworldFeatures('LT_C_ADEMD_INFO', `emd_cd:like:${guCode}`);
|
|
return features.map((f) => f.properties.emd_kor_nm).filter(Boolean);
|
|
}
|
|
|
|
export async function reverseGeocode(lat, lng) {
|
|
const { key, domain } = credentials();
|
|
if (!key) throw new Error('VWORLD_API_KEY 환경변수가 설정되지 않았습니다.');
|
|
|
|
const params = new URLSearchParams({
|
|
service: 'address',
|
|
request: 'getaddress',
|
|
version: '2.0',
|
|
crs: 'epsg:4326',
|
|
point: `${lng},${lat}`,
|
|
format: 'json',
|
|
type: 'PARCEL',
|
|
key,
|
|
domain,
|
|
});
|
|
|
|
const res = await fetch(`${VWORLD_ADDRESS_ENDPOINT}?${params}`);
|
|
if (!res.ok) throw new Error(`VWorld 지오코딩 실패 (HTTP ${res.status})`);
|
|
const json = await res.json();
|
|
const response = json.response;
|
|
if (response?.status !== 'OK') return { gu: '', dong: '' };
|
|
|
|
const first = Array.isArray(response.result) ? response.result[0] : response.result;
|
|
const structure = first?.structure;
|
|
if (!structure) return { gu: '', dong: '' };
|
|
|
|
return {
|
|
gu: structure.level2 || '',
|
|
dong: structure.level3 || structure.level4L || '',
|
|
};
|
|
}
|