๋์ฉ๋ ์์ฒญ ํธ๋ํฝ ์ฒ๋ฆฌ๋ฅผ ์ํ Reverse Geocoding ์๋ฒ ๊ตฌํ: ๊ณต๊ณต ๋ฐ์ดํฐ ํ์ฉ๊ณผ ํ์ํ ๊ธฐ์ ์คํ ์ ์
๋ชจ๋น๋ฆฌํฐ ์๋น์ค๋ฅผ ๋ง๋ค๋ฉด ํ์ฌ ์์น์ ์ฃผ์๋ฅผ ํ์ํด ์ฃผ๋ UX ๊ฐ ๋ง์ด ๋ค์ด๊ฐ๋ค.
์ฃผ์๋ฅผ ๊ตฌ์ฑํ๋ ๊ณ์ธต ํด๋ฆฌ๊ณค๋ค์ ๊ตญ๊ฐ์์ ๊ด๋ฆฌํ๋ ๊ณต๊ณต ๋ฐ์ดํ์ด๋ค.
์์น๋ก ์ฃผ์๋ฅผ ์ป์ ์ ์๋ reverse geocoding API ๋ ์๋น์ค์์ ์ฌ์ฉํ ์์ค์ ๋ฌด๋ฃ ๊ณต๊ณต API ์ฐพ๊ธฐ๋ ์ฝ์ง ์๋ค.
๊ตฌ๊ธ, ๋ค์ด๋ฒ, ์นด์นด์ค ๋ฑ์ ํฌํธ ์์น ์๋น์ค ํ์ฌ์ ์ ๋ฃ API ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
๊ณต๊ณต ๋ฐ์ดํ๋ฅผ ํ์ฉํด์ ๊ตฌํํ ์ ์๋ ์ ์ ํ ๊ธฐ์ ์คํ์ ์ ์ํ๋ค.
ํ ์ ์ด ํด๋ฆฌ๊ณค์ ํฌํจ ๋๋์ง๋ฅผ ํ๋จํ๋๊ฑด "ray casting algorithm" ์ด๋ค.
mysql, postgres ๊ฐ์ RDB ์ geometry ์ฐ์ฐ์ ์ด์ฉํ๋ ค๊ณ ํ
์ด๋ธ์ ๋ฃ๊ณ ์ฟผ๋ฆฌ๋ก ํ๋ฉด ์ฝ์ง ์์๊น ?
์ฝ๋ค, ํ์ง๋ง ๋๋ฆฐ ์๋ต ์๋ ํนํ ํ์ผ์ด ๋ฉ๋ชจ๋ฆฌ ์บ์ฌ๋ก ์ ํ๋๋ ์์ ์์ ์๋ฒ๋ ํญ์ฃผํ๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ๋ค.
๊ทธ๋ ๋ค๊ณ ํ์ผ ๋ณด๋ค ํฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ฃ์ด์ ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ์บ์ฌ์ ์ฌ๋ฆฌ๊ณ ์ฌ์ฉํ๋๊ฑด ๊ตณ์ด RDB ๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๊ฐ ์๋ค.
์๋ณธ ํ์ผ shapefile ์ ๋ก๋ฉํด์ spatial index ์ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋ฉํด์ ์๋ฒ๋ก ์๋นํ๋ค.
๋ง์ ๊ฐ๋ฐ ํญ๋ชฉ๋ค์ด ์์๊ฑฐ ๊ฐ์ง๋ง ๊ทธ๋ ์ง ์๋ค 25๋
ํ์ฌ ๋๋ฌด ์ข์ ์คํ ์์ค๋ค์ด ๋ง๋ค.
๊ณต๊ณต ๋ฐ์ดํ๋ก ๋ค์ด๋ฐ์ shapfile ์ ์ฌ์ฉํด์ ์ฃผ์ ์ง๋ฒ ํด๋ฆฌ๊ณค๊น์ง ๊ฒ์ํ๋ ์๋ฒ๋ฅผ ๊ตฌํํ๋ค.
์ ์ ํ ์ธ์ด, geometry ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ ์ํ๋ค
์ปจ๋ฒํฐ, ํด๋ฆฌ๊ณค ํธ์งํด๋ ๊ตฌํํ๋ค.
๋ฐ์ดํ๋ฅผ ๊ณต๊ฐํ ๋ ๋ช๊ฐ์ง ๊ณ ๋ คํ ์ฌํญ์ ์ ์ํ๋ค.
LLM ์์๋ค๋ฉด ๊ตฌํํ๊ธฐ๊ฐ ์ฝ์ง ์์์๊ฑฐ๋ค.
์๋ฐ/์ฝํธ๋ฆฐ, ํ์ด์ , node(javascript), ... ๋ก ์๋ฒ๋ฅผ ์ฝ๊ฒ ๊ตฌํํ๋ค.
ํฌํธ ์์ค์ ํธ๋ํฝ์ ๊ฐ๋นํ๋ ค๋ฉด ๊ต์ฅํ ๋ง์ ๋ฆฌ์์ค๊ฐ ๋ ๋ค.
ํธํจํฝ์ด ๋ง์ ๋ถ๋ถ์ apache/nginx ์ c/c++ module, erlang, ๊ฐ์ ์ธ์ด๋ก ๊ฐ๋ฐํ๋ค.
golang ์ผ๋ก ๊ฐ๋ฐ์ ํ๋ ๊ณณ๋ ๋ง๋ค ๋ฌ๋์ปค๋ธ๋ ๋ฎ๊ณ ์๋ฐ ๋ณด๋ค ์ฑ๋ฅ์ด ์ข๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฐ์ด๋ ์ฑ๋ฅ๊ณผ ์์ ์ ์ผ๋ก ์๋ฒ๋ฅผ ๊ตฌํํ ์ ์๋ ์ธ์ด๋ ๋ฌด์์ผ๊น?
์ปดํ์ผ๋ง ๋๋ฉด ์ปดํ์ผ ํ์ ๋ฉ๋ชจ๋ฆฌ ์์ ์ฑ, ๊ฐ๋น์ง ์ปฌ๋ ์
๋ถํ์, ์ค๋ ๋ ์์ ์ฑ ๋ณด์ฅ, ... ๋๋ฌด ์ข๋ค.
๋ฌ๋ ์ปค๋ธ๊ฐ ๋ง์ง๋ง LLM ์ ์ด์ฉํ๋ ๊ทธ๋ฆฌ ๋ถ๋ด์ ๋์ง ์์๋ค.
์ง์ญ๋ณ ๊ณ์ธต ํด๋ฆฌ๊ณค์ par_iter() ๋ก ๋ณ๋ ฌ ๋ก๋ฉํ๋ ์ฝ๋ ์
config.districts
.par_iter()
.try_for_each(|district| {
config.district_par
.par_iter()
.try_for_each(|name|
self.load_district_par_polygons(
config,
district,
name,
config.debug,
&config.debug_name,
)
)?;
config.district_par_any
.par_iter()
.try_for_each(|name|
self.load_district_par_any_polygons(
config,
district,
name,
config.debug,
&config.debug_name,
)
)
})?;
Eric Veach ๊ฐ ๊ตฌ๊ธ Maps ์ ๋ค์ด์์ ๋ง๋ค์๋ค.
S2 is a library for spherical geometry that aims to have the same robustness,
flexibility, and performance as the very best planar geometry libraries.
s2geometry ๊ฐ rust ๋ก ์ ์ฒด๊ฐ ํฌํ
๋์ด ์์ง ์๋ค.
rust ์์ c++ ๋ฅผ ์์ ํ๊ฒ ์ฌ์ฉํ๊ธฐ ์ํด autocxx ๋ฅผ ์ฌ์ฉํ๋ค.
์ค์ ํ์ผ์ ํ๋ก๊ทธ๋จ์ ์์ ๋กญ๊ฒ ๋์ํ๊ธฐ ์ํด ๋งค์ฐ ์ค์ํ๋ค.
xml, json, yml ... ๋ง์ ๊ฒ๋ค์ด ์๋ค.
๊ทธ ์ค ์ต์
์ ์ญ์ xml ์ธ๊ฒ ๊ฐ๋ค.
config.toml ์ค์ ํ์ผ์์ ๋ฐ์ดํ ๋ก๋ฉ, ๊ฒ์ ๋ฐฉ๋ฒ์ ์ ์ํ๋ค.
[search.shapefile]
path = "./data/converted"
[search.shapefile.attributes]
"TL_SCCO_CTPRVN" = { level = 1, names = ["CTPRVN_CD", "CTP_KOR_NM", "CTP_ENG_NM"]}
"TL_SCCO_SIG" = { level = 2, names = ["SIG_CD", "SIG_KOR_NM", "SIG_ENG_NM"]}
"TL_SCCO_EMD" = { level = 3, names = ["EMD_CD", "EMD_KOR_NM", "EMD_ENG_NM"]}
"TL_SCCO_LI" = { level = 4, names = ["LI_CD", "LI_KOR_NM", "LI_ENG_NM"]}
"AL_D002_" = { level = 5, names = ["A3", "A4", "A5"]}
[search]
districts = [
#"11000",
#"41000",
#"46000",
"36000",
]
hierarchies = [
"TL_SCCO_CTPRVN",
]
district_par = [
"TL_SCCO_SIG",
"TL_SCCO_EMD",
"TL_SCCO_LI",
]
district_par_any = [
"AL_D002_"
]
[rest]
port = 8080
host = "localhost"
[grpc]
port = 8090
host = "localhost"
์ค๋ฅ ํด๋ฆฌ๊ณค์ ํธ์งํ ํ์๊ฐ ์๋ค.
UI ํ๋ก๊ทธ๋จ์ ๋ณ๋์ uikit ์์ด ์น html ๋ก ํ๋๊ฒ ๊ฐ์ฅ ํจ์จ์ ์ด๋ค.
shapefile ์ ๋ก๋ฉํด์ ํด๋ฆฌ๊ณค์ ์์ ํ๊ณ ์ ์ฅํ๋ค.
์น ๊ธฐ์ (HTML, CSS, JavaScript)์ ์ฌ์ฉํ์ฌ ๋ฐ์คํฌํฑ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค ์ ์๊ฒ ํด์ฃผ๋ ์คํ ์์ค ํ๋ ์์ํฌ๋ค.
๊ฐ๋จํ ๋งํด, ์น ๊ฐ๋ฐ์๊ฐ ์น ๊ฐ๋ฐ ๊ฒฝํ๊ณผ ๊ธฐ์ ์ ํ์ฉํ์ฌ
Windows, macOS, Linux์ ๊ฐ์ ๋ค์ํ ์ด์์ฒด์ ์์ ์คํ๋๋ ๋ค์ดํฐ๋ธ ๋ฐ์คํฌํฑ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ ์ ์๋๋ก ๋์์ฃผ๋ ๋๊ตฌ๋ค.
LLM ์ผ๋ก ๋ฐ์ด๋ธ ์ฝ๋ฉํด์ ๊ฐ๋ฐํ๋ค.
rest, grpc ๋ฅผ test ํ ๋ ์ฌ์ฉํ๋ ๋๊ตฌ์ด๋ค
๊ตญ๊ฐ๋ ๋ํ๋ฏผ๊ตญ์ผ๋ก ํ๋ค.
ํ์ ๊ตฌ์ญ์ ๊ณ์ธต์ ๊ฐ์ง๋ค ex) ๊ฒฝ๊ธฐ๋(์๋) > ์ฉ์ธ์ ์์ง๊ตฌ(์๊ตฐ๊ตฌ) > ์ ๋ด๋(์๋ฉด๋) > ์ ๋ด1๋ก 214
๊ตญ๊ฐ๋ฅผ level0, ๊ฒฝ๊ธฐ๋ level1, ... ๋ก ์ ์ ํ๋ค
ํ์ ๊ตฌ์ญ ํด๋ฆฌ๊ณค์ "์ฃผ์๊ธฐ๋ฐ์ฐ์
์ง์์๋น์ค(์ ๊ณตํ๋ ์ฃผ์)" ์์ level1 ๋จ์๋ก ๋ค์ด๋ก๋ ํ ์ ์๋ค
์ง๋ฒ์ "v-world ๋์งํธ ๊ตญํ ํธ์ > ์ฐ์์ง์ ๋ํ์ ๋ณด" ์์ level1 ๋จ์๋ก ๋ค์ด๋ก๋ ํ ์ ์๋ค
์ฃผ์๊ธฐ๋ฐ์ฐ์
์ง์์๋น์ค(์ ๊ณตํ๋ ์ฃผ์), ์ขํ๊ณ(ITRF2000), ๊ธฐ์คํ์์ฒด(GRS80), ํฌ์๋ฒ(UTM), ์ฅ๋ฐ๊ฒฝ(6,378,137m)
https://business.juso.go.kr/addrlink/adresInfoProvd/guidance/provdAdresInfo.do
์ ์์ง๋ ๋ค์ด๋ก๋ > ๋๋ก๋ช
์ฃผ์ ์ ์์ง๋
- ์ธ์ข ํน๋ณ์ 36000.zip
์๋ TL_SCCO_CTPRVN.shp
์๊ตฐ๊ตฌ TL_SCCO_SIG.shp
์๋ฉด๋ TL_SCCO_EMD.shp
๋ฆฌ TL_SCCO_LI.shp
์ฐ์์ง์ ๋ํ์ ๋ณด, EPSG:5186(GRS80)
https://www.vworld.kr/dtmk/dtmk_ntads_s002.do?svcCde=NA&dsId=23
- ์ธ์ข ํน๋ณ์
AL_D002_36_20250504.zip
์๋ณธ ๋ฐ์ดํ๋ ๋ค์๊ณผ ๊ฐ์ ์์ ํ๊ธฐ ๋ถํธํ ์ด์๋ค์ด ์๋ค.
- euc_kr ๋ฌธ์์ด ์ธ์ฝ๋ฉ
- ํต์ผ๋์ง ์์ ์ขํ๊ณ
- ํ์ผ ํฌ๊ธฐ
convert ๋ฅผ ์คํํ๋ฉด euc_kr, 5179 ์ขํ๊ณ๋ฅผ utf8, 4326 ํ์ผ ํฌ๊ธฐ๋ฅผ 12MB ๊ฐ ๋์ผ๋ฉด 60๋ง๊ฐ ๋ณด๊ฐ์ ํ๊ณ๋ก ํ์ผ๋ฅผ ๋ถ๋ฆฌํ๋ค.
go run convert.go -h
../data/source/46000/AL_D002_46_20250204.shp -> ../data/all_converted/46000/AL_D002_46_20250204_part1.shp
../data/all_converted/46000/AL_D002_46_20250204_part2.shp
../data/all_converted/46000/AL_D002_46_20250204_part3.shp
...
../data/all_converted/46000/AL_D002_46_20250204_part24.shp
../data/all_converted/46000/AL_D002_46_20250204_part25.shp
../data/source/46000/TL_SCCO_CTPRVN.shp -> ../data/all_converted/46000/TL_SCCO_CTPRVN.shp
s2geometry ๋ฅผ ์ฌ์ฉํ๊ธฐ์ ๋ก๋ฉํ ์ ์๋ polygon ์ ๋๋ฝ ์ํจ๋ค.
์๋ ๊ฐ์ ์ค๋ฅ๋ค์ด ๋์จ๋ค.
- ์ค๋ณต ํฌ์ธํธ, ERROR Edge 1990 is degenerate (duplicate vertex)
- ์๋ก ๊ฒน์น๋ ์ ๋ถ, ERROR Edge 455 crosses edge 457
- outer, inner curvature
- empty polygon
cargo test -- --nocapture test_search_polygon
cargo test -- --nocapture test_load_polygon
์ค๋ฅ ํด๋ฆฌ๊ณค์ ํธ์งํ ์ ์๋ ๋๊ตฌ๋ฅผ ๋ง๋ค์๋ค.
์๋ ์ฒ๋ผ ์คํํด์ ์
๋ฐ์ดํธ ํ ์ ์๋ค.
npm run start -- --shapefile-path ../data/converted/46000/TL_SCCO_EMD.shp
npm run start -- --shapefile-path ../data/all_converted/46000/AL_D002_46_20250204_6_part22.shp
'u' ์
๋ฐ์ดํธ, 'x' ์ ํ ์ทจ์, 's' ์ ์ฅ
์๋ ์ฒ๋ผ ๋ค์ํ ์๋ณธ ๋ฐ์ดํ ์ค๋ฅ๊ฐ ์๋ค

cargo run
Running `target/debug/tarantula`
load_district_polygons 36000 TL_SCCO_SIG elapsed: 129.599833ms
load_district_polygons 36000 TL_SCCO_EMD elapsed: 297.3095ms
load_district_polygons 36000 TL_SCCO_LI elapsed: 311.834583ms
/github/s2geometry/src/s2/s2loop.cc:131 ERROR Edge 0 crosses edge 2
/github/tarantula/data/all_converted/36000/AL_D002_36_20250504_part4.shp load_polygon 5-2 error_code: 1
/github/s2geometry/src/s2/s2loop.cc:131 ERROR Edge 18 crosses edge 21
/github/tarantula/data/all_converted/36000/AL_D002_36_20250504_part4.shp load_polygon 38-4 error_code: 1
/github/s2geometry/src/s2/s2loop.cc:131 ERROR Edge 5 crosses edge 21
/github/tarantula/data/all_converted/36000/AL_D002_36_20250504_part4.shp load_polygon 81-1 error_code: 1
/github/tarantula/data/all_converted/36000/AL_D002_36_20250504_part4.shp load_polygon 138-2 error_code: 3
/github/s2geometry/src/s2/s2loop.cc:131 ERROR Edge 0 crosses edge 3
/github/tarantula/data/all_converted/36000/AL_D002_36_20250504_part2.shp load_polygon 524 error_code: 1
/github/s2geometry/src/s2/s2loop.cc:131 ERROR Edge 767 crosses edge 770
/github/tarantula/data/all_converted/36000/AL_D002_36_20250504_part3.shp load_polygon 24-1 error_code: 1
/github/s2geometry/src/s2/s2loop.cc:131 ERROR Edge 4 crosses edge 8
/github/tarantula/data/all_converted/36000/AL_D002_36_20250504_part3.shp load_polygon 134-1 error_code: 1
load_district_par_any_polygons 36000 AL_D002_ elapsed: 9.017407292s
Search load elapsed: 9.467750875s
grpc server listening on [::1]:8090
rest api server listening on [::1]:8080
- rest api
- grpc api
- rest api apple m1 pro 32G
ab -n 1000000 -c 60 "http://localhost:8080/tarantula?lon=126.973826366&lat=37.532190912"
Server Software:
Server Hostname: localhost
Server Port: 8080
Document Path: /tarantula?lon=126.973826366&lat=37.532190912
Document Length: 208 bytes
Concurrency Level: 60
Time taken for tests: 59.478 seconds
Complete requests: 1000000
Failed requests: 0
Total transferred: 317000000 bytes
HTML transferred: 208000000 bytes
Requests per second: 16812.83 [#/sec] (mean)
Time per request: 3.569 [ms] (mean)
Time per request: 0.059 [ms] (mean, across all concurrent requests)
Transfer rate: 5204.75 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.8 0 116
Processing: 0 3 3.6 3 125
Waiting: 0 3 3.5 3 125
Total: 0 4 3.6 3 125
- grpc api
bash pref_grpc.sh pref_grpc_req.json
- ๋ฐ์ดํฐ ํ์ง๊ณผ ์ ํ์ฑ ํฌ๋งท
์ค๋ฅ๋ ๋ถ์์ ํ ๋ฐ์ดํฐ๋ ์ฌ์ฉ์์๊ฒ ํผ๋์ ์ค ์ ์๋ค
๋ฌธ์์ด ์ธ์ฝ๋ฉ์ utf8, ์ขํ๊ณ EPSG:4326 ๋ก ํน์ดํ ์ฌํญ์ด ์์ผ๋ฉด ํต์ผ ํ๋ค
2์ฐจ ๊ฐ๊ณต์ด ํ์์๊ฒ ์๋น์ค์ ์ฌ์ฉ ๊ฐ๋ฅํ์ง ํ ์คํธ๋ ํด์ผ ํ๋ค - API ๊ธฐ์ ์ ์์ ์ฑ
API์ ๊ฐ์ฉ์ฑ๊ณผ ์์ ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด ์๋ฒ ์ฉ๋, ํธ๋ํฝ ๊ด๋ฆฌ, ์ฅ์ ๋์ ๊ณํ์ ์ค๋นํด์ผ ํ๋ค
์ด ๋ถ๋ถ์ด ์ง์ ๊ฐ๋ฅ์ฑ์ ๋ฌด๋ฆฌ๊ฐ ์์ผ๋ฉด ๋ฐ์ดํ๋ฅผ ๊ณต๊ฐ ํ๋ค - ํ์ฉ๋์ ๊ณต๊ณต์ฑ
๊ณต๊ณต๋ฐ์ดํฐ์ ๋ชฉ์ ์ด ๊ตญ๋ฏผ์ ํธ์ต ์ฆ์ง๊ณผ ๊ณต๊ณต ๋ฌธ์ ํด๊ฒฐ์ ์์ผ๋ฏ๋ก, ์ฌํ์ ยท๊ฒฝ์ ์ ๊ฐ์น๋ฅผ ์ฐฝ์ถํ ์ ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฐ์ ๊ณต๊ฐํ๋ค
๋ค์ํ ๋ถ์ผ(์: ๊ตํต, ์๋ฃ, ํ๊ฒฝ)์์ ํ์ฉ ๊ฐ๋ฅํ๋๋ก ๋ฐ์ดํฐ ๋ฒ์๋ฅผ ๊ณ ๋ คํ๋ค - ์ฌ์ฉ์ ํผ๋๋ฐฑ๊ณผ ๊ฐ์ ๊ณต๊ฐ ํ ์ฌ์ฉ์ ์๊ฒฌ์ ์์งํด ๋ฐ์ดํฐ ํ์ง, API ๊ธฐ๋ฅ, ๋ฌธ์ ๋ฑ์ ์ง์์ ์ผ๋ก ๊ฐ์ ํ๋ค
- ์๋ฒ๋ rust ์ธ์ด
- geometry ์์ง์ s2geometry c++, autocxx
- ๋๋ฒ๊น ์น์ electron cross-platform web app
https://www.youtube.com/watch?v=zJDaM0ofj6Y
- ์๋ณธ ๋ฐ์ดํ ์์ถ ํ๊ธฐ
์ธ์ข ํน๋ณ์ ๋ฐ์ดํ data/source/36000.zip (ํ์ ๊ณ), AL_D002_36_20250504.zip (์ง๋ฒ) ๊ฐ ์๊ณ
data/source/36000 ํด๋์ ์์ถ์ ํผ๋ค
bash data.sh
- ํ๊ธ ์ธ์ฝ๋ฉ, ์ขํ๊ณ, ํ์ผ ํฌ๊ธฐ ๋ถํ ์ปจ๋ฒ์ ผ data/converted ํด๋์ ์ปจ๋ฒ์ ผ ํ๋ค
cd shape_convert
go run convert.go
- ์๋ฒ ์คํ
cargo run
- ๋๋ฒ๊น ์น ์คํ
cd shape_edit
npm install --save-dev electron
npm run start