Добавил альтернативное имя у спутника
This commit is contained in:
@@ -43,8 +43,13 @@ function showSatelliteModal(satelliteId) {
|
||||
'<div class="col-md-6"><div class="card h-100">' +
|
||||
'<div class="card-header bg-light"><strong><i class="bi bi-info-circle"></i> Основная информация</strong></div>' +
|
||||
'<div class="card-body"><table class="table table-sm table-borderless mb-0"><tbody>' +
|
||||
'<tr><td class="text-muted" style="width: 40%;">Название:</td><td><strong>' + data.name + '</strong></td></tr>' +
|
||||
'<tr><td class="text-muted">NORAD ID:</td><td>' + data.norad + '</td></tr>' +
|
||||
'<tr><td class="text-muted" style="width: 40%;">Название:</td><td><strong>' + data.name + '</strong></td></tr>';
|
||||
|
||||
if (data.alternative_name && data.alternative_name !== '-') {
|
||||
html += '<tr><td class="text-muted">Альтернативное название:</td><td><strong>' + data.alternative_name + '</strong></td></tr>';
|
||||
}
|
||||
|
||||
html += '<tr><td class="text-muted">NORAD ID:</td><td>' + data.norad + '</td></tr>' +
|
||||
'<tr><td class="text-muted">Подспутниковая точка:</td><td><strong>' + data.undersat_point + '</strong></td></tr>' +
|
||||
'<tr><td class="text-muted">Диапазоны:</td><td>' + data.bands + '</td></tr>' +
|
||||
'</tbody></table></div></div></div>' +
|
||||
|
||||
@@ -180,6 +180,9 @@
|
||||
<button id="export-xlsx" class="btn btn-success" disabled>
|
||||
<i class="bi bi-file-earmark-excel"></i> Сохранить в Excel
|
||||
</button>
|
||||
<button id="export-json" class="btn btn-info ms-2" disabled>
|
||||
<i class="bi bi-filetype-json"></i> Сохранить в JSON
|
||||
</button>
|
||||
<button id="clear-table" class="btn btn-danger ms-2">
|
||||
<i class="bi bi-trash"></i> Очистить таблицу
|
||||
</button>
|
||||
@@ -292,18 +295,21 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
{
|
||||
title: "Действия",
|
||||
field: "actions",
|
||||
minWidth: 100,
|
||||
minWidth: 120,
|
||||
widthGrow: 1,
|
||||
hozAlign: "center",
|
||||
formatter: function(cell, formatterParams, onRendered) {
|
||||
const data = cell.getRow().getData();
|
||||
const btnClass = data.has_outliers ? 'btn-warning' : 'btn-info';
|
||||
return `<button class="btn btn-sm ${btnClass} btn-view-details" title="Просмотр точек"><i class="bi bi-eye"></i></button>`;
|
||||
return `<button class="btn btn-sm ${btnClass} btn-view-details" title="Просмотр точек"><i class="bi bi-eye"></i></button>
|
||||
<button class="btn btn-sm btn-danger btn-delete-row ms-1" title="Удалить строку"><i class="bi bi-trash"></i></button>`;
|
||||
},
|
||||
cellClick: function(e, cell) {
|
||||
const data = cell.getRow().getData();
|
||||
if (e.target.closest('.btn-view-details')) {
|
||||
showGroupDetails(data._groupIndex);
|
||||
} else if (e.target.closest('.btn-delete-row')) {
|
||||
deleteGroupRow(data._groupIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,6 +327,31 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
function updateGroupCount() {
|
||||
document.getElementById('group-count').textContent = allGroupsData.length;
|
||||
document.getElementById('export-xlsx').disabled = allGroupsData.length === 0;
|
||||
document.getElementById('export-json').disabled = allGroupsData.length === 0;
|
||||
}
|
||||
|
||||
// Delete group row
|
||||
function deleteGroupRow(groupIndex) {
|
||||
if (!confirm('Удалить эту группу из таблицы?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Удаляем группу из массива
|
||||
allGroupsData.splice(groupIndex, 1);
|
||||
|
||||
// Пересчитываем индексы для оставшихся групп
|
||||
allGroupsData.forEach((group, idx) => {
|
||||
group._groupIndex = idx;
|
||||
});
|
||||
|
||||
// Обновляем таблицу
|
||||
table.setData(allGroupsData.map(g => ({
|
||||
...g,
|
||||
status: g.has_outliers ? 'outliers' : 'ok'
|
||||
})));
|
||||
|
||||
updateGroupCount();
|
||||
updateAllPointsTable();
|
||||
}
|
||||
|
||||
// Show loading overlay
|
||||
@@ -837,6 +868,252 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
// Generate UUID v4
|
||||
function generateUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
const r = Math.random() * 16 | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
// Export to JSON
|
||||
document.getElementById('export-json').addEventListener('click', function() {
|
||||
if (allGroupsData.length === 0) {
|
||||
alert('Нет данных для экспорта');
|
||||
return;
|
||||
}
|
||||
|
||||
// Creator ID - можно изменить на нужное значение
|
||||
const CREATOR_ID = '6fd12c90-7f17-43d9-a03e-ee14e880f757';
|
||||
|
||||
// Начальный объект path (фиксированный)
|
||||
const pathObject = {
|
||||
"tacticObjectType": "path",
|
||||
"captionPosition": "right",
|
||||
"points": [
|
||||
{
|
||||
"id": "b92b9cbb-dd27-49aa-bcb6-e89a147bc02c",
|
||||
"latitude": 57,
|
||||
"longitude": -13,
|
||||
"altitude": 0,
|
||||
"customActions": [],
|
||||
"tags": { "creator": CREATOR_ID },
|
||||
"tacticObjectType": "point"
|
||||
},
|
||||
{
|
||||
"id": "8e3666d4-4990-4cb9-9594-63ad06333489",
|
||||
"latitude": 57,
|
||||
"longitude": 64,
|
||||
"altitude": 0,
|
||||
"customActions": [],
|
||||
"tags": { "creator": CREATOR_ID },
|
||||
"tacticObjectType": "point"
|
||||
},
|
||||
{
|
||||
"id": "5f137485-d2fc-443d-8507-c936f02f3569",
|
||||
"latitude": 11,
|
||||
"longitude": 64,
|
||||
"altitude": 0,
|
||||
"customActions": [],
|
||||
"tags": { "creator": CREATOR_ID },
|
||||
"tacticObjectType": "point"
|
||||
},
|
||||
{
|
||||
"id": "0fb90df7-8eb0-49fa-9d00-336389171bf5",
|
||||
"latitude": 11,
|
||||
"longitude": -13,
|
||||
"altitude": 0,
|
||||
"customActions": [],
|
||||
"tags": { "creator": CREATOR_ID },
|
||||
"tacticObjectType": "point"
|
||||
},
|
||||
{
|
||||
"id": "3ef12637-585e-40a4-b0ee-8f1786c89ce6",
|
||||
"latitude": 57,
|
||||
"longitude": -13,
|
||||
"altitude": 0,
|
||||
"customActions": [],
|
||||
"tags": { "creator": CREATOR_ID },
|
||||
"tacticObjectType": "point"
|
||||
}
|
||||
],
|
||||
"isCycle": false,
|
||||
"id": "2f604051-4984-4c2f-8c4c-c0cb64008f5f",
|
||||
"draggable": false,
|
||||
"selectable": false,
|
||||
"editable": false,
|
||||
"caption": "Ограничение для работы с поверхностями",
|
||||
"line": {
|
||||
"color": "rgb(148,0,211)",
|
||||
"thickness": 1,
|
||||
"dash": "solid",
|
||||
"border": null
|
||||
},
|
||||
"customActions": [],
|
||||
"tags": { "creator": CREATOR_ID }
|
||||
};
|
||||
|
||||
// Результирующий массив
|
||||
const result = [pathObject];
|
||||
|
||||
// Палитра цветов для групп (RGB формат)
|
||||
const jsonGroupColors = [
|
||||
"rgb(0,128,0)", // зелёный
|
||||
"rgb(0,0,255)", // синий
|
||||
"rgb(255,0,0)", // красный
|
||||
"rgb(255,165,0)", // оранжевый
|
||||
"rgb(128,0,128)", // фиолетовый
|
||||
"rgb(0,128,128)", // бирюзовый
|
||||
"rgb(255,20,147)", // розовый
|
||||
"rgb(139,69,19)", // коричневый
|
||||
"rgb(0,100,0)", // тёмно-зелёный
|
||||
"rgb(70,130,180)" // стальной синий
|
||||
];
|
||||
|
||||
// Обрабатываем каждую группу
|
||||
allGroupsData.forEach((group, groupIndex) => {
|
||||
// Цвет для текущей группы
|
||||
const groupColor = jsonGroupColors[groupIndex % jsonGroupColors.length];
|
||||
|
||||
// Формируем имя для усреднённой точки с пометкой "(усредн)"
|
||||
const avgName = group.source_name;
|
||||
const avgTime = group.avg_time || '-';
|
||||
const avgCaption = `${avgName} (усредн) - ${avgTime}`;
|
||||
|
||||
// Получаем координаты усреднённой точки
|
||||
const avgCoord = group.avg_coord_tuple;
|
||||
const avgLat = avgCoord[1];
|
||||
const avgLon = avgCoord[0];
|
||||
|
||||
// ID для усреднённой точки (source)
|
||||
const avgSourceId = generateUUID();
|
||||
|
||||
// Создаём source для усреднённой точки (triangle)
|
||||
const avgSource = {
|
||||
"tacticObjectType": "source",
|
||||
"captionPosition": "right",
|
||||
"id": avgSourceId,
|
||||
"icon": {
|
||||
"type": "triangle",
|
||||
"color": groupColor
|
||||
},
|
||||
"caption": avgCaption,
|
||||
"name": avgCaption,
|
||||
"customActions": [],
|
||||
"trackBehavior": {},
|
||||
"bearingStyle": {
|
||||
"color": groupColor,
|
||||
"thickness": 2,
|
||||
"dash": "solid",
|
||||
"border": null
|
||||
},
|
||||
"bearingBehavior": {},
|
||||
"tags": { "creator": CREATOR_ID }
|
||||
};
|
||||
result.push(avgSource);
|
||||
|
||||
// Создаём position для усреднённой точки
|
||||
const avgPosition = {
|
||||
"tacticObjectType": "position",
|
||||
"id": generateUUID(),
|
||||
"parentId": avgSourceId,
|
||||
"timeStamp": Date.now() / 1000,
|
||||
"latitude": avgLat,
|
||||
"altitude": 0,
|
||||
"longitude": avgLon,
|
||||
"caption": "",
|
||||
"tooltip": "",
|
||||
"customActions": [],
|
||||
"tags": {
|
||||
"layers": [],
|
||||
"creator": CREATOR_ID
|
||||
}
|
||||
};
|
||||
result.push(avgPosition);
|
||||
|
||||
// Обрабатываем все точки группы (не выбросы)
|
||||
group.points.forEach(point => {
|
||||
if (point.is_outlier) return; // Пропускаем выбросы
|
||||
|
||||
const pointCoord = point.coord_tuple;
|
||||
const pointLat = pointCoord[1];
|
||||
const pointLon = pointCoord[0];
|
||||
const pointName = point.name || '-';
|
||||
const pointTime = point.timestamp || '-';
|
||||
const pointCaption = `${pointName} - ${pointTime}`;
|
||||
|
||||
// ID для source точки
|
||||
const pointSourceId = generateUUID();
|
||||
|
||||
// Создаём source для точки (circle) с тем же цветом группы
|
||||
const pointSource = {
|
||||
"tacticObjectType": "source",
|
||||
"captionPosition": "right",
|
||||
"id": pointSourceId,
|
||||
"icon": {
|
||||
"type": "circle",
|
||||
"color": groupColor
|
||||
},
|
||||
"caption": pointCaption,
|
||||
"name": pointCaption,
|
||||
"customActions": [],
|
||||
"trackBehavior": {},
|
||||
"bearingStyle": {
|
||||
"color": groupColor,
|
||||
"thickness": 2,
|
||||
"dash": "solid",
|
||||
"border": null
|
||||
},
|
||||
"bearingBehavior": {},
|
||||
"tags": { "creator": CREATOR_ID }
|
||||
};
|
||||
result.push(pointSource);
|
||||
|
||||
// Создаём position для точки
|
||||
const pointPosition = {
|
||||
"tacticObjectType": "position",
|
||||
"id": generateUUID(),
|
||||
"parentId": pointSourceId,
|
||||
"timeStamp": point.timestamp_unix || (Date.now() / 1000),
|
||||
"latitude": pointLat,
|
||||
"altitude": 0,
|
||||
"longitude": pointLon,
|
||||
"caption": "",
|
||||
"tooltip": "",
|
||||
"customActions": [],
|
||||
"tags": {
|
||||
"layers": [],
|
||||
"creator": CREATOR_ID
|
||||
}
|
||||
};
|
||||
result.push(pointPosition);
|
||||
});
|
||||
});
|
||||
|
||||
// Конвертируем в JSON строку
|
||||
const jsonString = JSON.stringify(result, null, 2);
|
||||
|
||||
// Добавляем BOM для UTF-8
|
||||
const BOM = '\uFEFF';
|
||||
const blob = new Blob([BOM + jsonString], { type: 'application/json;charset=utf-8' });
|
||||
|
||||
// Генерируем имя файла
|
||||
const now = new Date();
|
||||
const dateStr = now.toISOString().slice(0, 10);
|
||||
const filename = `averaging_${dateStr}.json`;
|
||||
|
||||
// Скачиваем файл
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
});
|
||||
|
||||
// Initialize
|
||||
updateGroupCount();
|
||||
});
|
||||
|
||||
@@ -86,6 +86,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.alternative_name.id_for_label }}" class="form-label">
|
||||
{{ form.alternative_name.label }}
|
||||
</label>
|
||||
{{ form.alternative_name }}
|
||||
{% if form.alternative_name.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{{ form.alternative_name.errors.0 }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if form.alternative_name.help_text %}
|
||||
<div class="form-text">{{ form.alternative_name.help_text }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.norad.id_for_label }}" class="form-label">
|
||||
@@ -102,9 +121,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.international_code.id_for_label }}" class="form-label">
|
||||
@@ -121,7 +138,9 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.undersat_point.id_for_label }}" class="form-label">
|
||||
@@ -138,6 +157,23 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.launch_date.id_for_label }}" class="form-label">
|
||||
{{ form.launch_date.label }}
|
||||
</label>
|
||||
{{ form.launch_date }}
|
||||
{% if form.launch_date.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{{ form.launch_date.errors.0 }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if form.launch_date.help_text %}
|
||||
<div class="form-text">{{ form.launch_date.help_text }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -160,24 +196,7 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.launch_date.id_for_label }}" class="form-label">
|
||||
{{ form.launch_date.label }}
|
||||
</label>
|
||||
{{ form.launch_date }}
|
||||
{% if form.launch_date.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{{ form.launch_date.errors.0 }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if form.launch_date.help_text %}
|
||||
<div class="form-text">{{ form.launch_date.help_text }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-12">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.url.id_for_label }}" class="form-label">
|
||||
{{ form.url.label }}
|
||||
|
||||
@@ -190,6 +190,16 @@
|
||||
{% endif %}
|
||||
</a>
|
||||
</th>
|
||||
<th scope="col" style="min-width: 150px;">
|
||||
<a href="javascript:void(0)" onclick="updateSort('alternative_name')" class="text-white text-decoration-none">
|
||||
Альт. название
|
||||
{% if sort == 'alternative_name' %}
|
||||
<i class="bi bi-arrow-up"></i>
|
||||
{% elif sort == '-alternative_name' %}
|
||||
<i class="bi bi-arrow-down"></i>
|
||||
{% endif %}
|
||||
</a>
|
||||
</th>
|
||||
<th scope="col" style="min-width: 100px;">
|
||||
<a href="javascript:void(0)" onclick="updateSort('norad')" class="text-white text-decoration-none">
|
||||
NORAD ID
|
||||
@@ -274,6 +284,7 @@
|
||||
</td>
|
||||
<td class="text-center">{{ satellite.id }}</td>
|
||||
<td>{{ satellite.name }}</td>
|
||||
<td>{{ satellite.alternative_name|default:"-" }}</td>
|
||||
<td>{{ satellite.norad }}</td>
|
||||
<td>{{ satellite.international_code|default:"-" }}</td>
|
||||
<td>{{ satellite.bands }}</td>
|
||||
@@ -307,7 +318,7 @@
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="13" class="text-center text-muted">Нет данных для отображения</td>
|
||||
<td colspan="14" class="text-center text-muted">Нет данных для отображения</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user