Skip to content
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

Maj tracés réseaux #929

Draft
wants to merge 8 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 7 additions & 40 deletions docs/génération_tuiles_réseaux_de_chaleur.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ Processus légèrement différent du fill-tiles classique étant donné qu'on a
```sh
# générer un fichier geojson depuis la table reseaux_de_chaleur
psql postgres://postgres:postgres_fcu@localhost:5432/postgres -c "COPY (
SELECT jsonb_build_object(
SELECT json_build_object(
'type', 'FeatureCollection',
'features', jsonb_agg(feature)
'features', json_agg(feature)
)
FROM (
SELECT jsonb_build_object(
SELECT json_build_object(
'id', id_fcu,
'type', 'Feature',
'geometry', ST_AsGeoJSON(ST_ForcePolygonCCW(ST_Transform(geom,4326)))::jsonb,
'geometry', ST_AsGeoJSON(ST_ForcePolygonCCW(ST_Transform(geom,4326)))::json,
'properties', json_build_object(
'id_fcu', \"id_fcu\",
'Taux EnR&R', \"Taux EnR&R\",
Expand All @@ -35,45 +35,12 @@ psql postgres://postgres:postgres_fcu@localhost:5432/postgres -c "COPY (
'energie_ratio_solaireThermique', \"energie_ratio_solaireThermique\",
'energie_ratio_pompeAChaleur', \"energie_ratio_pompeAChaleur\",
'energie_ratio_gaz', \"energie_ratio_gaz\",
'energie_ratio_fioul', \"energie_ratio_fioul\",
'energie_majoritaire', CASE
WHEN energie_max_ratio = \"energie_ratio_biomasse\" THEN 'biomasse'
WHEN energie_max_ratio = \"energie_ratio_geothermie\" THEN 'geothermie'
WHEN energie_max_ratio = \"energie_ratio_uve\" THEN 'uve'
WHEN energie_max_ratio = \"energie_ratio_chaleurIndustrielle\" THEN 'chaleurIndustrielle'
WHEN energie_max_ratio = \"energie_ratio_solaireThermique\" THEN 'solaireThermique'
WHEN energie_max_ratio = \"energie_ratio_pompeAChaleur\" THEN 'pompeAChaleur'
WHEN energie_max_ratio = \"energie_ratio_gaz\" THEN 'gaz'
WHEN energie_max_ratio = \"energie_ratio_fioul\" THEN 'fioul'
WHEN energie_max_ratio = \"energie_ratio_autresEnr\" THEN 'autresEnr'
WHEN energie_max_ratio = \"energie_ratio_chaufferiesElectriques\" THEN 'chaufferiesElectriques'
WHEN energie_max_ratio = \"energie_ratio_charbon\" THEN 'charbon'
WHEN energie_max_ratio = \"energie_ratio_gpl\" THEN 'gpl'
WHEN energie_max_ratio = \"energie_ratio_autreChaleurRecuperee\" THEN 'autreChaleurRecuperee'
WHEN energie_max_ratio = \"energie_ratio_biogaz\" THEN 'biogaz'
ELSE NULL
END
'energie_ratio_fioul', \"energie_ratio_fioul\"
)
) AS feature
FROM (
SELECT
*,
greatest(
\"energie_ratio_biomasse\",
\"energie_ratio_geothermie\",
\"energie_ratio_uve\",
\"energie_ratio_chaleurIndustrielle\",
\"energie_ratio_solaireThermique\",
\"energie_ratio_pompeAChaleur\",
\"energie_ratio_gaz\",
\"energie_ratio_fioul\",
\"energie_ratio_autresEnr\",
\"energie_ratio_chaufferiesElectriques\",
\"energie_ratio_charbon\",
\"energie_ratio_gpl\",
\"energie_ratio_autreChaleurRecuperee\",
\"energie_ratio_biogaz\"
) as \"energie_max_ratio\"
*
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A un moment donné, on avait besoin de l'énergie majoritaire, mais ce n'est plus le cas, donc on simplifie la requête.

FROM (
SELECT
*,
Expand All @@ -98,7 +65,7 @@ psql postgres://postgres:postgres_fcu@localhost:5432/postgres -c "COPY (
) TO STDOUT" | sed -e 's/\\\\"/\\"/g' > reseaux_de_chaleur.geojson

# générer les tuiles à partir du fichier geojson
yarn cli generate-tiles-from-file reseaux_de_chaleur.geojson reseaux_de_chaleur_tiles
yarn cli generate-tiles-from-file reseaux_de_chaleur.geojson reseaux_de_chaleur_tiles 0 14

# synchronisation avec la BDD de dev ou prod
./scripts/copyLocalTableToRemote.sh dev reseaux_de_chaleur_tiles --data-only
Expand Down
2 changes: 1 addition & 1 deletion docs/import_reseau_chaleur.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
3. Mise à jour des données sur les réseaux depuis Airtable
- Si la table des réseaux a été mise à jour lors de l'étape précédente : `yarn cli update-networks network`
- Sinon
- `yarn cli download-network network`
- `yarn cli download-update-network network`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

J'ai changé un peu le processus, et je voulais que download-network (= synchro de airtable vers postgres) n'ait pas d'effets de bord et ne modifie pas airtable. J'ai donc changé la commande référencée ici.
Ca devrait bouger aussi un peu à l'avenir.

- Normalement ce script complète la table *zones_et_reseaux_en_construction* et regénère la table *zones_et_reseaux_en_construction_tiles*
- /!\\ Note : pour les **réseaux de chaleur**, il faut [générer les tuiles différemment](./génération_tuiles_réseaux_de_chaleur.md).

Expand Down
1 change: 1 addition & 0 deletions public/FCU_logo_Monogramme.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 41 additions & 8 deletions scripts/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import { DatabaseTileInfo, DatabaseSourceId, tilesInfo, zDatabaseSourceId } from
import { KnownAirtableBase, knownAirtableBases } from './airtable/bases';
import { createModificationsReseau } from './airtable/create-modifications-reseau';
import { fetchBaseSchema } from './airtable/dump-schema';
import { downloadNetwork } from './networks/download-network';
import { downloadNetwork, downloadUpdateNetwork } from './networks/download-network';
import { generateTilesFromGeoJSON } from './networks/generate-tiles';
import { applyGeometryUpdates } from './networks/geometry-updates';
import { importMvtDirectory } from './networks/import-mvt-directory';
import { syncPostgresToAirtable } from './networks/sync-pg-to-airtable';
import { upsertFixedSimulateurData } from './simulateur/import';
import { fillTiles } from './utils/tiles';

Expand Down Expand Up @@ -45,11 +47,18 @@ program
await downloadNetwork(table);
});

program
.command('download-update-network')
.argument('<network-id>', 'Network id', validateNetworkId)
.action(async (table) => {
await downloadUpdateNetwork(table);
});

program
.command('fill-tiles')
.argument('<network-id>', 'Network id', (v) => zDatabaseSourceId.parse(v))
.argument('[zoomMin]', 'Minimum zoom', parseInt, 0)
.argument('[zoomMax]', 'Maximum zoom', parseInt, 17)
.argument('[zoomMin]', 'Minimum zoom', (v) => parseInt(v), 0)
.argument('[zoomMax]', 'Maximum zoom', (v) => parseInt(v), 17)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En fait il n'y a pas que v qui est passé en argument et ça passait directement en 2e argument de parseInt comme base. Et on veut pas ça...

.argument('[withIndex]', 'With index', (v) => !!v, false)
.action(async (table, zoomMin, zoomMax, withIndex) => {
await db((tilesInfo[table] as DatabaseTileInfo).tiles).delete();
Expand Down Expand Up @@ -86,8 +95,8 @@ program
.command('generate-tiles-from-file')
.argument('<fileName>', 'input file (format GeoJSON)')
.argument('<destinationTable>', 'Destination table')
.argument('[zoomMin]', 'Minimum zoom', parseInt, 0)
.argument('[zoomMax]', 'Maximum zoom', parseInt, 17)
.argument('[zoomMin]', 'Minimum zoom', (v) => parseInt(v), 0)
.argument('[zoomMax]', 'Maximum zoom', (v) => parseInt(v), 17)
.action(async (fileName, destinationTable, zoomMin, zoomMax) => {
const geojson = JSON.parse(await readFile(fileName, 'utf8'));

Expand Down Expand Up @@ -117,15 +126,39 @@ program
program
.command('update-networks')
.argument('<network-id>', 'Network id', (v) => zDatabaseSourceId.parse(v))
.argument('[zoomMin]', 'Minimum zoom', parseInt, 0)
.argument('[zoomMax]', 'Maximum zoom', parseInt, 17)
.argument('[zoomMin]', 'Minimum zoom', (v) => parseInt(v), 0)
.argument('[zoomMax]', 'Maximum zoom', (v) => parseInt(v), 17)
.argument('[withIndex]', 'With index', (v) => !!v, false)
.action(async (table, zoomMin, zoomMax, withIndex) => {
await downloadNetwork(table);
await downloadUpdateNetwork(table);
await db((tilesInfo[table] as DatabaseTileInfo).tiles).delete();
await fillTiles(table, zoomMin, zoomMax, withIndex);
});

program
.command('apply-geometry-updates')
.option('--dry-run', 'Run the command in dry-run mode', false)
.action(async ({ dryRun }) => {
try {
await applyGeometryUpdates(dryRun);
} catch (err) {
console.error('err', err);
process.exit(2);
}
});

program
.command('sync-postgres-to-airtable')
.option('--dry-run', 'Run the command in dry-run mode', false)
.action(async ({ dryRun }) => {
try {
await syncPostgresToAirtable(dryRun);
} catch (err) {
console.error('err', err);
process.exit(2);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je pensais qu'il y avait un catch global pour chaque action mais que nenni. Il faudrait que ça soit global pour pas s'embêter avec des try catch.

});

program
.command('update-simulateur')
.description('Take AMORCE file and either create records in database or update them.')
Expand Down
2 changes: 1 addition & 1 deletion scripts/copyRemoteTableToLocal.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ table=$2
options=$3
if [[ $env != "dev" && $env != "prod" ]]; then
usage
exit 1+
exit 1
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo d'origine 👏

fi

if [[ "$table" = "" ]]; then
Expand Down
59 changes: 45 additions & 14 deletions scripts/networks/download-network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import db from 'src/db';
import base from 'src/db/airtable';
import { DatabaseTileInfo, tilesInfo, DatabaseSourceId } from 'src/services/tiles.config';

const TypeArray: unique symbol = Symbol('array');
const TypeBool: unique symbol = Symbol('bool');
const TypeJSONArray: unique symbol = Symbol('json');
const TypeNumber: unique symbol = Symbol('number');
const TypePercentage: unique symbol = Symbol('percentage');
const TypeString: unique symbol = Symbol('string');
const TypeStringToArray: unique symbol = Symbol('array');
export const TypeArray: unique symbol = Symbol('array');
export const TypeBool: unique symbol = Symbol('bool');
export const TypeJSONArray: unique symbol = Symbol('json');
export const TypeNumber: unique symbol = Symbol('number');
export const TypePercentage: unique symbol = Symbol('percentage');
export const TypeString: unique symbol = Symbol('string');
export const TypeStringToArray: unique symbol = Symbol('array');

type Type =
export type Type =
| typeof TypeArray
| typeof TypeBool
| typeof TypeJSONArray
Expand All @@ -32,7 +32,7 @@ const conversionConfigReseauxDeChaleur = {
'reseaux classes': TypeBool,
has_PDP: TypeBool,
nom_reseau: TypeString,
communes: TypeStringToArray,
// communes: TypeStringToArray,
MO: TypeString,
Gestionnaire: TypeString,
'Taux EnR&R': TypeNumber,
Expand All @@ -48,7 +48,7 @@ const conversionConfigReseauxDeChaleur = {
'Dev_reseau%': TypeNumber,
'Rend%': TypeNumber,
reseaux_techniques: TypeBool,
departement: TypeNumber,
departement: TypeString,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

La colonne departement contenait jusque là des codes départements / codes postaux. Et ça contient maintenant des labels... Du coup j'ai changé le type, et il faudra dans le futur compléter cette information automatiquement. C'est utilisé que côté airtable.

region: TypeString,
adresse_mo: TypeString,
CP_MO: TypeString,
Expand Down Expand Up @@ -114,12 +114,12 @@ const conversionConfigReseauxDeFroid = {
//has_trace: TypeBool,
'Taux EnR&R': TypeNumber,
Gestionnaire: TypeString,
communes: TypeStringToArray,
// communes: TypeStringToArray,
contenu_CO2_2023_tmp: TypeNumber,
contenu_CO2_ACV_2023_tmp: TypeNumber,
'contenu CO2': TypeNumber,
'contenu CO2 ACV': TypeNumber,
departement: TypeNumber,
departement: TypeString,
region: TypeString,
MO: TypeString,
adresse_mo: TypeString,
Expand All @@ -146,7 +146,7 @@ const conversionConfigReseauxDeFroid = {
const conversionConfigAutres = {
mise_en_service: TypeString,
gestionnaire: TypeString,
communes: TypeStringToArray,
// communes: TypeStringToArray,
is_zone: TypeBool,
} as const;

Expand All @@ -160,6 +160,35 @@ export const downloadNetwork = async (table: DatabaseSourceId) => {
}
const networksAirtable = await base(tileInfo.airtable).select().all();

const logger = parentLogger.child({
table: table,
count: networksAirtable.length,
});
const startTime = Date.now();
logger.info('start network update');
await Promise.all(
networksAirtable.map(async (network) => {
if (network.get('id_fcu')) {
await db(tileInfo.table).update(convertEntityFromAirtableToPostgres(table, network)).where('id_fcu', network.get('id_fcu'));
}
})
);
logger.info('end network update', {
duration: Date.now() - startTime,
});
};

/**
* Synchronise les données d'une table réseau dans Airtable vers la table correspondante dans Postgres (identique downloadNetwork)
* Met également à jour certains champs côté airtable (spécifiques à chaque table).
*/
export const downloadUpdateNetwork = async (table: DatabaseSourceId) => {
const tileInfo = tilesInfo[table] as DatabaseTileInfo;
if (!tileInfo || !tileInfo.airtable) {
throw new Error(`${table} not managed`);
}
const networksAirtable = await base(tileInfo.airtable).select().all();

const logger = parentLogger.child({
table: table,
count: networksAirtable.length,
Expand Down Expand Up @@ -270,7 +299,7 @@ function convertEntityFromAirtableToPostgres(type: DatabaseSourceId, airtableNet
/**
* Convertit et corrige le potentiel mauvais typage côté Airtable.
*/
function convertAirtableValue(value: any, type: Type) {
export function convertAirtableValue(value: any, type: Type) {
switch (type) {
case TypeArray:
return value instanceof Array ? value : [];
Expand All @@ -288,5 +317,7 @@ function convertAirtableValue(value: any, type: Type) {
return value !== undefined && value !== null && value !== 'NULL' ? value : null;
case TypeStringToArray:
return value !== undefined && value !== null && value !== 'NULL' ? value.split(',') : [];
default:
throw new Error(`invalid type ${type}`);
}
}
45 changes: 18 additions & 27 deletions scripts/networks/generate-tiles.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { logger } from '@helpers/logger';
import geojsonvt from 'geojson-vt';
import { processInParallel } from 'src/types/async';
import vtpbf from 'vt-pbf';

import { logger } from '@helpers/logger';
import { processInParallel } from 'src/types/async';

import db from '../../src/db';

const QUERY_PARALLELISM = 50; // max queries in //
Expand All @@ -15,12 +17,7 @@ const globalY13Max = 3100;
/**
* Generate tiles from GeoJSON data and store them in a postgres table
*/
export const generateTilesFromGeoJSON = async (
geojson: GeoJSON.GeoJSON,
destinationTable: string,
zoomMin: number,
zoomMax: number
) => {
export const generateTilesFromGeoJSON = async (geojson: GeoJSON.GeoJSON, destinationTable: string, zoomMin: number, zoomMax: number) => {
const startTime = Date.now();
logger.info('start generating vector tiles');
const tiles = geojsonvt(geojson, {
Expand All @@ -34,26 +31,20 @@ export const generateTilesFromGeoJSON = async (
const startTime = Date.now();
logger.info('start level', { zLevel: z });

await processInParallel(
getCoordinatesGenerator(z),
QUERY_PARALLELISM,
async ({ x, y, z }) => {
const tile = tiles.getTile(z, x, y);
if (tile) {
await db(destinationTable)
.insert({
x,
y,
z,
tile: Buffer.from(
vtpbf.fromGeojsonVt({ layer: tile }, { version: 2 })
),
})
.onConflict(['x', 'y', 'z'])
.ignore();
}
await processInParallel(getCoordinatesGenerator(z), QUERY_PARALLELISM, async ({ x, y, z }) => {
const tile = tiles.getTile(z, x, y);
if (tile) {
await db(destinationTable)
.insert({
x,
y,
z,
tile: Buffer.from(vtpbf.fromGeojsonVt({ layer: tile }, { version: 2 })),
})
.onConflict(['x', 'y', 'z'])
.ignore();
}
);
});

logger.info('finished level', {
zLevel: z,
Expand Down
Loading
Loading