
PostGISで座標系を変換するには? — ST_Transform と ST_SetSRID の違いと、SRIDを直しても点が動かない理由
座標が動きませんでした。
SRIDが間違っていたので直したのに、ST_AsText の中身は1桁も変わらない。地図に重ねると、やっぱりずれたまま。よくある詰まりどころです。
答えは ST_Transform です。変換したいなら、これ一択。ただし——このSQLは、エラーを出さないまま数メートルずれた値を返すことがあります。 日本のデータでは、そこが本題になります。
(本記事の記述は2026年9月時点の PostGIS / PROJ の公式ドキュメントに基づきます)
「宣言」と「変換」は別の関数
まず、名前の似た関数の役割分担です。
| 関数 | 何をするか | 座標値は動くか |
|---|---|---|
ST_SetSRID(geom, srid) |
ジオメトリに付いているSRIDの識別子だけを書き換える | 動かない |
UpdateGeometrySRID(table, col, srid) |
テーブルの列に対して同じことをする | 動かない |
ST_SRID(geom) |
今ついているSRIDを読む | — |
ST_Transform(geom, srid) |
座標値そのものを別の座標参照系へ変換する | 動く |
PostGIS の公式ドキュメントも、この2つの取り違えを名指しで注意しています。ST_Transform は座標を変換し、ST_SetSRID は識別子を変えるだけである、と。
-- 「この点はJGD2011(緯度経度)です」と宣言し直すだけ。数値は不変
SELECT ST_AsText(ST_SetSRID(ST_MakePoint(139.7671, 35.6812), 6668));
-- JGD2011(緯度経度)→ 平面直角座標系 第IX系 へ実際に変換する
SELECT ST_AsText(ST_Transform(ST_SetSRID(ST_MakePoint(139.7671, 35.6812), 6668), 6677));
使い分けの原則はひとつです。
- SRIDが「間違って付いていた」 →
ST_SetSRID/UpdateGeometrySRIDで正しい宣言に直す - SRIDは合っているが「別の座標系で欲しい」 →
ST_Transform
なお、ST_Transform に渡すジオメトリはSRIDが定義されている必要があります(SRIDが0のままだと変換できません)。この「宣言と変換は別物」という構図はコマンドライン側でも同じで、ogr2ogr の -a_srs と -t_srs の関係がちょうど対応します。詳しくはogr2ogrの回にまとめました。
SRIDはどこから来るのか — spatial_ref_sys
PostGIS の SRID は、データベース内の spatial_ref_sys テーブルで解決されます。ST_Transform の変換先SRIDは、このテーブルに存在していなければなりません。
日本でよく使うコードを並べておきます。
| SRID | 座標参照系 |
|---|---|
| 4301 | 旧日本測地系(Tokyo)緯度経度 |
| 4612 | JGD2000 緯度経度 |
| 6668 | JGD2011 緯度経度 |
| 6669〜6687 | JGD2011 平面直角座標系 第I系〜第XIX系 |
| 6697 | JGD2011 緯度経度+標高(3次元) |
JGD2024 は 2026年4月の EPSG Dataset v12.055 で登録されました。ただし、そのコードが手元のデータベースに入っているかは PostGIS に同梱された EPSG データベースの版次第です。入っていなければ ST_Transform は「そんなSRIDは無い」という趣旨のエラーになります。EPSG登録の経緯はJGD2024のEPSG登録の回に整理しました。
環境の確認は次の1行で足ります。PROJ のバージョンまで表示されます。
SELECT PostGIS_Full_Version();
ST_Transform は PROJ サポート付きでビルドされていないと使えません。まずここを見るのが早道です。
列にSRIDを縛っておくと事故が減る
SRIDの取り違えは、入り口で止めるのが一番安く済みます。 PostGIS の型修飾子を使うと、列の時点で座標系を固定できます。
CREATE TABLE survey_points (
id bigserial PRIMARY KEY,
name text,
geom geometry(Point, 6668) -- JGD2011の緯度経度しか入らない
);
こうしておくと、違うSRIDのジオメトリを INSERT しようとした時点で弾かれます。「途中まで旧測地系のデータが混ざっていたことに、集計の段階で気づく」という事故は、これでかなり減らせます。
型を geometry のままにしてSRIDを運用ルールだけで守るのは、人数が増えたときに必ず破れます。
エラーが出ないのに、数メートルずれるとき
ここが本題です。ST_Transform は、変換元と変換先から適切な変換経路(パイプライン)を自動で選びます。 公式ドキュメントにもそう書かれていて、ほとんどの場合それで正解です。
問題は、選べる経路の精度が場所によって大きく違うことです。
測地系の乗り換えを高い精度でやるには、地域ごとの歪みを格子で持ったグリッドファイルが要ります。グリッドが手元に無いとき、PROJ は EPSG に登録された数パラメータのヘルマート変換(地球全体を平行移動・回転させるだけの近似)へ落ちます。このとき例外は出ません。値はふつうに返ってきます。

上の図は、旧日本測地系から世界測地系への補正量を全国で見たものです。ずれ方は場所によって違い、一様な平行移動では表せません。だからグリッドが要る、という話になります。この落ち込み方と、日本向けに自前エンジンを持つ判断についてはPROJで足りること・足りないことの回に数値ごとまとめてあります。
対策は2つです。
1. 変換経路を明示する。 PostGIS 3.4 以降には ST_TransformPipeline があります。EPSG の座標操作コードや PROJ のパイプライン文字列で、使う経路を名指しできます。
-- 座標操作をEPSGコードで名指しする形
SELECT ST_AsText(
ST_TransformPipeline(geom, 'urn:ogc:def:coordinateOperation:EPSG::<座標操作コード>', <出力SRID>)
) FROM survey_points;
公式ドキュメントは「たいていの場合は ST_Transform が正しい経路を選ぶので、そちらを使うべき」という立場です。明示するのは、経路が意図どおりか確認したいときと、精度が要件になっているときと考えるのがよいと思います。
なお、+proj=pipeline 形式の文字列を直接書く場合、自動の軸順正規化がかからないため、projinfo の出力をそのまま貼ると axisswap の行が余計に効いてしまいます。公式の例でも「axisswap のステップは取り除くこと」と注記されています。
2. 変換経路の存在を前提にしない検算をする。 変換前後の点を数点だけ手計算や別ツールと突き合わせる、という地味な確認です。旧測地系のデータが混ざっているかどうかの見分け方は測地系が書いていない座標データの見分け方にまとめました。
軸順 — PostGIS は常に「X=経度、Y=緯度」
もうひとつ、外部データを出し入れするときに引っかかる点です。
PostGIS のジオメトリは、X に経度、Y に緯度を入れる伝統的なGIS順で保持されます。 一方 EPSG:4326 の権威的な定義では軸順は「緯度・経度」です。この食い違いは PostGIS の中で完結しているぶんには表に出ませんが、GeoJSON・WKT・外部APIとやり取りする境目で符号のように効いてきます。
ST_MakePoint の引数順も (x, y) すなわち (経度, 緯度) です。緯度を先に書くと、日本の点が南極海のあたりへ飛びます。形式ごとの軸順の違いは緯度と経度、どちらを先に書く?にまとめてあります。
高さ(Z)は、水平のSRIDだけでは面倒を見てくれない
PostGIS のジオメトリは Z 値を持てます。ST_Transform はその Z を持ち回りますが、水平のSRIDを指定しただけで、高さの基準(楕円体高なのか標高なのか)が切り替わるわけではありません。
日本では EPSG:6697 のように水平と高さを組み合わせたコードもありますが、鉛直方向の変換にはジオイドモデルが必要で、水平の変換とは別立ての手当てになります。「Zの数字が残っているから高さも変換された」と読まないこと——実務ではここが一番静かに事故ります。
変換をよく使うなら、関数インデックス
同じ変換を繰り返すクエリが遅いときは、変換結果に対する関数インデックスが効きます。公式ドキュメントも部分インデックスとの組み合わせを勧めています。
CREATE INDEX idx_survey_points_6677
ON survey_points
USING gist (ST_Transform(geom, 6677))
WHERE geom IS NOT NULL;
NULL のジオメトリを除いておくと、インデックスが小さくなり効率も上がります。
詰まったときの確認順
| # | 見るところ | コマンド・確認内容 |
|---|---|---|
| 1 | PROJ が入っているか | SELECT PostGIS_Full_Version(); |
| 2 | ジオメトリにSRIDが付いているか | SELECT ST_SRID(geom) FROM ...;(0なら未定義) |
| 3 | 変換先SRIDが存在するか | SELECT * FROM spatial_ref_sys WHERE srid = 6677; |
| 4 | 直したいのは宣言か変換か | 宣言=ST_SetSRID/変換=ST_Transform |
| 5 | 値は返るのに位置がおかしい | 変換経路がグリッド無しに落ちていないか。ST_TransformPipeline で明示 |
| 6 | 南極海や大西洋に飛んでいる | 軸順(ST_MakePoint は経度が先) |
| 7 | 高さが合わない | 鉛直変換は別立て。水平SRIDでは変わらない |
まとめ
ST_SetSRIDは宣言、ST_Transformは変換。 数値を動かしたいなら後者- SRIDは
spatial_ref_sysで解決される。JGD2024が使えるかは同梱EPSGデータベースの版次第 - 列を
geometry(Point, 6668)のように縛ると、混入を入り口で止められる ST_Transformは変換経路を自動で選ぶ。グリッドが無い経路でも例外は出ない。日本の旧測地系変換ではここに注意- 経路を名指ししたいときは
ST_TransformPipeline(PostGIS 3.4以降) - 軸順は X=経度・Y=緯度。 高さの基準は水平SRIDでは切り替わらない
GeoConverter Pro(GCVP)は、こうしたデータベース側の変換結果を現場で突き合わせるときの「答え合わせ」に使えます。TOKYO〜JGD2024・平面直角19系の変換を、グリッドを使った日本仕様の計算で確認できます。

出典
- PostGIS 公式ドキュメント「ST_Transform」 https://postgis.net/docs/ST_Transform.html
- PostGIS 公式ドキュメント「ST_TransformPipeline」 https://postgis.net/docs/ST_TransformPipeline.html
- 国土地理院「TKY2JGD(世界測地系移行のための座標変換ツール)」 https://vldb.gsi.go.jp/sokuchi/tky2jgd/
次に確認する
- ogr2ogrで
-a_srsと-t_srsを取り違えたときの症状を確認する — 同じ「宣言と変換」をコマンドライン側で見た回です - QGISでレイヤCRSとプロジェクトCRSを取り違えたときの症状を確認する — GUI側で同じ事故がどう見えるか
- PROJが日本の変換でどこまで届くのかを数値で確認する — グリッド無しのフォールバックで何メートル出るかの回です
- 平面直角座標系1〜19系のうちどれを使うかを確認する — SRID 6669〜6687 の選び方です
- 古い地図が数百mずれる理由を4択で確認する(GeoPrism JP) — 本記事の「グリッドが要る理由」を図と問題で押さえる回です
- 書籍でまとめて学ぶ: 『日本の測地系Q&A』第13章「座標データの落とし穴」(Kindle Unlimited対応)
入門から通しで読むなら: 『日本の測地系がわかる本』 — 旧日本測地系から JGD2024 まで、座標がズレる理由と実務での扱いを1冊にまとめました(Kindle Unlimited 対応)
開発者より: アプリ・Kindle本・オープンソースの一覧は GitHub: amru195704 にまとめています。
お願い
本記事の情報は参考目的で掲載しており、正確性・完全性を保証するものではありません。誤記・不正確な情報がございましたら、コメント欄よりご指摘いただければ、確認のうえ修正いたします。
アプリを入手(App Store):GeoConverter Pro(座標変換) | GeoPrism JP(測地系の可視化・学習)
コメントを残す