花火KMLジェネレーター

*{box-sizing:border-box;margin:0;padding:0}
.wrap{padding:1.5rem 0;font-family:var(–font-sans)}
h2{font-size:18px;font-weight:500;color:var(–color-text-primary);margin-bottom:4px}
.sub{font-size:14px;color:var(–color-text-secondary);margin-bottom:1.25rem}
.card{background:var(–color-background-primary);border:0.5px solid var(–color-border-tertiary);border-radius:var(–border-radius-lg);padding:1.25rem;margin-bottom:1rem}
.card-title{font-size:12px;font-weight:500;color:var(–color-text-tertiary);text-transform:uppercase;letter-spacing:.06em;margin-bottom:.75rem}
.step-hint{font-size:12px;color:var(–color-text-tertiary);padding:8px 10px;background:var(–color-background-secondary);border-radius:var(–border-radius-md);margin-bottom:.75rem;line-height:1.6}
.coord-row{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:.75rem}
label{display:block;font-size:13px;color:var(–color-text-secondary);margin-bottom:5px}
input[type=number],input[type=text]{width:100%}
.open-map-btn{width:100%;padding:9px;font-size:14px;background:var(–color-background-secondary);border:0.5px solid var(–color-border-secondary);border-radius:var(–border-radius-md);color:var(–color-text-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px;margin-bottom:.75rem;text-decoration:none}
.open-map-btn:hover{background:var(–color-background-tertiary)}
.coord-display{font-size:13px;color:var(–color-text-secondary);margin-top:.5rem}
.coord-display span{color:var(–color-text-primary);font-weight:500}
.hanabi-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:7px;margin-bottom:1rem}
.hanabi-btn{padding:10px 4px;border:0.5px solid var(–color-border-tertiary);border-radius:var(–border-radius-md);background:var(–color-background-secondary);cursor:pointer;text-align:center;transition:all .15s}
.hanabi-btn:hover{border-color:var(–color-border-secondary);background:var(–color-background-primary)}
.hanabi-btn.active{border:2px solid #e24b4a;background:var(–color-background-primary)}
.hanabi-btn .go{font-size:14px;font-weight:500;color:var(–color-text-primary);display:block}
.hanabi-btn .lbl{font-size:10px;color:var(–color-text-tertiary);display:block;margin-top:2px}
.hanabi-btn.active .go{color:#e24b4a}
.hanabi-info{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:1rem}
.info-card{background:var(–color-background-secondary);border-radius:var(–border-radius-md);padding:10px 12px}
.info-label{font-size:11px;color:var(–color-text-tertiary);margin-bottom:3px}
.info-value{font-size:15px;font-weight:500;color:var(–color-text-primary)}
.info-sub{font-size:10px;color:var(–color-text-tertiary);margin-top:1px}
.src-link{font-size:11px;color:var(–color-text-tertiary);margin-bottom:.75rem}
.src-link a{color:var(–color-text-info);text-decoration:none}
.manual-toggle{font-size:12px;color:var(–color-text-tertiary);cursor:pointer;display:flex;align-items:center;gap:4px;margin-bottom:.75rem;user-select:none}
.manual-toggle:hover{color:var(–color-text-secondary)}
.manual-fields{display:none}
.manual-fields.show{display:block}
.params-manual{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:.75rem}
.btn-gen{width:100%;padding:10px;font-size:15px;font-weight:500;background:var(–color-background-primary);border:0.5px solid var(–color-border-secondary);border-radius:var(–border-radius-md);color:var(–color-text-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px}
.btn-gen:hover{background:var(–color-background-secondary)}
.btn-gen:active{transform:scale(.98)}
.result{display:none;background:var(–color-background-secondary);border:0.5px solid var(–color-border-tertiary);border-radius:var(–border-radius-lg);padding:1.25rem;margin-top:1rem}
.result.show{display:block}
.res-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:1rem}
.stat{background:var(–color-background-primary);border-radius:var(–border-radius-md);padding:10px 12px}
.stat-label{font-size:11px;color:var(–color-text-tertiary);margin-bottom:2px}
.stat-value{font-size:14px;font-weight:500;color:var(–color-text-primary)}
.btn-dl{width:100%;padding:10px;font-size:14px;background:var(–color-background-primary);border:0.5px solid var(–color-border-secondary);border-radius:var(–border-radius-md);color:var(–color-text-info);cursor:pointer;display:flex;align-items:center;justify-content:center;gap:6px}
.btn-dl:hover{background:var(–color-background-info)}

花火KMLジェネレーター

号数を選んで座標を指定するだけで、Google Earth Pro用KMLを生成します。

ステップ1 — 打ち上げ場所を指定
Google Mapsで右クリック → 座標をコピー → 下に貼り付け


Google Mapsを開く

現在の座標: 134.6776307, 34.7788322
ステップ2 — 花火の号数を選択
 
打ち上げ高度
330m
地上からの高さ
開花半径
160m
中心からの距離
開花直径
320m
半径×2
玉の直径
29.5cm
球の大きさ

手動で数値を調整する

生成完了
号数
10号
打ち上げ高度
330m
開花直径
320m
弧の本数
7本

const HANABI = [
{go:’3号’, h:120, r:30, shell:’8.5cm’, lbl:’小型’},
{go:’4号’, h:160, r:65, shell:’11.5cm’, lbl:’小型’},
{go:’5号’, h:190, r:85, shell:’14.2cm’, lbl:’中型’},
{go:’6号’, h:220, r:110, shell:’16.7cm’, lbl:’中型’},
{go:’7号’, h:250, r:120, shell:’20.5cm’, lbl:’中型’},
{go:’8号’, h:280, r:140, shell:’23.5cm’, lbl:’大型’},
{go:’10号’, h:330, r:160, shell:’29.5cm’, lbl:’大型’},
{go:’20号’, h:500, r:240, shell:’58.5cm’, lbl:’超大型’},
{go:’30号’, h:600, r:275, shell:’88.5cm’, lbl:’超大型’},
{go:’カスタム’, h:null, r:null, shell:’-‘, lbl:’手動入力’},
];

let selectedIdx=6, kmlContent=”, dlFilename=”;

function buildGrid(){
const grid=document.getElementById(‘hanabi-grid’);
grid.innerHTML=”;
HANABI.forEach((h,i)=>{
const btn=document.createElement(‘button’);
btn.className=’hanabi-btn’+(i===selectedIdx?’ active’:”);
btn.innerHTML=`${h.go}${h.lbl}`;
btn.onclick=()=>selectHanabi(i);
grid.appendChild(btn);
});
}

function selectHanabi(i){
selectedIdx=i;
document.querySelectorAll(‘.hanabi-btn’).forEach((b,j)=>b.classList.toggle(‘active’,j===i));
const h=HANABI[i];
if(h.h!==null){
document.getElementById(‘launch_h’).value=h.h;
document.getElementById(‘radius’).value=h.r;
document.getElementById(‘manual-fields’).classList.remove(‘show’);
document.getElementById(‘manual-icon’).style.transform=”;
} else {
document.getElementById(‘manual-fields’).classList.add(‘show’);
document.getElementById(‘manual-icon’).style.transform=’rotate(180deg)’;
}
updateInfo();
}

function updateInfo(){
const h=parseFloat(document.getElementById(‘launch_h’).value)||0;
const r=parseFloat(document.getElementById(‘radius’).value)||0;
document.getElementById(‘info-h’).textContent=h+’m’;
document.getElementById(‘info-r’).textContent=r+’m’;
document.getElementById(‘info-d’).textContent=(r*2)+’m’;
document.getElementById(‘info-shell’).textContent=HANABI[selectedIdx].shell||’-‘;
}

function onManualChange(){updateInfo();}

function toggleManual(){
const f=document.getElementById(‘manual-fields’);
const ic=document.getElementById(‘manual-icon’);
const show=f.classList.toggle(‘show’);
ic.style.transform=show?’rotate(180deg)’:”;
}

function updateMapLink(){
const lon=parseFloat(document.getElementById(‘lon’).value)||134.6776307;
const lat=parseFloat(document.getElementById(‘lat’).value)||34.7788322;
document.getElementById(‘map-link’).href=`https://www.google.com/maps/@${lat},${lon},15z`;
document.getElementById(‘coord-text’).textContent=`${lon.toFixed(7)}, ${lat.toFixed(7)}`;
}

function onCoordChange(){updateMapLink();}

function parseAndFill(){
const raw=document.getElementById(‘paste-input’).value.trim();
if(!raw)return;
const nums=raw.match(/-?d+.?d*/g);
if(!nums||nums.length<2){alert('座標を認識できませんでした');return;}
let a=parseFloat(nums[0]),b=parseFloat(nums[1]),lon,lat;
if(Math.abs(a)90){lat=a;lon=b;}
else if(Math.abs(b)90){lon=a;lat=b;}
else if(Math.abs(a)d*Math.PI/180;
const toDeg=r=>r*180/Math.PI;
function mToDlat(m){return toDeg(m/R)}
function mToDlon(m,lat){return toDeg(m/(R*Math.cos(toRad(lat))))}
function generateArc(cLon,cLat,cAlt,radius,azDeg,nPts=74){
const az=toRad(azDeg),pts=[];
for(let i=0;i`

${String(az).padStart(3,’0′)}_deg
#dome_line

absolute
${generateArc(lon,lat,ca,radius,az)}

`).join(”);
return `

花火シミュレーション(${goName})

ff0000ff3
0

${arcs}

launch_line
#dome_line

absolute
${lon},${lat},${groundAlt} ${lon},${lat},${ca}

`;
}

function generate(){
const lon=parseFloat(document.getElementById(‘lon’).value);
const lat=parseFloat(document.getElementById(‘lat’).value);
const launchH=parseFloat(document.getElementById(‘launch_h’).value);
const radius=parseFloat(document.getElementById(‘radius’).value);
const groundAlt=parseFloat(document.getElementById(‘ground_alt’).value)||0;
const fname=document.getElementById(‘filename’).value.trim()||’firework’;
const goName=HANABI[selectedIdx].go;
if(isNaN(lon)||isNaN(lat)){alert(‘座標を入力してください’);return;}
kmlContent=buildKml(lon,lat,launchH,radius,groundAlt,goName);
dlFilename=fname+’_’+goName+’.kml’;
document.getElementById(‘s-go’).textContent=goName;
document.getElementById(‘s-h’).textContent=launchH+’m’;
document.getElementById(‘s-d’).textContent=(radius*2)+’m’;
document.getElementById(‘result’).classList.add(‘show’);
download();
}
function download(){
const blob=new Blob([kmlContent],{type:’application/vnd.google-earth.kml+xml’});
const a=document.createElement(‘a’);
a.href=URL.createObjectURL(blob);
a.download=dlFilename;
a.click();
}

buildGrid();
updateInfo();