diff --git a/js/countries.js b/js/countries.js index 3457f13004fc3d2f9999b1bb919ad992af98e6ae..bda58185b123cf3a2e5a5320983f314f5f77d4dc 100644 --- a/js/countries.js +++ b/js/countries.js @@ -43,6 +43,25 @@ App.Data.World.gridPointToCoordinate = function(p) { return [lat - latCell / 2, lon + lonCell / 2]; }; +/** + * Return the grid cell that encloses the given coordinates + * @param {number} lat + * @param {number} lon + * @returns {gridPoint} + */ +App.Data.World.coordinateToGridPoint = function(lat, lon) { + const width = App.Data.World.GridDimensions.width; + const height = App.Data.World.GridDimensions.height; + + const lonCell = 360 / width; + const latCell = 180 / height; + + const x = Math.floor((lon + 180) / lonCell); + const y = Math.floor((-lat + 90) / latCell); + + return [x, y]; +}; + /** * @param {gridPoint} p * @returns {number} diff --git a/src/events/intro/customizeSlaveTrade.js b/src/events/intro/customizeSlaveTrade.js index d989a707968fc64b533a692cab6793b568ebf2e1..44bcbfa66077005e968292ae50b0c471d0f48c55 100644 --- a/src/events/intro/customizeSlaveTrade.js +++ b/src/events/intro/customizeSlaveTrade.js @@ -430,20 +430,42 @@ App.Intro.CustomSlaveTrade = function() { } } + /** + * The distribution of population within the grid square must be uniform, but the data is essentially concentrated at the center point + * When the user clicks within a populated grid square, provide an alternative "distance" for that grid square's population + * Basically this stabilizes the weight of the grid you clicked inside, so clicking at the center or the edge of the cell doesn't affect it + * @param {number} lat Latitude of grid cell center + * @param {number} lon Longitude of grid cell center + */ + function altDist(lat, lon) { + const lonCell = 360 / App.Data.World.GridDimensions.width; + const latCell = 180 / App.Data.World.GridDimensions.height; + + // take one third of the corner-to-corner measurement of the grid cell as our alternate distance. + // ideally we want the average distance between coordinates within the grid cell and the grid cell's center, but that's hard to calculate, and it's about the same. + // the actual value for a square is around 0.3826, and for an equilateral triangle is 0.289. grid cells are almost square near the equator and almost triangular near the poles. + return distanceInKmBetweenEarthCoordinates(lat - 0.5 * latCell, lon - 0.5 * lonCell, lat + 0.5 * latCell, lon + 0.5 * lonCell) / 3; + } + /** * @param {number} lat * @param {number} lon */ function populateFromCoordinates(lat, lon) { V.nationalities = {}; + const thisGP = App.Data.World.coordinateToGridPoint(lat, lon); for (const p of App.Data.World.gridPoints()) { - const coords = App.Data.World.gridPointToCoordinate(p); - const dist = distanceInKmBetweenEarthCoordinates(lat, lon, coords[0], coords[1]); - const pop1 = 1; // assume the clicked grid's population is 1. we could look it up, but it doesn't matter (it cancels out during normalization) + const pop1 = 1; // or App.Data.World.populationAt(thisGP); const pop2 = App.Data.World.populationAt(p); - const gravity = (pop1 * pop2) / (dist * dist * frictionForDistance(dist)); - const nation = App.Data.World.nationAt(p); - addNationality(nation, Math.round(gravity)); + if (pop2 >= 0) { + const coords = App.Data.World.gridPointToCoordinate(p); + // is this the grid square we clicked inside of? use alternate distance if so + const clickedGrid = (p[0] === thisGP[0] && p[1] === thisGP[1]); + const dist = clickedGrid ? altDist(coords[0], coords[1]) : distanceInKmBetweenEarthCoordinates(lat, lon, coords[0], coords[1]); + const gravity = (pop1 * pop2) / (dist * dist * frictionForDistance(dist)); + const nation = App.Data.World.nationAt(p); + addNationality(nation, Math.round(gravity)); + } } refresh(); }