316 lines
15 KiB
HTML
316 lines
15 KiB
HTML
|
<!DOCTYPE html>
|
|||
|
<html lang="zh-cn">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<title>地图在线转换工具</title>
|
|||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|||
|
<style>
|
|||
|
body {
|
|||
|
background: linear-gradient(120deg, #f0f4f8 0%, #e0e7ef 100%);
|
|||
|
min-height: 100vh;
|
|||
|
}
|
|||
|
.container {
|
|||
|
max-width: 500px;
|
|||
|
margin-top: 60px;
|
|||
|
background: #fff;
|
|||
|
border-radius: 16px;
|
|||
|
box-shadow: 0 4px 24px rgba(0,0,0,0.08);
|
|||
|
padding: 32px 28px 24px 28px;
|
|||
|
}
|
|||
|
h2 {
|
|||
|
font-weight: 700;
|
|||
|
color: #2d3a4b;
|
|||
|
margin-bottom: 28px;
|
|||
|
}
|
|||
|
.form-label {
|
|||
|
font-weight: 500;
|
|||
|
}
|
|||
|
.btn-primary, .btn-success {
|
|||
|
width: 100%;
|
|||
|
margin-top: 12px;
|
|||
|
}
|
|||
|
#result {
|
|||
|
margin-top: 18px;
|
|||
|
background: #f8f9fa;
|
|||
|
border-radius: 8px;
|
|||
|
padding: 14px;
|
|||
|
min-height: 40px;
|
|||
|
font-size: 15px;
|
|||
|
color: #333;
|
|||
|
}
|
|||
|
.download-btn {
|
|||
|
margin-top: 16px;
|
|||
|
width: 100%;
|
|||
|
}
|
|||
|
.iray-params {
|
|||
|
margin-top: 18px;
|
|||
|
padding: 16px;
|
|||
|
background: #f8f9fa;
|
|||
|
border-radius: 8px;
|
|||
|
border: 1px solid #dee2e6;
|
|||
|
}
|
|||
|
.param-row {
|
|||
|
display: flex;
|
|||
|
gap: 10px;
|
|||
|
margin-bottom: 12px;
|
|||
|
}
|
|||
|
.param-row .form-control {
|
|||
|
flex: 1;
|
|||
|
}
|
|||
|
.param-section-title {
|
|||
|
font-weight: 600;
|
|||
|
color: #495057;
|
|||
|
margin-bottom: 10px;
|
|||
|
font-size: 14px;
|
|||
|
}
|
|||
|
</style>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<div class="container">
|
|||
|
<h2 class="text-center">地图在线转换</h2>
|
|||
|
|
|||
|
<!-- 转换类型选择 -->
|
|||
|
<div class="mb-3">
|
|||
|
<label class="form-label">选择转换类型</label>
|
|||
|
<div class="btn-group w-100" role="group">
|
|||
|
<input type="radio" class="btn-check" name="convertType" id="sceneConvert" value="scene" checked>
|
|||
|
<label class="btn btn-outline-primary" for="sceneConvert">SMAP转Scene</label>
|
|||
|
|
|||
|
<input type="radio" class="btn-check" name="convertType" id="irayConvert" value="iray">
|
|||
|
<label class="btn btn-outline-primary" for="irayConvert">SMAP转IRAY地图包</label>
|
|||
|
|
|||
|
<input type="radio" class="btn-check" name="convertType" id="sceneToSmapConvert" value="scene-to-smap">
|
|||
|
<label class="btn btn-outline-primary" for="sceneToSmapConvert">Scene更新SMAP</label>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<form id="uploadForm">
|
|||
|
<label for="fileInput" class="form-label" id="fileInputLabel">选择 .smap 文件 <span style="color:#888;font-size:13px;">(必选)</span></label>
|
|||
|
<input type="file" class="form-control" id="fileInput" name="file" accept=".smap" required>
|
|||
|
<label for="sceneInput" class="form-label" style="margin-top:18px;" id="sceneInputLabel">选择 .scene 文件 <span style="color:#888;font-size:13px;">(可选,仅Scene转换时使用)</span></label>
|
|||
|
<input type="file" class="form-control" id="sceneInput" name="scene" accept=".scene">
|
|||
|
|
|||
|
<!-- IRAY转换参数区域 -->
|
|||
|
<div id="irayParams" class="iray-params" style="display:none;">
|
|||
|
<div class="param-section-title">📐 地图参数设置 (IRAY转换必填)</div>
|
|||
|
|
|||
|
<div class="param-row">
|
|||
|
<div>
|
|||
|
<label for="mapWidth" class="form-label">地图宽度 (mm)</label>
|
|||
|
<input type="number" class="form-control" id="mapWidth" placeholder="94744" value="94744">
|
|||
|
</div>
|
|||
|
<div>
|
|||
|
<label for="mapHeight" class="form-label">地图高度 (mm)</label>
|
|||
|
<input type="number" class="form-control" id="mapHeight" placeholder="79960" value="79960">
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="param-row">
|
|||
|
<div>
|
|||
|
<label for="xAttrMin" class="form-label">X最小值 (mm)</label>
|
|||
|
<input type="number" class="form-control" id="xAttrMin" placeholder="-46762" value="-46762">
|
|||
|
</div>
|
|||
|
<div>
|
|||
|
<label for="yAttrMin" class="form-label">Y最小值 (mm)</label>
|
|||
|
<input type="number" class="form-control" id="yAttrMin" placeholder="-63362" value="-63362">
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div style="font-size:12px;color:#6c757d;margin-top:8px;">
|
|||
|
💡 提示:地图参数决定了生成的华睿地图包的坐标系范围和尺寸
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<button type="submit" class="btn btn-primary">上传并转换</button>
|
|||
|
</form>
|
|||
|
<div id="result"></div>
|
|||
|
<a id="downloadBtn" class="btn btn-outline-primary download-btn" style="display:none;" download>下载转换后的文件</a>
|
|||
|
</div>
|
|||
|
<script>
|
|||
|
// 转换类型切换事件
|
|||
|
document.querySelectorAll('input[name="convertType"]').forEach(radio => {
|
|||
|
radio.addEventListener('change', function() {
|
|||
|
const fileInputLabel = document.getElementById('fileInputLabel');
|
|||
|
const sceneInputLabel = document.getElementById('sceneInputLabel');
|
|||
|
const fileInput = document.getElementById('fileInput');
|
|||
|
const sceneInput = document.getElementById('sceneInput');
|
|||
|
const irayParams = document.getElementById('irayParams');
|
|||
|
|
|||
|
if (this.value === 'iray') {
|
|||
|
fileInputLabel.innerHTML = '选择 .smap 文件 <span style="color:#888;font-size:13px;">(必选)</span>';
|
|||
|
sceneInputLabel.innerHTML = '选择 .scene 文件 <span style="color:#888;font-size:13px;">(必选,IRAY转换需要Scene文件提取点位映射)</span>';
|
|||
|
fileInput.accept = '.smap';
|
|||
|
sceneInput.accept = '.scene';
|
|||
|
irayParams.style.display = 'block';
|
|||
|
} else if (this.value === 'scene-to-smap') {
|
|||
|
fileInputLabel.innerHTML = '选择 .scene 文件 <span style="color:#888;font-size:13px;">(必选,包含更新后的点位坐标)</span>';
|
|||
|
sceneInputLabel.innerHTML = '选择 .smap 文件 <span style="color:#888;font-size:13px;">(必选,需要更新的原SMAP文件)</span>';
|
|||
|
fileInput.accept = '.scene';
|
|||
|
sceneInput.accept = '.smap';
|
|||
|
irayParams.style.display = 'none';
|
|||
|
} else {
|
|||
|
fileInputLabel.innerHTML = '选择 .smap 文件 <span style="color:#888;font-size:13px;">(必选)</span>';
|
|||
|
sceneInputLabel.innerHTML = '选择 .scene 文件 <span style="color:#888;font-size:13px;">(可选,仅Scene转换时使用)</span>';
|
|||
|
fileInput.accept = '.smap';
|
|||
|
sceneInput.accept = '.scene';
|
|||
|
irayParams.style.display = 'none';
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
|
|||
|
document.getElementById('uploadForm').onsubmit = async function(e) {
|
|||
|
e.preventDefault();
|
|||
|
const fileInput = document.getElementById('fileInput');
|
|||
|
const sceneInput = document.getElementById('sceneInput');
|
|||
|
const convertType = document.querySelector('input[name="convertType"]:checked').value;
|
|||
|
|
|||
|
if (!fileInput.files.length) {
|
|||
|
document.getElementById('result').textContent = '请先选择 .smap 文件';
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// IRAY转换时的特殊验证
|
|||
|
if (convertType === 'iray') {
|
|||
|
if (!sceneInput.files.length) {
|
|||
|
document.getElementById('result').textContent = 'IRAY转换需要同时选择 .scene 文件用于提取点位映射';
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 验证必要参数
|
|||
|
const mapWidth = document.getElementById('mapWidth').value;
|
|||
|
const mapHeight = document.getElementById('mapHeight').value;
|
|||
|
const xAttrMin = document.getElementById('xAttrMin').value;
|
|||
|
const yAttrMin = document.getElementById('yAttrMin').value;
|
|||
|
|
|||
|
if (!mapWidth || !mapHeight || !xAttrMin || !yAttrMin) {
|
|||
|
document.getElementById('result').textContent = 'IRAY转换需要填写所有地图参数';
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Scene到SMAP转换时的特殊验证
|
|||
|
if (convertType === 'scene-to-smap') {
|
|||
|
if (!sceneInput.files.length) {
|
|||
|
document.getElementById('result').textContent = 'Scene到SMAP转换需要同时选择 .smap 文件';
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const formData = new FormData();
|
|||
|
|
|||
|
// 根据转换类型处理文件上传
|
|||
|
if (convertType === 'scene-to-smap') {
|
|||
|
// Scene到SMAP转换:第一个文件是scene,第二个是smap
|
|||
|
formData.append('scene_file', fileInput.files[0]);
|
|||
|
formData.append('smap_file', sceneInput.files[0]);
|
|||
|
} else {
|
|||
|
// 其他转换:第一个文件是smap,第二个是scene
|
|||
|
formData.append('smap_file', fileInput.files[0]);
|
|||
|
if (sceneInput.files.length) {
|
|||
|
formData.append('scene_file', sceneInput.files[0]);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 添加IRAY转换参数
|
|||
|
if (convertType === 'iray') {
|
|||
|
formData.append('map_width', document.getElementById('mapWidth').value);
|
|||
|
formData.append('map_height', document.getElementById('mapHeight').value);
|
|||
|
formData.append('x_attr_min', document.getElementById('xAttrMin').value);
|
|||
|
formData.append('y_attr_min', document.getElementById('yAttrMin').value);
|
|||
|
}
|
|||
|
|
|||
|
document.getElementById('result').textContent = '正在上传并转换...';
|
|||
|
document.getElementById('downloadBtn').style.display = 'none';
|
|||
|
|
|||
|
try {
|
|||
|
let apiUrl;
|
|||
|
if (convertType === 'iray') {
|
|||
|
apiUrl = 'http://127.0.0.1:8000/api/vwed-map-converter/smap-to-iray';
|
|||
|
} else if (convertType === 'scene-to-smap') {
|
|||
|
apiUrl = 'http://127.0.0.1:8000/api/vwed-map-converter/scene-to-smap';
|
|||
|
} else {
|
|||
|
apiUrl = 'http://127.0.0.1:8000/api/vwed-map-converter/smap-to-scene';
|
|||
|
}
|
|||
|
|
|||
|
let isIray = convertType === 'iray';
|
|||
|
if (isIray) {
|
|||
|
// 强制二进制返回模式
|
|||
|
formData.append('response_mode', 'binary');
|
|||
|
}
|
|||
|
|
|||
|
const resp = await fetch(apiUrl, { method: 'POST', body: formData });
|
|||
|
|
|||
|
const downloadBtn = document.getElementById('downloadBtn');
|
|||
|
if (isIray) {
|
|||
|
if (!resp.ok) {
|
|||
|
const text = await resp.text();
|
|||
|
document.getElementById('result').textContent = '转换失败:' + text;
|
|||
|
return;
|
|||
|
}
|
|||
|
// 读取zip二进制
|
|||
|
const blob = await resp.blob();
|
|||
|
if (blob.size === 0) {
|
|||
|
document.getElementById('result').textContent = '转换失败:返回空文件';
|
|||
|
return;
|
|||
|
}
|
|||
|
const disposition = resp.headers.get('Content-Disposition') || '';
|
|||
|
let filenameMatch = disposition.match(/filename=([^;]+)/i);
|
|||
|
let filename = filenameMatch ? filenameMatch[1].replace(/"/g,'') : 'map_package.zip';
|
|||
|
// 创建下载链接
|
|||
|
const url = URL.createObjectURL(blob);
|
|||
|
downloadBtn.href = url;
|
|||
|
downloadBtn.download = filename;
|
|||
|
downloadBtn.textContent = '下载华睿地图包 (' + filename + ')';
|
|||
|
downloadBtn.onclick = null;
|
|||
|
downloadBtn.style.display = 'block';
|
|||
|
document.getElementById('result').textContent = '转换成功,已生成华睿地图包。';
|
|||
|
return; // 结束
|
|||
|
}
|
|||
|
|
|||
|
// 非IRAY还是按照JSON处理
|
|||
|
const data = await resp.json();
|
|||
|
const dataStr = JSON.stringify(data, null, 2);
|
|||
|
const lines = dataStr.split('\n').slice(0, 100).join('\n');
|
|||
|
document.getElementById('result').textContent = lines + (dataStr.split('\n').length > 100 ? '\n...(内容已截断)' : '');
|
|||
|
|
|||
|
if (convertType === 'scene-to-smap' && data.success && data.data) {
|
|||
|
// Scene到SMAP转换成功,生成下载文件
|
|||
|
const smapContent = JSON.stringify(data.data, null, 2);
|
|||
|
const blob = new Blob([smapContent], { type: 'application/json' });
|
|||
|
const url = URL.createObjectURL(blob);
|
|||
|
downloadBtn.href = url;
|
|||
|
|
|||
|
// 使用服务器返回的输出文件名,如果没有则使用默认名称
|
|||
|
let filename = 'updated.smap';
|
|||
|
if (data.output_file) {
|
|||
|
// 从完整路径中提取文件名
|
|||
|
filename = data.output_file.split(/[/\\]/).pop();
|
|||
|
}
|
|||
|
downloadBtn.download = filename;
|
|||
|
downloadBtn.textContent = '下载更新后的SMAP文件';
|
|||
|
downloadBtn.onclick = null;
|
|||
|
downloadBtn.style.display = 'block';
|
|||
|
} else if (convertType === 'scene' && data.data) {
|
|||
|
// Scene转换成功,生成下载文件
|
|||
|
const sceneContent = JSON.stringify(data.data, null, 2);
|
|||
|
const blob = new Blob([sceneContent], { type: 'application/json' });
|
|||
|
const url = URL.createObjectURL(blob);
|
|||
|
downloadBtn.href = url;
|
|||
|
|
|||
|
// 使用服务器返回的文件名,如果没有则使用默认名称
|
|||
|
const filename = data.output_filename || 'converted.scene';
|
|||
|
downloadBtn.download = filename;
|
|||
|
downloadBtn.textContent = '下载Scene文件';
|
|||
|
downloadBtn.onclick = null;
|
|||
|
downloadBtn.style.display = 'block';
|
|||
|
} else {
|
|||
|
downloadBtn.style.display = 'none';
|
|||
|
}
|
|||
|
|
|||
|
} catch (err) {
|
|||
|
document.getElementById('result').textContent = '请求失败:' + err;
|
|||
|
}
|
|||
|
}
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|