@@ -28,14 +28,25 @@ function get(reqtype, req, res, next) {
2828 let xyz = { } ;
2929 let _source = null ; // Works just like ES _source
3030 let noDuplicates = false ;
31+ let get_group_id = null ;
32+ let get_id = null ;
33+ let filters = null ;
34+ let spatialFilter = null ; // Not implemented
3135
3236 if ( reqtype === "post" ) {
3337 layer = req . body . layer ;
3438 type = req . body . type || type ;
3539 if ( req . body . _source && Array . isArray ( req . body . _source ) )
3640 _source = req . body . _source ;
41+
3742 if ( req . body . noDuplicates === true || req . body . noDuplicates === "true" )
3843 noDuplicates = true ;
44+
45+ if ( req . body . group_id != null ) get_group_id = req . body . group_id ;
46+ if ( req . body . id != null ) get_id = req . body . id ;
47+ if ( req . body . filters != null ) filters = req . body . filters ;
48+ if ( req . body . spatialFilter != null ) spatialFilter = req . body . spatialFilter ;
49+
3950 if ( type === "mvt" ) {
4051 xyz = {
4152 x : parseInt ( req . body . x ) ,
@@ -53,6 +64,31 @@ function get(reqtype, req, res, next) {
5364
5465 if ( req . query . noDuplicates === true || req . query . noDuplicates === "true" )
5566 noDuplicates = true ;
67+
68+ if ( req . query . group_id != null ) get_group_id = req . query . group_id ;
69+ if ( req . query . id != null ) get_id = req . query . id ;
70+ if ( req . query . filters != null ) {
71+ const filterSplit = req . query . filters . split ( "," ) ;
72+ filters = [ ] ;
73+ filterSplit . forEach ( ( f ) => {
74+ const fSplit = f . split ( "+" ) ;
75+ filters . push ( {
76+ key : fSplit [ 0 ] ,
77+ op : fSplit [ 1 ] ,
78+ type : fSplit [ 2 ] ,
79+ value : fSplit [ 3 ] ,
80+ } ) ;
81+ } ) ;
82+ }
83+ if ( req . query . spatialFilter != null ) {
84+ const spatialFilterSplit = req . query . spatialFilter . split ( "," ) ;
85+ spatialFilter = {
86+ lat : spatialFilterSplit [ 0 ] ,
87+ lng : spatialFilterSplit [ 1 ] ,
88+ radius : spatialFilterSplit [ 2 ] ,
89+ } ;
90+ }
91+
5692 if ( type === "mvt" ) {
5793 xyz = {
5894 x : parseInt ( req . query . x ) ,
@@ -61,6 +97,10 @@ function get(reqtype, req, res, next) {
6197 } ;
6298 }
6399 }
100+
101+ console . log ( spatialFilter ) ;
102+ console . log ( filters ) ;
103+
64104 //First Find the table name
65105 Geodatasets . findOne ( { where : { name : layer } } )
66106 . then ( ( result ) => {
@@ -88,7 +128,13 @@ function get(reqtype, req, res, next) {
88128 else distinct = ` DISTINCT ON (geom)` ;
89129 }
90130
91- let q = `SELECT${ distinct } ${ properties } , ST_AsGeoJSON(geom), id, group_id, feature_id FROM ${ Utils . forceAlphaNumUnder (
131+ let cols = [ "id" ] ;
132+ if ( result . dataValues . group_id_field != null ) cols . push ( "group_id" ) ;
133+ if ( result . dataValues . feature_id_field != null )
134+ cols . push ( "feature_id" ) ;
135+ cols = cols . join ( ", " ) ;
136+
137+ let q = `SELECT${ distinct } ${ properties } , ST_AsGeoJSON(geom), ${ cols } FROM ${ Utils . forceAlphaNumUnder (
92138 table
93139 ) } `;
94140
@@ -147,7 +193,7 @@ function get(reqtype, req, res, next) {
147193 endProp = Utils . forceAlphaNumUnder ( req . query . endProp || endProp ) ;
148194 // prettier-ignore
149195 t += [
150- `(` ,
196+ `(( ` ,
151197 `${ startProp } IS NOT NULL AND ${ endProp } IS NOT NULL AND` ,
152198 ` ${ startProp } >= ${ start_time } ` ,
153199 ` AND ${ endProp } <= ${ end_time } ` ,
@@ -157,24 +203,86 @@ function get(reqtype, req, res, next) {
157203 `${ startProp } IS NULL AND ${ endProp } IS NOT NULL AND` ,
158204 ` ${ endProp } >= ${ start_time } ` ,
159205 ` AND ${ endProp } <= ${ end_time } ` ,
160- `)`
206+ `)) `
161207 ] . join ( '' )
162208 q += t ;
163209 }
164- q += `;` ;
210+
211+ if ( get_group_id != null ) {
212+ q += `${
213+ q . indexOf ( " WHERE " ) == - 1 ? " WHERE " : " AND "
214+ } group_id = :get_group_id`;
215+ } else if ( get_id != null ) {
216+ q += `${
217+ q . indexOf ( " WHERE " ) == - 1 ? " WHERE " : " AND "
218+ } id = :get_id`;
219+ }
165220
166221 const replacements = {
167222 startProp : startProp ,
168223 start_time : start_time ,
169224 endProp : endProp ,
170225 end_time : end_time ,
226+ get_group_id : get_group_id ,
227+ get_id : get_id ,
171228 } ;
229+
172230 if ( Array . isArray ( _source ) ) {
173231 _source . forEach ( ( v , i ) => {
174232 replacements [ `prop_${ i } ` ] = v ;
175233 } ) ;
176234 }
177235
236+ // Filters
237+ if ( filters != null && filters . length > 0 ) {
238+ let filterSQL = [ ] ;
239+ filters . forEach ( ( f , i ) => {
240+ replacements [ `filter_key_${ i } ` ] = f . key ;
241+ replacements [ `filter_value_${ i } ` ] = f . value ;
242+ let op = "=" ;
243+ switch ( f . op ) {
244+ case ">" :
245+ op = ">" ;
246+ break ;
247+ case "<" :
248+ op = "<" ;
249+ break ;
250+ case "in" :
251+ op = "IN" ;
252+ break ;
253+ case "=" :
254+ default :
255+ break ;
256+ }
257+ let value = "" ;
258+ if ( op === "IN" ) {
259+ const valueSplit = f . value . split ( "$" ) ;
260+ const values = [ ] ;
261+ valueSplit . forEach ( ( v ) => {
262+ replacements [ `filter_value_${ i } _${ v } ` ] = v ;
263+ values . push ( `:filter_value_${ i } _${ v } ` ) ;
264+ } ) ;
265+ value = `(${ values . join ( "," ) } )` ;
266+ } else {
267+ replacements [ `filter_value_${ i } ` ] = f . value ;
268+ value = `:filter_value_${ i } ` ;
269+ }
270+ if ( f . type === "number" ) {
271+ filterSQL . push (
272+ `(properties->>:filter_key_${ i } )::FLOAT ${ op } ${ value } `
273+ ) ;
274+ } else {
275+ filterSQL . push ( `properties->>:filter_key_${ i } ${ op } ${ value } ` ) ;
276+ }
277+ } ) ;
278+ q += `${
279+ q . indexOf ( " WHERE " ) == - 1 ? " WHERE " : " AND "
280+ } ${ filterSQL . join ( ` AND ` ) } `;
281+ }
282+
283+ q += `;` ;
284+
285+ console . log ( q ) ;
178286 sequelize
179287 . query ( q , {
180288 replacements : replacements ,
@@ -200,6 +308,10 @@ function get(reqtype, req, res, next) {
200308 feature . geometry = JSON . parse ( results [ i ] . st_asgeojson ) ;
201309 geojson . features . push ( feature ) ;
202310 }
311+ if ( get_id != null )
312+ geojson . feature_id_field = result . dataValues . feature_id_field ;
313+ if ( get_group_id != null )
314+ geojson . group_id_field = result . dataValues . group_id_field ;
203315
204316 res . setHeader ( "Access-Control-Allow-Origin" , "*" ) ;
205317
@@ -211,6 +323,7 @@ function get(reqtype, req, res, next) {
211323 } else {
212324 res . send ( geojson ) ;
213325 }
326+
214327 return null ;
215328 } )
216329 . catch ( ( err ) => {
@@ -338,10 +451,168 @@ function get(reqtype, req, res, next) {
338451 } )
339452 . catch ( ( err ) => {
340453 logger ( "error" , "Failure finding geodataset." , req . originalUrl , req , err ) ;
341- res . send ( { status : "failure" , message : "d " } ) ;
454+ res . send ( { status : "failure" , message : "Failure finding geodataset. " } ) ;
342455 } ) ;
343456}
344457
458+ /*
459+ req.query.limit
460+ req.query.minx
461+ req.query.miny
462+ req.query.maxx
463+ req.query.maxy
464+ req.query.starttime
465+ req.query.endtime
466+ */
467+ router . get ( "/aggregations" , function ( req , res , next ) {
468+ //First Find the table name
469+ Geodatasets . findOne ( { where : { name : req . query . layer } } )
470+ . then ( ( result ) => {
471+ if ( result ) {
472+ let table = result . dataValues . table ;
473+ let q = `SELECT properties FROM ${ Utils . forceAlphaNumUnder ( table ) } ` ;
474+
475+ let hasBounds = false ;
476+ let minx = req . query ?. minx ;
477+ let miny = req . query ?. miny ;
478+ let maxx = req . query ?. maxx ;
479+ let maxy = req . query ?. maxy ;
480+ if ( minx != null && miny != null && maxx != null && maxy != null ) {
481+ // ST_MakeEnvelope is (xmin, ymin, xmax, ymax, srid)
482+ q += ` WHERE ST_Intersects(ST_MakeEnvelope(${ Utils . forceAlphaNumUnder (
483+ parseFloat ( minx )
484+ ) } , ${ Utils . forceAlphaNumUnder (
485+ parseFloat ( miny )
486+ ) } , ${ Utils . forceAlphaNumUnder (
487+ parseFloat ( maxx )
488+ ) } , ${ Utils . forceAlphaNumUnder ( parseFloat ( maxy ) ) } , 4326), geom)`;
489+ hasBounds = true ;
490+ }
491+ let startProp = "start_time" ;
492+ let start_time = "" ;
493+ let endProp = "end_time" ;
494+ let end_time = "" ;
495+ if ( req . query ?. endtime != null ) {
496+ const format = req . query ?. format || "YYYY-MM-DDTHH:MI:SSZ" ;
497+ let t = ` ` ;
498+ if ( ! hasBounds ) t += `WHERE ` ;
499+ else t += `AND ` ;
500+
501+ if (
502+ req . query ?. starttime == null ||
503+ req . query ?. starttime . indexOf ( `'` ) != - 1 ||
504+ req . query ?. endtime == null ||
505+ req . query ?. endtime . indexOf ( `'` ) != - 1 ||
506+ format . indexOf ( `'` ) != - 1
507+ ) {
508+ res . send ( {
509+ status : "failure" ,
510+ message : "Missing inner or malformed time parameters." ,
511+ } ) ;
512+ return ;
513+ }
514+
515+ start_time = new Date (
516+ req . query . starttime || "1970-01-01T00:00:00Z"
517+ ) . getTime ( ) ;
518+ end_time = new Date ( req . query . endtime ) . getTime ( ) ;
519+
520+ startProp = Utils . forceAlphaNumUnder (
521+ req . query . startProp || startProp
522+ ) ;
523+ endProp = Utils . forceAlphaNumUnder ( req . query . endProp || endProp ) ;
524+ // prettier-ignore
525+ t += [
526+ `(` ,
527+ `${ startProp } IS NOT NULL AND ${ endProp } IS NOT NULL AND` ,
528+ ` ${ startProp } >= ${ start_time } ` ,
529+ ` AND ${ endProp } <= ${ end_time } ` ,
530+ `)` ,
531+ ` OR ` ,
532+ `(` ,
533+ `${ startProp } IS NULL AND ${ endProp } IS NOT NULL AND` ,
534+ ` ${ endProp } >= ${ start_time } ` ,
535+ ` AND ${ endProp } <= ${ end_time } ` ,
536+ `)`
537+ ] . join ( '' )
538+ q += t ;
539+ }
540+
541+ q += ` ORDER BY id DESC LIMIT :limit;` ;
542+
543+ sequelize
544+ . query ( q , {
545+ replacements : {
546+ limit : req . query . limit != null ? parseInt ( req . query . limit ) : 100 ,
547+ startProp : startProp ,
548+ start_time : start_time ,
549+ endProp : endProp ,
550+ end_time : end_time ,
551+ } ,
552+ } )
553+ . then ( ( [ results ] ) => {
554+ let aggs = { } ;
555+ results . forEach ( ( feature ) => {
556+ const flatProps = feature . properties ;
557+ for ( let p in flatProps ) {
558+ let value = flatProps [ p ] ;
559+ let type = null ;
560+
561+ if ( ! isNaN ( value ) && ! isNaN ( parseFloat ( value ) ) ) type = "number" ;
562+ else if ( typeof value === "string" ) type = "string" ;
563+ else if ( typeof value === "number" ) type = "number" ;
564+ else if ( typeof value === "boolean" ) type = "boolean" ;
565+
566+ if ( type != null ) {
567+ // First type will be from index 0
568+ aggs [ p ] = aggs [ p ] || { type : type , aggs : { } } ;
569+ // Because of that, strings can usurp numbers (ex. ["1", "2", "Melon", "Pastry"])
570+ if ( aggs [ p ] . type === "number" && type === "string" )
571+ aggs [ p ] . type = type ;
572+ aggs [ p ] . aggs [ flatProps [ p ] ] = aggs [ p ] . aggs [ flatProps [ p ] ] || 0 ;
573+ aggs [ p ] . aggs [ flatProps [ p ] ] ++ ;
574+ }
575+ }
576+ } ) ;
577+
578+ // sort
579+ Object . keys ( aggs ) . forEach ( ( agg ) => {
580+ const sortedAggs = { } ;
581+ Object . keys ( aggs [ agg ] . aggs )
582+ . sort ( )
583+ . reverse ( )
584+ . forEach ( ( agg2 ) => {
585+ sortedAggs [ agg2 ] = aggs [ agg ] . aggs [ agg2 ] ;
586+ } ) ;
587+ aggs [ agg ] . aggs = sortedAggs ;
588+ } ) ;
589+
590+ res . send ( { status : "success" , aggregations : aggs } ) ;
591+ } )
592+ . catch ( ( err ) => {
593+ logger (
594+ "error" ,
595+ "Failure querying geodataset aggregations." ,
596+ req . originalUrl ,
597+ req ,
598+ err
599+ ) ;
600+ res . send ( {
601+ status : "failure" ,
602+ message : "Failure querying geodataset aggregations." ,
603+ } ) ;
604+ } ) ;
605+ } else {
606+ res . send ( { status : "failure" , message : "Not Found" } ) ;
607+ }
608+ return null ;
609+ } )
610+ . catch ( ( err ) => {
611+ logger ( "error" , "Failure finding geodataset." , req . originalUrl , req , err ) ;
612+ res . send ( { status : "failure" , message : "Failure finding geodataset." } ) ;
613+ } ) ;
614+ } ) ;
615+
345616//Returns a list of entries in the geodatasets table
346617router . post ( "/entries" , function ( req , res , next ) {
347618 Geodatasets . findAll ( )
0 commit comments