@@ -876,6 +876,16 @@ export function ResultSet() {
876876 rsu . current . setLoaderText = setLoaderText ;
877877
878878 const isDataChangedRef = useRef ( false ) ;
879+ const lastGvSelectionRef = useRef ( {
880+ type : 'all' , // 'all' | 'rows' | 'columns' | 'range' | 'cell'
881+ geometryColumnKey : null ,
882+ rowIndices : [ ] ,
883+ columnIndices : new Set ( ) ,
884+ rangeStartIdx : null ,
885+ rangeEndIdx : null ,
886+ cellIdx : null ,
887+ } ) ;
888+
879889 useEffect ( ( ) => {
880890 isDataChangedRef . current = Boolean ( _ . size ( dataChangeStore . updated ) || _ . size ( dataChangeStore . added ) || _ . size ( dataChangeStore . deleted ) ) ;
881891 } , [ dataChangeStore ] ) ;
@@ -1460,30 +1470,103 @@ export function ResultSet() {
14601470 return ( ) => eventBus . deregisterListener ( QUERY_TOOL_EVENTS . TRIGGER_ADD_ROWS , triggerAddRows ) ;
14611471 } , [ columns , selectedRows . size ] ) ;
14621472
1473+ const openGeometryViewerTab = React . useCallback ( ( column , rowsData ) => {
1474+ layoutDocker . openTab ( {
1475+ id : PANELS . GEOMETRY ,
1476+ title : gettext ( 'Geometry Viewer' ) ,
1477+ content : < GeometryViewer rows = { rowsData } columns = { columns } column = { column } /> ,
1478+ closable : true ,
1479+ } , PANELS . MESSAGES , 'after-tab' , true ) ;
1480+ } , [ layoutDocker , columns ] ) ;
1481+
1482+ // Handle manual Geometry Viewer opening.
1483+ // Determines which rows to plot based on the current grid selection (rows, columns,
1484+ // range, or cell) and stores the selection indices in lastGvSelectionRef so the
1485+ // auto-update effect can re-apply the same selection on subsequent query re-runs.
14631486 useEffect ( ( ) => {
14641487 const renderGeometries = ( column ) => {
1488+ const defaultSel = { geometryColumnKey : column ?. key , rowIndices : [ ] , columnIndices : new Set ( ) , rangeStartIdx : null , rangeEndIdx : null , cellIdx : null } ;
14651489 let selRowsData = rows ;
1466- if ( selectedRows . size != 0 ) {
1467- selRowsData = rows . filter ( ( r ) => selectedRows . has ( rowKeyGetter ( r ) ) ) ;
1490+
1491+ if ( selectedRows . size > 0 ) {
1492+ // Specific rows selected in the grid — plot only those rows
1493+ const rowIndices = [ ] ;
1494+ rows . forEach ( ( r , i ) => {
1495+ if ( selectedRows . has ( rowKeyGetter ( r ) ) ) {
1496+ rowIndices . push ( i ) ;
1497+ }
1498+ } ) ;
1499+ selRowsData = rowIndices . map ( i => rows [ i ] ) ;
1500+ lastGvSelectionRef . current = { ...defaultSel , type : 'rows' , rowIndices } ;
14681501 } else if ( selectedColumns . size > 0 ) {
1469- let selectedCols = _ . filter ( columns , ( _c , i ) => selectedColumns . has ( i + 1 ) ) ;
1470- selRowsData = _ . map ( rows , ( r ) => _ . pick ( r , _ . map ( selectedCols , ( c ) => c . key ) ) ) ;
1502+ // Specific columns selected — plot all rows but only with selected column data
1503+ let selectedCols = _ . filter ( columns , ( _c , i ) => selectedColumns . has ( i + 1 ) ) ;
1504+ selRowsData = _ . map ( rows , ( r ) => _ . pick ( r , _ . map ( selectedCols , ( c ) => c . key ) ) ) ;
1505+ lastGvSelectionRef . current = { ...defaultSel , type : 'columns' , columnIndices : new Set ( selectedColumns ) } ;
14711506 } else if ( selectedRange . current ) {
1507+ // Cell range selected — plot the rows within the range
14721508 let [ , , startRowIdx , endRowIdx ] = getRangeIndexes ( ) ;
1473- selRowsData = rows . slice ( startRowIdx , endRowIdx + 1 ) ;
1509+ selRowsData = rows . slice ( startRowIdx , endRowIdx + 1 ) ;
1510+ lastGvSelectionRef . current = { ...defaultSel , type : 'range' , rangeStartIdx : startRowIdx , rangeEndIdx : endRowIdx } ;
14741511 } else if ( selectedCell . current ?. [ 0 ] ) {
1512+ // Single cell selected — plot only that row
1513+ const cellIdx = rows . indexOf ( selectedCell . current [ 0 ] ) ;
14751514 selRowsData = [ selectedCell . current [ 0 ] ] ;
1515+ lastGvSelectionRef . current = { ...defaultSel , type : 'cell' , cellIdx : cellIdx >= 0 ? cellIdx : null } ;
1516+ } else {
1517+ // No selection — plot all rows
1518+ lastGvSelectionRef . current = { ...defaultSel , type : 'all' } ;
14761519 }
1477- layoutDocker . openTab ( {
1478- id : PANELS . GEOMETRY ,
1479- title :gettext ( 'Geometry Viewer' ) ,
1480- content : < GeometryViewer rows = { selRowsData } columns = { columns } column = { column } /> ,
1481- closable : true ,
1482- } , PANELS . MESSAGES , 'after-tab' , true ) ;
1520+
1521+ openGeometryViewerTab ( column , selRowsData ) ;
14831522 } ;
14841523 eventBus . registerListener ( QUERY_TOOL_EVENTS . TRIGGER_RENDER_GEOMETRIES , renderGeometries ) ;
14851524 return ( ) => eventBus . deregisterListener ( QUERY_TOOL_EVENTS . TRIGGER_RENDER_GEOMETRIES , renderGeometries ) ;
1486- } , [ rows , columns , selectedRows . size , selectedColumns . size ] ) ;
1525+ } , [ openGeometryViewerTab , eventBus , rows , columns , selectedRows , selectedColumns ] ) ;
1526+
1527+ // Auto-update Geometry Viewer when rows/columns change
1528+ useEffect ( ( ) => {
1529+ if ( layoutDocker . isTabOpen ( PANELS . GEOMETRY ) ) {
1530+ const lastGeomKey = lastGvSelectionRef . current . geometryColumnKey ;
1531+ const matchedGeomCol = lastGeomKey
1532+ ? columns . find ( c => c . key === lastGeomKey && ( c . cell === 'geometry' || c . cell === 'geography' ) )
1533+ : null ;
1534+
1535+ if ( matchedGeomCol ) {
1536+ // Previously plotted geometry column still exists → re-apply selection and re-render
1537+ const lastSel = lastGvSelectionRef . current ;
1538+ let selRowsData = rows ;
1539+
1540+ // Re-apply row selection — plot only previously selected rows if indices are still valid
1541+ if ( lastSel . type === 'rows' && lastSel . rowIndices . length > 0 ) {
1542+ if ( lastSel . rowIndices . every ( idx => idx < rows . length ) ) {
1543+ selRowsData = lastSel . rowIndices . map ( idx => rows [ idx ] ) ;
1544+ }
1545+ // Re-apply column selection — filter each row to only the previously selected columns
1546+ } else if ( lastSel . type === 'columns' && lastSel . columnIndices . size > 0 ) {
1547+ let selectedCols = _ . filter ( columns , ( _c , i ) => lastSel . columnIndices . has ( i + 1 ) ) ;
1548+ if ( selectedCols . length > 0 ) {
1549+ selRowsData = _ . map ( rows , ( r ) => _ . pick ( r , _ . map ( selectedCols , ( c ) => c . key ) ) ) ;
1550+ }
1551+ // Re-apply range selection — plot the previously selected row range if bounds are still valid
1552+ } else if ( lastSel . type === 'range' && lastSel . rangeStartIdx != null ) {
1553+ if ( lastSel . rangeStartIdx < rows . length && lastSel . rangeEndIdx < rows . length ) {
1554+ selRowsData = rows . slice ( lastSel . rangeStartIdx , lastSel . rangeEndIdx + 1 ) ;
1555+ }
1556+ // Re-apply single cell selection — plot the row of the previously selected cell if still valid
1557+ } else if ( lastSel . type === 'cell' && lastSel . cellIdx != null ) {
1558+ if ( lastSel . cellIdx < rows . length ) {
1559+ selRowsData = [ rows [ lastSel . cellIdx ] ] ;
1560+ }
1561+ }
1562+ // If any validation fails above, selRowsData remains as all rows (default)
1563+ openGeometryViewerTab ( matchedGeomCol , selRowsData ) ;
1564+ } else {
1565+ // Previously plotted geometry column not found - clear GV
1566+ openGeometryViewerTab ( null , [ ] ) ;
1567+ }
1568+ }
1569+ } , [ rows , columns , layoutDocker ] ) ;
14871570
14881571 const triggerResetScroll = ( ) => {
14891572 // Reset the scroll position to previously saved location.
0 commit comments