Add RuleSet for comfort, safety & speed factor
This commit is contained in:
parent
12fc26fa44
commit
a751490e4b
9 changed files with 12234 additions and 18 deletions
17
javascript/.babelrc
Normal file
17
javascript/.babelrc
Normal 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
1
javascript/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Interpret rules and tags to calculate bicycle route scores
|
90
javascript/RuleSet.mjs
Normal file
90
javascript/RuleSet.mjs
Normal 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;
|
1
javascript/__tests__/ruleSetTests.js
Normal file
1
javascript/__tests__/ruleSetTests.js
Normal file
|
@ -0,0 +1 @@
|
|||
const RuleSet = require('../RuleSet.mjs');
|
4
javascript/index.js
Normal file
4
javascript/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import interpret from "./interpret.mjs";
|
||||
import RuleSet from "./RuleSet.mjs";
|
||||
|
||||
export { interpret, RuleSet };
|
88
javascript/interpret.mjs
Normal file
88
javascript/interpret.mjs
Normal 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;
|
|
@ -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
11998
javascript/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
35
javascript/package.json
Normal file
35
javascript/package.json
Normal 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"
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue