Add settings for radius of clear fog in meters

This commit is contained in:
Eugene Burmakin 2024-06-25 22:30:11 +02:00
parent 61431ac64f
commit 81d13f548e
9 changed files with 72 additions and 133 deletions

File diff suppressed because one or more lines are too long

View file

@ -9,7 +9,6 @@
pointer-events: none;
mix-blend-mode: multiply;
z-index: 1000;
filter: blur(5px); /* Apply a blur effect */
}
.unfogged-circle {
@ -18,5 +17,5 @@
border-radius: 50%;
background: white;
mix-blend-mode: destination-out;
filter: blur(0px); /* Apply no blur to the circles */
filter: blur(3px); /* Apply no blur to the circles */
}

View file

@ -29,6 +29,8 @@ class SettingsController < ApplicationController
private
def settings_params
params.require(:settings).permit(:meters_between_routes, :minutes_between_routes)
params.require(:settings).permit(
:meters_between_routes, :minutes_between_routes, :fog_of_war_meters
)
end
end

View file

@ -12,6 +12,7 @@ export default class extends Controller {
let center = markers[markers.length - 1] || JSON.parse(this.element.dataset.center);
center = center === undefined ? [52.514568, 13.350111] : center;
const timezone = this.element.dataset.timezone;
const clearFogRadius = this.element.dataset.fog_of_war_meters;
const map = L.map(this.containerTarget, {
layers: [this.osmMapLayer(), this.osmHotMapLayer()],
@ -23,7 +24,7 @@ export default class extends Controller {
const polylinesLayer = this.createPolylinesLayer(markers, map, timezone);
const heatmapLayer = L.heatLayer(heatmapMarkers, { radius: 20 }).addTo(map);
const fogOverlay = L.layerGroup();
const fogOverlay = L.layerGroup(); // Initialize fog layer
const controlsLayer = {
Points: markersLayer,
Polylines: polylinesLayer,
@ -40,16 +41,19 @@ export default class extends Controller {
})
.addTo(map);
L.control.layers(this.baseMaps(), controlsLayer).addTo(map);
const layerControl = L.control.layers(this.baseMaps(), controlsLayer).addTo(map);
let fogEnabled = false;
// Hide fog by default
document.getElementById('fog').style.display = 'none';
// Toggle fog layer visibility
map.on('overlayadd', function(e) {
if (e.name === 'Fog of War') {
fogEnabled = true;
document.getElementById('fog').style.display = 'block';
updateFog(markers);
updateFog(markers, clearFogRadius);
}
});
@ -63,20 +67,35 @@ export default class extends Controller {
// Update fog circles on zoom and move
map.on('zoomend moveend', function() {
if (fogEnabled) {
updateFog(markers);
updateFog(markers, clearFogRadius);
}
});
function updateFog(markers) {
function updateFog(markers, clearFogRadius) {
if (fogEnabled) {
var fog = document.getElementById('fog');
fog.innerHTML = ''; // Clear previous circles
markers.forEach(function(point) {
clearFog(point[0], point[1], 100); // Adjust the radius as needed
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]);
@ -87,6 +106,7 @@ export default class extends Controller {
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);
}

View file

@ -46,7 +46,8 @@
data-center="<%= MAP_CENTER %>"
data-timezone="<%= Rails.configuration.time_zone %>"
data-meters_between_routes="<%= current_user.settings['meters_between_routes'] %>"
data-minutes_between_routes="<%= current_user.settings['minutes_between_routes'] %>">
data-minutes_between_routes="<%= current_user.settings['minutes_between_routes'] %>"
data-fog_of_war_meters="<%= current_user.settings['fog_of_war_meters'] %>">
<div data-maps-target="container" class="h-[25rem] w-auto min-h-screen">
<div id="fog" class="fog"></div>
</div>

View file

@ -56,6 +56,30 @@
<% end %>
<%= f.number_field :minutes_between_routes, value: current_user.settings['minutes_between_routes'], class: "input input-bordered" %>
</div>
<div class="form-control my-2">
<%= f.label :fog_of_war_meters do %>
Fog of War meters
<!-- The button to open modal -->
<label for="fog_of_war_meters_info" class="btn">?</label>
<!-- Put this part before </body> tag -->
<input type="checkbox" id="fog_of_war_meters_info" class="modal-toggle" />
<div class="modal" role="dialog">
<div class="modal-box">
<h3 class="text-lg font-bold">Fog of War meters</h3>
<p class="py-4">
Value in meters.
</p>
<p class="py-4">
Here you can set the radius of the "cleared" area around a point when Fog of War mode is enabled. The area around the point will be cleared, and the rest of the map will be covered with fog. The cleared area will be a circle with the point as the center and the radius as the value you set here.
</p>
</div>
<label class="modal-backdrop" for="fog_of_war_meters_info">Close</label>
</div>
<% end %>
<%= f.number_field :fog_of_war_meters, value: current_user.settings['fog_of_war_meters'], class: "input input-bordered" %>
</div>
<div class="form-control my-2">
<%= f.submit "Update", class: "btn btn-primary" %>
</div>

View file

@ -0,0 +1,14 @@
# frozen_string_literal: true
class AddFogOfWarMetersToSettings < ActiveRecord::Migration[7.1]
def up
User.find_each do |user|
user.settings = user.settings.merge(fog_of_war_meters: 100)
user.save!
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View file

@ -1 +1 @@
DataMigrate::Data.define(version: 20240610170930)
DataMigrate::Data.define(version: 20240625201842)

121
test.html
View file

@ -1,121 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet Map with Fog of War</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<style>
#map { height: 100vh; }
.fog {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7); /* Adjust the opacity here */
pointer-events: none;
mix-blend-mode: multiply;
z-index: 1000;
}
.unfogged-circle {
position: absolute;
pointer-events: none;
border-radius: 50%;
background: white;
mix-blend-mode: destination-out;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="fog" class="fog"></div>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script>
// Initialize the map
var map = L.map('map').setView([52.516667, 13.383333], 14);
// Add OpenStreetMap tile layer
var osmLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
}).addTo(map);
// Create fog of war overlay
var fogOverlay = L.layerGroup();
// Function to clear fog around a given position
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';
fog.appendChild(circle);
}
// Example points to unfog
var points = [
[52.516667, 13.383333],
[52.520007, 13.404954],
[52.522222, 13.412222]
];
// Clear fog around each point
function updateFog() {
if (fogEnabled) {
var fog = document.getElementById('fog');
fog.innerHTML = ''; // Clear previous circles
points.forEach(function(point) {
clearFog(point[0], point[1], 100); // Adjust the radius as needed
});
}
}
// Initial fog clearing
var fogEnabled = true;
updateFog();
// Update fog clearing when the map moves or zooms
map.on('moveend', updateFog);
map.on('zoomend', updateFog);
// Layers for the controls
var markersLayer = L.layerGroup(); // Replace with your actual markers layer
var polylinesLayer = L.layerGroup(); // Replace with your actual polylines layer
var heatmapLayer = L.layerGroup(); // Replace with your actual heatmap layer
// Layers control
var controlsLayer = {
"Points": markersLayer,
"Polylines": polylinesLayer,
"Heatmap": heatmapLayer,
"Fog of War": fogOverlay
};
L.control.layers({ "OpenStreetMap": osmLayer }, controlsLayer).addTo(map);
// Toggle fog layer visibility
map.on('overlayadd', function(e) {
if (e.name === 'Fog of War') {
fogEnabled = true;
document.getElementById('fog').style.display = 'block';
updateFog();
}
});
map.on('overlayremove', function(e) {
if (e.name === 'Fog of War') {
fogEnabled = false;
document.getElementById('fog').style.display = 'none';
}
});
</script>
</body>
</html>