Добавил трек и поле Примечание к Source

This commit is contained in:
2025-11-25 17:45:34 +03:00
parent 68486d2283
commit 388753ba31
7 changed files with 207 additions and 18 deletions

View File

@@ -45,7 +45,7 @@ class LoadExcelData(forms.Form):
required=False,
initial=False,
widget=forms.CheckboxInput(attrs={"class": "form-check-input"}),
help_text="Если отмечено, точки не будут добавляться к объектам (Source)",
help_text="Если отмечено, точки не будут добавляться к объектам",
)
@@ -59,7 +59,7 @@ class LoadCsvData(forms.Form):
required=False,
initial=False,
widget=forms.CheckboxInput(attrs={"class": "form-check-input"}),
help_text="Если отмечено, точки не будут добавляться к объектам (Source)",
help_text="Если отмечено, точки не будут добавляться к объектам",
)
@@ -477,7 +477,7 @@ class SourceForm(forms.ModelForm):
class Meta:
model = Source
fields = ['info', 'ownership']
fields = ['info', 'ownership', 'note']
widgets = {
'info': forms.Select(attrs={
'class': 'form-select',
@@ -487,10 +487,16 @@ class SourceForm(forms.ModelForm):
'class': 'form-select',
'id': 'id_ownership',
}),
'note': forms.Textarea(attrs={
'class': 'form-control',
'rows': "3",
'id': 'id_note',
})
}
labels = {
'info': 'Тип объекта',
'ownership': 'Принадлежность объекта',
'note': 'Примечание'
}
help_texts = {
'info': 'Стационарные: координата усредняется. Подвижные: координата = последняя точка. При изменении типа координата пересчитывается автоматически.',

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2025-11-25 12:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mainapp', '0013_add_is_automatic_to_objitem'),
]
operations = [
migrations.AddField(
model_name='source',
name='note',
field=models.TextField(blank=True, help_text='Дополнительное описание объекта', null=True, verbose_name='Примечание'),
),
]

View File

@@ -491,6 +491,12 @@ class Source(models.Model):
verbose_name="Принадлежность объекта",
help_text="Принадлежность объекта (страна, организация и т.д.)",
)
note = models.TextField(
null=True,
blank=True,
verbose_name="Примечание",
help_text="Дополнительное описание объекта",
)
confirm_at = models.DateTimeField(
null=True,
blank=True,

View File

@@ -238,6 +238,17 @@
</div>
</div>
</div>
<div class="row">
<div class="mb-3">
<label for="id_ownership" class="form-label">{{ form.note.label }}:</label>
{{ form.note }}
{% if form.note.errors %}
<div class="invalid-feedback d-block">
{{ form.note.errors }}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Блок с картой -->

View File

@@ -148,11 +148,13 @@
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="8" checked onchange="toggleColumn(this)"> Координаты Кубсата</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="9" checked onchange="toggleColumn(this)"> Координаты визуального наблюдения</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="10" checked onchange="toggleColumn(this)"> Координаты справочные</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="11" onchange="toggleColumn(this)"> Создано</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="12" onchange="toggleColumn(this)"> Обновлено</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="13" checked onchange="toggleColumn(this)"> Дата подтверждения</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="14" checked onchange="toggleColumn(this)"> Последний сигнал</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="15" checked onchange="toggleColumn(this)"> Действия</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="11" checked onchange="toggleColumn(this)"> Примечание</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="12" onchange="toggleColumn(this)"> Создано</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="13" onchange="toggleColumn(this)"> Обновлено</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="14" checked onchange="toggleColumn(this)"> Дата подтверждения</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="15" checked onchange="toggleColumn(this)"> Последний сигнал</label></li>
<li><label class="dropdown-item"><input type="checkbox" class="column-toggle" data-column="16" checked onchange="toggleColumn(this)"> Действия</label></li>
</ul>
</div>
</div>
@@ -457,6 +459,7 @@
<th scope="col" style="min-width: 150px;">Координаты Кубсата</th>
<th scope="col" style="min-width: 150px;">Координаты визуального наблюдения</th>
<th scope="col" style="min-width: 150px;">Координаты справочные</th>
<th scope="col" style="min-width: 120px;">Примечание</th>
<th scope="col" style="min-width: 120px;">
{% include 'mainapp/components/_sort_header.html' with field='created_at' label='Создано' current_sort=sort %}
</th>
@@ -503,6 +506,7 @@
<td>{{ source.coords_kupsat }}</td>
<td>{{ source.coords_valid }}</td>
<td>{{ source.coords_reference }}</td>
<td>{{ source.note }}</td>
<td>{{ source.created_at|date:"d.m.Y H:i" }}</td>
<td>{{ source.updated_at|date:"d.m.Y H:i" }}</td>
<td>{{ source.confirm_at|date:"d.m.Y H:i"|default:"-" }}</td>
@@ -1122,6 +1126,7 @@ function initColumnVisibility() {
// Default state: hide Создано and Обновлено columns
const createdAtCheckbox = document.querySelector('input[data-column="11"]');
const updatedAtCheckbox = document.querySelector('input[data-column="12"]');
const noteCheckbox = document.querySelector('input[data-column="13"]');
if (createdAtCheckbox) {
createdAtCheckbox.checked = false;
@@ -1132,6 +1137,11 @@ function initColumnVisibility() {
updatedAtCheckbox.checked = false;
toggleColumnWithoutSave(updatedAtCheckbox);
}
if (noteCheckbox) {
noteCheckbox.checked = false;
toggleColumnWithoutSave(noteCheckbox);
}
// Save initial state
saveColumnVisibility();

View File

@@ -69,6 +69,7 @@
<script src="{% static 'leaflet/leaflet.js' %}"></script>
<script src="{% static 'leaflet-measure/leaflet-measure.ru.js' %}"></script>
<script src="{% static 'leaflet-tree/L.Control.Layers.Tree.js' %}"></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet-polylinedecorator@1.6.0/dist/leaflet.polylineDecorator.min.js"></script>
<script>
// Инициализация карты
@@ -119,6 +120,23 @@
var sourceOverlays = [];
var glPointLayers = [];
var glPointCoordinates = [];
var glPointsData = [];
var trackLayer = L.layerGroup(); // Layer group for track and related elements
var glPointsGroupLayer = L.layerGroup(); // Layer group specifically for GL points
// Сначала собираем все данные о точках ГЛ
{% for group in groups %}
{% for point_data in group.points %}
{% if not point_data.source_id %}
glPointsData.push({
name: "{{ point_data.name|escapejs }}",
frequency: "{{ point_data.frequency|escapejs }}",
coords: [{{ point_data.point.1|safe }}, {{ point_data.point.0|safe }}]
});
{% endif %}
{% endfor %}
{% endfor %}
// Создаём слои для координат объекта и точек ГЛ
{% for group in groups %}
@@ -138,14 +156,31 @@
{% else %}
// Это точка ГЛ
var pointName = "{{ point_data.name|escapejs }}";
var marker = L.marker([{{ point_data.point.1|safe }}, {{ point_data.point.0|safe }}], {
icon: groupIcon
}).bindPopup(pointName + '<br>' + "{{ point_data.frequency|escapejs }}");
var pointCoords = [{{ point_data.point.1|safe }}, {{ point_data.point.0|safe }}];
var pointNumber = {{ forloop.counter }};
// Определяем цвет маркера: первый - зеленый, последний - оранжевый, остальные - обычный
var markerIcon;
if (pointNumber === 1) {
markerIcon = getColorIcon('green');
} else if (pointNumber === glPointsData.length) {
markerIcon = getColorIcon('orange');
} else {
markerIcon = groupIcon;
}
var marker = L.marker(pointCoords, {
icon: markerIcon
}).bindPopup(pointNumber + '. ' + pointName + '<br>' + "{{ point_data.frequency|escapejs }}");
groupLayer.addLayer(marker);
glPointsGroupLayer.addLayer(marker); // Also add to GL points group layer
// Сохраняем координаты для построения трека
glPointCoordinates.push(pointCoords);
// Добавляем каждую точку ГЛ отдельно в список
glPointLayers.push({
label: "{{ forloop.counter }} - {{ point_data.name|escapejs }} ({{ point_data.frequency|escapejs }})",
label: pointNumber + " - {{ point_data.name|escapejs }} ({{ point_data.frequency|escapejs }})",
layer: marker
});
{% endif %}
@@ -158,11 +193,79 @@
layer: groupLayer
});
{% endif %}
// Добавляем слой группы на карту
{% if group.color in 'blue,orange,green,violet' %}
groupLayer.addTo(map);
{% endif %}
{% endfor %}
// Создаём трек между точками ГЛ с номерами на сегментах
if (glPointCoordinates.length > 1) {
// Создаём отдельные сегменты линий между точками
for (var i = 0; i < glPointCoordinates.length - 1; i++) {
var segmentNumber = i + 1;
// Создаем линию с стрелкой
var segment = L.polyline([glPointCoordinates[i], glPointCoordinates[i + 1]], {
color: 'blue',
weight: 3,
opacity: 0.7
});
trackLayer.addLayer(segment);
// Добавляем стрелку через декоратор
setTimeout(function(seg, segLayer, num) {
return function() {
var decorator = L.polylineDecorator(seg, {
patterns: [
{
offset: '65%',
repeat: 0,
symbol: L.Symbol.arrowHead({
pixelSize: 15,
polygon: false,
pathOptions: {
stroke: true,
color: 'blue',
weight: 3,
opacity: 0.7
}
})
}
]
});
segLayer.addLayer(decorator);
};
}(segment, trackLayer, segmentNumber), 100);
// Вычисляем центр сегмента для размещения номера
var midLat = (glPointCoordinates[i][0] + glPointCoordinates[i + 1][0]) / 2;
var midLng = (glPointCoordinates[i][1] + glPointCoordinates[i + 1][1]) / 2;
// Добавляем номер сегмента
var segmentIcon = L.divIcon({
className: 'segment-label',
html: '<div style="background: rgba(0, 0, 255, 0.9); color: white; border: 2px solid white; border-radius: 50%; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 14px; box-shadow: 0 2px 4px rgba(0,0,0,0.4);">' + segmentNumber + '</div>',
iconSize: [28, 28],
iconAnchor: [14, 14]
});
var segmentMarker = L.marker([midLat, midLng], { icon: segmentIcon });
trackLayer.addLayer(segmentMarker);
}
// Добавляем слой трека на карту
trackLayer.addTo(map);
}
// Добавляем GL точки на карту
if (glPointLayers.length > 0) {
glPointsGroupLayer.addTo(map);
}
// Создаём иерархию
var treeOverlays = [];
if (sourceOverlays.length > 0) {
treeOverlays.push({
label: "Координаты объекта #{{ source_id }}",
@@ -171,13 +274,21 @@
layer: L.layerGroup()
});
}
if (glPointLayers.length > 0) {
treeOverlays.push({
label: "Точки ГЛ",
selectAllCheckbox: true,
children: glPointLayers,
layer: L.layerGroup()
layer: glPointsGroupLayer
});
}
// Добавляем слой трека в контрол
if (glPointCoordinates.length > 1) {
treeOverlays.push({
label: "Трек между точками ГЛ",
layer: trackLayer
});
}
@@ -224,7 +335,7 @@
{% endif %}
{% endfor %}
// Точки ГЛ (все одним цветом)
// Точки ГЛ
{% for group in groups %}
{% if group.color not in 'blue,orange,green,violet' %}
div.innerHTML += '<div class="legend-section"><div class="legend-section-title">Точки ГЛ:</div></div>';
@@ -236,7 +347,33 @@
`;
{% endif %}
{% endfor %}
// Добавляем информацию о треке
if (glPointCoordinates.length > 1) {
div.innerHTML += '<div class="legend-section"><div class="legend-section-title">Трек между точками:</div></div>';
div.innerHTML += `
<div class="legend-item">
<div style="width: 18px; height: 3px; background: blue; margin-right: 6px;"></div>
<span>Соединительная линия</span>
</div>
`;
}
// Добавляем информацию о специальных точках
if (glPointCoordinates.length > 1) {
div.innerHTML += '<div class="legend-section"><div class="legend-section-title">Специальные точки:</div></div>';
div.innerHTML += `
<div class="legend-item">
<div class="legend-marker" style="background-image: url('{% static "leaflet-markers/img/marker-icon-green.png" %}');"></div>
<span>Первая точка</span>
</div>
<div class="legend-item">
<div class="legend-marker" style="background-image: url('{% static "leaflet-markers/img/marker-icon-orange.png" %}');"></div>
<span>Последняя точка</span>
</div>
`;
}
return div;
};
legend.addTo(map);

View File

@@ -671,6 +671,7 @@ class SourceListView(LoginRequiredMixin, View):
'has_lyngsat': has_lyngsat,
'lyngsat_id': lyngsat_id,
'marks': marks_data,
'note': source.note if source.note else '-'
})
# Prepare context for template