From 7d85b82f4ed3ae3524cc793305fd3ed1d11d42e2 Mon Sep 17 00:00:00 2001 From: Benoit Simard Date: Wed, 20 Nov 2024 16:55:17 +0100 Subject: [PATCH] [layer-leaflet/maplibre] when resizing keep the same center --- packages/layer-leaflet/src/index.ts | 12 +++- packages/layer-maplibre/src/index.ts | 25 ++++++++- packages/layer-maplibre/src/utils.ts | 6 +- .../layer-leaflet/resize.ts | 56 +++++++++++++++++++ .../layer-leaflet/stories.ts | 13 +++++ .../layer-maplibre/resize.ts | 56 +++++++++++++++++++ .../layer-maplibre/stories.ts | 13 +++++ 7 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 packages/storybook/stories/3-additional-packages/layer-leaflet/resize.ts create mode 100644 packages/storybook/stories/3-additional-packages/layer-maplibre/resize.ts diff --git a/packages/layer-leaflet/src/index.ts b/packages/layer-leaflet/src/index.ts index ca66194b9..e1b1f3dd0 100644 --- a/packages/layer-leaflet/src/index.ts +++ b/packages/layer-leaflet/src/index.ts @@ -107,10 +107,18 @@ export default function bindLeafletLayer( // When sigma is resize, we need to update the graph coordinate (the ref has changed) // and recompute the zoom bounds function fnOnResize() { - map.invalidateSize(); + // Ask the map to resize + // NB: resize can change the center of the map, and we want to keep it + const center = map.getCenter(); + map.invalidateSize({ pan: false, animate: false, duration: 0 }); + map.setView(center); + + // Map ref has changed, we need to update the graph coordinates & bounds updateGraphCoordinates(sigma.getGraph()); - fnSyncSigmaWithMap(); setSigmaRatioBounds(sigma, map); + + // Do the sync + fnSyncSigmaWithMap(); } // Clean up function to remove everything diff --git a/packages/layer-maplibre/src/index.ts b/packages/layer-maplibre/src/index.ts index 076237536..f18d95d68 100644 --- a/packages/layer-maplibre/src/index.ts +++ b/packages/layer-maplibre/src/index.ts @@ -90,8 +90,29 @@ export default function bindMaplibreLayer( // When sigma is resize, we need to update the graph coordinate (the ref has changed) // and recompute the zoom bounds function fnOnResize() { - updateGraphCoordinates(sigma.getGraph()); - fnSyncSigmaWithMap(); + // Avoid sync map with sigma while we do the resize + // otherwise there is a sideeffect... + sigma.off("afterRender", fnSyncMapWithSigma); + const center = map.getCenter(); + + // Ask the map to resize + map.once("resize", () => { + // NB: resize can change the center of the map, and we want to keep it + map.setCenter(center); + + // Map ref has changed, we need to update the graph coordinates + updateGraphCoordinates(sigma.getGraph()); + + // Do the sync + fnSyncSigmaWithMap(); + + // Re-enable the map sync with sigma in the next frame + setTimeout(() => { + fnSyncMapWithSigma(); + sigma.on("afterRender", fnSyncMapWithSigma); + }, 0); + }); + map.resize(); } // Clean up function to remove everything diff --git a/packages/layer-maplibre/src/utils.ts b/packages/layer-maplibre/src/utils.ts index 89298dc76..86573c1a0 100644 --- a/packages/layer-maplibre/src/utils.ts +++ b/packages/layer-maplibre/src/utils.ts @@ -29,12 +29,11 @@ export function graphToLatlng(map: Map, coords: { x: number; y: number }): { lat * BBOX sync : map to sigma */ export function syncSigmaWithMap(sigma: Sigma, map: Map): void { - const mapBound = map.getBounds(); - // Compute sigma center - const center = sigma.viewportToFramedGraph(sigma.graphToViewport(latlngToGraph(map, mapBound.getCenter()))); + const center = sigma.viewportToFramedGraph(sigma.graphToViewport(latlngToGraph(map, map.getCenter()))); // Compute sigma ratio + const mapBound = map.getBounds(); const northEast = sigma.graphToViewport(latlngToGraph(map, mapBound.getNorthEast())); const southWest = sigma.graphToViewport(latlngToGraph(map, mapBound.getSouthWest())); const viewportBoundDimension = { @@ -45,6 +44,7 @@ export function syncSigmaWithMap(sigma: Sigma, map: Map): void { const ratio = Math.min(viewportBoundDimension.width / viewportDim.width, viewportBoundDimension.height / viewportDim.height) * sigma.getCamera().getState().ratio; + sigma.getCamera().setState({ ...center, ratio: ratio }); } diff --git a/packages/storybook/stories/3-additional-packages/layer-leaflet/resize.ts b/packages/storybook/stories/3-additional-packages/layer-leaflet/resize.ts new file mode 100644 index 000000000..fe230a7c5 --- /dev/null +++ b/packages/storybook/stories/3-additional-packages/layer-leaflet/resize.ts @@ -0,0 +1,56 @@ +/** + * This is a minimal example of sigma. You can use it as a base to write new + * examples, or reproducible test cases for new issues, for instance. + */ +import bindLeafletLayer from "@sigma/layer-leaflet"; +import Graph from "graphology"; +import { Attributes, SerializedGraph } from "graphology-types"; +import Sigma from "sigma"; + +import data from "./data/airports.json"; + +export default () => { + const container = document.getElementById("sigma-container") as HTMLElement; + const graph = Graph.from(data as SerializedGraph); + graph.updateEachNodeAttributes((_node, attributes) => ({ + ...attributes, + label: attributes.fullName, + x: 0, + y: 0, + })); + + // initiate sigma + const renderer = new Sigma(graph, container, { + labelRenderedSizeThreshold: 20, + defaultNodeColor: "#e22352", + defaultEdgeColor: "#ffaeaf", + minEdgeThickness: 1, + nodeReducer: (node, attrs) => { + return { + ...attrs, + size: Math.sqrt(graph.degree(node)) / 2, + }; + }, + }); + + bindLeafletLayer(renderer, { + getNodeLatLng: (attrs: Attributes) => ({ lat: attrs.latitude, lng: attrs.longitude }), + }); + + let isSmall = false; + const toggleButton = document.createElement("button"); + toggleButton.innerText = "Toggle fullscreen"; + toggleButton.style.position = "absolute"; + toggleButton.style.zIndex = "1"; + toggleButton.onclick = () => { + container.style.width = isSmall ? "100%" : "50%"; + container.style.height = isSmall ? "100%" : "50%"; + renderer.refresh({ schedule: false }); + isSmall = !isSmall; + }; + container.appendChild(toggleButton); + + return () => { + renderer.kill(); + }; +}; diff --git a/packages/storybook/stories/3-additional-packages/layer-leaflet/stories.ts b/packages/storybook/stories/3-additional-packages/layer-leaflet/stories.ts index 1497082eb..3631a08c5 100644 --- a/packages/storybook/stories/3-additional-packages/layer-leaflet/stories.ts +++ b/packages/storybook/stories/3-additional-packages/layer-leaflet/stories.ts @@ -6,6 +6,8 @@ import basicSource from "./basic?raw"; import geojsonPlay from "./geojson"; import geojsonSource from "./geojson?raw"; import template from "./index.html?raw"; +import resizePlay from "./resize"; +import resizeSource from "./resize?raw"; import tilelayerPlay from "./tilelayer"; import tilelayerSource from "./tilelayer?raw"; @@ -49,3 +51,14 @@ export const withAGeoJson: Story = { }, }, }; + +export const resize: Story = { + name: "Change dimensions", + render: () => template, + play: wrapStory(resizePlay), + parameters: { + storySource: { + source: resizeSource, + }, + }, +}; diff --git a/packages/storybook/stories/3-additional-packages/layer-maplibre/resize.ts b/packages/storybook/stories/3-additional-packages/layer-maplibre/resize.ts new file mode 100644 index 000000000..41f75b23f --- /dev/null +++ b/packages/storybook/stories/3-additional-packages/layer-maplibre/resize.ts @@ -0,0 +1,56 @@ +/** + * This is a minimal example of sigma. You can use it as a base to write new + * examples, or reproducible test cases for new issues, for instance. + */ +import bindMapLayer from "@sigma/layer-maplibre"; +import Graph from "graphology"; +import { Attributes, SerializedGraph } from "graphology-types"; +import Sigma from "sigma"; + +import data from "./data/airports.json"; + +export default () => { + const container = document.getElementById("sigma-container") as HTMLElement; + const graph = Graph.from(data as SerializedGraph); + graph.updateEachNodeAttributes((_node, attributes) => ({ + ...attributes, + label: attributes.fullName, + x: 0, + y: 0, + })); + + // initiate sigma + const renderer = new Sigma(graph, container, { + labelRenderedSizeThreshold: 20, + defaultNodeColor: "#e22352", + defaultEdgeColor: "#ffaeaf", + minEdgeThickness: 1, + nodeReducer: (node, attrs) => { + return { + ...attrs, + size: Math.sqrt(graph.degree(node)) / 2, + }; + }, + }); + + bindMapLayer(renderer, { + getNodeLatLng: (attrs: Attributes) => ({ lat: attrs.latitude, lng: attrs.longitude }), + }); + + let isSmall = false; + const toggleButton = document.createElement("button"); + toggleButton.innerText = "Toggle fullscreen"; + toggleButton.style.position = "absolute"; + toggleButton.style.zIndex = "1"; + toggleButton.onclick = () => { + container.style.width = isSmall ? "100%" : "50%"; + container.style.height = isSmall ? "100%" : "50%"; + renderer.refresh({ schedule: false }); + isSmall = !isSmall; + }; + container.appendChild(toggleButton); + + return () => { + renderer.kill(); + }; +}; diff --git a/packages/storybook/stories/3-additional-packages/layer-maplibre/stories.ts b/packages/storybook/stories/3-additional-packages/layer-maplibre/stories.ts index 152c3fbca..ed8e2e3ab 100644 --- a/packages/storybook/stories/3-additional-packages/layer-maplibre/stories.ts +++ b/packages/storybook/stories/3-additional-packages/layer-maplibre/stories.ts @@ -6,6 +6,8 @@ import basicSource from "./basic?raw"; import geojsonPlay from "./geojson"; import geojsonSource from "./geojson?raw"; import template from "./index.html?raw"; +import resizePlay from "./resize"; +import resizeSource from "./resize?raw"; const meta: Meta = { id: "@sigma/layer-maplibre", @@ -36,3 +38,14 @@ export const withAGeoJson: Story = { }, }, }; + +export const resize: Story = { + name: "Change dimensions", + render: () => template, + play: wrapStory(resizePlay), + parameters: { + storySource: { + source: resizeSource, + }, + }, +};