Подправил маркеры на карте
This commit is contained in:
@@ -120,6 +120,32 @@
|
|||||||
.moving-marker {
|
.moving-marker {
|
||||||
transition: transform 0.1s linear;
|
transition: transform 0.1s linear;
|
||||||
}
|
}
|
||||||
|
.marker-size-control {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 999;
|
||||||
|
background: white;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 1px 5px rgba(0,0,0,0.3);
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
.marker-size-control label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.marker-size-control input[type="range"] {
|
||||||
|
width: 120px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.marker-size-control .size-value {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -133,6 +159,12 @@
|
|||||||
|
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
|
|
||||||
|
<div class="marker-size-control">
|
||||||
|
<label>Размер маркеров:</label>
|
||||||
|
<input type="range" id="markerSizeSlider" min="0.5" max="2" step="0.1" value="1">
|
||||||
|
<span class="size-value" id="sizeValue">1.0x</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="playback-control" id="playbackControl" style="display: none;">
|
<div class="playback-control" id="playbackControl" style="display: none;">
|
||||||
<button id="playBtn" title="Воспроизвести">▶</button>
|
<button id="playBtn" title="Воспроизвести">▶</button>
|
||||||
<button id="pauseBtn" title="Пауза" disabled>⏸</button>
|
<button id="pauseBtn" title="Пауза" disabled>⏸</button>
|
||||||
@@ -195,7 +227,7 @@ let currentMarkers = {};
|
|||||||
let trailPolylines = {};
|
let trailPolylines = {};
|
||||||
let staticMarkers = {};
|
let staticMarkers = {};
|
||||||
|
|
||||||
// Color mapping
|
// Color mapping - расширенная палитра
|
||||||
const colorMap = {
|
const colorMap = {
|
||||||
'red': '#dc3545',
|
'red': '#dc3545',
|
||||||
'blue': '#007bff',
|
'blue': '#007bff',
|
||||||
@@ -204,41 +236,85 @@ const colorMap = {
|
|||||||
'orange': '#fd7e14',
|
'orange': '#fd7e14',
|
||||||
'cyan': '#17a2b8',
|
'cyan': '#17a2b8',
|
||||||
'magenta': '#e83e8c',
|
'magenta': '#e83e8c',
|
||||||
'yellow': '#ffc107',
|
'pink': '#ff69b4',
|
||||||
'lime': '#32cd32',
|
'teal': '#20c997',
|
||||||
'pink': '#ff69b4'
|
'indigo': '#6610f2',
|
||||||
|
'brown': '#8b4513',
|
||||||
|
'navy': '#000080',
|
||||||
|
'maroon': '#800000',
|
||||||
|
'olive': '#808000',
|
||||||
|
'coral': '#ff7f50',
|
||||||
|
'turquoise': '#40e0d0'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create marker icon using leaflet-markers library for static markers
|
// Shape mapping - разные формы маркеров
|
||||||
function createStaticMarkerIcon(type) {
|
const shapeMap = {
|
||||||
let markerColor = 'grey';
|
'circle': (color, size) => {
|
||||||
|
const adjustedSize = Math.round(size * 0.85); // Уменьшаем на 15%
|
||||||
|
return `<div style="background: ${color}; width: ${adjustedSize}px; height: ${adjustedSize}px; border-radius: 50%; border: 1px solid black; box-sizing: border-box;"></div>`;
|
||||||
|
},
|
||||||
|
'square': (color, size) => {
|
||||||
|
const adjustedSize = Math.round(size * 0.85); // Уменьшаем на 15%
|
||||||
|
return `<div style="background: ${color}; width: ${adjustedSize}px; height: ${adjustedSize}px; border: 1px solid black; box-sizing: border-box;"></div>`;
|
||||||
|
},
|
||||||
|
'triangle': (color, size) => {
|
||||||
|
const adjustedSize = Math.round(size * 0.8); // Уменьшаем на 20%
|
||||||
|
return `<svg width="${adjustedSize}" height="${adjustedSize}" viewBox="0 0 100 100"><polygon points="50,10 90,90 10,90" fill="${color}" stroke="black" stroke-width="2"/></svg>`;
|
||||||
|
},
|
||||||
|
'star': (color, size) => `<svg width="${size}" height="${size}" viewBox="0 0 24 24"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" fill="${color}" stroke="black" stroke-width="0.8"/></svg>`,
|
||||||
|
'pentagon': (color, size) => `<svg width="${size}" height="${size}" viewBox="0 0 24 24"><path d="M12 2l7.5 5.5-2.9 9H7.4l-2.9-9z" fill="${color}" stroke="black" stroke-width="0.8"/></svg>`,
|
||||||
|
'hexagon': (color, size) => `<svg width="${size}" height="${size}" viewBox="0 0 24 24"><path d="M12 2l6 4v8l-6 4-6-4V6z" fill="${color}" stroke="black" stroke-width="0.8"/></svg>`,
|
||||||
|
'diamond': (color, size) => {
|
||||||
|
const adjustedSize = Math.round(size * 0.85); // Уменьшаем на 15%
|
||||||
|
return `<div style="background: ${color}; width: ${adjustedSize}px; height: ${adjustedSize}px; transform: rotate(45deg); border: 1px solid black; box-sizing: border-box;"></div>`;
|
||||||
|
},
|
||||||
|
'cross': (color, size) => `<svg width="${size}" height="${size}" viewBox="0 0 24 24"><path d="M9 2h6v7h7v6h-7v7H9v-7H2V9h7V2z" fill="${color}" stroke="black" stroke-width="0.8"/></svg>`
|
||||||
|
};
|
||||||
|
|
||||||
|
// Available shapes for assignment
|
||||||
|
const availableShapes = ['circle', 'square', 'triangle', 'star', 'pentagon', 'hexagon', 'diamond', 'cross'];
|
||||||
|
|
||||||
|
// Global marker size multiplier
|
||||||
|
let markerSizeMultiplier = 1.0;
|
||||||
|
|
||||||
|
// Create static marker icon (start/intermediate/end points)
|
||||||
|
function createStaticMarkerIcon(type, color, shape) {
|
||||||
|
const hexColor = colorMap[color] || color;
|
||||||
|
let baseSize = 12; // Уменьшенный базовый размер
|
||||||
|
|
||||||
if (type === 'start') {
|
if (type === 'start') {
|
||||||
markerColor = 'green';
|
baseSize = 14; // Начальная точка чуть больше
|
||||||
} else if (type === 'end') {
|
} else if (type === 'end') {
|
||||||
markerColor = 'red';
|
baseSize = 14; // Конечная точка чуть больше
|
||||||
} else if (type === 'intermediate') {
|
} else if (type === 'intermediate') {
|
||||||
markerColor = 'grey';
|
baseSize = 10; // Промежуточные точки меньше
|
||||||
}
|
}
|
||||||
|
|
||||||
return L.icon({
|
const size = Math.round(baseSize * markerSizeMultiplier);
|
||||||
iconUrl: '{% static "leaflet-markers/img/marker-icon-" %}' + markerColor + '.png',
|
const shapeFunc = shapeMap[shape] || shapeMap['circle'];
|
||||||
shadowUrl: '{% static "leaflet-markers/img/marker-shadow.png" %}',
|
|
||||||
iconSize: [25, 41],
|
return L.divIcon({
|
||||||
iconAnchor: [12, 41],
|
className: 'static-marker',
|
||||||
popupAnchor: [1, -34],
|
iconSize: [size, size],
|
||||||
shadowSize: [41, 41]
|
iconAnchor: [size/2, size/2],
|
||||||
|
popupAnchor: [0, -size/2],
|
||||||
|
html: shapeFunc(hexColor, size)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create moving marker icon (colored circle)
|
// Create moving marker icon (current position - larger and more prominent)
|
||||||
function createMovingMarkerIcon(color) {
|
function createMovingMarkerIcon(color, shape) {
|
||||||
const hexColor = colorMap[color] || color;
|
const hexColor = colorMap[color] || color;
|
||||||
|
const baseSize = 16; // Движущийся маркер немного больше
|
||||||
|
const size = Math.round(baseSize * markerSizeMultiplier);
|
||||||
|
const shapeFunc = shapeMap[shape] || shapeMap['circle'];
|
||||||
|
|
||||||
return L.divIcon({
|
return L.divIcon({
|
||||||
className: 'current-marker',
|
className: 'current-marker moving-marker',
|
||||||
iconSize: [18, 18],
|
iconSize: [size, size],
|
||||||
iconAnchor: [9, 9],
|
iconAnchor: [size/2, size/2],
|
||||||
html: `<div style="background: ${hexColor}; width: 100%; height: 100%; border-radius: 50%; border: 3px solid white; box-shadow: 0 0 6px rgba(0,0,0,0.5);"></div>`
|
popupAnchor: [0, -size/2],
|
||||||
|
html: shapeFunc(hexColor, size)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,22 +411,35 @@ function updateDisplay(progress) {
|
|||||||
sourcesData.forEach(source => {
|
sourcesData.forEach(source => {
|
||||||
const pos = getPositionAtProgress(source, progress);
|
const pos = getPositionAtProgress(source, progress);
|
||||||
const color = source.color;
|
const color = source.color;
|
||||||
|
const shape = source.shape;
|
||||||
|
|
||||||
if (pos) {
|
if (pos) {
|
||||||
// Show/update current marker (moving object - colored circle)
|
// Check if we've reached the end
|
||||||
|
const isAtEnd = (progress >= 1 || pos.pointIndex === source.points.length - 1) && pos.segmentProgress >= 0.99;
|
||||||
|
|
||||||
|
// Show/update current marker (moving object) - hide when at end
|
||||||
|
if (isAtEnd) {
|
||||||
|
// Remove moving marker when at end point
|
||||||
|
if (currentMarkers[source.source_id]) {
|
||||||
|
sourceLayerGroups[source.source_id].removeLayer(currentMarkers[source.source_id]);
|
||||||
|
delete currentMarkers[source.source_id];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show/update moving marker
|
||||||
if (!currentMarkers[source.source_id]) {
|
if (!currentMarkers[source.source_id]) {
|
||||||
// Get first point name for popup
|
// Get first point name for popup
|
||||||
const firstPointName = source.points && source.points.length > 0 ? source.points[0].name : '';
|
const firstPointName = source.points && source.points.length > 0 ? source.points[0].name : '';
|
||||||
const popupContent = `<b>ID ${source.source_id}:</b> ${firstPointName}<br><i style="color: #666;">Текущая позиция</i>`;
|
const popupContent = `<b>ID ${source.source_id}:</b> ${firstPointName}<br><i style="color: #666;">Текущая позиция</i>`;
|
||||||
|
|
||||||
currentMarkers[source.source_id] = L.marker([pos.lat, pos.lng], {
|
currentMarkers[source.source_id] = L.marker([pos.lat, pos.lng], {
|
||||||
icon: createMovingMarkerIcon(color),
|
icon: createMovingMarkerIcon(color, shape),
|
||||||
zIndexOffset: 1000
|
zIndexOffset: 1000
|
||||||
}).bindPopup(popupContent);
|
}).bindPopup(popupContent);
|
||||||
sourceLayerGroups[source.source_id].addLayer(currentMarkers[source.source_id]);
|
sourceLayerGroups[source.source_id].addLayer(currentMarkers[source.source_id]);
|
||||||
} else {
|
} else {
|
||||||
currentMarkers[source.source_id].setLatLng([pos.lat, pos.lng]);
|
currentMarkers[source.source_id].setLatLng([pos.lat, pos.lng]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update trail (dashed line showing path)
|
// Update trail (dashed line showing path)
|
||||||
const trailCoords = [];
|
const trailCoords = [];
|
||||||
@@ -386,7 +475,7 @@ function updateDisplay(progress) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const marker = L.marker([point.lat, point.lng], {
|
const marker = L.marker([point.lat, point.lng], {
|
||||||
icon: createStaticMarkerIcon(markerType),
|
icon: createStaticMarkerIcon(markerType, color, shape),
|
||||||
zIndexOffset: markerType === 'start' ? 500 : (markerType === 'end' ? 600 : 100)
|
zIndexOffset: markerType === 'start' ? 500 : (markerType === 'end' ? 600 : 100)
|
||||||
}).bindPopup(`<b>${source.source_name}</b><br>${popupPrefix}${point.name}<br>${point.frequency}<br>${formatDate(point.timestamp)}`);
|
}).bindPopup(`<b>${source.source_name}</b><br>${popupPrefix}${point.name}<br>${point.frequency}<br>${formatDate(point.timestamp)}`);
|
||||||
|
|
||||||
@@ -401,7 +490,7 @@ function updateDisplay(progress) {
|
|||||||
if (!staticMarkers[endMarkerKey] && source.points.length > 1) {
|
if (!staticMarkers[endMarkerKey] && source.points.length > 1) {
|
||||||
const lastPoint = source.points[source.points.length - 1];
|
const lastPoint = source.points[source.points.length - 1];
|
||||||
const endMarker = L.marker([lastPoint.lat, lastPoint.lng], {
|
const endMarker = L.marker([lastPoint.lat, lastPoint.lng], {
|
||||||
icon: createStaticMarkerIcon('end'),
|
icon: createStaticMarkerIcon('end', color, shape),
|
||||||
zIndexOffset: 600
|
zIndexOffset: 600
|
||||||
}).bindPopup(`<b>${source.source_name}</b><br><span style="color: red;">■ Конец</span><br>${lastPoint.name}<br>${lastPoint.frequency}<br>${formatDate(lastPoint.timestamp)}`);
|
}).bindPopup(`<b>${source.source_name}</b><br><span style="color: red;">■ Конец</span><br>${lastPoint.name}<br>${lastPoint.frequency}<br>${formatDate(lastPoint.timestamp)}`);
|
||||||
sourceLayerGroups[source.source_id].addLayer(endMarker);
|
sourceLayerGroups[source.source_id].addLayer(endMarker);
|
||||||
@@ -422,19 +511,21 @@ function resetPlayback() {
|
|||||||
// Recreate trails
|
// Recreate trails
|
||||||
sourcesData.forEach(source => {
|
sourcesData.forEach(source => {
|
||||||
const color = colorMap[source.color] || source.color;
|
const color = colorMap[source.color] || source.color;
|
||||||
|
const shape = source.shape;
|
||||||
|
|
||||||
trailPolylines[source.source_id] = L.polyline([], {
|
trailPolylines[source.source_id] = L.polyline([], {
|
||||||
color: color,
|
color: color,
|
||||||
weight: 3,
|
weight: 2, // Уменьшенная толщина линии
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
dashArray: '5, 10'
|
dashArray: '5, 10'
|
||||||
});
|
});
|
||||||
sourceLayerGroups[source.source_id].addLayer(trailPolylines[source.source_id]);
|
sourceLayerGroups[source.source_id].addLayer(trailPolylines[source.source_id]);
|
||||||
|
|
||||||
// Add start marker immediately (green)
|
// Add start marker immediately
|
||||||
if (source.points && source.points.length > 0) {
|
if (source.points && source.points.length > 0) {
|
||||||
const firstPoint = source.points[0];
|
const firstPoint = source.points[0];
|
||||||
const startMarker = L.marker([firstPoint.lat, firstPoint.lng], {
|
const startMarker = L.marker([firstPoint.lat, firstPoint.lng], {
|
||||||
icon: createStaticMarkerIcon('start'),
|
icon: createStaticMarkerIcon('start', color, shape),
|
||||||
zIndexOffset: 500
|
zIndexOffset: 500
|
||||||
}).bindPopup(`<b>${source.source_name}</b><br><span style="color: green;">▶ Начало</span><br>${firstPoint.name}<br>${firstPoint.frequency}<br>${formatDate(firstPoint.timestamp)}`);
|
}).bindPopup(`<b>${source.source_name}</b><br><span style="color: green;">▶ Начало</span><br>${firstPoint.name}<br>${firstPoint.frequency}<br>${formatDate(firstPoint.timestamp)}`);
|
||||||
sourceLayerGroups[source.source_id].addLayer(startMarker);
|
sourceLayerGroups[source.source_id].addLayer(startMarker);
|
||||||
@@ -446,6 +537,16 @@ function resetPlayback() {
|
|||||||
updateDisplay(currentProgress);
|
updateDisplay(currentProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update all marker sizes
|
||||||
|
function updateMarkerSizes() {
|
||||||
|
// Clear and recreate all markers with new size
|
||||||
|
pause();
|
||||||
|
const currentProg = currentProgress;
|
||||||
|
resetPlayback();
|
||||||
|
currentProgress = currentProg;
|
||||||
|
updateDisplay(currentProgress);
|
||||||
|
}
|
||||||
|
|
||||||
// Play animation
|
// Play animation
|
||||||
function play() {
|
function play() {
|
||||||
if (playbackInterval) return;
|
if (playbackInterval) return;
|
||||||
@@ -493,6 +594,11 @@ async function loadData() {
|
|||||||
// Filter out sources with no points
|
// Filter out sources with no points
|
||||||
sourcesData = sourcesData.filter(s => s.points && s.points.length > 0);
|
sourcesData = sourcesData.filter(s => s.points && s.points.length > 0);
|
||||||
|
|
||||||
|
// Assign shapes to sources (cycle through available shapes)
|
||||||
|
sourcesData.forEach((source, idx) => {
|
||||||
|
source.shape = availableShapes[idx % availableShapes.length];
|
||||||
|
});
|
||||||
|
|
||||||
if (!sourcesData || sourcesData.length === 0) {
|
if (!sourcesData || sourcesData.length === 0) {
|
||||||
document.getElementById('loadingOverlay').innerHTML = `
|
document.getElementById('loadingOverlay').innerHTML = `
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
@@ -567,6 +673,21 @@ async function loadData() {
|
|||||||
speedMultiplier = parseFloat(this.value);
|
speedMultiplier = parseFloat(this.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Marker size control
|
||||||
|
const markerSizeSlider = document.getElementById('markerSizeSlider');
|
||||||
|
const sizeValue = document.getElementById('sizeValue');
|
||||||
|
|
||||||
|
markerSizeSlider.addEventListener('input', function() {
|
||||||
|
markerSizeMultiplier = parseFloat(this.value);
|
||||||
|
sizeValue.textContent = markerSizeMultiplier.toFixed(1) + 'x';
|
||||||
|
updateMarkerSizes();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Disable map scroll/zoom when mouse is over marker size control
|
||||||
|
const markerSizeControl = document.querySelector('.marker-size-control');
|
||||||
|
L.DomEvent.disableScrollPropagation(markerSizeControl);
|
||||||
|
L.DomEvent.disableClickPropagation(markerSizeControl);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading data:', error);
|
console.error('Error loading data:', error);
|
||||||
document.getElementById('loadingOverlay').innerHTML = `
|
document.getElementById('loadingOverlay').innerHTML = `
|
||||||
@@ -593,22 +714,12 @@ function addLegend() {
|
|||||||
|
|
||||||
sourcesData.forEach(source => {
|
sourcesData.forEach(source => {
|
||||||
const color = colorMap[source.color] || source.color;
|
const color = colorMap[source.color] || source.color;
|
||||||
|
const shape = source.shape;
|
||||||
const points = source.points;
|
const points = source.points;
|
||||||
|
|
||||||
// Get marker color for this source
|
// Create shape preview
|
||||||
const markerColorMap = {
|
const shapeFunc = shapeMap[shape] || shapeMap['circle'];
|
||||||
'red': 'red',
|
const shapePreview = shapeFunc(color, 16);
|
||||||
'blue': 'blue',
|
|
||||||
'green': 'green',
|
|
||||||
'purple': 'violet',
|
|
||||||
'orange': 'orange',
|
|
||||||
'cyan': 'blue',
|
|
||||||
'magenta': 'red',
|
|
||||||
'yellow': 'yellow',
|
|
||||||
'lime': 'green',
|
|
||||||
'pink': 'red'
|
|
||||||
};
|
|
||||||
const markerColor = markerColorMap[source.color] || 'blue';
|
|
||||||
|
|
||||||
// Get first point name and time info
|
// Get first point name and time info
|
||||||
let firstPointName = '';
|
let firstPointName = '';
|
||||||
@@ -627,11 +738,12 @@ function addLegend() {
|
|||||||
html += `
|
html += `
|
||||||
<div class="legend-item" style="flex-direction: column; align-items: flex-start; margin-bottom: 8px;">
|
<div class="legend-item" style="flex-direction: column; align-items: flex-start; margin-bottom: 8px;">
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
<img src="{% static "leaflet-markers/img/marker-icon-" %}${markerColor}.png"
|
<div style="width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; margin-right: 6px;">
|
||||||
style="width: 18px; height: 30px; margin-right: 6px;">
|
${shapePreview}
|
||||||
|
</div>
|
||||||
<span><strong>ID ${source.source_id}:</strong> ${firstPointName}</span>
|
<span><strong>ID ${source.source_id}:</strong> ${firstPointName}</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-left: 24px;">
|
<div style="margin-left: 26px;">
|
||||||
<small style="color: #666;">${source.points_count} точек</small>
|
<small style="color: #666;">${source.points_count} точек</small>
|
||||||
${timeInfo}
|
${timeInfo}
|
||||||
</div>
|
</div>
|
||||||
@@ -639,31 +751,6 @@ function addLegend() {
|
|||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
|
|
||||||
html += '<div class="legend-section"><strong>Маркеры:</strong></div>';
|
|
||||||
html += `
|
|
||||||
<div class="legend-item">
|
|
||||||
<img src="{% static "leaflet-markers/img/marker-icon-green.png" %}"
|
|
||||||
style="width: 18px; height: 30px; margin-right: 6px;">
|
|
||||||
<span>Начальная точка</span>
|
|
||||||
</div>
|
|
||||||
<div class="legend-item">
|
|
||||||
<img src="{% static "leaflet-markers/img/marker-icon-red.png" %}"
|
|
||||||
style="width: 18px; height: 30px; margin-right: 6px;">
|
|
||||||
<span>Конечная точка</span>
|
|
||||||
</div>
|
|
||||||
<div class="legend-item">
|
|
||||||
<img src="{% static "leaflet-markers/img/marker-icon-grey.png" %}"
|
|
||||||
style="width: 18px; height: 30px; margin-right: 6px;">
|
|
||||||
<span>Промежуточная точка</span>
|
|
||||||
</div>
|
|
||||||
<div class="legend-item">
|
|
||||||
<div style="width: 18px; height: 18px; border-radius: 50%; background: #007bff; border: 3px solid white; box-shadow: 0 0 4px rgba(0,0,0,0.4); margin-right: 6px;"></div>
|
|
||||||
<span>Движущийся объект (цвет по объекту)</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
html += '<div class="legend-section"><small style="color: #666;">Все объекты движутся<br>с одинаковой скоростью</small></div>';
|
|
||||||
|
|
||||||
div.innerHTML = html;
|
div.innerHTML = html;
|
||||||
return div;
|
return div;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -124,7 +124,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="averaging-container">
|
<div class="averaging-container">
|
||||||
<h2>Усреднение точек по источникам</h2>
|
<h2>Усреднение точек по объектам</h2>
|
||||||
|
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -156,7 +156,7 @@
|
|||||||
<div class="table-section">
|
<div class="table-section">
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<h5>Источники <span id="source-count" class="badge bg-primary">0</span></h5>
|
<h5>Объекты <span id="source-count" class="badge bg-primary">0</span></h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group-custom">
|
<div class="btn-group-custom">
|
||||||
<button id="export-xlsx" class="btn btn-success" disabled>
|
<button id="export-xlsx" class="btn btn-success" disabled>
|
||||||
@@ -180,7 +180,7 @@
|
|||||||
<div class="modal-dialog modal-xl">
|
<div class="modal-dialog modal-xl">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="sourceDetailsModalLabel">Детали источника</h5>
|
<h5 class="modal-title" id="sourceDetailsModalLabel">Детали объекта</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body" id="modal-body-content">
|
<div class="modal-body" id="modal-body-content">
|
||||||
@@ -279,7 +279,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
{column: "frequency", dir: "asc"}
|
{column: "frequency", dir: "asc"}
|
||||||
],
|
],
|
||||||
columns: [
|
columns: [
|
||||||
{title: "Источник", field: "source_name", minWidth: 180, widthGrow: 2},
|
{title: "Объект", field: "source_name", minWidth: 180, widthGrow: 2},
|
||||||
{title: "Групп", field: "groups_count", minWidth: 70, hozAlign: "center"},
|
{title: "Групп", field: "groups_count", minWidth: 70, hozAlign: "center"},
|
||||||
{title: "Точек", field: "total_points", minWidth: 70, hozAlign: "center"},
|
{title: "Точек", field: "total_points", minWidth: 70, hozAlign: "center"},
|
||||||
{title: "Частота", field: "frequency", minWidth: 100, sorter: "number"},
|
{title: "Частота", field: "frequency", minWidth: 100, sorter: "number"},
|
||||||
@@ -327,7 +327,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// Delete source
|
// Delete source
|
||||||
function deleteSource(sourceIdx) {
|
function deleteSource(sourceIdx) {
|
||||||
//if (!confirm('Удалить этот источник со всеми группами?')) return;
|
//if (!confirm('Удалить этот объект со всеми группами?')) return;
|
||||||
allSourcesData.splice(sourceIdx, 1);
|
allSourcesData.splice(sourceIdx, 1);
|
||||||
updateSourcesTable();
|
updateSourcesTable();
|
||||||
}
|
}
|
||||||
@@ -338,7 +338,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const source = allSourcesData[sourceIdx];
|
const source = allSourcesData[sourceIdx];
|
||||||
if (!source) return;
|
if (!source) return;
|
||||||
|
|
||||||
document.getElementById('sourceDetailsModalLabel').textContent = `Источник: ${source.source_name}`;
|
document.getElementById('sourceDetailsModalLabel').textContent = `Объект: ${source.source_name}`;
|
||||||
renderModalContent();
|
renderModalContent();
|
||||||
|
|
||||||
const modal = new bootstrap.Modal(document.getElementById('sourceDetailsModal'));
|
const modal = new bootstrap.Modal(document.getElementById('sourceDetailsModal'));
|
||||||
@@ -619,7 +619,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
allSourcesData.forEach(source => {
|
allSourcesData.forEach(source => {
|
||||||
source.groups.forEach(group => {
|
source.groups.forEach(group => {
|
||||||
summaryData.push({
|
summaryData.push({
|
||||||
'Источник': source.source_name,
|
'Объект': source.source_name,
|
||||||
'Частота, МГц': group.frequency,
|
'Частота, МГц': group.frequency,
|
||||||
'Полоса, МГц': group.freq_range,
|
'Полоса, МГц': group.freq_range,
|
||||||
'Символьная скорость, БОД': group.bod_velocity,
|
'Символьная скорость, БОД': group.bod_velocity,
|
||||||
@@ -646,7 +646,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
source.groups.forEach(group => {
|
source.groups.forEach(group => {
|
||||||
group.points.forEach(point => {
|
group.points.forEach(point => {
|
||||||
allPointsData.push({
|
allPointsData.push({
|
||||||
'Источник': source.source_name,
|
'Объект': source.source_name,
|
||||||
'ID точки': point.id,
|
'ID точки': point.id,
|
||||||
'Имя точки': point.name,
|
'Имя точки': point.name,
|
||||||
'Частота, МГц': point.frequency,
|
'Частота, МГц': point.frequency,
|
||||||
|
|||||||
@@ -216,7 +216,7 @@
|
|||||||
filterPolygon.addTo(map);
|
filterPolygon.addTo(map);
|
||||||
|
|
||||||
// Добавляем popup с информацией
|
// Добавляем popup с информацией
|
||||||
filterPolygon.bindPopup('<strong>Область фильтра</strong><br>Отображаются только источники с точками в этой области');
|
filterPolygon.bindPopup('<strong>Область фильтра</strong><br>Отображаются только объекты с точками в этой области');
|
||||||
|
|
||||||
// Если нет других точек, центрируем карту на полигоне
|
// Если нет других точек, центрируем карту на полигоне
|
||||||
{% if not groups %}
|
{% if not groups %}
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ class ShowSourcesMapView(LoginRequiredMixin, View):
|
|||||||
points.append(
|
points.append(
|
||||||
{
|
{
|
||||||
"point": (coords.x, coords.y), # (lon, lat)
|
"point": (coords.x, coords.y), # (lon, lat)
|
||||||
"source_id": f"Источник #{source.id}",
|
"source_id": f"Объект #{source.id}",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
20
dbapp/mapsapp/migrations/0003_alter_transponders_sat_id.py
Normal file
20
dbapp/mapsapp/migrations/0003_alter_transponders_sat_id.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-12-03 07:51
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mainapp', '0017_add_satellite_alternative_name'),
|
||||||
|
('mapsapp', '0002_alter_transponders_snr'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='transponders',
|
||||||
|
name='sat_id',
|
||||||
|
field=models.ForeignKey(help_text='Спутник, которому принадлежит транспондер', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tran_satellite', to='mainapp.satellite', verbose_name='Спутник'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -103,7 +103,8 @@ class Transponders(models.Model):
|
|||||||
)
|
)
|
||||||
sat_id = models.ForeignKey(
|
sat_id = models.ForeignKey(
|
||||||
Satellite,
|
Satellite,
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
related_name="tran_satellite",
|
related_name="tran_satellite",
|
||||||
verbose_name="Спутник",
|
verbose_name="Спутник",
|
||||||
db_index=True,
|
db_index=True,
|
||||||
|
|||||||
@@ -64,5 +64,12 @@ def test_celery_connection():
|
|||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if __name__ == "__main__":
|
# if __name__ == "__main__":
|
||||||
test_celery_connection()
|
# test_celery_connection()
|
||||||
|
import requests
|
||||||
|
|
||||||
|
url = f"https://www.lyngsat.com/europe.html"
|
||||||
|
payload = {"cmd": "request.get", "url": url, "maxTimeout": 60000}
|
||||||
|
response = requests.post("http://localhost:8191/v1", json=payload)
|
||||||
|
|
||||||
|
print(response.content)
|
||||||
|
|||||||
Reference in New Issue
Block a user