- Google Maps API에서 Leaflet + OpenStreetMap으로 전환 - 구마모토 지역 특화 타일 서버 Docker 컨테이너 추가 - PostgreSQL + PostGIS + Mapnik 스택으로 지도 타일 생성 - API 키 불필요한 오픈소스 지도 솔루션 구현 - 개발/프로덕션 환경 Docker Compose 설정 완료 - 빠른 로딩과 오프라인 지원 가능한 지도 서비스 주요 변경사항: - src/components/Map.tsx: Leaflet 기반으로 완전 재작성 - docker/map-server/: 구마모토 지역 타일 서버 구축 - docker-compose.yml, docker-compose.dev.yml: 지도 서버 연동 - package.json: leaflet, react-leaflet 의존성 추가
115 lines
3.3 KiB
Python
115 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
구마모토 지역 타일 생성 스크립트
|
|
"""
|
|
|
|
import os
|
|
import math
|
|
import mapnik
|
|
import psycopg2
|
|
from PIL import Image
|
|
|
|
# 구마모토 지역 경계
|
|
KUMAMOTO_BOUNDS = {
|
|
'north': 33.2,
|
|
'south': 32.4,
|
|
'east': 131.2,
|
|
'west': 130.2
|
|
}
|
|
|
|
# 타일 크기
|
|
TILE_SIZE = 256
|
|
|
|
def deg2num(lat_deg, lon_deg, zoom):
|
|
"""위경도를 타일 번호로 변환"""
|
|
lat_rad = math.radians(lat_deg)
|
|
n = 2.0 ** zoom
|
|
xtile = int((lon_deg + 180.0) / 360.0 * n)
|
|
ytile = int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n)
|
|
return (xtile, ytile)
|
|
|
|
def num2deg(xtile, ytile, zoom):
|
|
"""타일 번호를 위경도로 변환"""
|
|
n = 2.0 ** zoom
|
|
lon_deg = xtile / n * 360.0 - 180.0
|
|
lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
|
|
lat_deg = math.degrees(lat_rad)
|
|
return (lat_deg, lon_deg)
|
|
|
|
def create_mapnik_map():
|
|
"""Mapnik 지도 객체 생성"""
|
|
m = mapnik.Map(TILE_SIZE, TILE_SIZE)
|
|
|
|
# 한국어/일본어 폰트 설정
|
|
mapnik.FontEngine.register_fonts('/usr/share/fonts/truetype/noto/')
|
|
|
|
# 스타일 로드
|
|
mapnik.load_map(m, '/opt/map-server/mapnik-style.xml')
|
|
|
|
return m
|
|
|
|
def generate_tile(m, z, x, y):
|
|
"""개별 타일 생성"""
|
|
# 타일 경계 계산
|
|
lat_north, lon_west = num2deg(x, y, z)
|
|
lat_south, lon_east = num2deg(x + 1, y + 1, z)
|
|
|
|
# 지도 범위 설정
|
|
bbox = mapnik.Box2d(lon_west, lat_south, lon_east, lat_north)
|
|
m.zoom_to_box(bbox)
|
|
|
|
# 타일 렌더링
|
|
im = mapnik.Image(TILE_SIZE, TILE_SIZE)
|
|
mapnik.render(m, im)
|
|
|
|
return im.tostring('png')
|
|
|
|
def generate_kumamoto_tiles():
|
|
"""구마모토 지역 타일 생성"""
|
|
print("구마모토 지역 타일 생성 시작...")
|
|
|
|
# Mapnik 지도 생성
|
|
m = create_mapnik_map()
|
|
|
|
# 줌 레벨별 타일 생성 (8-16레벨)
|
|
for zoom in range(8, 17):
|
|
print(f"줌 레벨 {zoom} 타일 생성 중...")
|
|
|
|
# 구마모토 지역의 타일 범위 계산
|
|
x_min, y_max = deg2num(KUMAMOTO_BOUNDS['north'], KUMAMOTO_BOUNDS['west'], zoom)
|
|
x_max, y_min = deg2num(KUMAMOTO_BOUNDS['south'], KUMAMOTO_BOUNDS['east'], zoom)
|
|
|
|
tile_count = 0
|
|
for x in range(x_min, x_max + 1):
|
|
for y in range(y_min, y_max + 1):
|
|
# 타일 디렉토리 생성
|
|
tile_dir = f"/var/www/html/tiles/{zoom}/{x}"
|
|
os.makedirs(tile_dir, exist_ok=True)
|
|
|
|
# 타일 파일 경로
|
|
tile_path = f"{tile_dir}/{y}.png"
|
|
|
|
# 이미 존재하는 타일은 건너뛰기
|
|
if os.path.exists(tile_path):
|
|
continue
|
|
|
|
try:
|
|
# 타일 생성
|
|
tile_data = generate_tile(m, zoom, x, y)
|
|
|
|
# 타일 저장
|
|
with open(tile_path, 'wb') as f:
|
|
f.write(tile_data)
|
|
|
|
tile_count += 1
|
|
|
|
except Exception as e:
|
|
print(f"타일 생성 실패 ({zoom}/{x}/{y}): {e}")
|
|
|
|
print(f"줌 레벨 {zoom}: {tile_count}개 타일 생성 완료")
|
|
|
|
print("구마모토 지역 타일 생성 완료!")
|
|
|
|
if __name__ == "__main__":
|
|
generate_kumamoto_tiles()
|