github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/util/locations.ts (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 import _ from "lodash"; 12 13 import { LocalityTier, LocalityTree } from "src/redux/localities"; 14 import { ILocation, LocationTree } from "src/redux/locations"; 15 import * as vector from "src/util/vector"; 16 17 /* 18 * getLocation retrieves the location for a given locality tier from the 19 * LocationTree, or null if none is found. 20 */ 21 export function getLocation(locations: LocationTree, tier: LocalityTier) { 22 if (!locations[tier.key]) { 23 return null; 24 } 25 26 return locations[tier.key][tier.value]; 27 } 28 29 /* 30 * hasLocation is a predicate to determine if a given locality tier exists in 31 * the LocationTree. 32 */ 33 export function hasLocation(locations: LocationTree, tier: LocalityTier) { 34 return !_.isNil(getLocation(locations, tier)); 35 } 36 37 /* 38 * findMostSpecificLocation searches for a location matching the given locality 39 * tiers in the LocationTree. It tries to find the most specific location that 40 * applies, and thus begins searching from the end of the list of tiers for a 41 * tier with a matching location. Returns null if none is found. 42 */ 43 export function findMostSpecificLocation(locations: LocationTree, tiers: LocalityTier[]) { 44 let currentIndex = tiers.length - 1; 45 while (currentIndex >= 0) { 46 const currentTier = tiers[currentIndex]; 47 const location = getLocation(locations, currentTier); 48 49 if (!_.isNil(location)) { 50 return location; 51 } 52 53 currentIndex -= 1; 54 } 55 56 return null; 57 } 58 59 /* 60 * findOrCalculateLocation tries to place a locality on the map. If there is 61 * no location assigned to the locality itself, calculate the centroid of the 62 * children. 63 */ 64 export function findOrCalculateLocation(locations: LocationTree, locality: LocalityTree) { 65 // If a location is assigned to this locality, return it. 66 const thisTier = locality.tiers[locality.tiers.length - 1]; 67 const thisLocation = getLocation(locations, thisTier); 68 if (!_.isNil(thisLocation)) { 69 return thisLocation; 70 } 71 72 // If this locality has nodes directly, we can't calculate a location; bail. 73 if (!_.isEmpty(locality.nodes)) { 74 return null; 75 } 76 77 // If this locality has no child localities, we can't calculate a location. 78 // Note, this shouldn't ever actually happen. 79 if (_.isEmpty(locality.localities)) { 80 return null; 81 } 82 83 // Find (or calculate) the location of each child locality. 84 const childLocations: ILocation[] = []; 85 _.values(locality.localities).forEach((tier) => { 86 _.values(tier).forEach((child) => { 87 childLocations.push(findOrCalculateLocation(locations, child)); 88 }); 89 }); 90 91 // If any child location is missing, bail. 92 if (_.some(childLocations, _.isNil)) { 93 return null; 94 } 95 96 // Calculate the centroid of the child locations. 97 let centroid: [number, number] = [0, 0]; 98 childLocations.forEach((loc) => centroid = vector.add(centroid, [loc.longitude, loc.latitude])); 99 centroid = vector.mult(centroid, 1 / childLocations.length); 100 return { longitude: centroid[0], latitude: centroid[1] }; 101 }