1
+ import { Node , Output } from "rete"
2
+ import { NodeData , WorkerInputs , WorkerOutputs } from "rete/types/core/data"
3
+ import { BaseComponent } from "./base_component"
4
+ import { numericDataSocket } from "../socket_types"
5
+ import { currentBbox , currentExtent } from "../bounding_box"
6
+ import GeoJSON from "ol/format/GeoJSON"
7
+ import { createXYZ } from "ol/tilegrid"
8
+ import { NumericTileGrid } from "../tile_grid"
9
+ import { SelectControl } from "../controls/select"
10
+
11
+ const censusOptions = [
12
+ {
13
+ "code" : "output_modified_HIGHEST_QUAL_0" ,
14
+ "name" : "No Qualifications" ,
15
+ "id" : 0
16
+ } ,
17
+ {
18
+ "code" : "output_modified_HIGHEST_QUAL_1" ,
19
+ "name" : "Level 1 and entry level" ,
20
+ "id" : 1
21
+ } ,
22
+ {
23
+ "code" : "output_modified_HIGHEST_QUAL_2" ,
24
+ "name" : "Level 2" ,
25
+ "id" : 2
26
+ } ,
27
+ {
28
+ "code" : "output_modified_HIGHEST_QUAL_3" ,
29
+ "name" : "Apprenticeship" ,
30
+ "id" : 3
31
+ } ,
32
+ {
33
+ "code" : "output_modified_HIGHEST_QUAL_4" ,
34
+ "name" : "Level 3 (A Level)" ,
35
+ "id" : 4
36
+ } ,
37
+ {
38
+ "code" : "output_modified_HIGHEST_QUAL_5" ,
39
+ "name" : "Level 4 (BSc/BA or higher)" ,
40
+ "id" : 5
41
+ } ,
42
+ {
43
+ "code" : "output_modified_HIGHEST_QUAL_6" ,
44
+ "name" : "Other" ,
45
+ "id" : 6
46
+ } ,
47
+ {
48
+ "code" : "output_modified_HIGHEST_QUAL_-8" ,
49
+ "name" : "Does not apply" ,
50
+ "id" : 7
51
+ }
52
+ ]
53
+
54
+ async function fetchCensusShapefilesFromExtent ( bbox : string ) {
55
+
56
+ const response = await fetch (
57
+ "https://landscapes.wearepal.ai/geoserver/wfs?" +
58
+ new URLSearchParams (
59
+ {
60
+ outputFormat : 'application/json' ,
61
+ request : 'GetFeature' ,
62
+ typeName : 'census:oa_2021_ew_bgc_v3_1294693978171420992__oa_2021_ew_bgc_v2' ,
63
+ srsName : 'EPSG:3857' ,
64
+ bbox
65
+ }
66
+ )
67
+ )
68
+ if ( ! response . ok ) throw new Error ( )
69
+
70
+ return await response . json ( )
71
+ }
72
+
73
+ export class CensusComponent extends BaseComponent {
74
+ DataCache : Map < any , any > // CACHING WIP
75
+
76
+ constructor ( ) {
77
+ super ( "UK Census - Highest Education" )
78
+ this . category = "Inputs"
79
+ }
80
+
81
+ async builder ( node : Node ) {
82
+ node . addOutput ( new Output ( 'out' , 'Census Data' , numericDataSocket ) )
83
+
84
+ node . meta . toolTip = "Experimental Feature - Census data for highest education per Output Area. Returns percentage value."
85
+
86
+ node . addControl (
87
+ new SelectControl (
88
+ this . editor ,
89
+ 'censusOptionId' ,
90
+ ( ) => censusOptions ,
91
+ ( ) => [ ] ,
92
+ 'Education Level'
93
+ )
94
+ )
95
+ }
96
+
97
+
98
+ async worker ( node : NodeData , inputs : WorkerInputs , outputs : WorkerOutputs , ...args : unknown [ ] ) {
99
+
100
+
101
+ const editorNode = this . editor ?. nodes . find ( n => n . id === node . id )
102
+ if ( editorNode === undefined ) { return }
103
+
104
+ const zoom = 20
105
+
106
+ const shapeFiles = await fetchCensusShapefilesFromExtent ( currentBbox )
107
+ const features = new GeoJSON ( ) . readFeatures ( shapeFiles )
108
+
109
+ const tileGrid = createXYZ ( )
110
+ const outputTileRange = tileGrid . getTileRangeForExtentAndZ ( currentExtent , zoom )
111
+
112
+ const result = editorNode . meta . output = outputs [ 'out' ] = new NumericTileGrid (
113
+ zoom ,
114
+ outputTileRange . minX ,
115
+ outputTileRange . minY ,
116
+ outputTileRange . getWidth ( ) ,
117
+ outputTileRange . getHeight ( )
118
+ )
119
+
120
+ let index = node . data . censusOptionId as number
121
+ if ( index === undefined ) { index = 0 }
122
+
123
+ for ( let feature of features ) {
124
+
125
+ let n =
126
+ + feature . get ( "output_modified_HIGHEST_QUAL_0" ) +
127
+ + feature . get ( "output_modified_HIGHEST_QUAL_1" ) +
128
+ + feature . get ( "output_modified_HIGHEST_QUAL_2" ) +
129
+ + feature . get ( "output_modified_HIGHEST_QUAL_3" ) +
130
+ + feature . get ( "output_modified_HIGHEST_QUAL_4" ) +
131
+ + feature . get ( "output_modified_HIGHEST_QUAL_5" ) +
132
+ + feature . get ( "output_modified_HIGHEST_QUAL_6" ) +
133
+ + feature . get ( "output_modified_HIGHEST_QUAL_-8" )
134
+
135
+ const geom = feature . getGeometry ( )
136
+ if ( geom === undefined ) { continue }
137
+
138
+ const featureTileRange = tileGrid . getTileRangeForExtentAndZ (
139
+ geom . getExtent ( ) ,
140
+ zoom
141
+ )
142
+ for (
143
+ let x = Math . max ( featureTileRange . minX , outputTileRange . minX ) ;
144
+ x <= Math . min ( featureTileRange . maxX , outputTileRange . maxX ) ;
145
+ ++ x
146
+ ) {
147
+ for (
148
+ let y = Math . max ( featureTileRange . minY , outputTileRange . minY ) ;
149
+ y <= Math . min ( featureTileRange . maxY , outputTileRange . maxY ) ;
150
+ ++ y
151
+ ) {
152
+ const center = tileGrid . getTileCoordCenter ( [ zoom , x , y ] )
153
+ if ( geom . intersectsCoordinate ( center ) ) {
154
+ result . set ( x , y , feature . get ( censusOptions [ index ] . code ) / n * 100 )
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+
161
+ editorNode . update ( )
162
+
163
+ }
164
+
165
+ }
0 commit comments