| 
									
										
										
										
											2021-01-03 03:09:52 +01:00
										 |  |  | import LayerConfig from "../../Customizations/JSON/LayerConfig"; | 
					
						
							|  |  |  | import FeatureSource from "./FeatureSource"; | 
					
						
							|  |  |  | import {UIEventSource} from "../UIEventSource"; | 
					
						
							|  |  |  | import {GeoOperations} from "../GeoOperations"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * The no overlap source takes a featureSource and applies a filter on it. | 
					
						
							|  |  |  |  * First, it'll figure out for each feature to which layer it belongs | 
					
						
							|  |  |  |  * Then, it'll check any feature of any 'lower' layer | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export default class NoOverlapSource { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     features: UIEventSource<{ feature: any, freshness: Date }[]> = new UIEventSource<{ feature: any, freshness: Date }[]>([]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-20 01:45:51 +01:00
										 |  |  |     constructor(layers: UIEventSource<{ | 
					
						
							| 
									
										
										
										
											2021-01-03 03:09:52 +01:00
										 |  |  |                     layerDef: LayerConfig | 
					
						
							| 
									
										
										
										
											2021-02-20 01:45:51 +01:00
										 |  |  |                 }[]>, | 
					
						
							| 
									
										
										
										
											2021-01-03 03:09:52 +01:00
										 |  |  |                 upstream: FeatureSource) { | 
					
						
							|  |  |  |         let noOverlapRemoval = true; | 
					
						
							| 
									
										
										
										
											2021-02-20 01:45:51 +01:00
										 |  |  |         for (const layer of layers.data) { | 
					
						
							| 
									
										
										
										
											2021-01-03 03:09:52 +01:00
										 |  |  |             if ((layer.layerDef.hideUnderlayingFeaturesMinPercentage ?? 0) !== 0) { | 
					
						
							|  |  |  |                 noOverlapRemoval = false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (noOverlapRemoval) { | 
					
						
							|  |  |  |             this.features = upstream.features; | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.features = upstream.features.map( | 
					
						
							|  |  |  |             features => { | 
					
						
							|  |  |  |                 if (features === undefined) { | 
					
						
							|  |  |  |                     return; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-02-20 01:45:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 const layerIds = [] | 
					
						
							|  |  |  |                 const layerDict = {}; | 
					
						
							|  |  |  |                 for (const layer of layers.data) { | 
					
						
							|  |  |  |                     layerDict[layer.layerDef.id] = layer; | 
					
						
							|  |  |  |                     layerIds.push(layer.layerDef.id); | 
					
						
							|  |  |  |                     if ((layer.layerDef.hideUnderlayingFeaturesMinPercentage ?? 0) !== 0) { | 
					
						
							|  |  |  |                         noOverlapRemoval = false; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-03 03:09:52 +01:00
										 |  |  |                 // There is overlap removal active
 | 
					
						
							|  |  |  |                 // We partition all the features with their respective layerIDs
 | 
					
						
							|  |  |  |                 const partitions = {}; | 
					
						
							|  |  |  |                 for (const layerId of layerIds) { | 
					
						
							|  |  |  |                     partitions[layerId] = [] | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 for (const feature of features) { | 
					
						
							| 
									
										
										
										
											2021-01-04 04:06:21 +01:00
										 |  |  |                     partitions[feature.feature._matching_layer_id].push(feature); | 
					
						
							| 
									
										
										
										
											2021-01-03 03:09:52 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // With this partitioning in hand, we run over every layer and remove every underlying feature if needed
 | 
					
						
							|  |  |  |                 for (let i = 0; i < layerIds.length; i++) { | 
					
						
							|  |  |  |                     let layerId = layerIds[i]; | 
					
						
							|  |  |  |                     const percentage = layerDict[layerId].layerDef.hideUnderlayingFeaturesMinPercentage ?? 0; | 
					
						
							|  |  |  |                     if (percentage === 0) { | 
					
						
							|  |  |  |                         // We don't have to remove underlying features!
 | 
					
						
							|  |  |  |                         continue; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     const guardPartition = partitions[layerId]; | 
					
						
							|  |  |  |                     for (let j = i + 1; j < layerIds.length; j++) { | 
					
						
							|  |  |  |                         let layerJd = layerIds[j]; | 
					
						
							|  |  |  |                         let partitionToShrink: { feature: any, freshness: Date }[] = partitions[layerJd]; | 
					
						
							|  |  |  |                         let newPartition = []; | 
					
						
							|  |  |  |                         for (const mightBeDeleted of partitionToShrink) { | 
					
						
							|  |  |  |                             const doesOverlap = GeoOperations.featureIsContainedInAny( | 
					
						
							|  |  |  |                                 mightBeDeleted.feature, | 
					
						
							|  |  |  |                                 guardPartition.map(f => f.feature), | 
					
						
							|  |  |  |                                 percentage | 
					
						
							|  |  |  |                             ); | 
					
						
							| 
									
										
										
										
											2021-02-20 01:45:51 +01:00
										 |  |  |                             if (!doesOverlap) { | 
					
						
							| 
									
										
										
										
											2021-01-03 03:09:52 +01:00
										 |  |  |                                 newPartition.push(mightBeDeleted); | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         partitions[layerJd] = newPartition; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // At last, we create the actual new features
 | 
					
						
							|  |  |  |                 let newFeatures: { feature: any, freshness: Date }[] = []; | 
					
						
							|  |  |  |                 for (const layerId of layerIds) { | 
					
						
							|  |  |  |                     newFeatures = newFeatures.concat(partitions[layerId]); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return newFeatures; | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |