https://kobore.net/coord_transform.html

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>座標変換(緯度経度 ←→ 投影座標)</title>
<!-- Proj4js (CDN) -->
<script src="https://cdn.jsdelivr.net/npm/proj4@2.11.0/dist/proj4.js"></script>
<style>
body { font-family: system-ui, -apple-system, "Segoe UI", sans-serif; margin: 16px; line-height: 1.4; }
.grid { display: grid; grid-template-columns: 1fr; gap: 12px; max-width: 900px; }
.card { border: 1px solid #ddd; border-radius: 10px; padding: 12px; }
.row { display: grid; grid-template-columns: 160px 1fr; gap: 10px; align-items: center; margin: 8px 0; }
input, select, textarea { width: 100%; padding: 8px; box-sizing: border-box; }
button { padding: 10px 14px; cursor: pointer; }
.btns { display: flex; gap: 8px; flex-wrap: wrap; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.hint { color: #555; font-size: 12px; }
</style>
</head>
<body>
<h1>座標変換(緯度経度 ←→ 投影座標)</h1>
<div class="grid">
<div class="card">
<div class="row">
<div>変換方向</div>
<select id="mode">
<option value="ll_to_proj">緯度経度 (EPSG:4326) → 投影座標</option>
<option value="proj_to_ll">投影座標 → 緯度経度 (EPSG:4326)</option>
</select>
</div>
<div class="row">
<div>投影座標系</div>
<select id="targetEpsg">
<!-- よく使うものを例示。必要に応じて追加 -->
<option value="EPSG:6677">EPSG:6677 (JGD2011 / 平面直角7系)</option>
<option value="EPSG:6676">EPSG:6676 (JGD2011 / 平面直角6系)</option>
<option value="EPSG:2451">EPSG:2451 (JGD2000 / 平面直角7系)</option>
<option value="EPSG:32654">EPSG:32654 (WGS84 / UTM Zone 54N)</option>
<option value="custom">カスタム(EPSGコードまたはPROJ文字列)</option>
</select>
</div>
<div class="row" id="customRow" style="display:none;">
<div>カスタム定義</div>
<textarea id="customDef" rows="2" class="mono"
placeholder="例: EPSG:3099 または +proj=tmerc +lat_0=... +lon_0=... +k=... +x_0=... +y_0=... +ellps=GRS80 +units=m +no_defs"></textarea>
</div>
<div class="row">
<div>入力</div>
<div class="hint">
・緯度経度→投影: lon,lat(経度,緯度)<br/>
・投影→緯度経度: x,y
</div>
</div>
<div class="row">
<div>値</div>
<input id="in1" class="mono" placeholder="例: 139.7670 または 405123.4" />
</div>
<div class="row">
<div>値</div>
<input id="in2" class="mono" placeholder="例: 35.6814 または 3958123.7" />
</div>
<div class="btns">
<button id="btnConvert">変換</button>
<button id="btnSwap">入力を入れ替え</button>
<button id="btnClear">クリア</button>
</div>
</div>
<div class="card">
<div class="row">
<div>出力</div>
<input id="out1" class="mono" readonly />
</div>
<div class="row">
<div>出力</div>
<input id="out2" class="mono" readonly />
</div>
<div class="row">
<div>出力形式</div>
<div class="hint" id="outHint">?</div>
</div>
<div class="row">
<div>ログ</div>
<textarea id="log" class="mono" rows="4" readonly></textarea>
</div>
</div>
</div>
<script>
const modeEl = document.getElementById('mode');
const targetEl = document.getElementById('targetEpsg');
const customRow = document.getElementById('customRow');
const customDef = document.getElementById('customDef');
const in1 = document.getElementById('in1');
const in2 = document.getElementById('in2');
const out1 = document.getElementById('out1');
const out2 = document.getElementById('out2');
const outHint = document.getElementById('outHint');
const logEl = document.getElementById('log');
function log(msg){
logEl.value = msg + "\n" + logEl.value;
}
function updateUI(){
const mode = modeEl.value;
if (mode === 'll_to_proj'){
in1.placeholder = "lon(経度)例: 139.7670";
in2.placeholder = "lat(緯度)例: 35.6814";
outHint.textContent = "出力:x,y(投影座標)";
} else {
in1.placeholder = "x(投影)例: 405123.4";
in2.placeholder = "y(投影)例: 3958123.7";
outHint.textContent = "出力:lon,lat(経度,緯度 / EPSG:4326)";
}
customRow.style.display = (targetEl.value === 'custom') ? '' : 'none';
}
function getTargetCrs(){
const v = targetEl.value;
if (v !== 'custom') return v.trim();
const c = customDef.value.trim();
if (!c) throw new Error("カスタム定義が空です(EPSG:xxxx か PROJ文字列を入力してください)");
return c;
}
function parseNum(s, name){
const v = Number(s);
if (!Number.isFinite(v)) throw new Error(`${name} が数値ではありません: ${s}`);
return v;
}
function convert(){
out1.value = "";
out2.value = "";
const mode = modeEl.value;
const target = getTargetCrs();
// proj4は多くのEPSGを内蔵していません。
// しかし「EPSG:6677」等は proj4 の defs が無い場合があるため、
// 必要なら defs を追加します(下の ensureDefs 参照)。
ensureDefs();
if (mode === 'll_to_proj'){
const lon = parseNum(in1.value, "lon(経度)");
const lat = parseNum(in2.value, "lat(緯度)");
const p = proj4("EPSG:4326", target, [lon, lat]);
out1.value = p[0];
out2.value = p[1];
log(`OK: EPSG:4326 (lon,lat) → ${target} (x,y)`);
} else {
const x = parseNum(in1.value, "x");
const y = parseNum(in2.value, "y");
const ll = proj4(target, "EPSG:4326", [x, y]);
out1.value = ll[0];
out2.value = ll[1];
log(`OK: ${target} (x,y) → EPSG:4326 (lon,lat)`);
}
}
// よく使う日本の平面直角(JGD2011)を、proj4に定義しておく
// ※厳密性を優先するなら、使用する系だけ残すのが安全です。
function ensureDefs(){
// すでに定義済みならスキップ
const already = proj4.defs("EPSG:6677");
if (already) return;
// JGD2011 / Japan Plane Rectangular CS VII (EPSG:6677)
// 中央子午線 137°10'、緯度起点 36°、縮尺 0.9999、GRS80
proj4.defs("EPSG:6677",
"+proj=tmerc +lat_0=36 +lon_0=137.1666666666667 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs");
// EPSG:6676 (VI系) 中央子午線 136°
proj4.defs("EPSG:6676",
"+proj=tmerc +lat_0=36 +lon_0=136 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs");
// EPSG:2451 (JGD2000 / VII系) ※概ね同形だが、基準系がJGD2000
proj4.defs("EPSG:2451",
"+proj=tmerc +lat_0=36 +lon_0=137.1666666666667 +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs");
// UTMはproj4が内蔵している場合が多いが、無い環境もあるので念のため
if (!proj4.defs("EPSG:32654")){
proj4.defs("EPSG:32654", "+proj=utm +zone=54 +datum=WGS84 +units=m +no_defs");
}
}
document.getElementById('btnConvert').addEventListener('click', () => {
try { convert(); } catch (e) { log("ERROR: " + e.message); alert(e.message); }
});
document.getElementById('btnSwap').addEventListener('click', () => {
const a = in1.value; in1.value = in2.value; in2.value = a;
});
document.getElementById('btnClear').addEventListener('click', () => {
in1.value = ""; in2.value = ""; out1.value = ""; out2.value = ""; logEl.value = "";
});
modeEl.addEventListener('change', updateUI);
targetEl.addEventListener('change', updateUI);
updateUI();
</script>
</body>
</html>






