mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
Implement live map updates with WebSockets
This commit is contained in:
parent
bec9db1198
commit
9c99a835de
9 changed files with 137 additions and 6 deletions
7
app/channels/points_channel.rb
Normal file
7
app/channels/points_channel.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PointsChannel < ApplicationCable::Channel
|
||||
def subscribed
|
||||
stream_for current_user
|
||||
end
|
||||
end
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
// Import all the channels to be used by Action Cable
|
||||
import "notifications_channel"
|
||||
import "points_channel"
|
||||
|
|
|
|||
15
app/javascript/channels/points_channel.js
Normal file
15
app/javascript/channels/points_channel.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import consumer from "./consumer"
|
||||
|
||||
consumer.subscriptions.create("PointsChannel", {
|
||||
connected() {
|
||||
// Called when the subscription is ready for use on the server
|
||||
},
|
||||
|
||||
disconnected() {
|
||||
// Called when the subscription has been terminated by the server
|
||||
},
|
||||
|
||||
received(data) {
|
||||
// Called when there's incoming data on the websocket for this channel
|
||||
}
|
||||
});
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { Controller } from "@hotwired/stimulus";
|
||||
import L from "leaflet";
|
||||
import "leaflet.heat";
|
||||
import consumer from "../channels/consumer"; // Add this import
|
||||
|
||||
import { createMarkersArray } from "../maps/markers";
|
||||
|
||||
|
|
@ -138,12 +139,92 @@ export default class extends Controller {
|
|||
this.map.removeControl(this.drawControl);
|
||||
}
|
||||
});
|
||||
|
||||
this.setupSubscription(); // Add this line
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.map.remove();
|
||||
}
|
||||
|
||||
setupSubscription() {
|
||||
consumer.subscriptions.create("PointsChannel", {
|
||||
received: (data) => {
|
||||
this.appendPoint(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
appendPoint(data) {
|
||||
// Parse the received point data
|
||||
console.log(data)
|
||||
const newPoint = data;
|
||||
|
||||
// Add the new point to the markers array
|
||||
this.markers.push(newPoint);
|
||||
|
||||
// Create a new marker for the point
|
||||
const markerOptions = {
|
||||
...this.userSettings, // Pass any relevant settings
|
||||
id: newPoint[6], // Assuming index 6 contains the point ID
|
||||
timestamp: newPoint[4] // Assuming index 2 contains the timestamp
|
||||
};
|
||||
|
||||
const newMarker = this.createMarker(newPoint, markerOptions);
|
||||
this.markersArray.push(newMarker);
|
||||
|
||||
// Update the markers layer
|
||||
this.markersLayer.clearLayers();
|
||||
this.markersLayer.addLayer(L.layerGroup(this.markersArray));
|
||||
|
||||
// Update heatmap
|
||||
this.heatmapMarkers.push([newPoint[0], newPoint[1], 0.2]);
|
||||
this.heatmapLayer.setLatLngs(this.heatmapMarkers);
|
||||
|
||||
// Update polylines
|
||||
this.polylinesLayer.clearLayers();
|
||||
this.polylinesLayer = createPolylinesLayer(
|
||||
this.markers,
|
||||
this.map,
|
||||
this.timezone,
|
||||
this.routeOpacity,
|
||||
this.userSettings
|
||||
);
|
||||
|
||||
// Pan map to new location
|
||||
this.map.setView([newPoint[0], newPoint[1]], 14);
|
||||
|
||||
// Update fog of war if enabled
|
||||
if (this.map.hasLayer(this.fogOverlay)) {
|
||||
this.updateFog(this.markers, this.clearFogRadius);
|
||||
}
|
||||
|
||||
// Update the last marker
|
||||
this.map.eachLayer((layer) => {
|
||||
if (layer instanceof L.Marker && !layer._popup) {
|
||||
this.map.removeLayer(layer);
|
||||
}
|
||||
});
|
||||
this.addLastMarker(this.map, this.markers);
|
||||
}
|
||||
|
||||
createMarker(point, options) {
|
||||
const marker = L.marker([point[0], point[1]]);
|
||||
|
||||
// Add popup content based on point data
|
||||
const popupContent = `
|
||||
<div>
|
||||
<p>Time: ${new Date(point[2]).toLocaleString()}</p>
|
||||
${point[3] ? `<p>Address: ${point[3]}</p>` : ''}
|
||||
${point[7] ? `<p>Country: ${point[7]}</p>` : ''}
|
||||
<a href="#" class="delete-point" data-id="${point[6]}">Delete</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
marker.bindPopup(popupContent);
|
||||
return marker;
|
||||
}
|
||||
|
||||
async setupScratchLayer(countryCodesMap) {
|
||||
this.scratchLayer = L.geoJSON(null, {
|
||||
style: {
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ class Notification < ApplicationRecord
|
|||
NotificationsChannel.broadcast_to(
|
||||
user,
|
||||
{
|
||||
id: id,
|
||||
title: title,
|
||||
content: content,
|
||||
kind: kind,
|
||||
timestamp: Time.current.to_i
|
||||
# id: id,
|
||||
# title: title,
|
||||
# content: content,
|
||||
# kind: kind,
|
||||
# timestamp: Time.current.to_i
|
||||
}
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ class Point < ApplicationRecord
|
|||
scope :not_visited, -> { where(visit_id: nil) }
|
||||
|
||||
after_create :async_reverse_geocode
|
||||
after_create_commit :broadcast_coordinates
|
||||
|
||||
def self.without_raw_data
|
||||
select(column_names - ['raw_data'])
|
||||
|
|
@ -37,4 +38,22 @@ class Point < ApplicationRecord
|
|||
|
||||
ReverseGeocodingJob.perform_later(self.class.to_s, id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def broadcast_coordinates
|
||||
PointsChannel.broadcast_to(
|
||||
user,
|
||||
[
|
||||
latitude.to_f,
|
||||
longitude.to_f,
|
||||
battery.to_s,
|
||||
altitude.to_s,
|
||||
timestamp.to_s,
|
||||
velocity.to_s,
|
||||
id.to_s,
|
||||
country.to_s
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -41,8 +41,10 @@
|
|||
<% end %>
|
||||
|
||||
<div
|
||||
id='map'
|
||||
class="w-full"
|
||||
data-controller="maps"
|
||||
data-controller="maps points"
|
||||
data-points-target="map"
|
||||
data-distance_unit="<%= DISTANCE_UNIT %>"
|
||||
data-api_key="<%= current_user.api_key %>"
|
||||
data-user_settings=<%= current_user.settings.to_json %>
|
||||
|
|
|
|||
|
|
@ -20,3 +20,4 @@ pin 'leaflet-draw' # @1.0.4
|
|||
pin '@rails/actioncable', to: 'actioncable.esm.js'
|
||||
pin_all_from 'app/javascript/channels', under: 'channels'
|
||||
pin 'notifications_channel', to: 'channels/notifications_channel.js'
|
||||
pin 'points_channel', to: 'channels/points_channel.js'
|
||||
|
|
|
|||
5
spec/channels/points_channel_spec.rb
Normal file
5
spec/channels/points_channel_spec.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe PointsChannel, type: :channel do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
Loading…
Reference in a new issue