Add RuleSet for comfort, safety & speed factor

This commit is contained in:
pgm-chardelv1 2021-07-14 16:28:02 +02:00
parent 12fc26fa44
commit a751490e4b
9 changed files with 12234 additions and 18 deletions

17
javascript/.babelrc Normal file
View file

@ -0,0 +1,17 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
],
"env": {
"test": {
"plugins": ["@babel/plugin-transform-modules-commonjs"]
}
}
}

1
javascript/README.md Normal file
View file

@ -0,0 +1 @@
# Interpret rules and tags to calculate bicycle route scores

90
javascript/RuleSet.mjs Normal file
View file

@ -0,0 +1,90 @@
/**
* RuleSet Class
* Constructor
* @param name {string} Name of RuleSet
* @param defaultValue {number} Default score value
* @param unit {string} Meta field with some info // !TODO: Decide if I'm going to keep this or remove it from the class
* @param values {object} Main data object
*/
class RuleSet {
constructor(name, defaultValue = 1, unit, values) {
this.name = name;
this.defaultValue = defaultValue;
this.values = values;
this.unit = unit;
this.score = this.defaultValue;
this.scoreValues = null;
this.order = null;
}
/**
* toString
* Returns constructor values in string for display in the console
*/
toString() {
return `${this.name} | ${this.unit} | ${this.defaultValue} | ${this.values}`;
}
/**
* getScore calculates a score for the RuleSet
* @param tags {object} Active tags to compare against
*/
getScore(tags) {
const [[program,keys], values] = Object.entries(this.values);
if (program === '$multiply') {
this.scoreValues = keys;
this.score *= this.multiplyScore(tags);
console.log(`${this.name}: ${this.score}`)
} else if (program === '$firstMatchOf') {
this.scoreValues = values;
this.order = keys;
this.getFirstMatchScore(tags);
} else {
console.error(`Error: Program ${program} is not implemented yet. ${JSON.stringify(keys)}`);
}
}
/**
* Multiplies the default score with the proper values
* @param tags {object} the active tags to check against
* @returns score after multiplication
*/
multiplyScore(tags) {
let number = this.defaultValue;
Object.entries(tags).forEach(tag => {
const [key, value] = tag;
Object.entries(this.scoreValues).forEach(property => {
const [propKey, propValues] = property;
// console.log(propKey, key)
if (key === propKey) {
for (let propEntry of Object.entries(propValues)) {
const [propValueKey, propValue] = propEntry;
if (value === propValueKey) number = propValue;
}
}
})
});
return number;
}
getFirstMatchScore(tags) {
const [[tagKey, tagValue]] = Object.entries(tags);
for (let item of this.order) {
if (item === tagKey) {
const options = Object.entries(this.scoreValues[1]);
// console.log(tagKey)
for (let optGroup of options) {
if (tagKey === optGroup[0]) {
for (let prop of Object.entries(optGroup[1])) {
const [propKey, propValue] = prop;
if (tagValue === propKey) {
console.log(`${this.name}: ${propValue}`);
return;
}
}
}
}
}
}
}
}
export default RuleSet;

View file

@ -0,0 +1 @@
const RuleSet = require('../RuleSet.mjs');

4
javascript/index.js Normal file
View file

@ -0,0 +1,4 @@
import interpret from "./interpret.mjs";
import RuleSet from "./RuleSet.mjs";
export { interpret, RuleSet };

88
javascript/interpret.mjs Normal file
View file

@ -0,0 +1,88 @@
/**
* Import packages
*/
import fs from 'fs';
import RuleSet from './RuleSet.mjs';
/**
* Example tags
*/
const tags1 = {
"highway": "residential", // 1
"surface": "paved", // 0.99
}
const tags2 = {
"bicycle": "yes",
"cycleway": "lane",
"highway": "secondary",
"maxspeed": "50",
}
const tags3 = {
"cyclestreet": "yes",
"highway":"residential",
"maxspeed":"30",
"surface":"asphalt"
}
const tags4 = {
"highway": "track",
"surface": "asphalt",
"incline": "10%"
}
/**
* Interpret JsonFile w/ Tags
* @param definitionFile {any} JSON input defining the score distribution
* @param tags {any} OSM tags as key/value pairs
*/
const interpret = (definitionFile, tags) => {
const rawData = fs.readFileSync(definitionFile);
const ruleSet = JSON.parse(rawData);
const { name, unit, $default, $multiply, value} = ruleSet;
if (!!value) {
const currentSet = new RuleSet(name, $default, unit, value);
currentSet.getScore(tags);
} else {
const currentSet = new RuleSet(name, $default, unit, {$multiply});
currentSet.toString();
currentSet.getScore(tags);
}
};
console.info('Comfort')
interpret("../Examples/bicycle/aspects/bicycle.comfort.json", tags1);
interpret("../Examples/bicycle/aspects/bicycle.comfort.json", tags2);
interpret("../Examples/bicycle/aspects/bicycle.comfort.json", tags3);
interpret("../Examples/bicycle/aspects/bicycle.comfort.json", tags4);
console.log('*******************')
console.info('Safety')
interpret("../Examples/bicycle/aspects/bicycle.safety.json", tags1);
interpret("../Examples/bicycle/aspects/bicycle.safety.json", tags2);
interpret("../Examples/bicycle/aspects/bicycle.safety.json", tags3);
interpret("../Examples/bicycle/aspects/bicycle.safety.json", tags4);
console.log('*******************')
console.info('Legal Access')
interpret("../Examples/bicycle/aspects/bicycle.legal_access.json", tags1);
interpret("../Examples/bicycle/aspects/bicycle.legal_access.json", tags2);
interpret("../Examples/bicycle/aspects/bicycle.legal_access.json", tags3);
interpret("../Examples/bicycle/aspects/bicycle.legal_access.json", tags4);
// !TODO: Add default value = "no" as a fallback. Fix logic
console.log('*******************')
console.info('Speed Factor')
interpret("./.routeExamples/bicycle.speed_factor.json", tags1);
interpret("./.routeExamples/bicycle.speed_factor.json", tags2);
interpret("./.routeExamples/bicycle.speed_factor.json", tags3);
interpret("./.routeExamples/bicycle.speed_factor.json", tags4);
console.log('*******************')
export default interpret;

View file

@ -1,18 +0,0 @@
const interpret = (definitionFile, tags) => {
}
const example = interpret({
"highway": {
"residential": 1.2
}
},
{
"highway": "residential"
})
console.log("Expect", 1.2, "got", example)

11998
javascript/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

35
javascript/package.json Normal file
View file

@ -0,0 +1,35 @@
{
"name": "js-interpreter",
"version": "1.0.0",
"description": "Calculate bicycle route stress score to display on MapComplete/OSM.",
"main": "index.js",
"jest": {
"transform": {
"\\.[jt]sx?$": "babel-jest"
}
},
"scripts": {
"babel": "./node_modules/.bin/babel src -d dist",
"test": "jest"
},
"keywords": [],
"author": "Pieter Vander Vennet, Charlotte Delvaux",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.14.6",
"@babel/node": "^7.14.7",
"@babel/plugin-transform-modules-commonjs": "^7.14.5",
"@babel/preset-env": "^7.14.7",
"@babel/preset-typescript": "^7.14.5",
"@types/jest": "^26.0.24",
"@types/node": "^16.3.1",
"babel-jest": "^27.0.6",
"babel-plugin-add-module-exports": "^1.0.4",
"babel-preset-es2015": "^6.24.1",
"jest": "^27.0.6",
"ts-node": "^10.1.0"
},
"dependencies": {
"babel-polyfill": "^6.26.0"
}
}