Остання активність 1757160510

OGG ревизій цього gist 1757160510. До ревизії

1 file changed, 1 insertion

OSGeo4W\353\245\274 \354\235\264\354\232\251\355\225\234 \354\227\254\353\237\254\352\260\234\354\235\230 SHP\355\214\214\354\235\274 \354\240\201\354\236\254.md"

@@ -203,6 +203,7 @@ SELECT ST_SRID(geom) FROM tablename LIMIT 1;
203 203
204 204 * SHP 파일 좌표계가 일치하지 않으면 위치가 엉뚱하게 표시될 수 있음
205 205 * 필드 구조가 다른 SHP 파일은 적재 실패
206 + * shp파일 하나의 용량이 과도하게 많으면(4Gb 이상) 메모리 부족으로 인한 오류가 날 수 있음
206 207
207 208 ---
208 209

OGG ревизій цього gist 1757159857. До ревизії

1 file changed, 1 deletion

OSGeo4W\353\245\274 \354\235\264\354\232\251\355\225\234 \354\227\254\353\237\254\352\260\234\354\235\230 SHP\355\214\214\354\235\274 \354\240\201\354\236\254.md"

@@ -1,7 +1,6 @@
1 1 # SHP 파일 여러 개를 하나의 PostGIS 테이블에 적재하기
2 2
3 3 > OSGeo4W와 ogr2ogr를 이용하여 Windows 환경에서 SHP 파일 여러 개를 하나의 PostGIS 테이블로 적재하는 방법 정리
4 - > 모든 테이블구조와 좌표계가 일정해야함
5 4
6 5 ---
7 6

OGG ревизій цього gist 1757159828. До ревизії

1 file changed, 230 insertions

OSGeo4W\353\245\274 \354\235\264\354\232\251\355\225\234 \354\227\254\353\237\254\352\260\234\354\235\230 SHP\355\214\214\354\235\274 \354\240\201\354\236\254.md" (файл створено)

@@ -0,0 +1,230 @@
1 + # SHP 파일 여러 개를 하나의 PostGIS 테이블에 적재하기
2 +
3 + > OSGeo4W와 ogr2ogr를 이용하여 Windows 환경에서 SHP 파일 여러 개를 하나의 PostGIS 테이블로 적재하는 방법 정리
4 + > 모든 테이블구조와 좌표계가 일정해야함
5 +
6 + ---
7 +
8 + ## 1. 환경 준비
9 +
10 + ### 필수 설치
11 +
12 + * **OSGeo4W** : [https://trac.osgeo.org/osgeo4w/](https://trac.osgeo.org/osgeo4w/)
13 + 설치 후 `OSGeo4W Shell` 사용
14 + ---
15 +
16 + ## 2. 데이터 준비
17 +
18 + * 적재할 SHP 파일을 한 폴더에 모음
19 + 예:
20 +
21 + ```
22 + C:/upload/shp/
23 + ```
24 +
25 + ---
26 +
27 + ## 3. python code 작성
28 +
29 + ```python
30 + import os
31 + import subprocess
32 + from glob import glob
33 + from datetime import datetime
34 +
35 + # ----------------------------
36 + # 설정
37 + # ----------------------------
38 + PGHOST = "host"
39 + PGPORT = 5432
40 + PGDB = "dbname"
41 + PGUSER = "username"
42 + PGPASS = "password"
43 + PGSCHEMA = "public"
44 + PGTABLE = "tablename"
45 +
46 + SRS = "EPSG:5186"
47 +
48 + SHP_FOLDER = r"C:\upload\shp\lsmd_cont_ldreg"
49 + os.makedirs(SHP_FOLDER, exist_ok=True)
50 +
51 + LOGFILE = os.path.join(
52 + SHP_FOLDER,
53 + f"import_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
54 + )
55 +
56 + ERROR_FILES = []
57 +
58 + # ----------------------------
59 + # 환경 변수에 PGPASSWORD 설정
60 + # ----------------------------
61 + env = os.environ.copy()
62 + env["PGPASSWORD"] = PGPASS
63 +
64 + # ----------------------------
65 + # 로그 기록 함수
66 + # ----------------------------
67 + def logger(event, file_or_message, message=None):
68 + timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
69 + if message:
70 + log_line = f"{timestamp} | {event} | {file_or_message} | {message}"
71 + else:
72 + log_line = f"{timestamp} | {event} | {file_or_message}"
73 + with open(LOGFILE, "a", encoding="utf-8") as f:
74 + f.write(log_line + "\n")
75 + print(log_line)
76 +
77 + # ----------------------------
78 + # ogr2ogr 실행 함수
79 + # ----------------------------
80 + def run_ogr2ogr(file_path, append=False):
81 + cmd = [
82 + "ogr2ogr",
83 + "-f", "PostgreSQL",
84 + f"PG:host={PGHOST} port={PGPORT} dbname={PGDB} user={PGUSER} password={PGPASS}",
85 + file_path,
86 + "-nln", f"{PGSCHEMA}.{PGTABLE}",
87 + "-nlt", "PROMOTE_TO_MULTI",
88 + "-progress",
89 + "--config", "PG_USE_COPY", "YES",
90 + "-a_srs", SRS
91 + ]
92 + if not append:
93 + # create 옵션
94 + cmd.extend([
95 + "-lco", "GEOMETRY_NAME=geom",
96 + "-lco", "FID=gid"
97 + ])
98 + else:
99 + # append 모드
100 + cmd.append("-append")
101 +
102 + try:
103 + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, env=env)
104 + for line in process.stdout:
105 + line = line.strip()
106 + if line:
107 + print(line) # 진행률 출력
108 + process.wait()
109 + if process.returncode != 0:
110 + raise subprocess.CalledProcessError(process.returncode, cmd)
111 + except subprocess.CalledProcessError as e:
112 + logger("ERROR", file_path, f"ogr2ogr failed with code {e.returncode}")
113 + ERROR_FILES.append(file_path)
114 + return False
115 + return True
116 +
117 + # ----------------------------
118 + # SHP 파일 반복 처리
119 + # ----------------------------
120 + shp_files = glob(os.path.join(SHP_FOLDER, "*.shp"))
121 + first = True
122 + total_start = datetime.now()
123 +
124 + for i, shp in enumerate(shp_files, start=1):
125 + logger("START", shp)
126 + start_time = datetime.now()
127 + success = run_ogr2ogr(shp, append=not first)
128 + end_time = datetime.now()
129 + elapsed = (end_time - start_time).total_seconds()
130 + logger("END", shp, f"{elapsed:.2f}s (File {i}/{len(shp_files)})")
131 +
132 + if first and not success:
133 + logger("ERROR", "First file failed, aborting remaining imports.")
134 + break
135 +
136 + first = False
137 +
138 + # ----------------------------
139 + # GIST 인덱스 생성 및 ANALYZE
140 + # ----------------------------
141 + def create_index_analyze():
142 + # GIST 인덱스
143 + logger("START", "GIST index creation")
144 + try:
145 + subprocess.run(
146 + ["psql", "-h", PGHOST, "-p", str(PGPORT), "-U", PGUSER, "-d", PGDB,
147 + "-c", f"CREATE INDEX IF NOT EXISTS {PGTABLE}_geom_gist ON {PGSCHEMA}.{PGTABLE} USING GIST (geom);"],
148 + check=True, capture_output=True, text=True, env=env
149 + )
150 + except subprocess.CalledProcessError as e:
151 + logger("ERROR", "GIST index creation", e.stderr)
152 + ERROR_FILES.append("GIST index creation")
153 + logger("END", "GIST index creation")
154 +
155 + # ANALYZE
156 + logger("START", "ANALYZE table")
157 + try:
158 + subprocess.run(
159 + ["psql", "-h", PGHOST, "-p", str(PGPORT), "-U", PGUSER, "-d", PGDB,
160 + "-c", f"ANALYZE {PGSCHEMA}.{PGTABLE};"],
161 + check=True, capture_output=True, text=True, env=env
162 + )
163 + except subprocess.CalledProcessError as e:
164 + logger("ERROR", "ANALYZE table", e.stderr)
165 + ERROR_FILES.append("ANALYZE table")
166 + logger("END", "ANALYZE table")
167 +
168 + create_index_analyze()
169 +
170 + total_end = datetime.now()
171 + total_elapsed = (total_end - total_start).total_seconds()
172 +
173 + logger("INFO", "All SHP files processed!")
174 + logger("INFO", "Total elapsed time", f"{total_elapsed:.2f}s")
175 +
176 + # ----------------------------
177 + # 실패 파일 기록
178 + # ----------------------------
179 + if ERROR_FILES:
180 + logger("INFO", "Failed tasks:")
181 + for f in ERROR_FILES:
182 + logger("INFO", f)
183 + logger("INFO", "Some tasks failed. See log for details.")
184 + else:
185 + logger("INFO", "All tasks completed successfully.")
186 + ```
187 + ---
188 +
189 + ## 4. 데이터 확인
190 +
191 + PostgreSQL 접속 후 데이터 확인:
192 +
193 + ```sql
194 + -- 총 데이터 수 확인
195 + SELECT COUNT(*) FROM tablename;
196 +
197 + -- 좌표계 확인
198 + SELECT ST_SRID(geom) FROM tablename LIMIT 1;
199 + ```
200 +
201 + ---
202 +
203 + ## 5. 주의 사항
204 +
205 + * SHP 파일 좌표계가 일치하지 않으면 위치가 엉뚱하게 표시될 수 있음
206 + * 필드 구조가 다른 SHP 파일은 적재 실패
207 +
208 + ---
209 +
210 + ### 6. 예시 화면 (OSGeo4W Shell)
211 +
212 + ```
213 + 2025-09-06_20-44-10 | START | C:\upload\shp\lsmd_cont_ldreg\LSMD_CONT_LDREG_11_202508.shp
214 + WARNING: database "rsonegreet" has no actual collation version, but a version was recorded
215 + 0...10...20...30...40...50...60...70...80...90...100 - done in 00:00:13.
216 + 2025-09-06_20-44-23 | END | C:\upload\shp\lsmd_cont_ldreg\LSMD_CONT_LDREG_11_202508.shp | 13.20s (File 1/2)
217 + 2025-09-06_20-44-23 | START | C:\upload\shp\lsmd_cont_ldreg\LSMD_CONT_LDREG_41_202508.shp
218 + WARNING: database "rsonegreet" has no actual collation version, but a version was recorded
219 + 0...10...20...30...40...50...60...70...80...90...100 - done in 00:01:22.
220 + 2025-09-06_20-45-46 | END | C:\upload\shp\lsmd_cont_ldreg\LSMD_CONT_LDREG_41_202508.shp | 82.49s (File 2/2)
221 + 2025-09-06_20-45-46 | START | GIST index creation
222 + 2025-09-06_20-45-57 | END | GIST index creation
223 + 2025-09-06_20-45-57 | START | ANALYZE table
224 + 2025-09-06_20-45-57 | END | ANALYZE table
225 + 2025-09-06_20-45-57 | INFO | All SHP files processed!
226 + 2025-09-06_20-45-57 | INFO | Total elapsed time | 107.02s
227 + 2025-09-06_20-45-57 | INFO | All tasks completed successfully.
228 + ```
229 +
230 + ---
Новіше Пізніше