1
+ import { LayerType , IJGISLayer } from '@jupytergis/schema' ;
1
2
import React , { useEffect , useState } from 'react' ;
2
3
3
4
import { useGetProperties } from '@/src/dialogs/symbology/hooks/useGetProperties' ;
4
5
import { ISymbologyDialogProps } from '@/src/dialogs/symbology/symbologyDialog' ;
5
6
import {
6
7
getColorCodeFeatureAttributes ,
7
8
getNumericFeatureAttributes ,
9
+ objectEntries ,
8
10
} from '@/src/tools' ;
9
- import { SymbologyTab } from '@/src/types' ;
11
+ import { SymbologyTab , VectorRenderType } from '@/src/types' ;
10
12
import Canonical from './types/Canonical' ;
11
13
import Categorized from './types/Categorized' ;
12
14
import Graduated from './types/Graduated' ;
13
15
import Heatmap from './types/Heatmap' ;
14
16
import SimpleSymbol from './types/SimpleSymbol' ;
15
17
18
+ interface IRenderTypeProps {
19
+ component : any ;
20
+ attributeChecker ?: ( ...args : any [ ] ) => any ;
21
+ supportedLayerTypes : string [ ] ;
22
+ isTabbed : boolean ;
23
+ }
24
+ type RenderTypeOptions = {
25
+ [ key in VectorRenderType ] : IRenderTypeProps ;
26
+ } ;
27
+
28
+ interface ISelectableRenderTypeProps extends IRenderTypeProps {
29
+ selectableAttributesAndValues ?: Record < string , Set < any > > ;
30
+ layerTypeSupported : boolean ;
31
+ }
32
+ type SelectableRenderTypes = {
33
+ [ key in VectorRenderType ] : ISelectableRenderTypeProps ;
34
+ } ;
35
+
36
+ const RENDER_TYPE_OPTIONS : RenderTypeOptions = {
37
+ 'Single Symbol' : {
38
+ component : SimpleSymbol ,
39
+ supportedLayerTypes : [ 'VectorLayer' , 'VectorTileLayer' , 'HeatmapLayer' ] ,
40
+ isTabbed : true ,
41
+ } ,
42
+ Canonical : {
43
+ component : Canonical ,
44
+ attributeChecker : getColorCodeFeatureAttributes ,
45
+ supportedLayerTypes : [ 'VectorLayer' , 'HeatmapLayer' ] ,
46
+ isTabbed : false ,
47
+ } ,
48
+ Graduated : {
49
+ component : Graduated ,
50
+ attributeChecker : getNumericFeatureAttributes ,
51
+ supportedLayerTypes : [ 'VectorLayer' , 'HeatmapLayer' ] ,
52
+ isTabbed : true ,
53
+ } ,
54
+ Categorized : {
55
+ component : Categorized ,
56
+ attributeChecker : getNumericFeatureAttributes ,
57
+ supportedLayerTypes : [ 'VectorLayer' , 'HeatmapLayer' ] ,
58
+ isTabbed : true ,
59
+ } ,
60
+ Heatmap : {
61
+ component : Heatmap ,
62
+ supportedLayerTypes : [ 'VectorLayer' , 'HeatmapLayer' ] ,
63
+ isTabbed : false ,
64
+ } ,
65
+ } as const ;
66
+
67
+ const getSelectableRenderTypes = (
68
+ featureProperties : Record < string , Set < any > > ,
69
+ layerType : LayerType ,
70
+ ) : SelectableRenderTypes => {
71
+ const entries = objectEntries ( RENDER_TYPE_OPTIONS ) . map (
72
+ ( [ renderType , renderTypeProps ] ) => [
73
+ renderType ,
74
+ {
75
+ ...renderTypeProps ,
76
+ ...( renderTypeProps . attributeChecker
77
+ ? {
78
+ selectableAttributesAndValues :
79
+ renderTypeProps . attributeChecker ( featureProperties ) ,
80
+ }
81
+ : { } ) ,
82
+ layerTypeSupported :
83
+ renderTypeProps . supportedLayerTypes . includes ( layerType ) ,
84
+ } ,
85
+ ] ,
86
+ ) ;
87
+ return Object . fromEntries ( entries ) ;
88
+ } ;
89
+
90
+ const useLayerRenderType = (
91
+ layer : IJGISLayer ,
92
+ setSelectedRenderType : React . Dispatch <
93
+ React . SetStateAction < VectorRenderType | undefined >
94
+ > ,
95
+ ) =>
96
+ useEffect ( ( ) => {
97
+ let renderType = layer . parameters ?. symbologyState ?. renderType ;
98
+ if ( ! renderType ) {
99
+ renderType = layer . type === 'HeatmapLayer' ? 'Heatmap' : 'Single Symbol' ;
100
+ }
101
+ setSelectedRenderType ( renderType ) ;
102
+ } , [ ] ) ;
103
+
16
104
const VectorRendering = ( {
17
105
model,
18
106
state,
19
107
okSignalPromise,
20
108
cancel,
21
109
layerId,
22
110
} : ISymbologyDialogProps ) => {
23
- const [ selectedRenderType , setSelectedRenderType ] = useState ( '' ) ;
24
- const [ componentToRender , setComponentToRender ] = useState < any > ( null ) ;
25
- const [ renderTypeOptions , setRenderTypeOptions ] = useState < string [ ] > ( [
26
- 'Single Symbol' ,
27
- ] ) ;
111
+ const [ selectedRenderType , setSelectedRenderType ] = useState <
112
+ VectorRenderType | undefined
113
+ > ( ) ;
28
114
const [ symbologyTab , setSymbologyTab ] = useState < SymbologyTab > ( 'color' ) ;
29
115
30
- let RenderComponent ;
31
-
32
116
if ( ! layerId ) {
33
117
return ;
34
118
}
@@ -37,110 +121,31 @@ const VectorRendering = ({
37
121
return ;
38
122
}
39
123
40
- const { featureProperties } = useGetProperties ( {
124
+ const { featureProperties, isLoading : featuresLoading } = useGetProperties ( {
41
125
layerId,
42
126
model : model ,
43
127
} ) ;
44
128
45
- useEffect ( ( ) => {
46
- let renderType = layer . parameters ?. symbologyState ?. renderType ;
47
- if ( ! renderType ) {
48
- renderType = layer . type === 'HeatmapLayer' ? 'Heatmap' : 'Single Symbol' ;
49
- }
50
- setSelectedRenderType ( renderType ) ;
129
+ useLayerRenderType ( layer , setSelectedRenderType ) ;
51
130
52
- const vectorLayerOptions = [ 'Single Symbol' , 'Heatmap' ] ;
53
-
54
- if (
55
- Object . keys ( getColorCodeFeatureAttributes ( featureProperties ) ) . length > 0
56
- ) {
57
- vectorLayerOptions . push ( 'Canonical' ) ;
58
- }
59
- if (
60
- Object . keys ( getNumericFeatureAttributes ( featureProperties ) ) . length > 0
61
- ) {
62
- vectorLayerOptions . push ( 'Graduated' , 'Categorized' ) ;
63
- }
131
+ if ( featuresLoading ) {
132
+ return < p > Loading...</ p > ;
133
+ }
64
134
65
- const options : Record < string , string [ ] > = {
66
- VectorLayer : vectorLayerOptions ,
67
- VectorTileLayer : [ 'Single Symbol' ] ,
68
- HeatmapLayer : [ 'Single Symbol' , 'Graduated' , 'Categorized' , 'Heatmap' ] ,
69
- } ;
70
- setRenderTypeOptions ( options [ layer . type ] ) ;
71
- } , [ featureProperties ] ) ;
135
+ if ( selectedRenderType === undefined ) {
136
+ // typeguard
137
+ return ;
138
+ }
72
139
73
- useEffect ( ( ) => {
74
- switch ( selectedRenderType ) {
75
- case 'Single Symbol' :
76
- RenderComponent = (
77
- < SimpleSymbol
78
- model = { model }
79
- state = { state }
80
- okSignalPromise = { okSignalPromise }
81
- cancel = { cancel }
82
- layerId = { layerId }
83
- symbologyTab = { symbologyTab }
84
- />
85
- ) ;
86
- break ;
87
- case 'Graduated' :
88
- RenderComponent = (
89
- < Graduated
90
- model = { model }
91
- state = { state }
92
- okSignalPromise = { okSignalPromise }
93
- cancel = { cancel }
94
- layerId = { layerId }
95
- symbologyTab = { symbologyTab }
96
- />
97
- ) ;
98
- break ;
99
- case 'Categorized' :
100
- RenderComponent = (
101
- < Categorized
102
- model = { model }
103
- state = { state }
104
- okSignalPromise = { okSignalPromise }
105
- cancel = { cancel }
106
- layerId = { layerId }
107
- symbologyTab = { symbologyTab }
108
- />
109
- ) ;
110
- break ;
111
- case 'Canonical' :
112
- RenderComponent = (
113
- < Canonical
114
- model = { model }
115
- state = { state }
116
- okSignalPromise = { okSignalPromise }
117
- cancel = { cancel }
118
- layerId = { layerId }
119
- />
120
- ) ;
121
- break ;
122
- case 'Heatmap' :
123
- RenderComponent = (
124
- < Heatmap
125
- model = { model }
126
- state = { state }
127
- okSignalPromise = { okSignalPromise }
128
- cancel = { cancel }
129
- layerId = { layerId }
130
- />
131
- ) ;
132
- break ;
133
- default :
134
- RenderComponent = < div > Select a render type</ div > ;
135
- }
136
- setComponentToRender ( RenderComponent ) ;
137
- } , [ selectedRenderType , symbologyTab ] ) ;
140
+ const selectableRenderTypes = getSelectableRenderTypes (
141
+ featureProperties ,
142
+ layer . type ,
143
+ ) ;
144
+ const selectedRenderTypeProps = selectableRenderTypes [ selectedRenderType ] ;
138
145
139
146
return (
140
147
< >
141
- { [ 'Single Symbol' , 'Graduated' , 'Categorized' ] . includes (
142
- selectedRenderType ,
143
- ) && (
148
+ { selectedRenderTypeProps . isTabbed && (
144
149
< div className = "jp-gis-symbology-tabs" >
145
150
{ ( [ 'color' , 'radius' ] as const ) . map ( tab => (
146
151
< button
@@ -155,27 +160,45 @@ const VectorRendering = ({
155
160
) }
156
161
< div className = "jp-gis-symbology-row" >
157
162
< label htmlFor = "render-type-select" > Render Type:</ label >
158
- < select
159
- name = "render-type-select"
160
- id = "render-type-select"
161
- value = { selectedRenderType }
162
- onChange = { event => {
163
- setSelectedRenderType ( event . target . value ) ;
164
- } }
165
- >
166
- { renderTypeOptions
167
- . filter (
168
- option => ! ( symbologyTab === 'radius' && option === 'Heatmap' ) ,
169
- )
170
- . map ( option => (
171
- < option key = { option } value = { option } >
172
- { option }
173
- </ option >
174
- ) ) }
175
- </ select >
163
+ < div className = "jp-select-wrapper" >
164
+ < select
165
+ name = "render-type-select"
166
+ id = "render-type-select"
167
+ className = "jp-mod-styled"
168
+ value = { selectedRenderType }
169
+ onChange = { event => {
170
+ setSelectedRenderType ( event . target . value as VectorRenderType ) ;
171
+ } }
172
+ >
173
+ { objectEntries ( selectableRenderTypes )
174
+ . filter (
175
+ ( [ renderType , renderTypeProps ] ) =>
176
+ renderTypeProps . layerTypeSupported &&
177
+ ! ( renderType === 'Heatmap' && symbologyTab === 'radius' ) ,
178
+ )
179
+ . map ( ( [ renderType , _ ] ) => (
180
+ < option key = { renderType } value = { renderType } >
181
+ { renderType }
182
+ </ option >
183
+ ) ) }
184
+ </ select >
185
+ </ div >
176
186
</ div >
177
187
178
- { componentToRender }
188
+ < selectedRenderTypeProps . component
189
+ model = { model }
190
+ state = { state }
191
+ okSignalPromise = { okSignalPromise }
192
+ cancel = { cancel }
193
+ layerId = { layerId }
194
+ { ...( selectedRenderTypeProps . isTabbed ? { symbologyTab } : { } ) }
195
+ { ...( selectedRenderTypeProps . selectableAttributesAndValues
196
+ ? {
197
+ selectableAttributesAndValues :
198
+ selectedRenderTypeProps . selectableAttributesAndValues ,
199
+ }
200
+ : { } ) }
201
+ />
179
202
</ >
180
203
) ;
181
204
} ;
0 commit comments