11// @flow
22
33import { ObjectJs as ObjectUtils } from '@performant-software/shared-components' ;
4- import { feature , featureCollection } from '@turf/turf' ;
4+ import { Map as MapUtils } from '@performant-software/geospatial' ;
5+ import { feature , featureCollection , truncate } from '@turf/turf' ;
56import { history } from 'instantsearch.js/es/lib/routers' ;
67import TypesenseInstantsearchAdapter from 'typesense-instantsearch-adapter' ;
78import _ from 'underscore' ;
89import type { Event as EventType } from '../types/Event' ;
910import type { TypesenseSearchResult } from '../types/typesense/SearchResult' ;
1011
1112type Options = {
13+ geometries : {
14+ [ uuid : string ] : any
15+ } ,
1216 type ?: string
1317} ;
1418
@@ -163,6 +167,31 @@ const getFieldId = (attribute: string) => {
163167 return value ;
164168} ;
165169
170+ /**
171+ * Returns the geometry object for the passed place/path.
172+ *
173+ * @param place
174+ * @param path
175+ *
176+ * @returns {* }
177+ */
178+ const getGeometry = ( place , path ) => {
179+ return _ . get ( place , path ) ;
180+ } ;
181+
182+ /**
183+ * Returns the geometry URL for the passed place.
184+ *
185+ * @param place
186+ * @param hash
187+ *
188+ * @returns {* }
189+ */
190+ const getGeometryUrl = ( place , hash ) => {
191+ const object = hash [ place ?. uuid ] ;
192+ return object ?. url ;
193+ } ;
194+
166195/**
167196 * Takes a <relationship-uuid>.<field-uuid>_facet formatted attribute and returns the parsed relationship UUID.
168197 *
@@ -186,6 +215,8 @@ const getRelationshipId = (attribute: string) => {
186215 * @param geometry
187216 *
188217 * @returns {Feature<*, {ccode: [], record_id: *, names: *, name: *, id: *, title: *, type: *, uuid: *, items: [*]}> }
218+ *
219+ * @deprecated
189220 */
190221const toFeature = ( record : any , item : any , geometry : any ) => {
191222 const properties = {
@@ -197,7 +228,9 @@ const toFeature = (record: any, item: any, geometry: any) => {
197228 name : record . name ,
198229 names : record . names ?. map ( ( toponym : string ) => ( { toponym } ) ) ,
199230 type : record . type ,
200- items : [ item ]
231+ items : [ item ] ,
232+ url : record . url ,
233+ layerId : record . layerId
201234 } ;
202235
203236 const id = parseInt ( record . record_id , 10 ) ;
@@ -212,6 +245,8 @@ const toFeature = (record: any, item: any, geometry: any) => {
212245 * @param options
213246 *
214247 * @returns {FeatureCollection<Geometry, Properties> }
248+ *
249+ * @deprecated
215250 */
216251const toFeatureCollection = ( results : Array < any > , path : string , options : Options = { } ) => {
217252 const features = [ ] ;
@@ -265,11 +300,87 @@ const toFeatureCollection = (results: Array<any>, path: string, options: Options
265300 return featureCollection ( features ) ;
266301} ;
267302
303+ /**
304+ * Returns a set of GeoJSON features for the passed results.
305+ *
306+ * @param features
307+ * @param results
308+ * @param path
309+ * @param options
310+ *
311+ * @returns {* }
312+ */
313+ const getFeatures = ( features , results , path , options = { } ) => {
314+ const newFeatures = [ ...features ] ;
315+
316+ const objectPath = path . substring ( 0 , path . lastIndexOf ( ATTRIBUTE_DELIMITER ) ) ;
317+ const geometryPath = path . substring ( path . lastIndexOf ( ATTRIBUTE_DELIMITER ) + 1 , path . length ) ;
318+
319+ const placeIds = [ ] ;
320+ const recordIds = [ ] ;
321+
322+ _ . each ( results , ( result ) => {
323+ recordIds . push ( result . uuid ) ;
324+
325+ const places = _ . isEmpty ( objectPath ) ? [ result ] : ObjectUtils . getNestedValue ( result , objectPath ) ;
326+
327+ _ . each ( places , ( place ) => {
328+ placeIds . push ( place . uuid ) ;
329+
330+ let geometry ;
331+ let geometryUrl ;
332+ let layerId ;
333+
334+ if ( options . geometries ) {
335+ geometryUrl = getGeometryUrl ( place , options . geometries ) ;
336+ } else {
337+ geometry = getGeometry ( place , geometryPath ) ;
338+ }
339+
340+ const include = geometryUrl || ( geometry && ( ! options . type || geometry . type === options . type ) ) ;
341+
342+ if ( include ) {
343+ const record = _ . find ( newFeatures , ( f ) => f . properties ?. uuid === place . uuid ) ;
344+ const trimmedResult = trimResult ( result , objectPath ) ;
345+
346+ if ( record ) {
347+ const item = _ . find ( record . properties ?. items , ( item ) => item . uuid === trimmedResult . uuid ) ;
348+
349+ if ( ! item ) {
350+ record . properties ?. items . push ( trimmedResult ) ;
351+ }
352+ } else {
353+ newFeatures . push ( MapUtils . toFeature ( { ...place , layerId, url : geometryUrl } , trimmedResult , geometry ) ) ;
354+ }
355+ }
356+ } ) ;
357+ } ) ;
358+
359+ return _ . map ( newFeatures , ( feature ) => ( {
360+ ...feature ,
361+ properties : {
362+ ...feature . properties ,
363+ visible : placeIds . includes ( feature . properties . uuid ) ,
364+ items : _ . filter ( feature . properties . items , ( item ) => recordIds . includes ( item . uuid ) )
365+ }
366+ } ) ) ;
367+ } ;
368+
369+ /**
370+ * Trims the Typesense document to only include data needed for map visualizations.
371+ *
372+ * @param result
373+ *
374+ * @returns {* }
375+ */
376+ const trimResult = ( result ) => _ . pick ( result , 'id' , 'uuid' , 'record_id' , 'name' , 'names' ) ;
377+
268378export default {
269379 createCachedHits,
270380 createRouting,
271381 createTypesenseAdapter,
272382 getDate,
383+ getFeatures,
273384 getFieldId,
274385 getRelationshipId,
275386 toFeature,
0 commit comments