2023-07-29 14:43:01 +02:00
|
|
|
/**
|
|
|
|
* convertisseur de données de bornes de recharge électrique à partir de données Chargemap et open data Etalab
|
|
|
|
*/
|
2023-08-05 10:57:07 +02:00
|
|
|
import * as fs from 'fs'
|
|
|
|
|
2023-08-02 13:20:50 +02:00
|
|
|
import mappingConfigIRVE from './mappings/converters/configIRVE'
|
|
|
|
import mappingConfigIRVE_simple from './mappings/converters/mappingConfigIRVE_simple'
|
|
|
|
import mapping_engine from './mappings/engine'
|
2023-08-05 11:52:25 +02:00
|
|
|
import {BoundingBoxCoordinatesType, FeatureCollection} from "./mappings/mapping-config.type";
|
2023-08-05 10:57:07 +02:00
|
|
|
import utils from './mappings/utils'
|
2023-08-05 10:59:45 +02:00
|
|
|
|
2023-08-05 11:04:29 +02:00
|
|
|
|
2023-08-05 10:57:07 +02:00
|
|
|
const minimist = require('minimist')
|
2023-08-05 09:55:14 +02:00
|
|
|
|
|
|
|
const debugLog = utils.debugLog;
|
2023-07-30 18:37:07 +02:00
|
|
|
|
2023-08-05 13:45:31 +02:00
|
|
|
let use_mapping_engine = false
|
|
|
|
use_mapping_engine = true
|
2023-07-31 19:27:17 +02:00
|
|
|
|
2023-07-30 19:18:17 +02:00
|
|
|
let Mapping_engine = new mapping_engine(mappingConfigIRVE)
|
2023-07-29 23:16:36 +02:00
|
|
|
|
2023-08-05 10:57:07 +02:00
|
|
|
let mini_arguments: any = minimist(process.argv.slice(2))
|
2023-07-29 17:19:56 +02:00
|
|
|
|
2023-08-05 09:55:14 +02:00
|
|
|
|
2023-08-05 13:45:31 +02:00
|
|
|
// let sourceFilePathGeoJson = './etalab_data/small.json'
|
|
|
|
// let sourceFilePathGeoJson = './etalab_data/all.json'
|
|
|
|
// let sourceFilePathGeoJson = './output/my_converted_data_set_filtered_zipcode_91.json'
|
|
|
|
let sourceFilePathGeoJson = './output/my_converted_data_set_filtered_zipcode_91_small.json'
|
2023-07-30 18:37:07 +02:00
|
|
|
|
2023-07-29 22:10:50 +02:00
|
|
|
// wip filter
|
|
|
|
let filterOnBoundingBox = true
|
2023-07-29 22:16:36 +02:00
|
|
|
filterOnBoundingBox = false
|
2023-07-29 22:10:50 +02:00
|
|
|
|
2023-08-05 10:57:07 +02:00
|
|
|
let boundingBoxCoordinates: BoundingBoxCoordinatesType = {
|
|
|
|
xMin: 1.91,
|
|
|
|
xMax: 2.38,
|
|
|
|
yMin: 48.7,
|
|
|
|
yMax: 48.4,
|
2023-07-29 22:10:50 +02:00
|
|
|
}
|
|
|
|
|
2023-07-29 22:16:36 +02:00
|
|
|
let filterCoordinates = true
|
2023-08-05 11:52:25 +02:00
|
|
|
filterCoordinates = false
|
|
|
|
|
2023-07-30 18:37:07 +02:00
|
|
|
let enable_filter_on_department = true
|
2023-07-31 19:27:17 +02:00
|
|
|
enable_filter_on_department = false
|
2023-07-31 23:01:41 +02:00
|
|
|
|
2023-07-29 23:16:36 +02:00
|
|
|
let filterDepartment = 91
|
|
|
|
if (mini_arguments['department']) {
|
2023-08-05 10:57:07 +02:00
|
|
|
filterDepartment = mini_arguments['department']
|
|
|
|
enable_filter_on_department = true
|
2023-07-30 18:37:07 +02:00
|
|
|
}
|
|
|
|
if (mini_arguments['source']) {
|
2023-08-05 10:57:07 +02:00
|
|
|
sourceFilePathGeoJson = mini_arguments['source']
|
2023-07-29 23:16:36 +02:00
|
|
|
}
|
2023-07-31 22:30:55 +02:00
|
|
|
if (mini_arguments['engine']) {
|
2023-08-05 13:45:31 +02:00
|
|
|
use_mapping_engine = mini_arguments['engine']
|
2023-07-31 22:30:55 +02:00
|
|
|
}
|
2023-07-29 23:16:36 +02:00
|
|
|
|
2023-07-29 22:16:36 +02:00
|
|
|
let filterZipCode = new RegExp(`^${filterDepartment}`)
|
|
|
|
let filterZipCodeAdresse = new RegExp(` ${filterDepartment}`)
|
|
|
|
let filteredName = ''
|
2023-07-29 17:19:56 +02:00
|
|
|
|
2023-07-30 18:37:07 +02:00
|
|
|
if (enable_filter_on_department) {
|
2023-08-05 10:57:07 +02:00
|
|
|
filteredName = '_filtered_zipcode_' + filterDepartment
|
2023-07-29 23:04:11 +02:00
|
|
|
} else if (filterOnBoundingBox) {
|
2023-08-05 10:57:07 +02:00
|
|
|
filteredName = '_filtered_bbox_' + boundingBoxCoordinates.xMin + '-' + boundingBoxCoordinates.xMax + '_' + boundingBoxCoordinates.yMin + '-' + boundingBoxCoordinates.yMax
|
2023-07-29 22:10:50 +02:00
|
|
|
}
|
2023-08-05 10:59:45 +02:00
|
|
|
let pointCounterMax: number = 1000000
|
2023-07-29 17:19:56 +02:00
|
|
|
|
2023-08-05 10:59:45 +02:00
|
|
|
let limitConversionToFirstPoint: boolean = false
|
2023-07-31 23:01:41 +02:00
|
|
|
// limitConversionToFirstPoint = true
|
2023-08-05 09:55:14 +02:00
|
|
|
|
2023-07-29 22:10:50 +02:00
|
|
|
if (limitConversionToFirstPoint) {
|
2023-08-05 10:57:07 +02:00
|
|
|
pointCounterMax = 1
|
2023-07-29 22:10:50 +02:00
|
|
|
}
|
2023-08-05 10:59:45 +02:00
|
|
|
let defaultPropertiesOfPoint: any = {
|
2023-08-05 10:57:07 +02:00
|
|
|
'amenity': 'charging_station'
|
2023-07-29 23:04:11 +02:00
|
|
|
}
|
|
|
|
|
2023-08-05 10:59:45 +02:00
|
|
|
let converted_geo_json: any = {
|
2023-08-05 10:57:07 +02:00
|
|
|
type: 'FeatureCollection',
|
|
|
|
features: []
|
2023-07-29 14:43:01 +02:00
|
|
|
}
|
2023-07-30 19:18:17 +02:00
|
|
|
|
2023-08-05 11:04:29 +02:00
|
|
|
|
|
|
|
let output_folder = 'output';
|
2023-08-05 11:52:25 +02:00
|
|
|
|
2023-08-05 11:04:29 +02:00
|
|
|
/**
|
|
|
|
* crée un fichier dans le dossier par défaut, output
|
|
|
|
* @param fileName
|
|
|
|
* @param fileContent
|
|
|
|
*/
|
|
|
|
function writeFile(fileName: string, fileContent: any) {
|
|
|
|
debugLog('write file ', fileName)
|
|
|
|
|
|
|
|
|
|
|
|
return fs.writeFile(
|
|
|
|
`./${output_folder}/${fileName}`,
|
|
|
|
fileContent,
|
|
|
|
'utf8',
|
|
|
|
(err) => {
|
|
|
|
if (err) {
|
|
|
|
debugLog(`Error writing file: ${err}`)
|
|
|
|
} else {
|
|
|
|
debugLog(`File ${fileName} is written successfully!`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-07-30 18:37:07 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param sourceFilePath
|
|
|
|
* @param mapping
|
|
|
|
* @param pointCounterMax
|
|
|
|
* @param boundingBoxCoordinates
|
|
|
|
*/
|
2023-08-05 11:04:29 +02:00
|
|
|
function convertDataForIRVE(sourceFilePath: string, mapping: any, pointCounterMax: number, boundingBoxCoordinates: any) {
|
2023-08-05 11:52:25 +02:00
|
|
|
debugLog('convertDataForIRVE from ', sourceFilePath)
|
2023-08-05 10:57:07 +02:00
|
|
|
|
|
|
|
fs.readFile(sourceFilePath, 'utf8', function (err, data) {
|
|
|
|
let point_counter = 0
|
2023-08-05 11:52:25 +02:00
|
|
|
let feature_points_after_filter: any = []
|
|
|
|
|
2023-08-05 10:57:07 +02:00
|
|
|
if (err) {
|
|
|
|
return debugLog(err)
|
|
|
|
}
|
|
|
|
let data_transformed: FeatureCollection = JSON.parse(data)
|
|
|
|
// debug('data keys ', Object.keys(dataTransformed))
|
2023-08-05 11:52:25 +02:00
|
|
|
// debugLog('properties of point 0', data_transformed.features[0])
|
2023-08-05 10:57:07 +02:00
|
|
|
|
|
|
|
if (data_transformed.features) {
|
|
|
|
|
|
|
|
debugLog('data found, features:', data_transformed.features.length)
|
|
|
|
|
|
|
|
// find interesting list of points to use
|
2023-08-05 10:59:45 +02:00
|
|
|
let list_of_points: any = data_transformed.features
|
2023-08-05 11:52:25 +02:00
|
|
|
debugLog('listOfPoints.length', list_of_points.length)
|
2023-08-05 10:57:07 +02:00
|
|
|
|
2023-08-05 11:52:25 +02:00
|
|
|
/**
|
|
|
|
* filtering
|
|
|
|
* TODO: do it in the engine
|
|
|
|
*/
|
|
|
|
// for each point from the data source, filter if we take it or not
|
2023-08-05 10:57:07 +02:00
|
|
|
list_of_points.forEach((feature_point: any) => {
|
|
|
|
|
|
|
|
let regex_filter_test_result = true
|
|
|
|
|
|
|
|
if (enable_filter_on_department) {
|
|
|
|
debugLog('filtre sur les départements activé')
|
|
|
|
regex_filter_test_result = (
|
|
|
|
filterZipCode.test(feature_point.properties.consolidated_code_postal)
|
|
|
|
||
|
|
|
|
filterZipCodeAdresse.test(feature_point.properties.adresse_station)
|
|
|
|
)
|
2023-08-05 11:52:25 +02:00
|
|
|
} else {
|
|
|
|
debugLog('pas de filtre sur les départements')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filterOnBoundingBox) {
|
|
|
|
debugLog('filtre sur les coordonnées bounding box activé')
|
2023-08-05 10:57:07 +02:00
|
|
|
|
|
|
|
let x = feature_point.properties.coordonneesXY[0]
|
|
|
|
let xMin = boundingBoxCoordinates.xMin
|
|
|
|
let xMax = boundingBoxCoordinates.xMax
|
|
|
|
let yMin = boundingBoxCoordinates.yMin
|
|
|
|
let yMax = boundingBoxCoordinates.yMax
|
|
|
|
|
|
|
|
let y = feature_point.properties.coordonneesXY[1]
|
|
|
|
regex_filter_test_result = (
|
|
|
|
(x >= xMin && x <= xMax)
|
|
|
|
&&
|
|
|
|
(y >= yMin && y <= yMax)
|
|
|
|
)
|
|
|
|
|
2023-08-05 11:52:25 +02:00
|
|
|
} else {
|
|
|
|
debugLog('pas de filtre sur les coordonnées bounding box')
|
2023-08-05 10:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-08-05 11:52:25 +02:00
|
|
|
// TODO add filter offset max
|
|
|
|
// filter points depending on zipcode
|
|
|
|
if (regex_filter_test_result) {
|
|
|
|
feature_points_after_filter.push(feature_point)
|
|
|
|
debugLog(' +1 point', point_counter)
|
|
|
|
point_counter++
|
|
|
|
}
|
2023-08-05 10:57:07 +02:00
|
|
|
|
2023-08-05 11:52:25 +02:00
|
|
|
})
|
2023-08-05 10:57:07 +02:00
|
|
|
|
2023-08-05 11:52:25 +02:00
|
|
|
/**
|
|
|
|
* conversion
|
|
|
|
*/
|
2023-08-05 12:01:24 +02:00
|
|
|
debugLog(' after filtering, feature_points_after_filter number of points: ', feature_points_after_filter.length)
|
2023-08-05 11:52:25 +02:00
|
|
|
feature_points_after_filter.forEach((feature_point: any) => {
|
|
|
|
// debugLog('featurePoint.properties.consolidated_code_postal', feature_point.properties.consolidated_code_postal)
|
|
|
|
debugLog('convert : work on 1 point')
|
2023-08-05 12:01:24 +02:00
|
|
|
debugLog('convert :featurePoint', feature_point)
|
2023-08-05 10:57:07 +02:00
|
|
|
|
2023-08-05 12:01:24 +02:00
|
|
|
console.log('convert :feature_point', feature_point)
|
2023-08-05 11:52:25 +02:00
|
|
|
let mapped_point: any = {}
|
2023-08-05 10:57:07 +02:00
|
|
|
|
2023-08-05 13:45:31 +02:00
|
|
|
if (use_mapping_engine) {
|
2023-08-05 11:52:25 +02:00
|
|
|
debugLog('convert :using mapping engine on feature point'
|
|
|
|
, feature_point
|
|
|
|
)
|
|
|
|
mapped_point = Mapping_engine.mapElementFromConf(feature_point)
|
|
|
|
console.log('mapped_point', mapped_point)
|
|
|
|
} else {
|
|
|
|
debugLog('convert :using simple converter on feature point', feature_point)
|
|
|
|
mapped_point = mapElementFromConfSimple(feature_point, mapping)
|
|
|
|
}
|
|
|
|
// debugLog('mapped_point one point', mapped_point)
|
|
|
|
if (mapped_point) {
|
|
|
|
converted_geo_json.features.push(mapped_point)
|
|
|
|
debugLog('convert : added one point to converted_geo_json')
|
|
|
|
} else {
|
|
|
|
debugLog('convert : !!! there is no map one point')
|
2023-08-05 10:57:07 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
// output new geojson
|
2023-08-05 11:52:25 +02:00
|
|
|
|
|
|
|
debugLog('convert : convertedGeoJson.features.length', converted_geo_json.features.length)
|
2023-08-05 10:57:07 +02:00
|
|
|
// write file on disk
|
2023-08-05 11:52:25 +02:00
|
|
|
if (converted_geo_json.features.length) {
|
|
|
|
|
|
|
|
let fileNameToWrite = 'my_converted_data_set' + filteredName + '.json'
|
|
|
|
debugLog('write file ', fileNameToWrite)
|
|
|
|
writeFile(fileNameToWrite, JSON.stringify(converted_geo_json, null, 2))
|
2023-08-05 10:57:07 +02:00
|
|
|
|
2023-08-05 11:52:25 +02:00
|
|
|
} else {
|
|
|
|
debugLog('no writing of file, because there is no converted feature')
|
|
|
|
}
|
2023-08-05 10:57:07 +02:00
|
|
|
debugLog('mapped output:', converted_geo_json.features)
|
|
|
|
|
|
|
|
return converted_geo_json
|
|
|
|
}
|
|
|
|
})
|
2023-07-29 23:04:11 +02:00
|
|
|
}
|
|
|
|
|
2023-07-29 14:43:01 +02:00
|
|
|
/**
|
|
|
|
* retuns the converted element from mapping config if present, null otherwise
|
|
|
|
*/
|
2023-08-05 11:04:29 +02:00
|
|
|
function mapElementFromConfSimple(featurePoint: any, mappingConfig: any) {
|
2023-08-05 10:57:07 +02:00
|
|
|
let mappingKeys = Object.keys(mappingConfig)
|
|
|
|
let featurePointPropertiesKeys = Object.keys(featurePoint.properties)
|
|
|
|
|
|
|
|
debugLog('keys', mappingKeys, featurePointPropertiesKeys)
|
|
|
|
|
2023-08-05 10:59:45 +02:00
|
|
|
let newProperties: any = defaultPropertiesOfPoint
|
2023-08-05 10:57:07 +02:00
|
|
|
|
|
|
|
// reinit properties of current point
|
|
|
|
let basePoint = Object.create(featurePoint)
|
|
|
|
basePoint.type = featurePoint.type
|
|
|
|
basePoint.geometry = featurePoint.geometry
|
|
|
|
basePoint.properties = newProperties
|
|
|
|
|
|
|
|
// apply new properties if found in mapping config
|
|
|
|
featurePointPropertiesKeys.forEach((pointKeyName: string) => {
|
|
|
|
|
|
|
|
if (mappingKeys.indexOf(pointKeyName) !== -1) {
|
2023-08-05 10:59:45 +02:00
|
|
|
// debugLog('found element', pointKeyName, '=>', mappingConfig[pointKeyName], 'value : ', featurePoint.properties[pointKeyName])
|
2023-08-05 10:57:07 +02:00
|
|
|
let convertedValue: any = ''
|
|
|
|
if (utils.isBooleanKey(pointKeyName)) {
|
|
|
|
|
|
|
|
let copyOfValue: any = '' + featurePoint.properties[pointKeyName]
|
|
|
|
if (typeof copyOfValue === typeof Object && copyOfValue.key_converted) {
|
|
|
|
copyOfValue = copyOfValue.key_converted
|
|
|
|
}
|
|
|
|
convertedValue = copyOfValue.toLowerCase() == 'true' ? 'yes' : 'no'
|
|
|
|
} else {
|
|
|
|
convertedValue = featurePoint.properties[pointKeyName]
|
|
|
|
}
|
|
|
|
|
|
|
|
if (convertedValue) {
|
2023-08-05 11:52:25 +02:00
|
|
|
let convertedKey: any = mappingConfig[pointKeyName]
|
2023-08-05 11:04:29 +02:00
|
|
|
newProperties[convertedKey] = convertedValue
|
2023-08-05 10:57:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
debugLog('basePoint', basePoint)
|
|
|
|
return basePoint
|
2023-07-29 14:43:01 +02:00
|
|
|
}
|
|
|
|
|
2023-08-05 13:45:31 +02:00
|
|
|
if (use_mapping_engine) {
|
2023-08-05 11:52:25 +02:00
|
|
|
debugLog(' - using mapping engine')
|
|
|
|
debugLog(' - pointCounterMax', pointCounterMax)
|
2023-08-05 10:57:07 +02:00
|
|
|
Mapping_engine.setConfig(mappingConfigIRVE)
|
|
|
|
convertDataForIRVE(sourceFilePathGeoJson, mappingConfigIRVE, pointCounterMax, boundingBoxCoordinates)
|
2023-07-29 14:43:01 +02:00
|
|
|
|
2023-07-31 19:27:17 +02:00
|
|
|
} else {
|
2023-07-30 19:18:17 +02:00
|
|
|
|
2023-08-05 10:57:07 +02:00
|
|
|
let mappingConfigIRVE = mappingConfigIRVE_simple
|
|
|
|
convertDataForIRVE(sourceFilePathGeoJson, mappingConfigIRVE, pointCounterMax, boundingBoxCoordinates)
|
2023-07-30 19:18:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-31 19:27:17 +02:00
|
|
|
|
|
|
|
|