@@ -30,9 +30,13 @@ import {
3030 toggleBulkSelector ,
3131 buildingHasPositiveSelection ,
3232 WallOrientation ,
33+ resolveZoneGroupForMode ,
34+ zoneGroupType ,
35+ zoneTypeShortLabel ,
3336} from '../types/zones' ;
3437import type { PickResult } from '../three/types' ;
3538import { useSurfaceZoneSelection } from '../hooks/useSurfaceZoneSelection' ;
39+ import { getSurfaceZones , shouldEnableZoningSurfaceSelection } from '../three/surfaceSelection' ;
3640
3741interface ZoningTabProps {
3842 hasModel : boolean ;
@@ -113,24 +117,6 @@ const ZoningTab: React.FC<ZoningTabProps> = ({ hasModel, figureJson, zones, onZo
113117
114118 const interaction : MapInteraction = shape === 'rect' ? 'draw_rect_3pt' : 'draw_polygon' ;
115119
116- // Render existing zones as paint_zone overlays.
117- const pendingEdits : PendingEdit [ ] = useMemo ( ( ) => {
118- if ( ! geo ) return [ ] ;
119- return zones . flatMap ( ( z ) => {
120- if ( z . type !== 'horizontal' ) return [ ] ;
121- return [ {
122- kind : 'paint_zone' as const ,
123- cells : polygonToCells ( z . ring_lonlat , geo . grid_geom ) ,
124- ring : z . ring_lonlat ,
125- selected :
126- z . id === selectedId ||
127- ( activeGroupId != null && ( z . groupId ?? z . id ) === activeGroupId ) ,
128- color : z . color ,
129- target : 'evaluation' as const ,
130- } ] ;
131- } ) ;
132- } , [ geo , zones , selectedId , activeGroupId ] ) ;
133-
134120 /**
135121 * Group sibling zones (same `groupId`) together so the zone list shows
136122 * one row per logical zone. The first member's metadata acts as the
@@ -159,16 +145,37 @@ const ZoningTab: React.FC<ZoningTabProps> = ({ hasModel, figureJson, zones, onZo
159145 return [ ...committed , ...drafts ] ;
160146 } , [ committedGroups , draftGroups ] ) ;
161147
162- // Resolve which group new polygons attach to: explicit `activeGroupId`,
163- // else the most recent group, else "start a new one".
164- const effectiveActiveGroupId =
165- activeGroupId ?? ( groups . length > 0 ? groups [ groups . length - 1 ] . id : null ) ;
148+ const effectiveActiveGroupId = useMemo (
149+ ( ) => resolveZoneGroupForMode ( { zones, candidates : groups , activeGroupId, zoneType } ) ,
150+ [ zones , groups , activeGroupId , zoneType ] ,
151+ ) ;
152+
153+ // Render existing zones as paint_zone overlays.
154+ const pendingEdits : PendingEdit [ ] = useMemo ( ( ) => {
155+ if ( ! geo ) return [ ] ;
156+ return zones . flatMap ( ( z ) => {
157+ if ( z . type !== 'horizontal' ) return [ ] ;
158+ return [ {
159+ kind : 'paint_zone' as const ,
160+ cells : polygonToCells ( z . ring_lonlat , geo . grid_geom ) ,
161+ ring : z . ring_lonlat ,
162+ selected : zoneType === 'horizontal' && (
163+ z . id === selectedId ||
164+ ( effectiveActiveGroupId != null && ( z . groupId ?? z . id ) === effectiveActiveGroupId )
165+ ) ,
166+ color : z . color ,
167+ target : 'evaluation' as const ,
168+ } ] ;
169+ } ) ;
170+ } , [ geo , zones , selectedId , effectiveActiveGroupId , zoneType ] ) ;
171+ const surfaceZoneCount = useMemo ( ( ) => getSurfaceZones ( zones ) . length , [ zones ] ) ;
172+ const surfaceSelectionEnabled = shouldEnableZoningSurfaceSelection ( { zoneType, surfaceZoneCount } ) ;
166173
167174 const { surfaceSelection } = useSurfaceZoneSelection ( {
168175 hasModel,
169176 geometryToken,
170177 zones,
171- enabled : zoneType === 'building_surface' ,
178+ enabled : surfaceSelectionEnabled ,
172179 displayMode : 'fill' ,
173180 activeGroupId : effectiveActiveGroupId ,
174181 requireSurfaceZones : false ,
@@ -447,11 +454,13 @@ const ZoningTab: React.FC<ZoningTabProps> = ({ hasModel, figureJson, zones, onZo
447454 ) }
448455 { groups . map ( ( g ) => {
449456 const isActive = ( effectiveActiveGroupId ?? null ) === g . id ;
457+ const groupType = g . draft ? 'horizontal' : zoneGroupType ( zones , g . id ) ;
450458 return (
451459 < div
452460 key = { g . id }
453461 className = { `zone-row${ isActive ? ' selected' : '' } ` }
454462 onClick = { ( ) => {
463+ if ( groupType ) setZoneType ( groupType ) ;
455464 setActiveGroupId ( g . id ) ;
456465 setSelectedId ( g . members [ 0 ] ?. id ?? null ) ;
457466 setRefiningBuildingId ( null ) ;
@@ -495,6 +504,12 @@ const ZoningTab: React.FC<ZoningTabProps> = ({ hasModel, figureJson, zones, onZo
495504 ) }
496505 </ span >
497506 ) }
507+ < span
508+ className = { `zone-type-badge ${ groupType === 'building_surface' ? 'building' : 'two-d' } ` }
509+ title = { groupType === 'building_surface' ? 'Building surface zone' : '2D area zone' }
510+ >
511+ { groupType ? zoneTypeShortLabel ( groupType ) : 'Mixed' }
512+ </ span >
498513 < button
499514 title = "Rename"
500515 onClick = { ( e ) => {
@@ -654,7 +669,7 @@ const ZoningTab: React.FC<ZoningTabProps> = ({ hasModel, figureJson, zones, onZo
654669 showZones
655670 colorOverride = { colorOverride }
656671 onPick = { zoneType === 'building_surface' ? handleSurfacePick : undefined }
657- surfaceSelection = { zoneType === 'building_surface' ? surfaceSelection : null }
672+ surfaceSelection = { surfaceSelection }
658673 />
659674 ) : (
660675 < div className = "alert alert-info" style = { { marginTop : 0 } } >
0 commit comments