mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
Allow users to delete points from the map
This commit is contained in:
parent
66e1feaf29
commit
4371d28ef7
6 changed files with 139 additions and 69 deletions
|
|
@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||
|
||||
## [0.9.4] — 2024-07-21
|
||||
|
||||
### Added
|
||||
|
||||
- A popup being shown when user clicks on a point now contains a link to delete the point. This is useful if you want to delete a point that was imported by mistake or you just want to clean up your data.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added `public/imports` and `public/exports` folders to git to prevent errors when exporting data
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
12
app/controllers/api/v1/points_controller.rb
Normal file
12
app/controllers/api/v1/points_controller.rb
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::PointsController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
|
||||
def destroy
|
||||
point = current_user.points.find(params[:id])
|
||||
point.destroy
|
||||
|
||||
render json: { message: 'Point deleted successfully' }
|
||||
end
|
||||
end
|
||||
|
|
@ -4,12 +4,13 @@ class MapController < ApplicationController
|
|||
before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
@points = current_user.tracked_points.without_raw_data.where('timestamp >= ? AND timestamp <= ?', start_at, end_at).order(timestamp: :asc)
|
||||
@points = current_user.tracked_points.without_raw_data.where('timestamp >= ? AND timestamp <= ?', start_at,
|
||||
end_at).order(timestamp: :asc)
|
||||
|
||||
@countries_and_cities = CountriesAndCities.new(@points).call
|
||||
@coordinates =
|
||||
@points.pluck(:latitude, :longitude, :battery, :altitude, :timestamp, :velocity, :id)
|
||||
.map { [_1.to_f, _2.to_f, _3.to_s, _4.to_s, _5.to_s, _6.to_s, _7] }
|
||||
.map { [_1.to_f, _2.to_f, _3.to_s, _4.to_s, _5.to_s, _6.to_s, _7.to_s] }
|
||||
@distance = distance
|
||||
@start_at = Time.zone.at(start_at)
|
||||
@end_at = Time.zone.at(end_at)
|
||||
|
|
|
|||
|
|
@ -16,28 +16,29 @@ export default class extends Controller {
|
|||
connect() {
|
||||
console.log("Map controller connected");
|
||||
|
||||
const markers = JSON.parse(this.element.dataset.coordinates);
|
||||
// The default map center is Victory Column in Berlin
|
||||
let center = markers[markers.length - 1] || [52.514568, 13.350111];
|
||||
const timezone = this.element.dataset.timezone;
|
||||
const clearFogRadius = this.element.dataset.fog_of_war_meters;
|
||||
this.markers = JSON.parse(this.element.dataset.coordinates);
|
||||
this.timezone = this.element.dataset.timezone;
|
||||
this.clearFogRadius = this.element.dataset.fog_of_war_meters;
|
||||
|
||||
const map = L.map(this.containerTarget, {
|
||||
this.center = this.markers[this.markers.length - 1] || [52.514568, 13.350111];
|
||||
|
||||
this.map = L.map(this.containerTarget, {
|
||||
layers: [osmMapLayer(), osmHotMapLayer()],
|
||||
}).setView([center[0], center[1]], 14);
|
||||
}).setView([this.center[0], this.center[1]], 14);
|
||||
|
||||
const markersArray = this.createMarkersArray(markers);
|
||||
const markersLayer = L.layerGroup(markersArray);
|
||||
const heatmapMarkers = markers.map((element) => [element[0], element[1], 0.3]);
|
||||
this.markersArray = this.createMarkersArray(this.markers);
|
||||
this.markersLayer = L.layerGroup(this.markersArray);
|
||||
this.heatmapMarkers = this.markers.map((element) => [element[0], element[1], 0.3]);
|
||||
|
||||
this.polylinesLayer = this.createPolylinesLayer(this.markers, this.map, this.timezone);
|
||||
this.heatmapLayer = L.heatLayer(this.heatmapMarkers, { radius: 20 }).addTo(this.map);
|
||||
this.fogOverlay = L.layerGroup(); // Initialize fog layer
|
||||
|
||||
const polylinesLayer = this.createPolylinesLayer(markers, map, timezone);
|
||||
const heatmapLayer = L.heatLayer(heatmapMarkers, { radius: 20 }).addTo(map);
|
||||
const fogOverlay = L.layerGroup(); // Initialize fog layer
|
||||
const controlsLayer = {
|
||||
Points: markersLayer,
|
||||
Polylines: polylinesLayer,
|
||||
Heatmap: heatmapLayer,
|
||||
"Fog of War": fogOverlay,
|
||||
Points: this.markersLayer,
|
||||
Polylines: this.polylinesLayer,
|
||||
Heatmap: this.heatmapLayer,
|
||||
"Fog of War": this.fogOverlay,
|
||||
};
|
||||
|
||||
L.control
|
||||
|
|
@ -47,9 +48,9 @@ export default class extends Controller {
|
|||
imperial: false,
|
||||
maxWidth: 120,
|
||||
})
|
||||
.addTo(map);
|
||||
.addTo(this.map);
|
||||
|
||||
L.control.layers(this.baseMaps(), controlsLayer).addTo(map);
|
||||
L.control.layers(this.baseMaps(), controlsLayer).addTo(this.map);
|
||||
|
||||
let fogEnabled = false;
|
||||
|
||||
|
|
@ -57,15 +58,15 @@ export default class extends Controller {
|
|||
document.getElementById('fog').style.display = 'none';
|
||||
|
||||
// Toggle fog layer visibility
|
||||
map.on('overlayadd', function (e) {
|
||||
this.map.on('overlayadd', (e) => {
|
||||
if (e.name === 'Fog of War') {
|
||||
fogEnabled = true;
|
||||
document.getElementById('fog').style.display = 'block';
|
||||
updateFog(markers, clearFogRadius);
|
||||
this.updateFog(this.markers, this.clearFogRadius);
|
||||
}
|
||||
});
|
||||
|
||||
map.on('overlayremove', function (e) {
|
||||
this.map.on('overlayremove', (e) => {
|
||||
if (e.name === 'Fog of War') {
|
||||
fogEnabled = false;
|
||||
document.getElementById('fog').style.display = 'none';
|
||||
|
|
@ -73,53 +74,15 @@ export default class extends Controller {
|
|||
});
|
||||
|
||||
// Update fog circles on zoom and move
|
||||
map.on('zoomend moveend', function () {
|
||||
this.map.on('zoomend moveend', () => {
|
||||
if (fogEnabled) {
|
||||
updateFog(markers, clearFogRadius);
|
||||
this.updateFog(this.markers, this.clearFogRadius);
|
||||
}
|
||||
});
|
||||
|
||||
function updateFog(markers, clearFogRadius) {
|
||||
if (fogEnabled) {
|
||||
var fog = document.getElementById('fog');
|
||||
fog.innerHTML = ''; // Clear previous circles
|
||||
markers.forEach(function (point) {
|
||||
const radiusInPixels = metersToPixels(map, clearFogRadius);
|
||||
clearFog(point[0], point[1], radiusInPixels);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function metersToPixels(map, meters) {
|
||||
const zoom = map.getZoom();
|
||||
const latLng = map.getCenter(); // Get map center for correct projection
|
||||
const metersPerPixel = getMetersPerPixel(latLng.lat, zoom);
|
||||
return meters / metersPerPixel;
|
||||
}
|
||||
|
||||
function getMetersPerPixel(latitude, zoom) {
|
||||
// Might be a total bullshit, generated by ChatGPT, but works
|
||||
const earthCircumference = 40075016.686; // Earth's circumference in meters
|
||||
const metersPerPixel = earthCircumference * Math.cos(latitude * Math.PI / 180) / Math.pow(2, zoom + 8);
|
||||
return metersPerPixel;
|
||||
}
|
||||
|
||||
function clearFog(lat, lng, radius) {
|
||||
var fog = document.getElementById('fog');
|
||||
var point = map.latLngToContainerPoint([lat, lng]);
|
||||
var size = radius * 2;
|
||||
var circle = document.createElement('div');
|
||||
circle.className = 'unfogged-circle';
|
||||
circle.style.width = size + 'px';
|
||||
circle.style.height = size + 'px';
|
||||
circle.style.left = (point.x - radius) + 'px';
|
||||
circle.style.top = (point.y - radius) + 'px';
|
||||
circle.style.backdropFilter = 'blur(0px)'; // Remove blur for the circles
|
||||
fog.appendChild(circle);
|
||||
}
|
||||
|
||||
addTileLayer(map);
|
||||
this.addLastMarker(map, markers);
|
||||
addTileLayer(this.map);
|
||||
this.addLastMarker(this.map, this.markers);
|
||||
this.addEventListeners();
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
|
|
@ -149,10 +112,62 @@ export default class extends Controller {
|
|||
<b>Longitude:</b> ${marker[1]}<br>
|
||||
<b>Altitude:</b> ${marker[3]}m<br>
|
||||
<b>Velocity:</b> ${marker[5]}km/h<br>
|
||||
<b>Battery:</b> ${marker[2]}%
|
||||
<b>Battery:</b> ${marker[2]}%<br>
|
||||
<a href="#" data-id="${marker[6]}" class="delete-point">[Delete]</a>
|
||||
`;
|
||||
}
|
||||
|
||||
addEventListeners() {
|
||||
document.addEventListener('click', (event) => {
|
||||
if (event.target && event.target.classList.contains('delete-point')) {
|
||||
event.preventDefault();
|
||||
const pointId = event.target.getAttribute('data-id');
|
||||
|
||||
if (confirm('Are you sure you want to delete this point?')) {
|
||||
this.deletePoint(pointId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deletePoint(id) {
|
||||
fetch(`/api/v1/points/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Point deleted:', data);
|
||||
|
||||
// Remove the marker from the map
|
||||
this.removeMarker(id);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('There was a problem with the delete request:', error);
|
||||
});
|
||||
}
|
||||
|
||||
removeMarker(id) {
|
||||
const markerIndex = this.markersArray.findIndex(marker => marker.getPopup().getContent().includes(`data-id="${id}"`));
|
||||
if (markerIndex !== -1) {
|
||||
this.markersArray[markerIndex].remove(); // Assuming your marker object has a remove method
|
||||
this.markersArray.splice(markerIndex, 1);
|
||||
this.markersLayer.clearLayers();
|
||||
this.markersLayer.addLayer(L.layerGroup(this.markersArray));
|
||||
|
||||
// Remove from the markers data array
|
||||
this.markers = this.markers.filter(marker => marker[6] !== parseInt(id));
|
||||
}
|
||||
}
|
||||
|
||||
addLastMarker(map, markers) {
|
||||
if (markers.length > 0) {
|
||||
const lastMarker = markers[markers.length - 1].slice(0, 2);
|
||||
|
|
@ -160,6 +175,42 @@ export default class extends Controller {
|
|||
}
|
||||
}
|
||||
|
||||
updateFog(markers, clearFogRadius) {
|
||||
var fog = document.getElementById('fog');
|
||||
fog.innerHTML = ''; // Clear previous circles
|
||||
markers.forEach((point) => {
|
||||
const radiusInPixels = this.metersToPixels(this.map, clearFogRadius);
|
||||
this.clearFog(point[0], point[1], radiusInPixels);
|
||||
});
|
||||
}
|
||||
|
||||
metersToPixels(map, meters) {
|
||||
const zoom = map.getZoom();
|
||||
const latLng = map.getCenter(); // Get map center for correct projection
|
||||
const metersPerPixel = this.getMetersPerPixel(latLng.lat, zoom);
|
||||
return meters / metersPerPixel;
|
||||
}
|
||||
|
||||
getMetersPerPixel(latitude, zoom) {
|
||||
const earthCircumference = 40075016.686; // Earth's circumference in meters
|
||||
const metersPerPixel = earthCircumference * Math.cos(latitude * Math.PI / 180) / Math.pow(2, zoom + 8);
|
||||
return metersPerPixel;
|
||||
}
|
||||
|
||||
clearFog(lat, lng, radius) {
|
||||
var fog = document.getElementById('fog');
|
||||
var point = this.map.latLngToContainerPoint([lat, lng]);
|
||||
var size = radius * 2;
|
||||
var circle = document.createElement('div');
|
||||
circle.className = 'unfogged-circle';
|
||||
circle.style.width = size + 'px';
|
||||
circle.style.height = size + 'px';
|
||||
circle.style.left = (point.x - radius) + 'px';
|
||||
circle.style.top = (point.y - radius) + 'px';
|
||||
circle.style.backdropFilter = 'blur(0px)'; // Remove blur for the circles
|
||||
fog.appendChild(circle);
|
||||
}
|
||||
|
||||
addHighlightOnHover(polyline, map, startPoint, endPoint, prevPoint, nextPoint, timezone) {
|
||||
const originalStyle = { color: "blue", opacity: 0.6, weight: 3 };
|
||||
const highlightStyle = { color: "yellow", opacity: 1, weight: 5 };
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ Rails.application.routes.draw do
|
|||
|
||||
namespace :api do
|
||||
namespace :v1 do
|
||||
resources :points, only: %i[destroy]
|
||||
|
||||
namespace :overland do
|
||||
resources :batches, only: :create
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue