update propose mapping from open data for Rouen métropole trees

This commit is contained in:
Tykayn 2025-03-27 15:18:38 +01:00 committed by tykayn
parent f309db257d
commit d7c622bf71
6 changed files with 276 additions and 33 deletions

View file

@ -61,6 +61,11 @@ Résultat: `output/my_converted_data_set__mappingIssy2Roues.json`[makefile](make
## Examen des jeux de données Osmose ## Examen des jeux de données Osmose
Osmose permet la détection et suggestion de corrections de données dans OSM. On peut utiliser un export de ces données à la conversion d'un Mapper. Osmose permet la détection et suggestion de corrections de données dans OSM. On peut utiliser un export de ces données à la conversion d'un Mapper.
Pour cela, récupérez un jeu de données et utilisez le convertisseur Osmose de votre choix sur ces données afin d'en faire un nouveau jeu de données. Pour cela, récupérez un jeu de données et utilisez le convertisseur Osmose de votre choix sur ces données afin d'en faire un nouveau jeu de données.
## Création d'une configuration de conversion
Utilisez le script `create_mapping.ts`, celui-ci vous posera quelques questions et se chargera d'ajouter la nouvelle classe dans le fichier principal de conversion.
`ts-node create_mapping.ts`
[doc sur l'ajout d'un jeu de données](ajout_jeu_de_données.md) [doc sur l'ajout d'un jeu de données](ajout_jeu_de_données.md)
## Bornes de recharge - IRVE ## Bornes de recharge - IRVE

View file

@ -53,9 +53,11 @@ import MappingAskAngela from "./mappings/converters/configAskAngela";
import MappingPlanningFamlial from "./mappings/converters/configPlanningFamilial"; import MappingPlanningFamlial from "./mappings/converters/configPlanningFamilial";
import MappingSurveillanceRouen from "./mappings/converters/configSurveillance"; import MappingSurveillanceRouen from "./mappings/converters/configSurveillance";
import MappingRnb from "./mappings/converters/configRnb"; import MappingRnb from "./mappings/converters/configRnb";
import MappingArbresRemarquablesRouen from './mappings/converters/configArbresRemarquablesRouen'
const limitWarningPercentageChangeInPoints = 5; // show a warning when more than N percent of the number of points changed const limitWarningPercentageChangeInPoints = 5; // show a warning when more than N percent of the number of points changed
const allowed_configs: any = { const allowed_configs: any = {
MappingArbresRemarquablesRouen,
MappingRnb, MappingRnb,
mappingIssy2Roues, mappingIssy2Roues,
mappingConfigIRVE, mappingConfigIRVE,

View file

@ -0,0 +1,74 @@
/**
* arbres monumentaux de Rouen
*/
import MappingConfigType from "../mapping-config.type";
const MappingArbresRemarquablesRouen: MappingConfigType = {
config_name: "MappingArbresRemarquablesRouen",
config_author: "tykayn contact@cipherbliss.com",
default_properties_of_point: {
natural: 'tree',
source: 'Métropole de Rouen',
historic: 'monument'
},
source: {
geojson_path: 'https://data.metropole-rouen-normandie.fr/api/explore/v2.1/catalog/datasets/arbres-remarquables-metropole-rouen-normandie-2019/exports/geojson?lang=fr&timezone=Europe%2FBerlin',
url: ''
},
filters: {
// exclude_point_if_tag_not_empty: ['id_osm'], // exclure les points ayant déjà un id_osm pour éviter les doublons
// offset: 1
},
add_not_mapped_tags_too: false,
boolean_keys: [],
tags_to_ignore_if_value_is: ['Non renseigne'],
tags: {
// Mapping des champs du fichier source vers les tags OSM
// 'champ_source': 'tag_osm_cible',
// 'champ_source_id': 'ref:FR:votre_id',
// Exemple de transformation plus complexe
// nom: {
// key_converted: 'name',
// convert_to_name: true,
// },
// "geo_point_2d": "",
"gml_id": {
key_converted: 'ref:FR:rouen:gml_id',
convert_to_name: true,
},
// "objectid": "",
"nom": "name",
// "foret": "",
// "variete": "",
// "diametre_pied": "",
// "hauteur": "",
// "age": "",
// "parcelle": "",
// "photo": "",
// "pdf": "",
// "gps": "",
// "x": "",
// "y": "",
"description": "description",
"essence_forestiere": {
conditional_values: {
"Feuillu ": {
'tags_to_add': {
"leaf_type": "broadleaved",
}
},
"Résineux": {
'tags_to_add': {
"leaf_type": "needleleaved",
}
},
}
},
}
}
export default MappingArbresRemarquablesRouen;

View file

@ -1,33 +0,0 @@
import json
import sys
import json
# examiner un jeu de données geojson, et proposer des tags à mettre dans une config
# comme celle ci mappings/converters/configIRVE.ts
#
# exemple de lancement de commande:
# python propose_mapping_from_data.py mon_fichier.geojson
# Vérifie si un argument est fourni
if len(sys.argv) < 2:
print("Usage: python propose_mapping_from_data.py <geojson_file>")
sys.exit(1)
# Ouvre le fichier GeoJSON donné
with open(sys.argv[1]) as f:
data = json.load(f)
# Extraire les propriétés de tous les points de la FeatureCollection
properties = []
for feature in data['features']:
props = feature['properties']
properties.extend(props.keys())
# Créer un dictionnaire avec toutes les clés comme clés et des valeurs par défaut vides
defaults = {prop: '' for prop in properties}
# Remplacer les clés contenant "web" ou "téléphone" par "contact:website" ou "contact:phone"
defaults.update({prop: 'contact:website' if 'web' in prop else 'contact:phone' if 'téléphone' in prop else '' for prop in properties})
# Convertir le dictionnaire en JSON et l'afficher
print(json.dumps(defaults, indent=2))

View file

@ -0,0 +1,195 @@
/**
* propose_mapping_from_data.ts
* Examine un jeu de données GeoJSON et propose des tags à mettre dans une config
* comme celle dans mappings/converters/configIRVE.ts
*
* Exemple de lancement:
* npx ts-node propose_mapping_from_data.ts mon_fichier.geojson
*/
import * as fs from 'fs';
import * as path from 'path';
interface GeoJSONFeature {
type: string;
geometry: any;
properties: Record<string, any>;
}
interface GeoJSONData {
type: string;
features: GeoJSONFeature[];
}
/**
* Vérifie les arguments de la ligne de commande
*/
function checkArguments(): string {
if (process.argv.length < 3) {
console.error("Usage: npx ts-node propose_mapping_from_data.ts <geojson_file>");
process.exit(1);
}
return process.argv[2];
}
/**
* Charge et parse le fichier GeoJSON
*/
function loadGeoJSONFile(filePath: string): GeoJSONData {
try {
const fileContent = fs.readFileSync(filePath, 'utf8');
const data = JSON.parse(fileContent);
// Vérifie que le fichier est bien un GeoJSON avec des features
if (!data.features || !Array.isArray(data.features)) {
console.error("Le fichier ne semble pas être un GeoJSON valide avec des features");
process.exit(1);
}
return data;
} catch (error) {
console.error(`Erreur lors du chargement du fichier: ${error}`);
process.exit(1);
}
}
/**
* Extrait toutes les propriétés uniques des features
*/
function extractProperties(data: GeoJSONData): Set<string> {
const properties: Set<string> = new Set();
for (const feature of data.features) {
if (feature.properties) {
Object.keys(feature.properties).forEach(key => properties.add(key));
}
}
return properties;
}
/**
* Compte les occurrences des différentes valeurs pour chaque propriété
*/
function countPropertyValues(data: GeoJSONData): Record<string, Record<string, number>> {
const valuesCounts: Record<string, Record<string, number>> = {};
// Initialiser le comptage pour chaque propriété
for (const feature of data.features) {
if (feature.properties) {
Object.entries(feature.properties).forEach(([key, value]) => {
// Convertir la valeur en chaîne pour pouvoir la compter
const stringValue = String(value);
// Initialiser le compteur pour cette propriété si nécessaire
if (!valuesCounts[key]) {
valuesCounts[key] = {};
}
// Incrémenter le compteur pour cette valeur
valuesCounts[key][stringValue] = (valuesCounts[key][stringValue] || 0) + 1;
});
}
}
return valuesCounts;
}
/**
* Crée les mappings suggérés pour les propriétés
*/
function createMappingSuggestions(properties: Set<string>): Record<string, string> {
const defaults: Record<string, string> = {};
properties.forEach(prop => {
// Cherche les propriétés liées au web ou au téléphone
if (prop.toLowerCase().includes('web') || prop.toLowerCase().includes('site') || prop.toLowerCase().includes('url')) {
defaults[prop] = 'contact:website';
}
else if (prop.toLowerCase().includes('telephone') || prop.toLowerCase().includes('téléphone') || prop.toLowerCase().includes('phone')) {
defaults[prop] = 'contact:phone';
}
else if (prop.toLowerCase().includes('nom') || prop.toLowerCase().includes('name')) {
defaults[prop] = 'name';
}
else if (prop.toLowerCase().includes('adresse') || prop.toLowerCase().includes('address')) {
defaults[prop] = 'addr:full';
}
else if (prop.toLowerCase().includes('id') || prop.toLowerCase().includes('ref')) {
defaults[prop] = 'ref';
}
else {
defaults[prop] = '';
}
});
return defaults;
}
/**
* Affiche les statistiques des valeurs pour chaque propriété
*/
function displayPropertyValueStats(valuesCounts: Record<string, Record<string, number>>): void {
console.log('\n=== Statistiques des valeurs par propriété ===');
Object.entries(valuesCounts).forEach(([prop, values]) => {
const totalValues = Object.values(values).reduce((sum, count) => sum + count, 0);
const uniqueValues = Object.keys(values).length;
console.log(`\nPropriété: ${prop}`);
console.log(`- Total d'occurrences: ${totalValues}`);
console.log(`- Valeurs uniques: ${uniqueValues}`);
// Si trop de valeurs uniques, n'afficher que les plus fréquentes
if (uniqueValues > 10) {
console.log('- Top 5 des valeurs les plus fréquentes:');
const sortedValues = Object.entries(values)
.sort((a, b) => b[1] - a[1])
.slice(0, 5);
sortedValues.forEach(([value, count]) => {
// Tronquer les valeurs très longues
const displayValue = value.length > 50 ? value.substring(0, 47) + '...' : value;
console.log(` "${displayValue}": ${count} occurrences (${Math.round(count / totalValues * 100)}%)`);
});
console.log(` ... et ${uniqueValues - 5} autres valeurs`);
} else {
console.log('- Toutes les valeurs:');
Object.entries(values)
.sort((a, b) => b[1] - a[1])
.forEach(([value, count]) => {
// Tronquer les valeurs très longues
const displayValue = value.length > 50 ? value.substring(0, 47) + '...' : value;
console.log(` "${displayValue}": ${count} occurrences (${Math.round(count / totalValues * 100)}%)`);
});
}
});
console.log('\n=== Fin des statistiques ===\n');
}
/**
* Fonction principale
*/
function main(): void {
const filePath = checkArguments();
const data = loadGeoJSONFile(filePath);
console.log(`\nFichier GeoJSON chargé: ${filePath}`);
console.log(`Nombre de features: ${data.features.length}`);
const properties = extractProperties(data);
const valuesCounts = countPropertyValues(data);
displayPropertyValueStats(valuesCounts);
const mappingSuggestions = createMappingSuggestions(properties);
console.log('Suggestions de mapping OSM:');
console.log(JSON.stringify(mappingSuggestions, null, 2));
console.log(`Nombre de propriétés uniques: ${properties.size}`);
}
// Exécution du programme
main();