-
Notifications
You must be signed in to change notification settings - Fork 20
Add data source selection to population calculation #1767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ import knex from 'chaire-lib-backend/lib/config/shared/db.config'; | |
| import TrError from 'chaire-lib-common/lib/utils/TrError'; | ||
|
|
||
| import { truncate } from 'chaire-lib-backend/lib/models/db/default.db.queries'; | ||
| import dsQueries from 'chaire-lib-backend/lib/models/db/dataSources.db.queries'; | ||
|
|
||
| interface CensusAttributes { | ||
| id: number; | ||
|
|
@@ -63,28 +64,46 @@ const addPopulationBatch = async (inputArray: { internalId: string; population: | |
| }; | ||
|
|
||
| const getPopulationInPolygon = async ( | ||
| accessibilityPolygon: GeoJSON.MultiPolygon | GeoJSON.Polygon | ||
| ): Promise<number | null> => { | ||
| accessibilityPolygon: GeoJSON.MultiPolygon | GeoJSON.Polygon, | ||
| populationDataSourceName: string | ||
| ): Promise<{ population: number | null; dataSourceAreaRatio: number }> => { | ||
GabrielBruno24 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| try { | ||
| // Find all the zones that intersect the input polygon, and fetch their population, area, and the area of the intersection. | ||
| const dataSourceId = (await dsQueries.findByName(populationDataSourceName))?.id; | ||
| if (dataSourceId === undefined) { | ||
| return { population: null, dataSourceAreaRatio: 0 }; | ||
| } | ||
| // Find all the zones that intersect the input polygon from a particular data source, and fetch their population, area, and the area of the intersection. | ||
| // We multiply the population of each zone by the ratio between the area of its intersection with the input polygon and its total area, to estimate the true population of zones that aren't entirely contained within the input polygon. | ||
| // TO avoid division by zero in the edge case of a degenerate zone with no area, we use the NULLIF function. | ||
| // To avoid division by zero in the edge case of a degenerate zone with no area, we use the NULLIF function. | ||
| // We also get the ratio of the area of the zones in the polygon that have data to the area of the polygon. | ||
| // When we get a polygon for which we have complete data, we expect that number to be 1 or very close to it, however it will be lower if data is missing from that area (for example, if we input a polygon that's over the Canada-US border while using a data source that only has data for Canada). | ||
| const populationResponse = await knex.raw( | ||
| ` | ||
| SELECT COUNT(*) as row_count, | ||
| ROUND(SUM( | ||
| population * | ||
| ST_AREA(ST_INTERSECTION(ST_GeomFromGeoJSON(:polygon), geography::geometry)) / | ||
| population * | ||
| ST_AREA(ST_INTERSECTION(ST_GeomFromGeoJSON(:polygon), geography::geometry)) / | ||
| NULLIF(ST_AREA(geography::geometry), 0) | ||
| )) as weighted_population | ||
| )) as weighted_population, | ||
| ( | ||
| ST_AREA(ST_INTERSECTION(ST_UNION(geography::geometry), ST_GeomFromGeoJSON(:polygon))) / | ||
| NULLIF(ST_AREA(ST_GeomFromGeoJSON(:polygon)), 0) | ||
| ) as data_source_area_ratio | ||
GabrielBruno24 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| FROM ${tableName} c JOIN ${parentTable} z ON c.zone_id = z.id | ||
| WHERE ST_INTERSECTS(geography::geometry, ST_GeomFromGeoJSON(:polygon)); | ||
| WHERE z.data_source_id = :dataSourceId | ||
| AND ST_INTERSECTS(geography::geometry, ST_GeomFromGeoJSON(:polygon)); | ||
GabrielBruno24 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| `, | ||
| { polygon: JSON.stringify(accessibilityPolygon) } | ||
| { polygon: JSON.stringify(accessibilityPolygon), dataSourceId } | ||
| ); | ||
|
|
||
| const result = populationResponse.rows[0]; | ||
| return Number(result.row_count) === 0 ? null : Number(result.weighted_population); | ||
|
|
||
| // If there is no population data, we set 'population' to null, and 'dataSourceAreaRatio' is irrelevant. | ||
| // This is distinct from a population of 0, where we calculate that there is no one living in the zone according to the data, and so 'dataSourceAreaRatio' is something we are interested in in this case. | ||
| return { | ||
| population: Number(result.row_count) === 0 ? null : Number(result.weighted_population), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 0 === null? Et si oui, pourquoi dataSourceAreaRatio ne serait pas null aussi? En fait dans ce cas null === 0! D'ailleurs si
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On met a null spécifiquement le nombre de rows est 0, pas la population. |
||
| dataSourceAreaRatio: result.data_source_area_ratio === null ? 0 : Number(result.data_source_area_ratio) | ||
| }; | ||
| } catch (error) { | ||
| throw new TrError(`Problem getting population (knex error: ${error})`, 'CSDB0003', 'ProblemGettingPopulation'); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.