From a7cfe6639fefef974625430473f901019d0ce940 Mon Sep 17 00:00:00 2001 From: ccmdi Date: Fri, 10 Oct 2025 12:21:07 -0400 Subject: [PATCH] autocenter --- src/main.ts | 2 + src/map-renderer.ts | 72 +++++++++++++++++++----------------- src/settings/map-settings.ts | 10 +++++ src/views/map-bases-view.ts | 3 +- 4 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/main.ts b/src/main.ts index b096de9..46936a5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,6 +8,7 @@ interface MapPluginSettings { lngKey: string; strokeWidth: number; iconFill: boolean; + autoCenter: boolean; tagSettings: MapTagSettings; } @@ -16,6 +17,7 @@ const DEFAULT_SETTINGS: MapPluginSettings = { lngKey: '', strokeWidth: 2.5, iconFill: false, + autoCenter: true, tagSettings: DEFAULT_MAP_TAG_SETTINGS }; diff --git a/src/map-renderer.ts b/src/map-renderer.ts index a5e07bf..1b6cb23 100644 --- a/src/map-renderer.ts +++ b/src/map-renderer.ts @@ -62,6 +62,7 @@ export interface MapRendererOptions { tileLayer?: string; showSearch?: boolean; showTags?: boolean; + autoCenter?: boolean; onMarkerClick?: (point: MapPoint, event: MjolnirEvent) => void; onTilesLoaded?: () => void; }; @@ -188,34 +189,33 @@ function calculateBounds(points: MapPoint[], containerEl: HTMLElement): { latitu const centerLat = (maxLat + minLat) / 2; const centerLng = (maxLng + minLng) / 2; - // Calculate range with minimum thresholds to handle clustered points - let latRange = Math.max(maxLat - minLat, 0.01); - let lngRange = Math.max(maxLng - minLng, 0.01); + // Add padding (20% on each side) + const padding = 1.4; // 1 + 0.2*2 for both sides + const latRange = (maxLat - minLat) * padding; + const lngRange = (maxLng - minLng) * padding; - // Add 20% padding to the bounds - const paddingFactor = 0.2; - latRange = latRange * (1 + paddingFactor * 2); - lngRange = lngRange * (1 + paddingFactor * 2); + // Handle single point or very clustered points + const minRange = 0.01; // Roughly ~1km + const adjustedLatRange = Math.max(latRange, minRange); + const adjustedLngRange = Math.max(lngRange, minRange); // Get container dimensions - const containerWidth = containerEl.clientWidth || 800; - const containerHeight = containerEl.clientHeight || 600; + const width = containerEl.clientWidth || 800; + const height = containerEl.clientHeight || 600; - // Web Mercator zoom calculation - const lngZoom = Math.log2((containerWidth / 256) * (360 / lngRange)); + // Calculate zoom level to fit bounds + // At zoom level z, the world is 256 * 2^z pixels wide + const WORLD_SIZE = 256; + const latZoom = Math.log2(height / (adjustedLatRange * WORLD_SIZE / 180)); + const lngZoom = Math.log2(width / (adjustedLngRange * WORLD_SIZE / 360)); - // For latitude, account for Mercator projection distortion - const latRadians = centerLat * Math.PI / 180; - const latitudeFactor = Math.cos(latRadians); - const latZoom = Math.log2((containerHeight / 256) * (180 / latRange) * latitudeFactor); - - // Use the smaller zoom (more zoomed out) to ensure everything fits - const autoZoom = Math.max(1, Math.min(18, Math.floor(Math.min(lngZoom, latZoom)) - 0.5)); + // Use the smaller zoom to ensure everything fits, and clamp + const zoom = Math.max(1, Math.min(18, Math.min(latZoom, lngZoom) - 0.5)); return { latitude: centerLat, longitude: centerLng, - zoom: autoZoom, + zoom: zoom, }; } @@ -291,6 +291,7 @@ export function updateMapPoints(deck: Deck, points: MapPoint[], c const markerType = options.markerType || 'pins'; const markerSize = options.markerSize || 100; const defaultColor = options.markerColor || 'var(--color-accent)'; + const autoCenter = options.autoCenter !== false; // Default to true const deckData: DeckDataPoint[] = points.map(point => ({ position: [point.lng, point.lat] as [number, number], @@ -306,25 +307,28 @@ export function updateMapPoints(deck: Deck, points: MapPoint[], c const markerLayer = createMarkerLayer(deckData, markerType, settings, tagSettings, options, app); - // Calculate new bounds and smoothly transition to them - const newViewState = calculateBounds(points, containerEl); - // Update layers deck.setProps({ layers: [tileLayer, markerLayer] }); - // Smoothly transition view to new bounds with easing - deck.setProps({ - initialViewState: { - MapView: { - ...newViewState, - pitch: 0, - bearing: 0, - transitionDuration: 800, - transitionInterpolator: new FlyToInterpolator(), - transitionEasing: easeCubic, + // Only auto-center if enabled and we have points + if (autoCenter && points.length > 0) { + // Calculate new bounds and smoothly transition to them + const newViewState = calculateBounds(points, containerEl); + + // Smoothly transition view to new bounds with easing + deck.setProps({ + initialViewState: { + MapView: { + ...newViewState, + pitch: 0, + bearing: 0, + transitionDuration: 800, + transitionInterpolator: new FlyToInterpolator(), + transitionEasing: easeCubic, + } } - } - }); + }); + } } export async function createMapRenderer(config: MapRendererOptions): Promise> { diff --git a/src/settings/map-settings.ts b/src/settings/map-settings.ts index 579dd81..743d02c 100644 --- a/src/settings/map-settings.ts +++ b/src/settings/map-settings.ts @@ -62,6 +62,16 @@ export class MapSettingTab extends PluginSettingTab { this.plugin.refreshAllMapViews(); })); + new Setting(containerEl) + .setName('Auto-center on update') + .setDesc('Automatically zoom and center map when data changes') + .addToggle(toggle => toggle + .setValue(this.plugin.settings.autoCenter) + .onChange(async (value) => { + this.plugin.settings.autoCenter = value; + await this.plugin.saveSettings(); + })); + renderTagCustomizations(containerEl, this.app, this.plugin); } } \ No newline at end of file diff --git a/src/views/map-bases-view.ts b/src/views/map-bases-view.ts index 879d179..b44e398 100644 --- a/src/views/map-bases-view.ts +++ b/src/views/map-bases-view.ts @@ -258,7 +258,8 @@ export class MapBasesView extends BasesView { settings: this.plugin.settings, tagSettings: this.plugin.tagSettings, options: { - markerType: this.markerType + markerType: this.markerType, + autoCenter: this.plugin.settings.autoCenter } }); }