Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/bin/mapvas/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn main() -> eframe::Result {
let config = Config::new();
init_style_config(config.vector_style_file.as_deref());

let (map, remote, data_holder) = Map::new(cc.egui_ctx.clone());
let (map, remote, data_holder) = Map::new(cc.egui_ctx.clone(), config.clone());
spawn_remote_runner(runtime, remote.clone());
Ok(Box::new(MapApp::new(
map,
Expand Down
2 changes: 1 addition & 1 deletion src/headless.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl HeadlessRenderer {
#[must_use]
pub fn render(&self, events: &[MapEvent], width: u32, height: u32) -> image::RgbaImage {
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, self.config.clone());
map.set_headless();

for event in events {
Expand Down
7 changes: 4 additions & 3 deletions src/map/mapvas_egui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ pub struct Map {

impl Map {
#[must_use]
pub fn new(ctx: egui::Context) -> (Self, Remote, Rc<dyn MapLayerHolder>) {
let cfg = crate::config::Config::new();

pub fn new(
ctx: egui::Context,
cfg: crate::config::Config,
) -> (Self, Remote, Rc<dyn MapLayerHolder>) {
let tile_layer = layer::TileLayer::from_config(ctx.clone(), &cfg);
let shape_info = std::sync::Arc::new(std::sync::RwLock::new(std::collections::HashMap::new()));
let shape_layer = layer::ShapeLayer::new(cfg.clone(), ctx.clone(), shape_info.clone());
Expand Down
47 changes: 28 additions & 19 deletions src/map/mapvas_egui/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,36 @@ pub(crate) fn fit_to_screen(transform: &mut Transform, rect: &Rect) {
transform.zoom = transform.zoom.clamp(MIN_ZOOM, MAX_ZOOM);

let inv = transform.invert();
let PixelCoordinate { x: _, y } = inv.apply(PixelPosition { x: 0., y: 0. });
if y < 0. {
transform.translate(
PixelPosition {
x: 0.,
y: y.min(0.),
} * transform.zoom,
);
}
let world_h_screen = CANVAS_SIZE * transform.zoom;
let view_h = rect.height();
let top_y = inv.apply(PixelPosition { x: 0., y: 0. }).y;

let PixelCoordinate { x: _, y } = inv.apply(PixelPosition {
x: rect.max.x,
y: rect.max.y,
});
if y > CANVAS_SIZE {
transform.translate(
PixelPosition {
if view_h >= world_h_screen {
// Viewport is taller than the world — center the world vertically. Without this,
// top-anchor and bottom-anchor clamps below would both fire and oscillate.
let desired_top_y = -(view_h - world_h_screen) / (2. * transform.zoom);
let shift = (top_y - desired_top_y) * transform.zoom;
if shift.abs() > 0.01 {
transform.translate(PixelPosition { x: 0., y: shift });
}
} else if top_y < 0. {
transform.translate(PixelPosition {
x: 0.,
y: top_y * transform.zoom,
});
} else {
let bottom_y = inv
.apply(PixelPosition {
x: rect.max.x,
y: rect.max.y,
})
.y;
if bottom_y > CANVAS_SIZE {
transform.translate(PixelPosition {
x: 0.,
y: (y - CANVAS_SIZE).max(0.),
} * transform.zoom,
);
y: (bottom_y - CANVAS_SIZE) * transform.zoom,
});
}
}

// Wrap the x-translation so the viewport stays within one canonical world.
Expand Down
19 changes: 14 additions & 5 deletions tests/grid_snapshot_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::io::Cursor;
fn create_test_app_with_geojson_grid_mode(geojson_content: String) -> MapApp {
let config = Config::default();
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, config.clone());
map.set_headless();

// Parse the GeoJSON content and inject it as a map event
Expand All @@ -35,7 +35,7 @@ fn create_test_app_with_geojson_grid_mode(geojson_content: String) -> MapApp {
fn create_test_app_grid_mode() -> MapApp {
let config = Config::default();
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, config.clone());
map.set_headless();
MapApp::new(map, remote, data_holder, config, None)
}
Expand Down Expand Up @@ -221,17 +221,26 @@ async fn grid_mode_ui_workflow() {
app,
);

// Step 1: Initial state with loaded data
// Setup: switch to grid-only display via the settings UI so subsequent
// snapshots don't depend on real tile loads.
harness.run();
harness.get_by_label("Tile Layer").click();
harness.run();
harness.get_by_label("Grid Only").click();
harness.run();
harness.get_by_label("Tile Layer").click();
harness.run();

// Step 1: Initial state with loaded data
harness.snapshot("workflow_initial_with_data");

// Step 2: Open tile layer settings
// Step 2: Open tile layer settings (Grid Only is already selected from setup)
let tile_layer = harness.get_by_label("Tile Layer");
tile_layer.click();
harness.run();
harness.snapshot("workflow_settings_opened");

// Step 3: Switch directly to grid mode (avoiding tile dependencies)
// Step 3: Re-click Grid Only to verify the radio still works
let grid_only_button = harness.get_by_label("Grid Only");
grid_only_button.click();
harness.run();
Expand Down
15 changes: 14 additions & 1 deletion tests/highlight_snapshot_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::io::Cursor;
fn create_test_app_with_geometries() -> MapApp {
let config = Config::default();
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, config.clone());
map.set_headless();

// Use nested folders KML to have both collections and individual geometries
Expand Down Expand Up @@ -90,6 +90,19 @@ async fn test_individual_geometry_highlighting() {

harness.run();

// Switch to grid mode so snapshots don't depend on real tile loads
let tile_layer = harness.get_by_label("Tile Layer");
tile_layer.click();
harness.run();

let grid_only_button = harness.get_by_label("Grid Only");
grid_only_button.click();
harness.run();

let tile_layer = harness.get_by_label("Tile Layer");
tile_layer.click();
harness.run();

// Setup to reach individual geometries
let shape_layer = harness.get_by_label("Shape Layer");
shape_layer.click();
Expand Down
2 changes: 1 addition & 1 deletion tests/popup_double_click_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::io::Cursor;
fn create_test_app_with_nested_kml() -> MapApp {
let config = Config::default();
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, config.clone());
map.set_headless();

// Create nested KML content with multiple levels
Expand Down
2 changes: 1 addition & 1 deletion tests/popup_snapshot_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::io::Cursor;
fn create_test_app_with_popup_kml() -> MapApp {
let config = Config::default();
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, config.clone());
map.set_headless();

// Read and parse the test KML file with MultiGeometry
Expand Down
Binary file modified tests/snapshots/coordinates_grid_only_mode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/coordinates_off_mode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/debug_range_timeline_fixed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/empty_grid_mode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/empty_grid_mode_clean.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/geojson_grid_clean_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/geojson_on_grid_mode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/geometry_shapes_expanded.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/geometry_ui_ready_for_interaction.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/grid_mode_sidebar_collapsed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/grid_mode_sidebar_full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/grid_only_mode_basic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/individual_geometries_final_state.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/individual_geometries_shape_layer_expanded.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/large_file_temporal_range.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/popup_functionality_test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/popup_initial_sidebar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/popup_memory_management_stable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/popup_nested_expanded.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/popup_nested_initial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/popup_nested_ready_for_interaction.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/popup_shape_layer_expanded.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/popup_shapes_visible.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/popup_sidebar_with_collections.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/snapshots/temporal_filtering_after_animation_started.png
Binary file modified tests/snapshots/temporal_filtering_after_controls_visible.png
Binary file modified tests/snapshots/temporal_initial_state.png
Binary file modified tests/snapshots/temporal_morning_state.png
Binary file modified tests/snapshots/temporal_timeline_expanded.png
Binary file modified tests/snapshots/temporal_with_geometries.png
Binary file modified tests/snapshots/temporal_workflow_step1_timeline_available.png
Binary file modified tests/snapshots/temporal_workflow_step2_controls_expanded.png
Binary file modified tests/snapshots/unix_epoch_first_timestamp.png
Binary file modified tests/snapshots/unix_epoch_no_timezone_parsed.png
Binary file modified tests/snapshots/unix_epoch_timeline_controls_visible.png
Binary file modified tests/snapshots/unix_epoch_timeline_detected.png
Binary file modified tests/snapshots/workflow_final_grid_view.png
Binary file modified tests/snapshots/workflow_grid_mode_active.png
Binary file modified tests/snapshots/workflow_initial_with_data.png
Binary file modified tests/snapshots/workflow_settings_opened.png
10 changes: 5 additions & 5 deletions tests/temporal_snapshot_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::io::Cursor;
fn create_test_app_with_temporal_kml() -> MapApp {
let config = Config::default();
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, config.clone());
map.set_headless();

// Read and parse the temporal KML file
Expand All @@ -38,7 +38,7 @@ fn create_test_app_with_temporal_kml() -> MapApp {
fn create_test_app_with_unix_epoch_kml() -> MapApp {
let config = Config::default();
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, config.clone());
map.set_headless();

// Read and parse the unix epoch KML file
Expand Down Expand Up @@ -390,7 +390,7 @@ async fn unix_epoch_temporal_filtering() {
fn create_test_app_with_debug_range_kml() -> MapApp {
let config = Config::default();
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, config.clone());
map.set_headless();

// Read and parse the debug range KML file (2-minute span)
Expand All @@ -415,7 +415,7 @@ fn create_test_app_with_debug_range_kml() -> MapApp {
fn create_test_app_with_unix_epoch_no_tz_kml() -> MapApp {
let config = Config::default();
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, config.clone());
map.set_headless();

// Read and parse the unix epoch KML file without timezone
Expand Down Expand Up @@ -529,7 +529,7 @@ async fn large_file_temporal_parsing() {

let config = Config::default();
let ctx = egui::Context::default();
let (mut map, remote, data_holder) = Map::new(ctx);
let (mut map, remote, data_holder) = Map::new(ctx, config.clone());
map.set_headless();

let mut parser = KmlParser::new();
Expand Down
2 changes: 1 addition & 1 deletion tests/ui_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use mapvas::{config::Config, map::mapvas_egui::Map, mapvas_ui::MapApp};
fn create_test_app() -> MapApp {
let config = Config::default();
let ctx = egui::Context::default();
let (map, remote, data_holder) = Map::new(ctx);
let (map, remote, data_holder) = Map::new(ctx, config.clone());
MapApp::new(map, remote, data_holder, config, None)
}

Expand Down
Loading